Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.qoder.com/llms.txt

Use this file to discover all available pages before exploring further.

By default, query() launches the bundled qodercli locally. Pass options.experimentalCloudAgent and the SDK switches to the Qoder Cloud Agent runtime instead — the agent and session run in a Qoder Cloud container, while the local process only sends requests and consumes the SSE event stream.
Status: experimental / unstable. The API shape may change between minor versions; do not depend on unreleased fields in production code paths.

When to use

  • You don’t want to manage qodercli, the bundled binary, or a local runtime
  • You need a long-lived agent reused across machines (the agent is persisted in the Cloud)
  • You want session context to live in the Cloud so multiple processes / hosts can resume it
Local-CLI-only capabilities — mcpServers / settings / hooks / plugins / local permissions / checkpoint — are not available under the Cloud runtime; passing any of them throws synchronously.

Prerequisites

  • Personal Access Token (PAT): generated at qoder.com/account/integrations; see SDK Authentication. The Cloud runtime accepts only accessToken() / accessTokenFromEnv(). Passing qodercliAuth() / jobToken() throws synchronously.
  • Cloud environment_id: required when creating a session. Get it from the Qoder console or the management API.
export QODER_PERSONAL_ACCESS_TOKEN="<your-pat>"
export QODER_CLOUD_AGENT_ENVIRONMENT_ID="<your-env-id>"

First call: create agent + create session

The most common entry path — create a new Cloud Agent and immediately open a session for it to run a prompt:
import { accessTokenFromEnv, query } from '@qoder-ai/qoder-agent-sdk';

const q = query({
  prompt: 'Summarize this repository in one short paragraph.',
  options: {
    auth: accessTokenFromEnv(),
    experimentalCloudAgent: {
      agent: {
        create: {
          name: 'my-cloud-agent',
          model: 'ultimate',
          system: 'You are a concise code assistant.',
          tools: [
            {
              type: 'agent_toolset_20260401',
              enabled_tools: ['read', 'glob', 'grep'],
            },
          ],
        },
      },
      session: {
        create: {
          environment_id: process.env.QODER_CLOUD_AGENT_ENVIRONMENT_ID!,
          title: 'first-cloud-session',
        },
      },
    },
  },
});

for await (const msg of q) {
  if (msg.type === 'result') {
    console.log('done:', msg.subtype, msg.result);
  }
}
When the turn finishes, read session_id from the final result message — later turns use it to resume the same session (see Multi-turn: resuming a session).

Built-in tool allowlist

tools[].enabled_tools currently supports: bash, write, glob, web_fetch, read, edit, grep, web_search. Omit tools to give the agent no tools.

Mounting files into a session

After uploading a file via the Files API, mount it into the session container with session.create.resources:
session: {
  create: {
    environment_id,
    resources: [
      { type: 'file', file_id: 'file_abc123', path: '/workspace/data.json' },
    ],
  },
}

Reusing an existing agent

If you already have an agent.id (created via the console or a previous call), pass agent: { id } and skip create:
experimentalCloudAgent: {
  agent: { id: 'agent_xxx' },
  session: { create: { environment_id } },
}

Multi-turn: resuming a session

Once you have a session_id from the first turn, the next call passes only session: { id } — do not include agent. The session already binds an agent, and combining the two throws synchronously.
// Turn 1: create agent + session
const first = query({
  prompt: 'My favorite color is teal. Reply with: noted.',
  options: {
    auth: accessTokenFromEnv(),
    experimentalCloudAgent: {
      agent: { create: { name: 'demo', model: 'ultimate' } },
      session: { create: { environment_id } },
    },
  },
});

let sessionId: string | undefined;
for await (const msg of first) {
  if (msg.type === 'result') sessionId = msg.session_id;
}

// Turn 2: continue the same Cloud session
const second = query({
  prompt: 'What is my favorite color?',
  options: {
    auth: accessTokenFromEnv(),
    experimentalCloudAgent: {
      session: { id: sessionId! },
    },
  },
});

for await (const msg of second) {
  if (msg.type === 'result') console.log(msg.result);  // → "teal"
}
Session context lives in the Cloud, so the script can restart or move between machines between turns — as long as you have the session_id, you can resume.

Consuming SSE events

The Cloud runtime streams session events back over SSE. The SDK wraps each event as a cloud_agent_event message:
for await (const msg of q) {
  if (msg.type === 'cloud_agent_event') {
    console.log(msg.event, msg.data);   // e.g. "user.message", "agent.message", "session.status_idle"
  } else if (msg.type === 'result') {
    // SDK synthesizes a result after receiving session.status_idle for the current turn
    console.log('turn end:', msg.subtype);
  }
}
Event shape:
FieldDescription
eventCloud event name (e.g. user.message, agent.message, session.status_idle)
idEvent ID in the SSE stream; usable as a replay anchor
dataCloud event payload (includes turn_id and other fields)
session_idCloud session ID this event belongs to

History replay isolation

When resuming an existing session, the SSE stream first replays history events. The SDK isolates by turn_id: only the current turn’s session.status_idle triggers the result terminal — historical events will not end your query early.

SSE tuning

experimentalCloudAgent: {
  session: { id: sessionId },
  stream: {
    afterId: 'evt_xxx',         // start replay after this event ID
    deltaFlushIntervalMs: 250,  // delta merge / flush interval (SDK default if omitted)
  },
}

Abnormal close

If SSE disconnects before the current turn reaches a terminal event, the SDK synthesizes an error result (subtype !== 'success', is_error: true) so callers can handle it uniformly.

The result terminal

FieldDescription
subtypesuccess or an error subtype
is_errorBoolean; whether the turn ended abnormally
session_idCloud session ID (backfilled by the SDK on the create branch)
resultAgent’s text reply for the turn (multiple text blocks are concatenated)
usage / modelUsage / total_cost_usdBackfilled from the current turn’s span.model_request_end.usage

Constraints at a glance

  • agent and session each have their own id / create union — they are mutually exclusive (enforced by the TypeScript types).
  • When passing an existing session.id, do not also pass agent.
  • session.create must include environment_id explicitly.
  • The Cloud runtime rejects local-CLI-only top-level options: model, agent, mcpServers, settings, hooks, plugins, permissionMode, etc. Passing any of them throws synchronously.
  • The Cloud runtime does not support q.setModel(), q.reloadPlugins(), MCP OAuth, etc. Only for await consumption and q.close() are guaranteed.

Error codes

codeWhen it triggers
cloud_agent_auth_requires_access_tokenNon-PAT auth was used (e.g. qodercliAuth() / jobToken())
cloud_agent_api_errorCloud OpenAPI returned non-2xx, or the SSE channel failed