Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/agent-sessions/agents.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ At every rung the agent is just a small directory — at most `agent.toml` + `pr

## Session-pinned config

When a session is created it **freezes** the agent's **active [revision](/agent-sessions/revisions)** — `prompt`, `model`, `runtime`, and `skills` — into the session, and pins the resolved credential too. Editing the named agent afterwards never changes a running or resumed session — its behavior is pinned for its whole life. Update an agent freely: existing sessions keep what they started with, new sessions pick up the change. (Rotating a credential's **key value** is the exception — that flows to running sessions, so a compromised key can be replaced; see [credentials](/agent-sessions/credentials).)
When a session is created it **freezes** the agent's **active [revision](/agent-sessions/revisions)** — `prompt`, `model`, `runtime`, and `skills` — into the session, and pins the resolved credential too. (A session can start with a one-off [`model`](/agent-sessions/sessions#model) override in place of the agent's, pinned the same way for that session's life.) Editing the named agent afterwards never changes a running or resumed session — its behavior is pinned for its whole life. Update an agent freely: existing sessions keep what they started with, new sessions pick up the change. (Rotating a credential's **key value** is the exception — that flows to running sessions, so a compromised key can be replaced; see [credentials](/agent-sessions/credentials).)

## Model

Expand Down
1 change: 1 addition & 0 deletions docs/agent-sessions/api-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ oc agent deploy # deploy the agent directory in the cwd

- `input` — the task (string or [envelope](/agent-sessions/messaging#message-input)). Truncate large inputs; let the agent fetch the rest via [sandbox tools](/agent-sessions/runtime-tools).
- `revision?` — pin a specific revision (number / `rev_…`); default = the agent's active one. Use to test a [staged](/agent-sessions/revisions#staging-vs-activating) revision.
- `model?` — run this session on a different model than the agent's (same `provider/model` form; its provider must match the agent's [runtime](/agent-sessions/runtimes), e.g. `anthropic/…` for `claude`). Default = the agent's model. Pinned for the session's life like the rest of the snapshot. Rejected (`400 invalid`) for [flue](/agent-sessions/flue) agents — their model is fixed in the deployed artifact.
- `key?` — get-or-create (one session per key). Keyless: an `Idempotency-Key` header makes create retry-safe.
- `metadata?` — opaque routing state (≤ 16 KB); on get/list + **verbatim in webhooks**; never sent to the model, not indexed.
- `limits?` `{ tokens, turn_seconds, turns }` — non-monetary; tripping one ends the turn (matching `yield_reason`).
Expand Down
2 changes: 1 addition & 1 deletion docs/agent-sessions/flue.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ isn't in the artifact.
| Sandbox | You pick one (`local()`, containers, …) | **Leave `sandbox:` unset** — the session's workspace sandbox is supplied (build error) |
| Persistence | `db.ts` (or volatile in-memory default) | **No `db.ts`** — the conversation is stored durably in the session's state; survives restarts and hibernation (build error) |
| Model credentials | Env keys or `registerProvider` in code | **No keys anywhere in the app** — the deploy scans the artifact for key-shaped strings and fails on a hit. Managed billing or an [OC credential](/agent-sessions/credentials); routing is injected at run time |
| Model choice | In code | Still in code — but declared three times total (`defineAgent`, `agent.toml`, the OC agent) and all three **must be equal**; the deploy rejects divergence |
| Model choice | In code | Still in code — but declared three times total (`defineAgent`, `agent.toml`, the OC agent) and all three **must be equal**; the deploy rejects divergence. Fixed for good at deploy: a [session](/agent-sessions/sessions#model) **can't override** a flue agent's model — passing `model` on a flue session is rejected |
| Skills | Packaged imports (`with {type:'skill'}`) or workspace files | **`src/skills/<name>/SKILL.md`** — ships with every deploy. Packaged imports are a build error (for now) |
| Asking the user | No human-in-the-loop primitive | An **`ask` tool** is added: the session yields `needs_input` and hibernates until the user replies |
| Channels / workflows | Slack/Discord ingress, `defineWorkflow` | Not supported — inbound is session messages + [GitHub watches](/agent-sessions/watches) |
Expand Down
2 changes: 1 addition & 1 deletion docs/agent-sessions/runtimes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ A **runtime** executes the agent loop: provider SDK, model calls, and tool use.
| `pi` | `anthropic/…` today — the runtime is provider-agnostic by construction; more providers land next | Managed, or an Anthropic key |
| `flue` <sup>experimental</sup> | `anthropic/…` today — bring your own [Flue](/agent-sessions/flue) app | Managed, or an Anthropic key |

`model` is passed straight through to the provider, so any model that provider serves works — only the **`provider/` prefix** is validated against the runtime (a bad prefix is rejected at agent create, a model the provider doesn't recognize fails on the first turn). Common choices:
`model` is passed straight through to the provider, so any model that provider serves works — only the **`provider/` prefix** is validated against the runtime (a bad prefix is rejected at agent create, a model the provider doesn't recognize fails on the first turn). A [session](/agent-sessions/sessions#model) can override this per run — pass `model` at create to run one session on a different model than its agent, validated the same way (brain-box runtimes `claude`/`codex`/`pi`; not `flue`, which pins its model in the artifact). Common choices:

- **`claude`:** `anthropic/claude-sonnet-5` (default, balanced), `anthropic/claude-opus-4-8` (most capable), `anthropic/claude-fable-5`, `anthropic/claude-haiku-4-5` (fastest/cheapest).
- **`codex`:** `openai/gpt-5-codex`.
Expand Down
32 changes: 31 additions & 1 deletion docs/agent-sessions/sessions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ A **session** is an [agent](/agent-sessions/agents) run with an append-only [eve

## Session flow

1. You create a session from an [agent](/agent-sessions/agents). It **pins the agent's active [revision](/agent-sessions/revisions)** (prompt / model / skills) for its whole life — editing the agent later never affects a running session. Pass `revision` to pin a specific one instead (e.g. to test a [staged](/agent-sessions/revisions#staging-vs-activating) revision before promoting). The model runs **Managed** (billed to your credits, no key — the default) or on a [model credential](/agent-sessions/credentials) you supply. Pass [`sources`](/agent-sessions/repos) to check repos out into `/workspace/sources/` before turn 1 — the GitHub token never enters the sandbox. The session starts immediately.
1. You create a session from an [agent](/agent-sessions/agents). It **pins the agent's active [revision](/agent-sessions/revisions)** (prompt / model / skills) for its whole life — editing the agent later never affects a running session. Pass `revision` to pin a specific one instead (e.g. to test a [staged](/agent-sessions/revisions#staging-vs-activating) revision before promoting). Pass `model` to run this one session on a different model than the agent's — same `provider/model` form, and it must resolve to the agent's runtime's provider ([flue](/agent-sessions/flue) agents don't take an override; their model is fixed in the artifact). The model runs **Managed** (billed to your credits, no key — the default) or on a [model credential](/agent-sessions/credentials) you supply. Pass [`sources`](/agent-sessions/repos) to check repos out into `/workspace/sources/` before turn 1 — the GitHub token never enters the sandbox. The session starts immediately.
2. The [runtime](/agent-sessions/runtimes) executes the agent in a sandbox, appending [events](/agent-sessions/events) to the log as it works.
3. With nothing left to do, the session goes **idle** and the sandbox **hibernates**.
4. A [steer](/agent-sessions/messaging) message wakes it; it resumes with its prior context. Repeat.
Expand Down Expand Up @@ -83,6 +83,36 @@ Three independent knobs — don't overload one for another:

Cap a session at create (or default it on the [agent](/agent-sessions/agents)) with `limits: { tokens, turn_seconds, turns }` — a token budget, per-turn wall-clock, and auto-run count. These are runtime limits, not spend controls; model usage is billed to your provider key (BYO) or to your OpenComputer credits (Managed). Hitting one ends the turn with the matching `yield_reason`.

## Model

A session runs the model pinned in its agent snapshot. Pass `model` at create to run **this one session** on a different model — omit it and the session inherits the agent's model (the default). The override is the same `provider/model` form as the agent's model, and its provider must match the agent's [runtime](/agent-sessions/runtimes) (e.g. `anthropic/…` for a `claude` agent — a mismatched prefix is rejected at create, a model the provider doesn't recognize fails on the [first turn](/agent-sessions/runtimes)). Like the rest of the snapshot it's **pinned for the session's life** — there's no mid-session model switch; start another session to run another model.

Not available for [flue](/agent-sessions/flue) agents: their model is fixed in the deployed artifact (the [model triangle](/agent-sessions/flue)), so a `model` on a flue session is rejected.

<CodeGroup>

```ts TypeScript SDK
const session = await oc.sessions.create({
agent: "agt_...",
input: "Summarize the incident channel and draft a postmortem.",
model: "anthropic/claude-sonnet-5", // this session only; omit to use the agent's model
});
```

```http REST API
POST https://api.opencomputer.dev/v3/sessions
Authorization: Bearer $OPENCOMPUTER_API_KEY
Content-Type: application/json

{
"agent": "agt_...",
"input": "Summarize the incident channel and draft a postmortem.",
"model": "anthropic/claude-sonnet-5"
}
```

</CodeGroup>

## Stop a session

`POST /sessions/:id/cancel` **requests** a cooperative stop: the turn ends at its next checkpoint with a `turn.completed` (`yield_reason: "canceled"`), so an in-flight model call may run to its timeout — billing for that turn stops when it actually exits, not the instant you call. `POST /sessions/:id/archive` cancels any active turn, then closes the session read-only.
Expand Down
4 changes: 2 additions & 2 deletions sdks/typescript/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion sdks/typescript/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@opencomputer/sdk",
"version": "0.11.2",
"version": "0.12.0",
"description": "TypeScript SDK for OpenComputer - cloud sandbox platform",
"type": "module",
"main": "dist/index.js",
Expand Down
7 changes: 7 additions & 0 deletions sdks/typescript/src/agents/sessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ export interface CreateSessionParams {
* test a staged revision before promoting it. Defaults to the active revision.
*/
revision?: number | string;
/**
* Override the agent's model for this session — the session runs this model instead of the
* one pinned by the agent's revision. Same `provider/model` form as the agent's model, and
* must resolve to the agent's runtime's provider. Defaults to the agent's model. Not
* supported for `flue` agents (their model is fixed in the deployed artifact).
*/
model?: string;
/** get-or-create idempotency/routing key — one session per key. */
key?: string;
webhook?: string;
Expand Down
3 changes: 3 additions & 0 deletions web/src/api/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,9 @@ export const createSession = (
body: {
agent: string
input: string
// Optional per-session model override — runs this model instead of the agent's.
// Omit to inherit the agent's model. Not supported for flue agents.
model?: string
sources?: { repo: string; ref: string; name?: string }[]
},
idempotencyKey?: string,
Expand Down
32 changes: 32 additions & 0 deletions web/src/pages/Sessions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
DialogTitle,
} from '@/components/ui/dialog'
import { Field, Label, Select } from '@/components/form'
import { Input } from '@/components/ui/input'
import {
WorkingRepoField,
type WorkingRepo,
Expand Down Expand Up @@ -45,12 +46,20 @@ export default function Sessions() {
const [showStart, setShowStart] = useState(false)
const [agentId, setAgentId] = useState('')
const [message, setMessage] = useState('')
// Optional per-session model override — empty inherits the agent's model
// (cleared when the agent changes, since a model is runtime-specific).
const [model, setModel] = useState('')
// Optional working repo — explicitly chosen, per-agent (cleared when the agent changes).
const [workingRepo, setWorkingRepo] = useState<WorkingRepo | null>(null)

const selectedAgent = agents?.find((a) => a.id === agentId)
// flue agents pin their model in the deployed artifact — no per-session override.
const isFlueAgent = selectedAgent?.runtime === 'flue'

const openStart = () => {
setAgentId(agents?.[0]?.id ?? '')
setMessage('')
setModel('')
setWorkingRepo(null)
setShowStart(true)
}
Expand All @@ -61,6 +70,8 @@ export default function Sessions() {
{
agent: agentId,
input: message.trim(),
// Only send a model when overridden — omitting it inherits the agent's.
...(model.trim() && !isFlueAgent ? { model: model.trim() } : {}),
...(workingRepo
? { sources: [{ repo: workingRepo.repo, ref: workingRepo.ref }] }
: {}),
Expand Down Expand Up @@ -217,6 +228,7 @@ export default function Sessions() {
value={agentId}
onValueChange={(v) => {
setAgentId(v)
setModel('') // a model override is per-agent (runtime-specific)
setWorkingRepo(null) // a working repo is per-agent
}}
options={(agents ?? []).map((a) => ({
Expand All @@ -243,6 +255,26 @@ export default function Sessions() {
className="min-h-24"
/>
</Field>
{agentId && !isFlueAgent ? (
<Field
label="Model"
htmlFor="start-model"
description={
selectedAgent?.model
? `Optional — overrides just this session. Defaults to the agent's model (${selectedAgent.model}).`
: "Optional — overrides just this session. Defaults to the agent's model."
}
>
<Input
id="start-model"
value={model}
onChange={(e) => setModel(e.target.value)}
placeholder={selectedAgent?.model ?? 'provider/model'}
autoComplete="off"
spellCheck={false}
/>
</Field>
) : null}
{agentId ? (
<WorkingRepoField
value={workingRepo}
Expand Down