{"info":{"title":"intentic sandbox daemon","version":"1","description":"Every call an intentic sandbox daemon answers, generated from the wire contract the daemon and its browser client both import.\n\nThe daemon runs beside your code. There is no shared server and no central API: the address below is your sandbox and nobody else's, which is also why the playground on this site answers from a simulation in your own tab rather than by calling anything.\n\nTwo conventions cover the whole surface. Input rides in the path and query for a `GET` and in a JSON body otherwise. A failure comes back as a JSON object with a `message`, never as an empty body — a refusal is a result, not a crash.\n\nOne route is open: `GET /health`. It is not in this document because it is not part of the contract — it exists so a script can tell a live sandbox from a dead port, and it deliberately checks nothing.","license":{"name":"MIT","identifier":"MIT"}},"servers":[{"url":"{sandbox}","description":"Your own sandbox. In a browser signed in to the workspace this is the address in the bar.","variables":{"sandbox":{"default":"http://localhost:39247","description":"The sandbox's base URL: its platform hostname, or the loopback listener on your own machine."}}}],"security":[{"session":[]},{"control":[]}],"tags":[{"name":"One agent","description":"The surface most callers are here for. Starting a turn answers with a run id and nothing else: the work happens inside the sandbox whether or not anybody stays connected, and attaching is how you watch it, from the beginning or from wherever you had got to. The rest is the things a person does to a turn in flight — answer a question it asked, redirect it, stop it, or put the whole conversation back to an earlier point. Everything here addresses one conversation by id."},{"name":"The fleet","description":"The roster rather than the turn. Each conversation works in its own private copy of the repos, so it has a cumulative set of changes you can read and two ways for it to end: merge that work into the shared tree, or throw it away. The rest is the bookkeeping a board needs — renaming, marking read, archiving, purging."},{"name":"Past sessions","description":"Two reads. The list of past conversations, and one conversation's full record by id."},{"name":"Workflows","description":"A workflow is a design: run these conversations, in this order, each handing its result to the next. These routes hold the saved designs and the run history, start a run, stop one in flight, and archive the ones you are done reading."},{"name":"Loops","description":"A loop repeats a conversation towards a goal until it converges or gives up. One half is what is running right now; the other is the designs somebody authored once and can point at a different job each time."},{"name":"Automations","description":"Scheduled and triggered work: what can trigger one here, what is configured, and switching one on or off, deleting it or firing it by hand. The other half is the approval queue — an automation set to ask first lands there each time it would have run."},{"name":"Workspace","description":"Everything under the workspace root. The tree and one folder's contents, a window of a file's text, search that blends text, structure, meaning and history, and the ordinary changes: make a folder, delete, move, copy. It also holds what a workspace knows about itself — which repos it contains, how its packages depend on each other, which apps are in it, and starting or stopping one."},{"name":"Git","description":"The largest group here, and the shape is consistent: the repo rides in the address and the verb is the route. Reading is history, differences and status; writing is staging, committing, branching, tagging, stashing, cherry-picking, reverting and pushing. Two routes cover a repo caught mid-merge or mid-rebase, and one answers what undoing would actually do before you undo it."},{"name":"Diffs","description":"What a diff's two sides are when they are not lines of text. A document, spreadsheet, presentation or notebook that changed is rendered to markdown on both sides — the same reading an agent gets instead of the bytes — so the change shows as tracked changes rather than two downloads. The side on disk reuses the shadow the sandbox already keeps; a past version is rendered from its bytes and kept by content hash, so the same version is never rendered twice. A side nothing can read answers with the reason instead. The bytes themselves are not here: that route streams a body and so sits outside this contract."},{"name":"History","description":"The points the sandbox saves as work happens, what changed between one and the next, one file's two sides, and restoring from one."},{"name":"Chores","description":"Maintenance evidence: read the measurements, ask for one to be retaken, record what somebody concluded. There is deliberately no \"run this chore\" route, because a chore run is an ordinary conversation and so already has its own working copy, record and cost."},{"name":"Panels","description":"One entry per repo, with whether its preview server is up and what the app worked out about its contents. Starting and stopping are here; watching the output is the terminal's job."},{"name":"Ports","description":"The ports something is answering on, and giving one an address on the outside or taking that away."},{"name":"Areas","description":"An area is a name over a set of folders, and it is the unit access is handed out in: widening what a team sees is one edit here rather than one edit per person. Reading them is anybody's, since a fenced person is shown the name of the fence they are behind; writing one changes who sees what on their next request, so both writes are the sandbox owner's. An area people still hold cannot be deleted, because a grant pointing at nothing is a fence with no answer."},{"name":"Personas","description":"A persona records a decision about accounts that already exist: which of them this character speaks for, what a conversation wearing it may do, and what it is told. The kit routes edit the files behind one — its own instructions, and the skills only its conversations reach."},{"name":"Skills","description":"What is available and whether each is on, the text of one, and writing, switching or deleting one of your own. The list joins every separate source into a single answer."},{"name":"Extensions","description":"The runtime half of the extension format documented under Developers. These routes enumerate what is installed, read and write each one's settings, switch it on or off, check for updates and apply or undo one, and start or stop the long-running processes an extension declares."},{"name":"Settings","description":"Read and write the settings that govern how agents behave here. The other three routes are read-only reports on their effects: what the token-saving measures were actually worth, the text behind a built-in prompt, and when each rule last did something."},{"name":"Safety policy","description":"Settings next door are read by a parser; this one is read by a model. The policy is prose about which of the things an agent may already do are worth interrupting you about, and the two policy routes read and replace it whole. The third is the log every verdict lands in, including the ones nobody was interrupted for, and it is what makes the document writable: an owner can only author a rule for behaviour they can see."},{"name":"Privacy shield","description":"The shield replaces names, numbers and other personal data with tokens before a request reaches a provider you have not trusted, and puts the real values back on the way out. These routes read its state and replace its policy, which only the owner may do; read the log of what it masked; look a word up in the name dictionary; and list, teach and forget the datasets of known values it masks wherever they appear."},{"name":"Capabilities","description":"A capability is a system the agent can reach: a forge account, a chat server, a database, one of your own machines. These routes connect and disconnect them, carry the credential each needs, report whether a connection is live, and drive the interactive parts of a sign-in, including a one-time code."},{"name":"Needs","description":"An agent raises a need when a task stops on something only you can give: a capability, a credential, a change to its environment. These routes raise one and list an agent's own, list what is open for you, answer one or hand over its secret, withdraw one, and read or revoke the standing grants an answer left behind."},{"name":"Secrets","description":"Write a secret, list which names exist, delete one. Revealing a value is the deliberate exception and the only route that hands one back; everywhere else the daemon substitutes a secret by reference at the moment a command runs."},{"name":"VPN","description":"What is configured, dialling and dropping one, and reading connections out of an exported client configuration. Link state is read back from the operating system, not from memory."},{"name":"Network disks","description":"What is configured, mounting and unmounting one. Mount state is read back from the kernel's mount table, not from memory, and a disk added as read-only is mounted read-only."},{"name":"Exit locations","description":"Which countries a provider offers, bringing an exit up, moving it, taking a fresh address in the same country, and checking where the world actually sees you. That last check is what the others are judged against: a switch that quietly left traffic where it was is the failure this exists to rule out."},{"name":"Inventory","description":"The entries in the workspace's deployment configuration. Adding or removing one rewrites that file and commits it, exactly as an agent editing it by hand would, and answers with the whole updated list so a caller redraws from one response."},{"name":"Platform CLI","description":"Runs the sandbox's own command-line tool and streams its output line by line. The reconcile is separated out because it takes minutes: it starts a background job and answers at once, and its event stream replays from the beginning and then follows live, so a page refresh does not lose the progress."},{"name":"Accounts","description":"One route family for every provider whose credential lives in this daemon's own auth tree — sign in, finish or abandon a sign-in, list what is connected with how full each account's limits were, rename one, disconnect one. The provider is a parameter rather than a group of its own because the operations are the same six for all of them; what differs is each provider's mechanism, which its own module declares. The translator group next door is the other shape of the same idea, for subscriptions a proxy holds and re-serves. No answer here can carry a credential: the account rows have no field one could ride in, and a sign-in's proof never leaves the sandbox."},{"name":"Routed providers","description":"The bundled translator runs a non-Claude model on the user's own subscription, so each provider connects by signing in rather than with an API key, and one provider can hold several accounts at once. Two sign-in shapes ride these routes: a code typed on a device page, which finishes by itself, and a redirect whose landing address is handed back."},{"name":"Endpoints","description":"Every built-in provider's catalogue is one fixed route, because there is one of each. Endpoints are user-created and unbounded, so the id rides in the address and the answer is whatever the configured server says about itself. The trial belongs here because the trial is an endpoint — the one the daemon provisions rather than you."},{"name":"Providers","description":"One read, for the providers the sandbox ships with. Never empty, and left in the provider's own preference order rather than rearranged."},{"name":"Usage","description":"One read: the spending record over a range of days, grouped finely enough that every cost screen is a rearrangement of it rather than a second call."},{"name":"Pipelines","description":"Read the runs and the jobs inside them, re-run or cancel one, and mark the board read. The interesting one hands a failing run to an agent rather than to you."},{"name":"Offloaded work","description":"Heavy commands are sorted into kinds (tests, typechecks, verify…), and the owner can send each kind to a runner on one of their machines instead of running it here. The sandbox's own `offload-run` command drives the run: it asks whether the runner can take the work, hands it a snapshot of the code and streams the output back, ending with the exit code and every file the command changed. The rest lists the kinds and the recent runs."},{"name":"Outbox","description":"What the outbox holds and its address. Publishing copies a workspace file or folder in; withdrawing the last one removes the outbox, so its existing always means exactly \"something is published\". There is no route to read a published file back, because that is what the open address is for."},{"name":"Sharing","description":"What is currently shared, publishing a conversation, re-rendering one from how it stands now, and taking it down. As with the outbox, the page itself is the read."},{"name":"Approvals","description":"The owner's side of the queue: posts to publish, actions to carry out. The agent writes the files directly; this is the inbox, the one call that covers approving, editing and retrying, and the deletion that is a rejection."},{"name":"Issues","description":"The owner's side of the bug intake, and the counterpart to the pipelines group: an inbox of failures with one route that hands a failure to an agent rather than to you. Reports arrive at the public /intake/… endpoints from the reporter embedded on your sites — a separate prefix precisely because any browser on the internet can reach those and none of these — and the daemon groups them by fingerprint, so a crash that hit a thousand browsers is one row with a count. These routes read that inbox, move a row between open, resolved and ignored, start a turn on one, throw one away, and answer which sites have actually loaded the reporter. Nothing here creates an issue."},{"name":"System","description":"The group with the widest job. The identity read is what the daemon says it is, including the routes it implements, which is the one call that tells a newer client what this sandbox can do. The event stream is the sandbox-wide live feed. The rest is the machinery an agent leaves running: terminals and their history, browsers, and the records of helpers it delegated to."},{"name":"Activity","description":"Read-only by design. Entries are written by the sandbox alone and never by a caller, which is the whole reason the record can be trusted."},{"name":"Logs","description":"What log files exist, and a window of one. Captured terminal output, command runs, and the daemon's own log."},{"name":"Push notifications","description":"What a device needs in order to subscribe, subscribing and unsubscribing, and a test. The test earns its place because there are four separate places a notification can be lost that nobody can inspect from outside."}],"openapi":"3.1.1","paths":{"/agent":{"post":{"operationId":"agent.run","summary":"Say something to an agent","description":"Answers at once with what became of the message: it starts a turn when the conversation is free, is said into the running turn where that turn takes words mid-way, and otherwise waits in the conversation's queue for the next turn, where every window sees it. The work runs inside the sandbox whether or not anybody stays connected; watch it by attaching. Naming a conversation that does not exist yet opens it. Give the message an id, and sending it again after a lost answer is met with what became of it the first time rather than a second delivery.","tags":["One agent"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string","description":"What to say to the agent. May be empty if you are only attaching files."},"errand":{"description":"What the words are for, when the app or the sandbox composed them rather than a person typing them: a land conflict to resolve, a failed CI run to fix. Shown as the sandbox's words, not yours. Leave it out for your own words.","type":"string","enum":["land-conflict","verify-nudge","land-breakage","land-fix","land-fix-nudge","land-held","push-fix","push-fix-nudge","ci-fix","ci-fix-nudge"]},"messageId":{"description":"Your id for this message. Sending again under an id the sandbox already took is answered with what it did with it the first time, never a second delivery. Leave it out and the sandbox names the message itself.","type":"string","minLength":1,"maxLength":128},"title":{"description":"A title for a conversation this turn is opening. Ignored for a conversation that already has one.","type":"string","maxLength":80},"attachments":{"description":"Files to hand the agent along with the prompt, as workspace paths. Upload them first.","maxItems":20,"type":"array","items":{"type":"string","minLength":1}},"mentions":{"description":"Workspace paths the prompt mentions with `@`. Unlike attachments, one that escapes the workspace or names no file is ignored rather than refused.","maxItems":20,"type":"array","items":{"type":"string","minLength":1}},"agent":{"description":"Which model provider serves this turn. Leave it out for Claude.","type":"string","minLength":1},"harness":{"description":"Which agentic loop runs the turn. Leave it out to use each provider's own.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which of that provider's connected accounts pays for the turn. Leave it out to continue on the account the conversation runs on, or, for its first turn on this provider, to take whichever account can serve with the most room. To move a running conversation, use `switchAccount`.","type":"string"},"actsAs":{"description":"Which persona the turn speaks as out in the world. Not the same as which account pays for it.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"sessionId":{"description":"Resume this provider session instead of starting a fresh one.","type":"string"},"conversationId":{"description":"The conversation this turn belongs to. You choose it, it survives model switches, and it is how you address the conversation later. Naming one that does not exist opens it.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"isolated":{"description":"Work in this conversation's own private copy of the repos rather than the shared tree, so several agents can work at once. Needs a conversation id.","type":"boolean"},"startIn":{"description":"Which folder the conversation opens in, relative to the workspace root; the project it belongs to. Decided on the first turn. A persona that names its own start folder wins.","type":"string","maxLength":200},"placement":{"description":"Where this conversation runs: this sandbox (leave it out), or a paired runner by id. Decided on the first turn; later turns follow the conversation.","anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"local"}},"required":["kind"]},{"type":"object","properties":{"kind":{"type":"string","const":"runner"},"id":{"type":"string","minLength":1}},"required":["kind","id"]}]},"worktreeBase":{"description":"Pin a new private copy to these exact commits instead of today's workspace. Used when several agents must start from identical files.","minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"repo":{"type":"string"},"base":{"type":"string","minLength":1}},"required":["repo","base"]}},"autoLand":{"description":"Whether this turn's work merges into the workspace when it finishes. Overrides the conversation's own setting for this turn only.","type":"boolean"},"runRole":{"description":"What started this turn, when it was not a person typing: which of the sandbox's per-job model lists answers for it. Only used when the turn names no model of its own.","type":"string","enum":["commit-message","session-title","safety-judge","loop-verdict","model-router","pipeline-fix","deployment-fix","maintenance-chore","documentation-run","acceptance-run","approval-queue","extension-review","loop-iteration"]},"origin":{"description":"Set by the sandbox alone: this turn opened a conversation on behalf of a message from outside rather than a person.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"]},"forkOf":{"description":"Where this conversation was cut from, on its first turn only. Only the client knows this, so only the client can say it.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$","description":"The conversation this one was cut from."},"keep":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"How many of that conversation's messages to copy in before this turn runs."},"files":{"type":"string","enum":["then","now"],"description":"Which files the fork opens on: \"now\" is the workspace as it stands, \"then\" is the files as they were at the cut, which needs a private copy."}},"required":["conversationId","keep","files"]},"model":{"description":"Which model to use. Leave it out for the provider's default.","type":"string"},"unattended":{"description":"Nobody is watching this turn: a schedule, a queue or another agent started it and no chat is open on it. A card that needs a person is refused rather than raised, plan mode and the terminal hand-off are withheld, and the sandbox's signed-in accounts stay out of it unless a persona carries them.","type":"boolean"},"outsideWake":{"description":"Content from outside caused this turn, and what to call the source. It is what makes the sandbox treat the turn as carrying somebody else's words.","type":"string","minLength":1},"permissionMode":{"description":"How tool calls are gated: ask before each tool, propose a plan first, or run everything. The agent can move itself between these mid-turn.","type":"string","enum":["default","plan","bypassPermissions"]},"allowedTools":{"description":"Narrow the turn to these tools. Leave it out for everything the runtime has. For a turn driven by an outside message this list is the real boundary, because prompt wording is only advice.","type":"array","items":{"type":"string","minLength":1}},"effort":{"description":"How hard the model should think, where the provider offers a choice.","type":"string"},"thinking":{"description":"Whether to show the model's reasoning as it works.","type":"boolean"},"fast":{"description":"Ask for the same work at a higher rate for a higher price. A request rather than a promise: the answer says what actually happened.","type":"boolean"},"autoPicked":{"description":"Whether this turn's model was chosen for you by reading the conversation's opening message, rather than picked by hand. Recorded so the choice can be judged later against what you did next.","type":"boolean"},"editorContext":{"description":"What the user has open in their editor, folded into the prompt so that pointing words like \"this\" resolve.","type":"object","properties":{"file":{"type":"string","minLength":1,"description":"The file open in the editor, as a workspace path."},"startLine":{"description":"First line of the selection, counting from one. Leave both out when the whole file is the context.","type":"integer","minimum":1,"maximum":9007199254740991},"endLine":{"description":"Last line of the selection, counting from one.","type":"integer","minimum":1,"maximum":9007199254740991},"selection":{"description":"The selected text itself. Cut it down before sending if it is long: this is context, not an upload.","type":"string","maxLength":20000}},"required":["file"]},"sendAt":{"description":"Hold this message until then (epoch milliseconds) instead of starting a turn now: a time you chose, or the reopen of an allowance you know is spent. It waits in the conversation's queue, where it can be sent early, reworded, rescheduled or removed, and goes by itself at that instant, even for a conversation this message opens or one whose turn is running now. Ignored when already past; at most a month ahead.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"sendAfter":{"description":"Hold this message until the conversation named here has finished and all of its work has landed in the workspace, instead of starting a turn now: for work that builds on another agent's. It waits in this conversation's queue, where it can be sent early, reworded, rescheduled or removed. Sent at once when that conversation has nothing running and nothing left to land.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"conversationAutoLand":{"description":"Whether this conversation's finished work merges into the workspace by itself from now on: its own answer to the sandbox-wide setting, the one `agents.autoLand` changes later. Read only from the message that opens the conversation, and only from a maintainer. Unlike `autoLand`, it holds for every later turn.","type":"boolean"},"continues":{"description":"Carry the conversation on from where its last turn stopped instead of saying anything: nothing of yours is added to the conversation, and `prompt` is ignored. A turn the sandbox still holds runs again as it was; otherwise the agent is told to continue in its own session. Refused while a turn is running.","type":"boolean","const":true}},"required":["prompt"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"delivered":{"type":"string","enum":["started","steered","queued"],"description":"What became of the message: it started a turn, it was said into the turn already running, or it waits in the conversation's queue for the next one."},"run":{"description":"The run the message is in: the turn it started, or the one it was said into. Hand it back when you attach. Absent while the message waits in the queue.","type":"string"},"duplicate":{"description":"The sandbox had already taken a message under this id: this is what became of it, and nothing new happened.","type":"boolean","const":true}},"required":["delivered"],"additionalProperties":false}}}}}}},"/agent/attach":{"post":{"operationId":"agent.attach","summary":"Watch a turn happen","description":"Streams everything the agent does: its words, the tools it reaches for, and the answers it gets. It opens with the turn's transcript whole as it stands, then sends every change as it lands, so a reload or a dropped connection loses nothing: attaching again hands over the whole transcript again. The window that started the turn holds no special claim, and any number of watchers on any number of devices see the same thing.","tags":["One agent"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$","description":"Which conversation to watch."},"run":{"description":"The run you were watching. The stream opens on the conversation's newest run whatever you name: if a newer turn has started since, the head names that one instead, and its rows are that turn's.","type":"string"}},"required":["conversationId"]}}}},"responses":{"200":{"description":"OK","content":{"text/event-stream":{"schema":{"oneOf":[{"type":"object","properties":{"event":{"const":"message"},"data":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"attached","description":"The first frame, identifying the run you have joined and handing you its transcript so far."},"run":{"type":"string","description":"The run's id."},"startedAt":{"type":"number","description":"When it started, in milliseconds, so a window joining late can show how long it has been going."},"seq":{"type":"number","description":"How many frames the run has produced so far. A fact at or below this number is being replayed; a patch is never."},"rows":{"type":"array","items":{"type":"object","properties":{"role":{"type":"string","enum":["user","assistant","notice"],"description":"Who said it. A notice is neither side: it is something that happened to the turn, recorded so a reopened conversation can say it. Without those, a turn a provider refused ends on the user's message and reads as broken."},"text":{"type":"string","description":"The words."},"run":{"description":"The run that produced this row. Present on everything a turn produced, absent on rows written outside one. A client draws a run's rows over whatever it already holds for that run, which is what this identifies; content cannot, because the last row of a live run keeps growing.","type":"string"},"sentAt":{"description":"When it was sent, in milliseconds. On the user's rows only, because that is the only moment actually known: a turn's own frames arrive with no clock, so stamping the agent's rows could only ever mean the whole turn's start or end.","type":"number"},"messageId":{"description":"The message's own id, on the rows of messages sent to the agent: what its sender named it, or what the sandbox did. A rewind names the message by it, and the same id sent again is recognised rather than delivered twice.","type":"string"},"attachments":{"description":"Files attached to this message, as workspace paths.","type":"array","items":{"type":"string"}},"checkpointId":{"description":"The saved point this message can be rewound to. Looked up on each read rather than stored, so what is offered is exactly what is still there to go back to.","type":"string"},"rewindIndex":{"description":"This message's position in the conversation's record, which is how a rewind names it. Present only beside a checkpoint.","type":"integer","minimum":0,"maximum":9007199254740991},"thinking":{"description":"What the agent was reasoning about.","type":"string"},"tools":{"description":"The tool calls this part of the turn made.","type":"array","items":{"$ref":"#/$defs/__schema0"}},"todos":{"description":"The agent's task checklist, as of this bubble.","type":"array","items":{"type":"object","properties":{"content":{"type":"string","description":"The item, as the agent wrote it."},"status":{"type":"string","enum":["pending","in_progress","completed"],"description":"Where it is."},"activeForm":{"description":"How to phrase it while it is happening, so a screen can say what the agent is doing rather than what it plans to do.","type":"string"}},"required":["content","status"],"additionalProperties":false}},"usage":{"description":"What the turn cost, on the bubble its answer ended in.","type":"object","properties":{"costUsd":{"type":"number"},"inputTokens":{"type":"number"},"outputTokens":{"type":"number"},"durationMs":{"type":"number"},"numTurns":{"type":"number"}},"additionalProperties":false},"notes":{"description":"What the sandbox added to this message before the model saw it. Carried on the message rather than as rows of their own, because they genuinely were part of what was sent.","type":"array","items":{"type":"object","properties":{"title":{"type":"string","description":"The one line a reader sees, on a row that opens to the text below."},"text":{"type":"string","description":"The note itself, which is also exactly what the model was told."}},"required":["title","text"],"additionalProperties":false}},"speaker":{"description":"Who sent this message, as the sandbox verified it. Absent where it does not say.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"type":"string","description":"The signed-in member, as the sandbox verified them."},"name":{"description":"Their display name, where the sign-in carries one.","type":"string"}},"required":["kind","email"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"program"},"token":{"type":"string","description":"The label of the control token a person minted and handed to this program."}},"required":["kind","token"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"conversationId":{"type":"string","description":"The conversation whose agent is speaking: a child reporting back, a peer's message."}},"required":["kind","conversationId"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"sandbox"},"source":{"description":"What in the sandbox spoke: an automation, a watch that fired, a job that ended, a repair. Absent when it does not say.","type":"string"}},"required":["kind"],"additionalProperties":false}]},"errand":{"description":"What this message is for, when the sandbox or the app composed it rather than a person typing it. Absent on a person's own words.","type":"string","enum":["land-conflict","verify-nudge","land-breakage","land-fix","land-fix-nudge","land-held","push-fix","push-fix-nudge","ci-fix","ci-fix-nudge"]},"placed":{"description":"A person wrote this in the agent's voice, with no turn behind it. Marked for the human re-reading the conversation months later, so their own words do not pass as the agent's. The agent itself never sees the mark.","type":"boolean"},"noticeAction":{"description":"A one-press follow-up this notice offers, by name. The chat decides what it does and whether it still applies.","type":"string","enum":["landHold","depsInstall","watchStop","sandboxMemory","sendAnyway","sendAgain"]},"sandboxHeld":{"description":"The sandbox kept this refused turn whole, its message still above, so the notice's press runs that turn again instead of letting the conversation's queue go, which never held these words.","type":"boolean"},"noticeWait":{"description":"The wait this notice describes, by name, so a reader can say whether it is still on.","type":"string","enum":["credentialRenewal","chatRoute","watch"]},"noticeWaitId":{"description":"Which instance of the wait this notice names, for a kind that can have several running at once.","type":"string"},"noticeCode":{"description":"Which of the sandbox's own notices this row is, and the facts it was worded from, so a reader can say it in the reader's own language. The text stays the English sentence.","type":"object","properties":{"code":{"type":"string","description":"Which of the sandbox's notices this row is, by name. A reader that does not know the name shows the row's text."},"params":{"description":"The facts the notice was worded from, by name: counts, names, and the provider's own sentence where the notice quotes one.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number","boolean"]}}},"required":["code"],"additionalProperties":false},"agentNotice":{"description":"This notice was said by the agent's runtime or one of its extensions rather than by the sandbox: how loud, and who said it.","type":"object","properties":{"level":{"type":"string","enum":["info","warning","error"],"description":"How loud it was said: a muted line, a warning or an error."},"source":{"description":"Who said it, when the runtime named one: the extension or plugin, as the runtime spells it. Absent when the runtime's loop said it itself.","type":"string"}},"required":["level"],"additionalProperties":false},"plan":{"description":"The plan this row asked approval for, and the answer.","type":"object","properties":{"requestId":{"type":"string","description":"What to send back when you answer."},"text":{"type":"string","description":"The plan itself."},"document":{"description":"The write-up this plan refers to, when the plan itself is a pointer to one.","type":"object","properties":{"path":{"type":"string","description":"Where it lives, as a workspace path."},"title":{"type":"string","description":"What it is called: its opening heading, or its file name."},"markdown":{"type":"string","description":"The document itself."},"truncated":{"description":"It was clipped at the wire cap; the file on disk has more.","type":"boolean"},"plan":{"description":"It is one of the CLI's plan files, written to be approved rather than merely read.","type":"boolean"}},"required":["path","title","markdown"],"additionalProperties":false},"status":{"type":"string","enum":["pending","approved","rejected","cancelled"],"description":"Where the decision stands."}},"required":["requestId","text","status"],"additionalProperties":false},"question":{"description":"The questions this row asked, and the picks that answered them.","type":"object","properties":{"requestId":{"type":"string","description":"What to send back when you answer."},"questions":{"type":"array","items":{"type":"object","properties":{"question":{"type":"string","description":"What the agent is asking."},"header":{"type":"string","description":"A short label for the question."},"multiSelect":{"type":"boolean","description":"Whether more than one answer can be picked."},"options":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","description":"The choice, in a few words."},"description":{"type":"string","description":"What picking it means."},"preview":{"description":"Something to look at while deciding: a mock-up, a snippet, a layout.","type":"string"}},"required":["label","description"],"additionalProperties":false},"description":"The choices offered. A free-text answer is always possible as well."}},"required":["question","header","multiSelect","options"],"additionalProperties":false},"description":"What it wants to know."},"document":{"description":"The document this turn wrote and is asking about, so the choice can be read beside it.","type":"object","properties":{"path":{"type":"string","description":"Where it lives, as a workspace path."},"title":{"type":"string","description":"What it is called: its opening heading, or its file name."},"markdown":{"type":"string","description":"The document itself."},"truncated":{"description":"It was clipped at the wire cap; the file on disk has more.","type":"boolean"},"plan":{"description":"It is one of the CLI's plan files, written to be approved rather than merely read.","type":"boolean"}},"required":["path","title","markdown"],"additionalProperties":false},"status":{"type":"string","enum":["pending","answered","cancelled"],"description":"Where the answer stands."},"answers":{"description":"What was chosen, keyed by the question, with the chosen labels or the user's own words.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"string"}}},"attachments":{"description":"Files the user attached to their own-words answer, keyed by the question, as workspace-relative paths.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"string"}}}},"required":["requestId","questions","status"],"additionalProperties":false},"permission":{"description":"The tool this row asked permission for, and the decision.","type":"object","properties":{"toolName":{"type":"string","description":"Which tool it wants to use."},"title":{"description":"The whole question, as a sentence, exactly as the runtime words it.","type":"string"},"displayName":{"description":"A short phrase for the button, such as read file.","type":"string"},"description":{"description":"More about what it is asking for.","type":"string"},"reason":{"description":"Why it is asking at all: a rule, the current mode, something that looked risky.","type":"string"},"path":{"description":"Which file it concerns, when it concerns one.","type":"string"},"alwaysLabel":{"description":"The wording for an always-allow answer. Present only when there is something an always could actually remember; without it the only answers are once and no.","type":"string"},"alwaysAsks":{"description":"This request asks every time: an owner's hard rule, a sandbox restart other conversations would feel, or a change only the owner may make. Allow everything in this conversation is not offered on it and never answers it.","type":"boolean","const":true},"program":{"description":"The program this request is holding, when the request is about one. Present on a command gate's request and absent on every other permission ask.","type":"object","properties":{"text":{"type":"string","description":"What would run."},"language":{"type":"string","enum":["bash","javascript"],"description":"Which of the two backends it is written for, named as the grammar that colours it."},"truncated":{"type":"boolean","description":"Whether this is an excerpt of a longer program, so the request can say so instead of ending mid-word. An excerpt always carries the flagged fragment: the beginning, then a window around the fragment, with any skipped middle written into the text as a bracketed count."},"spans":{"type":"array","items":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"maximum":9007199254740991},"end":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["start","end"],"additionalProperties":false},"description":"Which fragments of the text the pattern match fired on: every matched class's, or, under the hard rule, only the class the title names. Offsets into text, in order, never overlapping."}},"required":["text","language","truncated","spans"],"additionalProperties":false},"child":{"description":"The subagent this request would start or reach, and what it runs on. Present on the request to start or reach a subagent and absent on every other permission ask.","type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves it."},"model":{"type":"string","minLength":1,"description":"Which of that provider's models."},"harness":{"description":"Which agentic loop runs it. Absent is the provider's own.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which of that provider's connected accounts pays for it. Absent is whichever has the most room when it starts.","type":"string"},"effort":{"description":"How hard it thinks, where the model offers a choice. Absent is the model's own default.","type":"string"},"thinking":{"description":"Whether it reasons before it answers, where the model offers the choice.","type":"boolean"},"fast":{"description":"Whether it asks for the faster rate, at the higher price.","type":"boolean"},"move":{"type":"string","enum":["spawn","send","answer"],"description":"What the parent asks to do: start a new child, say something to one it started, or answer one's question."},"child":{"description":"The child's id, for one that already exists.","type":"string"},"task":{"description":"What the child is for, in a line.","type":"string"},"message":{"description":"What the parent would say to it: the message it sends, or the answers it gives. Clipped for the request.","type":"string"},"on":{"description":"Which machine it runs on, when one is named: a runner, or \"here\" for this sandbox.","type":"string"},"proposed":{"description":"What the agent asked to start it on, when the owner started it on something else instead. Present only once the request has settled that way.","type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves it."},"model":{"type":"string","minLength":1,"description":"Which of that provider's models."},"harness":{"description":"Which agentic loop runs it. Absent is the provider's own.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which of that provider's connected accounts pays for it. Absent is whichever has the most room when it starts.","type":"string"},"effort":{"description":"How hard it thinks, where the model offers a choice. Absent is the model's own default.","type":"string"},"thinking":{"description":"Whether it reasons before it answers, where the model offers the choice.","type":"boolean"},"fast":{"description":"Whether it asks for the faster rate, at the higher price.","type":"boolean"}},"required":["provider","model"],"additionalProperties":false}},"required":["provider","model","move"],"additionalProperties":false},"explain":{"description":"One plain sentence saying what the program does and why it is being asked about, where the title says something else. Written by the judge that read your safety policy, never by the agent being gated.","type":"string"},"requestId":{"type":"string","description":"What to send back when you answer."},"status":{"type":"string","enum":["pending","allowed","always","everything","denied","cancelled"],"description":"Where the decision stands."}},"required":["toolName","requestId","status"],"additionalProperties":false},"browserHelp":{"description":"The browser hand-over this row asked for, and how it ended.","type":"object","properties":{"requestId":{"type":"string"},"session":{"type":"string"},"account":{"type":"string"},"message":{"type":"string"},"status":{"type":"string","enum":["pending","helped","declined","cancelled"],"description":"How the hand-over ended."}},"required":["requestId","session","account","message","status"],"additionalProperties":false},"terminalHelp":{"description":"The terminal hand-over this row asked for, and how it ended.","type":"object","properties":{"requestId":{"type":"string"},"session":{"type":"string"},"message":{"type":"string"},"status":{"type":"string","enum":["pending","helped","declined","cancelled"],"description":"How the hand-over ended."}},"required":["requestId","session","message","status"],"additionalProperties":false},"capabilityOffer":{"description":"The capability setup this row asked for, the decision, and the outcome.","type":"object","properties":{"requestId":{"type":"string"},"offer":{"type":"object","properties":{"entry":{"type":"string","description":"Which catalog entry is being asked for."},"name":{"type":"string","description":"What it is called, as the catalogue titles it rather than as the agent named it."},"why":{"description":"The agent's case for connecting it, and the only words on this request that are the agent's.","type":"string"}},"required":["entry","name"],"additionalProperties":false},"status":{"type":"string","enum":["pending","connecting","skipped","cancelled"],"description":"Where the decision stands."},"outcome":{"description":"How an accepted ask's setup ended (the capability_outcome frame).","type":"object","properties":{"outcome":{"type":"string","enum":["connected","unfinished"]},"id":{"type":"string"}},"required":["outcome"],"additionalProperties":false}},"required":["requestId","offer","status"],"additionalProperties":false},"paymentOffer":{"description":"The payment this row asked for, the decision, and the receipt.","type":"object","properties":{"requestId":{"type":"string"},"offer":{"type":"object","properties":{"url":{"type":"string","description":"What is being paid for."},"description":{"description":"What the endpoint says it is.","type":"string"},"payTo":{"type":"string","description":"Where the money goes, taken verbatim from the endpoint's own demand."},"network":{"type":"string","description":"On which network."},"asset":{"type":"string","description":"In which token."},"assetName":{"type":"string","description":"That token's name. It is pegged to the dollar, which is what lets every amount here read as dollars."},"amountUsd":{"type":"string","description":"The exact price. Not a ceiling: this scheme has no ranges, so this is the whole spend."},"spentTodayUsd":{"type":"string","description":"What has already gone out today."},"dailyCapUsd":{"type":"string","description":"What may go out in a day."},"why":{"description":"The agent's case for paying, and the only words on this request that are the agent's.","type":"string"}},"required":["url","payTo","network","asset","assetName","amountUsd","spentTodayUsd","dailyCapUsd"],"additionalProperties":false},"status":{"type":"string","enum":["pending","approved","skipped","cancelled"],"description":"Where the decision stands."},"receipt":{"description":"How the approved payment ended (the payment_receipt frame).","type":"object","properties":{"outcome":{"type":"string","enum":["paid","failed"]},"amountUsd":{"type":"string"},"transaction":{"type":"string"},"network":{"type":"string"}},"required":["outcome","amountUsd"],"additionalProperties":false}},"required":["requestId","offer","status"],"additionalProperties":false},"watchWake":{"description":"The condition watch that woke this conversation, and the prompt it was woken with.","type":"object","properties":{"outcome":{"type":"string","enum":["met","timeout","restart-expired","broken"],"description":"How the watch ended: the condition held, the deadline passed, or a restart cut it short."},"note":{"type":"string","description":"The agent's own line on what it was waiting for."},"elapsed":{"type":"string","description":"How long the watch stood, already worded ('43m'): carried rather than recomputed, since the arming instant is not on the row."},"sent":{"type":"string","description":"The whole prompt the model was woken with, disclosed under the row."}},"required":["outcome","note","elapsed","sent"],"additionalProperties":false},"need":{"description":"Something the agent asked a person for, as it was when raised. Its live state (answered, met) is read by its id, since it outlives the turn.","type":"object","properties":{"id":{"type":"string","description":"The need's handle, the one `needs cancel` takes."},"conversationId":{"type":"string","description":"The conversation that asked, and the one its answer wakes."},"subject":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"capability"},"entry":{"type":"string","minLength":1,"description":"The catalog entry, as the catalog names it."},"name":{"type":"string","description":"What the catalog calls it, never the agent's spelling."},"mode":{"type":"string","enum":["connect","reconnect","change"],"description":"Connect something new, give a connected one a credential that works again, or change a setting on a connected one."},"instance":{"description":"The connection a reconnect or a change is about.","type":"string"},"target":{"description":"The site, host or address the connection is for, when the entry can hold several.","type":"string"},"prefill":{"description":"Settings the agent could fill in for a new connection, never a credential: the daemon keeps only the entry's own non-secret fields.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"changes":{"description":"For a change: each setting and the value it would take. Never a credential.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"reason":{"description":"The daemon's own sentence on why this is the ask, such as the refusal a connected credential keeps getting.","type":"string"},"reported":{"description":"The agent reported the credential refused while the connection still probes as working, so only a person's word that it is fixed meets it: the probe could not see the refusal in the first place.","type":"boolean"}},"required":["kind","entry","name","mode"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret"},"name":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,127}$","description":"The name it is stored under, and what `{{secret:NAME}}` will resolve."},"where":{"description":"How it will be used: the header, the command, the site it goes to.","type":"string","maxLength":200},"link":{"description":"Where a person gets one, shown as a link on the card.","type":"string","format":"uri"},"hint":{"description":"What a valid one looks like, so a wrong paste is caught by eye.","type":"string","maxLength":120},"replace":{"description":"One is stored under this name and is being refused: the ask is for a new value in its place.","type":"boolean"}},"required":["kind","name"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"grant"},"subject":{"type":"string","enum":["capability","folder","shelf","site"],"description":"A connected capability the persona leaves out, a folder outside the conversation's reach, a whole shelf of tools, or a site in the person's own browser, which only their browser extension can allow."},"what":{"type":"string","minLength":1,"description":"The capability's id, the folder, or the shelf."},"label":{"type":"string","description":"What it is, in the daemon's words: the account and its kind, the folder, the shelf's name."},"persona":{"description":"The persona that withholds it, when one does.","type":"string"},"scope":{"description":"How far the yes went, once there was one.","type":"string","enum":["conversation","persona"]}},"required":["kind","subject","what","label"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"release"},"subject":{"type":"string","minLength":1,"description":"The gated account or connector."},"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it. Anyone else's answer is refused and leaves it waiting."}},"required":["kind","subject","approvers"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"environment"},"tool":{"type":"string","minLength":1,"description":"What the steps install, the name the proposal is filed under."},"steps":{"type":"string","minLength":1,"description":"The Dockerfile steps proposed for the image's custom section: RUN and ENV lines only."},"approvedHash":{"description":"The overlay these steps were approved into; met once the running container was built from it.","type":"string"}},"required":["kind","tool","steps"],"additionalProperties":false}],"description":"What exactly is asked for."},"title":{"type":"string","description":"The one line it leads with, in the daemon's words."},"why":{"description":"The agent's case for it, and the only words on a need that are the agent's.","type":"string","maxLength":280},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."},"createdAt":{"type":"number","description":"When it was raised, in milliseconds."},"updatedAt":{"type":"number","description":"When it last moved, in milliseconds."},"answeredBy":{"description":"Who answered it, as the sandbox verified them.","type":"string"},"outcome":{"description":"How it ended, in the daemon's words, once it has.","type":"string"},"told":{"description":"How the agent heard the outcome, once it has.","type":"string","enum":["call","turn","queued"]},"unattended":{"description":"Raised by a turn nobody was watching, so the card waited for whoever came next.","type":"boolean"}},"required":["id","conversationId","subject","title","status","createdAt","updatedAt"],"additionalProperties":false},"needWake":{"description":"The answered need that reached this conversation, and the prompt it came as.","type":"object","properties":{"outcome":{"type":"string","enum":["met","declined"],"description":"How the need ended: a person gave it, or said no."},"title":{"type":"string","description":"The need, as its card leads with it."},"id":{"type":"string","description":"The need's handle, which its live card is keyed by."},"sent":{"type":"string","description":"The whole prompt the model received, disclosed under the row."}},"required":["outcome","title","id","sent"],"additionalProperties":false},"agentWords":{"description":"Another agent's words that reached this conversation, whose they are, and the prompt they came as.","type":"object","properties":{"kind":{"type":"string","enum":["peer","child"],"description":"Who sent it: another conversation in the workspace, or a subagent this one started."},"from":{"type":"string","description":"The sending conversation's id."},"title":{"description":"The sender's title, when it had one.","type":"string"},"failed":{"description":"A child's report on a turn that failed rather than finished.","type":"boolean"},"sent":{"type":"string","description":"The whole prompt the model received, disclosed under the row."}},"required":["kind","from","sent"],"additionalProperties":false},"backgroundJob":{"description":"The background job this row marks the start of.","type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for the job, which its live state on the conversation's card is keyed by."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"command":{"type":"string","description":"The command as the agent wrote it, folded to one line."},"startedAt":{"type":"number","description":"When it started, in milliseconds."}},"required":["id","label","command","startedAt"],"additionalProperties":false},"credentialOffer":{"description":"The gated credential this row asked to use, who may release it, and who did.","type":"object","properties":{"requestId":{"type":"string"},"offer":{"type":"object","properties":{"subject":{"type":"string","description":"Which credential is being asked for."},"kind":{"type":"string","enum":["secret","capability"],"description":"Whether this gate covers one stored secret, by the name a reference carries, or one whole connected capability, by its id."},"lane":{"type":"string","enum":["shell","code","browser","session","otp"],"description":"What the credential was about to be used for: a shell command, a script, typing into a page, mounting a connected account, or one one-time code."},"detail":{"description":"Where it would go: the start of the command, the site, or what is being mounted. Never a value: the command still reads as a reference at this point.","type":"string"},"why":{"description":"The agent's case for using it, and the only words on this request that are the agent's.","type":"string"},"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it. A click from anyone else is refused and leaves the request standing."},"scope":{"type":"string","enum":["use","conversation"],"description":"How far one release goes: `use` asks again every single time (one click releases exactly one use), `conversation` covers the rest of this conversation and is forgotten when the daemon restarts."}},"required":["subject","kind","lane","approvers","scope"],"additionalProperties":false},"status":{"type":"string","enum":["pending","approved","skipped","cancelled"],"description":"Where the decision stands."},"receipt":{"description":"Who released it, or that somebody refused (the credential_receipt frame).","type":"object","properties":{"outcome":{"type":"string","enum":["released","refused"]},"approvedBy":{"type":"string"}},"required":["outcome"],"additionalProperties":false}},"required":["requestId","offer","status"],"additionalProperties":false}},"required":["role","text"],"additionalProperties":false},"description":"The turn's rows as they stand: what was asked, and everything the agent has said and done since. Draw these, then apply the patches that follow."}},"required":["kind","run","startedAt","seq","rows"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"patch","description":"One change to the run's rows."},"seq":{"type":"number","description":"Its position in the run, counting from one."},"patch":{"oneOf":[{"type":"object","properties":{"op":{"type":"string","const":"append","description":"A new row at the end."},"row":{"type":"object","properties":{"role":{"type":"string","enum":["user","assistant","notice"],"description":"Who said it. A notice is neither side: it is something that happened to the turn, recorded so a reopened conversation can say it. Without those, a turn a provider refused ends on the user's message and reads as broken."},"text":{"type":"string","description":"The words."},"run":{"description":"The run that produced this row. Present on everything a turn produced, absent on rows written outside one. A client draws a run's rows over whatever it already holds for that run, which is what this identifies; content cannot, because the last row of a live run keeps growing.","type":"string"},"sentAt":{"description":"When it was sent, in milliseconds. On the user's rows only, because that is the only moment actually known: a turn's own frames arrive with no clock, so stamping the agent's rows could only ever mean the whole turn's start or end.","type":"number"},"messageId":{"description":"The message's own id, on the rows of messages sent to the agent: what its sender named it, or what the sandbox did. A rewind names the message by it, and the same id sent again is recognised rather than delivered twice.","type":"string"},"attachments":{"description":"Files attached to this message, as workspace paths.","type":"array","items":{"type":"string"}},"checkpointId":{"description":"The saved point this message can be rewound to. Looked up on each read rather than stored, so what is offered is exactly what is still there to go back to.","type":"string"},"rewindIndex":{"description":"This message's position in the conversation's record, which is how a rewind names it. Present only beside a checkpoint.","type":"integer","minimum":0,"maximum":9007199254740991},"thinking":{"description":"What the agent was reasoning about.","type":"string"},"tools":{"description":"The tool calls this part of the turn made.","type":"array","items":{"$ref":"#/$defs/__schema0"}},"todos":{"description":"The agent's task checklist, as of this bubble.","type":"array","items":{"type":"object","properties":{"content":{"type":"string","description":"The item, as the agent wrote it."},"status":{"type":"string","enum":["pending","in_progress","completed"],"description":"Where it is."},"activeForm":{"description":"How to phrase it while it is happening, so a screen can say what the agent is doing rather than what it plans to do.","type":"string"}},"required":["content","status"],"additionalProperties":false}},"usage":{"description":"What the turn cost, on the bubble its answer ended in.","type":"object","properties":{"costUsd":{"type":"number"},"inputTokens":{"type":"number"},"outputTokens":{"type":"number"},"durationMs":{"type":"number"},"numTurns":{"type":"number"}},"additionalProperties":false},"notes":{"description":"What the sandbox added to this message before the model saw it. Carried on the message rather than as rows of their own, because they genuinely were part of what was sent.","type":"array","items":{"type":"object","properties":{"title":{"type":"string","description":"The one line a reader sees, on a row that opens to the text below."},"text":{"type":"string","description":"The note itself, which is also exactly what the model was told."}},"required":["title","text"],"additionalProperties":false}},"speaker":{"description":"Who sent this message, as the sandbox verified it. Absent where it does not say.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"type":"string","description":"The signed-in member, as the sandbox verified them."},"name":{"description":"Their display name, where the sign-in carries one.","type":"string"}},"required":["kind","email"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"program"},"token":{"type":"string","description":"The label of the control token a person minted and handed to this program."}},"required":["kind","token"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"conversationId":{"type":"string","description":"The conversation whose agent is speaking: a child reporting back, a peer's message."}},"required":["kind","conversationId"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"sandbox"},"source":{"description":"What in the sandbox spoke: an automation, a watch that fired, a job that ended, a repair. Absent when it does not say.","type":"string"}},"required":["kind"],"additionalProperties":false}]},"errand":{"description":"What this message is for, when the sandbox or the app composed it rather than a person typing it. Absent on a person's own words.","type":"string","enum":["land-conflict","verify-nudge","land-breakage","land-fix","land-fix-nudge","land-held","push-fix","push-fix-nudge","ci-fix","ci-fix-nudge"]},"placed":{"description":"A person wrote this in the agent's voice, with no turn behind it. Marked for the human re-reading the conversation months later, so their own words do not pass as the agent's. The agent itself never sees the mark.","type":"boolean"},"noticeAction":{"description":"A one-press follow-up this notice offers, by name. The chat decides what it does and whether it still applies.","type":"string","enum":["landHold","depsInstall","watchStop","sandboxMemory","sendAnyway","sendAgain"]},"sandboxHeld":{"description":"The sandbox kept this refused turn whole, its message still above, so the notice's press runs that turn again instead of letting the conversation's queue go, which never held these words.","type":"boolean"},"noticeWait":{"description":"The wait this notice describes, by name, so a reader can say whether it is still on.","type":"string","enum":["credentialRenewal","chatRoute","watch"]},"noticeWaitId":{"description":"Which instance of the wait this notice names, for a kind that can have several running at once.","type":"string"},"noticeCode":{"description":"Which of the sandbox's own notices this row is, and the facts it was worded from, so a reader can say it in the reader's own language. The text stays the English sentence.","type":"object","properties":{"code":{"type":"string","description":"Which of the sandbox's notices this row is, by name. A reader that does not know the name shows the row's text."},"params":{"description":"The facts the notice was worded from, by name: counts, names, and the provider's own sentence where the notice quotes one.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number","boolean"]}}},"required":["code"],"additionalProperties":false},"agentNotice":{"description":"This notice was said by the agent's runtime or one of its extensions rather than by the sandbox: how loud, and who said it.","type":"object","properties":{"level":{"type":"string","enum":["info","warning","error"],"description":"How loud it was said: a muted line, a warning or an error."},"source":{"description":"Who said it, when the runtime named one: the extension or plugin, as the runtime spells it. Absent when the runtime's loop said it itself.","type":"string"}},"required":["level"],"additionalProperties":false},"plan":{"description":"The plan this row asked approval for, and the answer.","type":"object","properties":{"requestId":{"type":"string","description":"What to send back when you answer."},"text":{"type":"string","description":"The plan itself."},"document":{"description":"The write-up this plan refers to, when the plan itself is a pointer to one.","type":"object","properties":{"path":{"type":"string","description":"Where it lives, as a workspace path."},"title":{"type":"string","description":"What it is called: its opening heading, or its file name."},"markdown":{"type":"string","description":"The document itself."},"truncated":{"description":"It was clipped at the wire cap; the file on disk has more.","type":"boolean"},"plan":{"description":"It is one of the CLI's plan files, written to be approved rather than merely read.","type":"boolean"}},"required":["path","title","markdown"],"additionalProperties":false},"status":{"type":"string","enum":["pending","approved","rejected","cancelled"],"description":"Where the decision stands."}},"required":["requestId","text","status"],"additionalProperties":false},"question":{"description":"The questions this row asked, and the picks that answered them.","type":"object","properties":{"requestId":{"type":"string","description":"What to send back when you answer."},"questions":{"type":"array","items":{"type":"object","properties":{"question":{"type":"string","description":"What the agent is asking."},"header":{"type":"string","description":"A short label for the question."},"multiSelect":{"type":"boolean","description":"Whether more than one answer can be picked."},"options":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","description":"The choice, in a few words."},"description":{"type":"string","description":"What picking it means."},"preview":{"description":"Something to look at while deciding: a mock-up, a snippet, a layout.","type":"string"}},"required":["label","description"],"additionalProperties":false},"description":"The choices offered. A free-text answer is always possible as well."}},"required":["question","header","multiSelect","options"],"additionalProperties":false},"description":"What it wants to know."},"document":{"description":"The document this turn wrote and is asking about, so the choice can be read beside it.","type":"object","properties":{"path":{"type":"string","description":"Where it lives, as a workspace path."},"title":{"type":"string","description":"What it is called: its opening heading, or its file name."},"markdown":{"type":"string","description":"The document itself."},"truncated":{"description":"It was clipped at the wire cap; the file on disk has more.","type":"boolean"},"plan":{"description":"It is one of the CLI's plan files, written to be approved rather than merely read.","type":"boolean"}},"required":["path","title","markdown"],"additionalProperties":false},"status":{"type":"string","enum":["pending","answered","cancelled"],"description":"Where the answer stands."},"answers":{"description":"What was chosen, keyed by the question, with the chosen labels or the user's own words.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"string"}}},"attachments":{"description":"Files the user attached to their own-words answer, keyed by the question, as workspace-relative paths.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"string"}}}},"required":["requestId","questions","status"],"additionalProperties":false},"permission":{"description":"The tool this row asked permission for, and the decision.","type":"object","properties":{"toolName":{"type":"string","description":"Which tool it wants to use."},"title":{"description":"The whole question, as a sentence, exactly as the runtime words it.","type":"string"},"displayName":{"description":"A short phrase for the button, such as read file.","type":"string"},"description":{"description":"More about what it is asking for.","type":"string"},"reason":{"description":"Why it is asking at all: a rule, the current mode, something that looked risky.","type":"string"},"path":{"description":"Which file it concerns, when it concerns one.","type":"string"},"alwaysLabel":{"description":"The wording for an always-allow answer. Present only when there is something an always could actually remember; without it the only answers are once and no.","type":"string"},"alwaysAsks":{"description":"This request asks every time: an owner's hard rule, a sandbox restart other conversations would feel, or a change only the owner may make. Allow everything in this conversation is not offered on it and never answers it.","type":"boolean","const":true},"program":{"description":"The program this request is holding, when the request is about one. Present on a command gate's request and absent on every other permission ask.","type":"object","properties":{"text":{"type":"string","description":"What would run."},"language":{"type":"string","enum":["bash","javascript"],"description":"Which of the two backends it is written for, named as the grammar that colours it."},"truncated":{"type":"boolean","description":"Whether this is an excerpt of a longer program, so the request can say so instead of ending mid-word. An excerpt always carries the flagged fragment: the beginning, then a window around the fragment, with any skipped middle written into the text as a bracketed count."},"spans":{"type":"array","items":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"maximum":9007199254740991},"end":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["start","end"],"additionalProperties":false},"description":"Which fragments of the text the pattern match fired on: every matched class's, or, under the hard rule, only the class the title names. Offsets into text, in order, never overlapping."}},"required":["text","language","truncated","spans"],"additionalProperties":false},"child":{"description":"The subagent this request would start or reach, and what it runs on. Present on the request to start or reach a subagent and absent on every other permission ask.","type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves it."},"model":{"type":"string","minLength":1,"description":"Which of that provider's models."},"harness":{"description":"Which agentic loop runs it. Absent is the provider's own.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which of that provider's connected accounts pays for it. Absent is whichever has the most room when it starts.","type":"string"},"effort":{"description":"How hard it thinks, where the model offers a choice. Absent is the model's own default.","type":"string"},"thinking":{"description":"Whether it reasons before it answers, where the model offers the choice.","type":"boolean"},"fast":{"description":"Whether it asks for the faster rate, at the higher price.","type":"boolean"},"move":{"type":"string","enum":["spawn","send","answer"],"description":"What the parent asks to do: start a new child, say something to one it started, or answer one's question."},"child":{"description":"The child's id, for one that already exists.","type":"string"},"task":{"description":"What the child is for, in a line.","type":"string"},"message":{"description":"What the parent would say to it: the message it sends, or the answers it gives. Clipped for the request.","type":"string"},"on":{"description":"Which machine it runs on, when one is named: a runner, or \"here\" for this sandbox.","type":"string"},"proposed":{"description":"What the agent asked to start it on, when the owner started it on something else instead. Present only once the request has settled that way.","type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves it."},"model":{"type":"string","minLength":1,"description":"Which of that provider's models."},"harness":{"description":"Which agentic loop runs it. Absent is the provider's own.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which of that provider's connected accounts pays for it. Absent is whichever has the most room when it starts.","type":"string"},"effort":{"description":"How hard it thinks, where the model offers a choice. Absent is the model's own default.","type":"string"},"thinking":{"description":"Whether it reasons before it answers, where the model offers the choice.","type":"boolean"},"fast":{"description":"Whether it asks for the faster rate, at the higher price.","type":"boolean"}},"required":["provider","model"],"additionalProperties":false}},"required":["provider","model","move"],"additionalProperties":false},"explain":{"description":"One plain sentence saying what the program does and why it is being asked about, where the title says something else. Written by the judge that read your safety policy, never by the agent being gated.","type":"string"},"requestId":{"type":"string","description":"What to send back when you answer."},"status":{"type":"string","enum":["pending","allowed","always","everything","denied","cancelled"],"description":"Where the decision stands."}},"required":["toolName","requestId","status"],"additionalProperties":false},"browserHelp":{"description":"The browser hand-over this row asked for, and how it ended.","type":"object","properties":{"requestId":{"type":"string"},"session":{"type":"string"},"account":{"type":"string"},"message":{"type":"string"},"status":{"type":"string","enum":["pending","helped","declined","cancelled"],"description":"How the hand-over ended."}},"required":["requestId","session","account","message","status"],"additionalProperties":false},"terminalHelp":{"description":"The terminal hand-over this row asked for, and how it ended.","type":"object","properties":{"requestId":{"type":"string"},"session":{"type":"string"},"message":{"type":"string"},"status":{"type":"string","enum":["pending","helped","declined","cancelled"],"description":"How the hand-over ended."}},"required":["requestId","session","message","status"],"additionalProperties":false},"capabilityOffer":{"description":"The capability setup this row asked for, the decision, and the outcome.","type":"object","properties":{"requestId":{"type":"string"},"offer":{"type":"object","properties":{"entry":{"type":"string","description":"Which catalog entry is being asked for."},"name":{"type":"string","description":"What it is called, as the catalogue titles it rather than as the agent named it."},"why":{"description":"The agent's case for connecting it, and the only words on this request that are the agent's.","type":"string"}},"required":["entry","name"],"additionalProperties":false},"status":{"type":"string","enum":["pending","connecting","skipped","cancelled"],"description":"Where the decision stands."},"outcome":{"description":"How an accepted ask's setup ended (the capability_outcome frame).","type":"object","properties":{"outcome":{"type":"string","enum":["connected","unfinished"]},"id":{"type":"string"}},"required":["outcome"],"additionalProperties":false}},"required":["requestId","offer","status"],"additionalProperties":false},"paymentOffer":{"description":"The payment this row asked for, the decision, and the receipt.","type":"object","properties":{"requestId":{"type":"string"},"offer":{"type":"object","properties":{"url":{"type":"string","description":"What is being paid for."},"description":{"description":"What the endpoint says it is.","type":"string"},"payTo":{"type":"string","description":"Where the money goes, taken verbatim from the endpoint's own demand."},"network":{"type":"string","description":"On which network."},"asset":{"type":"string","description":"In which token."},"assetName":{"type":"string","description":"That token's name. It is pegged to the dollar, which is what lets every amount here read as dollars."},"amountUsd":{"type":"string","description":"The exact price. Not a ceiling: this scheme has no ranges, so this is the whole spend."},"spentTodayUsd":{"type":"string","description":"What has already gone out today."},"dailyCapUsd":{"type":"string","description":"What may go out in a day."},"why":{"description":"The agent's case for paying, and the only words on this request that are the agent's.","type":"string"}},"required":["url","payTo","network","asset","assetName","amountUsd","spentTodayUsd","dailyCapUsd"],"additionalProperties":false},"status":{"type":"string","enum":["pending","approved","skipped","cancelled"],"description":"Where the decision stands."},"receipt":{"description":"How the approved payment ended (the payment_receipt frame).","type":"object","properties":{"outcome":{"type":"string","enum":["paid","failed"]},"amountUsd":{"type":"string"},"transaction":{"type":"string"},"network":{"type":"string"}},"required":["outcome","amountUsd"],"additionalProperties":false}},"required":["requestId","offer","status"],"additionalProperties":false},"watchWake":{"description":"The condition watch that woke this conversation, and the prompt it was woken with.","type":"object","properties":{"outcome":{"type":"string","enum":["met","timeout","restart-expired","broken"],"description":"How the watch ended: the condition held, the deadline passed, or a restart cut it short."},"note":{"type":"string","description":"The agent's own line on what it was waiting for."},"elapsed":{"type":"string","description":"How long the watch stood, already worded ('43m'): carried rather than recomputed, since the arming instant is not on the row."},"sent":{"type":"string","description":"The whole prompt the model was woken with, disclosed under the row."}},"required":["outcome","note","elapsed","sent"],"additionalProperties":false},"need":{"description":"Something the agent asked a person for, as it was when raised. Its live state (answered, met) is read by its id, since it outlives the turn.","type":"object","properties":{"id":{"type":"string","description":"The need's handle, the one `needs cancel` takes."},"conversationId":{"type":"string","description":"The conversation that asked, and the one its answer wakes."},"subject":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"capability"},"entry":{"type":"string","minLength":1,"description":"The catalog entry, as the catalog names it."},"name":{"type":"string","description":"What the catalog calls it, never the agent's spelling."},"mode":{"type":"string","enum":["connect","reconnect","change"],"description":"Connect something new, give a connected one a credential that works again, or change a setting on a connected one."},"instance":{"description":"The connection a reconnect or a change is about.","type":"string"},"target":{"description":"The site, host or address the connection is for, when the entry can hold several.","type":"string"},"prefill":{"description":"Settings the agent could fill in for a new connection, never a credential: the daemon keeps only the entry's own non-secret fields.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"changes":{"description":"For a change: each setting and the value it would take. Never a credential.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"reason":{"description":"The daemon's own sentence on why this is the ask, such as the refusal a connected credential keeps getting.","type":"string"},"reported":{"description":"The agent reported the credential refused while the connection still probes as working, so only a person's word that it is fixed meets it: the probe could not see the refusal in the first place.","type":"boolean"}},"required":["kind","entry","name","mode"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret"},"name":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,127}$","description":"The name it is stored under, and what `{{secret:NAME}}` will resolve."},"where":{"description":"How it will be used: the header, the command, the site it goes to.","type":"string","maxLength":200},"link":{"description":"Where a person gets one, shown as a link on the card.","type":"string","format":"uri"},"hint":{"description":"What a valid one looks like, so a wrong paste is caught by eye.","type":"string","maxLength":120},"replace":{"description":"One is stored under this name and is being refused: the ask is for a new value in its place.","type":"boolean"}},"required":["kind","name"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"grant"},"subject":{"type":"string","enum":["capability","folder","shelf","site"],"description":"A connected capability the persona leaves out, a folder outside the conversation's reach, a whole shelf of tools, or a site in the person's own browser, which only their browser extension can allow."},"what":{"type":"string","minLength":1,"description":"The capability's id, the folder, or the shelf."},"label":{"type":"string","description":"What it is, in the daemon's words: the account and its kind, the folder, the shelf's name."},"persona":{"description":"The persona that withholds it, when one does.","type":"string"},"scope":{"description":"How far the yes went, once there was one.","type":"string","enum":["conversation","persona"]}},"required":["kind","subject","what","label"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"release"},"subject":{"type":"string","minLength":1,"description":"The gated account or connector."},"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it. Anyone else's answer is refused and leaves it waiting."}},"required":["kind","subject","approvers"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"environment"},"tool":{"type":"string","minLength":1,"description":"What the steps install, the name the proposal is filed under."},"steps":{"type":"string","minLength":1,"description":"The Dockerfile steps proposed for the image's custom section: RUN and ENV lines only."},"approvedHash":{"description":"The overlay these steps were approved into; met once the running container was built from it.","type":"string"}},"required":["kind","tool","steps"],"additionalProperties":false}],"description":"What exactly is asked for."},"title":{"type":"string","description":"The one line it leads with, in the daemon's words."},"why":{"description":"The agent's case for it, and the only words on a need that are the agent's.","type":"string","maxLength":280},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."},"createdAt":{"type":"number","description":"When it was raised, in milliseconds."},"updatedAt":{"type":"number","description":"When it last moved, in milliseconds."},"answeredBy":{"description":"Who answered it, as the sandbox verified them.","type":"string"},"outcome":{"description":"How it ended, in the daemon's words, once it has.","type":"string"},"told":{"description":"How the agent heard the outcome, once it has.","type":"string","enum":["call","turn","queued"]},"unattended":{"description":"Raised by a turn nobody was watching, so the card waited for whoever came next.","type":"boolean"}},"required":["id","conversationId","subject","title","status","createdAt","updatedAt"],"additionalProperties":false},"needWake":{"description":"The answered need that reached this conversation, and the prompt it came as.","type":"object","properties":{"outcome":{"type":"string","enum":["met","declined"],"description":"How the need ended: a person gave it, or said no."},"title":{"type":"string","description":"The need, as its card leads with it."},"id":{"type":"string","description":"The need's handle, which its live card is keyed by."},"sent":{"type":"string","description":"The whole prompt the model received, disclosed under the row."}},"required":["outcome","title","id","sent"],"additionalProperties":false},"agentWords":{"description":"Another agent's words that reached this conversation, whose they are, and the prompt they came as.","type":"object","properties":{"kind":{"type":"string","enum":["peer","child"],"description":"Who sent it: another conversation in the workspace, or a subagent this one started."},"from":{"type":"string","description":"The sending conversation's id."},"title":{"description":"The sender's title, when it had one.","type":"string"},"failed":{"description":"A child's report on a turn that failed rather than finished.","type":"boolean"},"sent":{"type":"string","description":"The whole prompt the model received, disclosed under the row."}},"required":["kind","from","sent"],"additionalProperties":false},"backgroundJob":{"description":"The background job this row marks the start of.","type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for the job, which its live state on the conversation's card is keyed by."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"command":{"type":"string","description":"The command as the agent wrote it, folded to one line."},"startedAt":{"type":"number","description":"When it started, in milliseconds."}},"required":["id","label","command","startedAt"],"additionalProperties":false},"credentialOffer":{"description":"The gated credential this row asked to use, who may release it, and who did.","type":"object","properties":{"requestId":{"type":"string"},"offer":{"type":"object","properties":{"subject":{"type":"string","description":"Which credential is being asked for."},"kind":{"type":"string","enum":["secret","capability"],"description":"Whether this gate covers one stored secret, by the name a reference carries, or one whole connected capability, by its id."},"lane":{"type":"string","enum":["shell","code","browser","session","otp"],"description":"What the credential was about to be used for: a shell command, a script, typing into a page, mounting a connected account, or one one-time code."},"detail":{"description":"Where it would go: the start of the command, the site, or what is being mounted. Never a value: the command still reads as a reference at this point.","type":"string"},"why":{"description":"The agent's case for using it, and the only words on this request that are the agent's.","type":"string"},"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it. A click from anyone else is refused and leaves the request standing."},"scope":{"type":"string","enum":["use","conversation"],"description":"How far one release goes: `use` asks again every single time (one click releases exactly one use), `conversation` covers the rest of this conversation and is forgotten when the daemon restarts."}},"required":["subject","kind","lane","approvers","scope"],"additionalProperties":false},"status":{"type":"string","enum":["pending","approved","skipped","cancelled"],"description":"Where the decision stands."},"receipt":{"description":"Who released it, or that somebody refused (the credential_receipt frame).","type":"object","properties":{"outcome":{"type":"string","enum":["released","refused"]},"approvedBy":{"type":"string"}},"required":["outcome"],"additionalProperties":false}},"required":["requestId","offer","status"],"additionalProperties":false}},"required":["role","text"],"additionalProperties":false}},"required":["op","row"],"additionalProperties":false},{"type":"object","properties":{"op":{"type":"string","const":"replace","description":"This row, whole, in place of the one at that index."},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"row":{"type":"object","properties":{"role":{"type":"string","enum":["user","assistant","notice"],"description":"Who said it. A notice is neither side: it is something that happened to the turn, recorded so a reopened conversation can say it. Without those, a turn a provider refused ends on the user's message and reads as broken."},"text":{"type":"string","description":"The words."},"run":{"description":"The run that produced this row. Present on everything a turn produced, absent on rows written outside one. A client draws a run's rows over whatever it already holds for that run, which is what this identifies; content cannot, because the last row of a live run keeps growing.","type":"string"},"sentAt":{"description":"When it was sent, in milliseconds. On the user's rows only, because that is the only moment actually known: a turn's own frames arrive with no clock, so stamping the agent's rows could only ever mean the whole turn's start or end.","type":"number"},"messageId":{"description":"The message's own id, on the rows of messages sent to the agent: what its sender named it, or what the sandbox did. A rewind names the message by it, and the same id sent again is recognised rather than delivered twice.","type":"string"},"attachments":{"description":"Files attached to this message, as workspace paths.","type":"array","items":{"type":"string"}},"checkpointId":{"description":"The saved point this message can be rewound to. Looked up on each read rather than stored, so what is offered is exactly what is still there to go back to.","type":"string"},"rewindIndex":{"description":"This message's position in the conversation's record, which is how a rewind names it. Present only beside a checkpoint.","type":"integer","minimum":0,"maximum":9007199254740991},"thinking":{"description":"What the agent was reasoning about.","type":"string"},"tools":{"description":"The tool calls this part of the turn made.","type":"array","items":{"$ref":"#/$defs/__schema0"}},"todos":{"description":"The agent's task checklist, as of this bubble.","type":"array","items":{"type":"object","properties":{"content":{"type":"string","description":"The item, as the agent wrote it."},"status":{"type":"string","enum":["pending","in_progress","completed"],"description":"Where it is."},"activeForm":{"description":"How to phrase it while it is happening, so a screen can say what the agent is doing rather than what it plans to do.","type":"string"}},"required":["content","status"],"additionalProperties":false}},"usage":{"description":"What the turn cost, on the bubble its answer ended in.","type":"object","properties":{"costUsd":{"type":"number"},"inputTokens":{"type":"number"},"outputTokens":{"type":"number"},"durationMs":{"type":"number"},"numTurns":{"type":"number"}},"additionalProperties":false},"notes":{"description":"What the sandbox added to this message before the model saw it. Carried on the message rather than as rows of their own, because they genuinely were part of what was sent.","type":"array","items":{"type":"object","properties":{"title":{"type":"string","description":"The one line a reader sees, on a row that opens to the text below."},"text":{"type":"string","description":"The note itself, which is also exactly what the model was told."}},"required":["title","text"],"additionalProperties":false}},"speaker":{"description":"Who sent this message, as the sandbox verified it. Absent where it does not say.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"type":"string","description":"The signed-in member, as the sandbox verified them."},"name":{"description":"Their display name, where the sign-in carries one.","type":"string"}},"required":["kind","email"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"program"},"token":{"type":"string","description":"The label of the control token a person minted and handed to this program."}},"required":["kind","token"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"conversationId":{"type":"string","description":"The conversation whose agent is speaking: a child reporting back, a peer's message."}},"required":["kind","conversationId"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"sandbox"},"source":{"description":"What in the sandbox spoke: an automation, a watch that fired, a job that ended, a repair. Absent when it does not say.","type":"string"}},"required":["kind"],"additionalProperties":false}]},"errand":{"description":"What this message is for, when the sandbox or the app composed it rather than a person typing it. Absent on a person's own words.","type":"string","enum":["land-conflict","verify-nudge","land-breakage","land-fix","land-fix-nudge","land-held","push-fix","push-fix-nudge","ci-fix","ci-fix-nudge"]},"placed":{"description":"A person wrote this in the agent's voice, with no turn behind it. Marked for the human re-reading the conversation months later, so their own words do not pass as the agent's. The agent itself never sees the mark.","type":"boolean"},"noticeAction":{"description":"A one-press follow-up this notice offers, by name. The chat decides what it does and whether it still applies.","type":"string","enum":["landHold","depsInstall","watchStop","sandboxMemory","sendAnyway","sendAgain"]},"sandboxHeld":{"description":"The sandbox kept this refused turn whole, its message still above, so the notice's press runs that turn again instead of letting the conversation's queue go, which never held these words.","type":"boolean"},"noticeWait":{"description":"The wait this notice describes, by name, so a reader can say whether it is still on.","type":"string","enum":["credentialRenewal","chatRoute","watch"]},"noticeWaitId":{"description":"Which instance of the wait this notice names, for a kind that can have several running at once.","type":"string"},"noticeCode":{"description":"Which of the sandbox's own notices this row is, and the facts it was worded from, so a reader can say it in the reader's own language. The text stays the English sentence.","type":"object","properties":{"code":{"type":"string","description":"Which of the sandbox's notices this row is, by name. A reader that does not know the name shows the row's text."},"params":{"description":"The facts the notice was worded from, by name: counts, names, and the provider's own sentence where the notice quotes one.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number","boolean"]}}},"required":["code"],"additionalProperties":false},"agentNotice":{"description":"This notice was said by the agent's runtime or one of its extensions rather than by the sandbox: how loud, and who said it.","type":"object","properties":{"level":{"type":"string","enum":["info","warning","error"],"description":"How loud it was said: a muted line, a warning or an error."},"source":{"description":"Who said it, when the runtime named one: the extension or plugin, as the runtime spells it. Absent when the runtime's loop said it itself.","type":"string"}},"required":["level"],"additionalProperties":false},"plan":{"description":"The plan this row asked approval for, and the answer.","type":"object","properties":{"requestId":{"type":"string","description":"What to send back when you answer."},"text":{"type":"string","description":"The plan itself."},"document":{"description":"The write-up this plan refers to, when the plan itself is a pointer to one.","type":"object","properties":{"path":{"type":"string","description":"Where it lives, as a workspace path."},"title":{"type":"string","description":"What it is called: its opening heading, or its file name."},"markdown":{"type":"string","description":"The document itself."},"truncated":{"description":"It was clipped at the wire cap; the file on disk has more.","type":"boolean"},"plan":{"description":"It is one of the CLI's plan files, written to be approved rather than merely read.","type":"boolean"}},"required":["path","title","markdown"],"additionalProperties":false},"status":{"type":"string","enum":["pending","approved","rejected","cancelled"],"description":"Where the decision stands."}},"required":["requestId","text","status"],"additionalProperties":false},"question":{"description":"The questions this row asked, and the picks that answered them.","type":"object","properties":{"requestId":{"type":"string","description":"What to send back when you answer."},"questions":{"type":"array","items":{"type":"object","properties":{"question":{"type":"string","description":"What the agent is asking."},"header":{"type":"string","description":"A short label for the question."},"multiSelect":{"type":"boolean","description":"Whether more than one answer can be picked."},"options":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","description":"The choice, in a few words."},"description":{"type":"string","description":"What picking it means."},"preview":{"description":"Something to look at while deciding: a mock-up, a snippet, a layout.","type":"string"}},"required":["label","description"],"additionalProperties":false},"description":"The choices offered. A free-text answer is always possible as well."}},"required":["question","header","multiSelect","options"],"additionalProperties":false},"description":"What it wants to know."},"document":{"description":"The document this turn wrote and is asking about, so the choice can be read beside it.","type":"object","properties":{"path":{"type":"string","description":"Where it lives, as a workspace path."},"title":{"type":"string","description":"What it is called: its opening heading, or its file name."},"markdown":{"type":"string","description":"The document itself."},"truncated":{"description":"It was clipped at the wire cap; the file on disk has more.","type":"boolean"},"plan":{"description":"It is one of the CLI's plan files, written to be approved rather than merely read.","type":"boolean"}},"required":["path","title","markdown"],"additionalProperties":false},"status":{"type":"string","enum":["pending","answered","cancelled"],"description":"Where the answer stands."},"answers":{"description":"What was chosen, keyed by the question, with the chosen labels or the user's own words.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"string"}}},"attachments":{"description":"Files the user attached to their own-words answer, keyed by the question, as workspace-relative paths.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"string"}}}},"required":["requestId","questions","status"],"additionalProperties":false},"permission":{"description":"The tool this row asked permission for, and the decision.","type":"object","properties":{"toolName":{"type":"string","description":"Which tool it wants to use."},"title":{"description":"The whole question, as a sentence, exactly as the runtime words it.","type":"string"},"displayName":{"description":"A short phrase for the button, such as read file.","type":"string"},"description":{"description":"More about what it is asking for.","type":"string"},"reason":{"description":"Why it is asking at all: a rule, the current mode, something that looked risky.","type":"string"},"path":{"description":"Which file it concerns, when it concerns one.","type":"string"},"alwaysLabel":{"description":"The wording for an always-allow answer. Present only when there is something an always could actually remember; without it the only answers are once and no.","type":"string"},"alwaysAsks":{"description":"This request asks every time: an owner's hard rule, a sandbox restart other conversations would feel, or a change only the owner may make. Allow everything in this conversation is not offered on it and never answers it.","type":"boolean","const":true},"program":{"description":"The program this request is holding, when the request is about one. Present on a command gate's request and absent on every other permission ask.","type":"object","properties":{"text":{"type":"string","description":"What would run."},"language":{"type":"string","enum":["bash","javascript"],"description":"Which of the two backends it is written for, named as the grammar that colours it."},"truncated":{"type":"boolean","description":"Whether this is an excerpt of a longer program, so the request can say so instead of ending mid-word. An excerpt always carries the flagged fragment: the beginning, then a window around the fragment, with any skipped middle written into the text as a bracketed count."},"spans":{"type":"array","items":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"maximum":9007199254740991},"end":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["start","end"],"additionalProperties":false},"description":"Which fragments of the text the pattern match fired on: every matched class's, or, under the hard rule, only the class the title names. Offsets into text, in order, never overlapping."}},"required":["text","language","truncated","spans"],"additionalProperties":false},"child":{"description":"The subagent this request would start or reach, and what it runs on. Present on the request to start or reach a subagent and absent on every other permission ask.","type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves it."},"model":{"type":"string","minLength":1,"description":"Which of that provider's models."},"harness":{"description":"Which agentic loop runs it. Absent is the provider's own.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which of that provider's connected accounts pays for it. Absent is whichever has the most room when it starts.","type":"string"},"effort":{"description":"How hard it thinks, where the model offers a choice. Absent is the model's own default.","type":"string"},"thinking":{"description":"Whether it reasons before it answers, where the model offers the choice.","type":"boolean"},"fast":{"description":"Whether it asks for the faster rate, at the higher price.","type":"boolean"},"move":{"type":"string","enum":["spawn","send","answer"],"description":"What the parent asks to do: start a new child, say something to one it started, or answer one's question."},"child":{"description":"The child's id, for one that already exists.","type":"string"},"task":{"description":"What the child is for, in a line.","type":"string"},"message":{"description":"What the parent would say to it: the message it sends, or the answers it gives. Clipped for the request.","type":"string"},"on":{"description":"Which machine it runs on, when one is named: a runner, or \"here\" for this sandbox.","type":"string"},"proposed":{"description":"What the agent asked to start it on, when the owner started it on something else instead. Present only once the request has settled that way.","type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves it."},"model":{"type":"string","minLength":1,"description":"Which of that provider's models."},"harness":{"description":"Which agentic loop runs it. Absent is the provider's own.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which of that provider's connected accounts pays for it. Absent is whichever has the most room when it starts.","type":"string"},"effort":{"description":"How hard it thinks, where the model offers a choice. Absent is the model's own default.","type":"string"},"thinking":{"description":"Whether it reasons before it answers, where the model offers the choice.","type":"boolean"},"fast":{"description":"Whether it asks for the faster rate, at the higher price.","type":"boolean"}},"required":["provider","model"],"additionalProperties":false}},"required":["provider","model","move"],"additionalProperties":false},"explain":{"description":"One plain sentence saying what the program does and why it is being asked about, where the title says something else. Written by the judge that read your safety policy, never by the agent being gated.","type":"string"},"requestId":{"type":"string","description":"What to send back when you answer."},"status":{"type":"string","enum":["pending","allowed","always","everything","denied","cancelled"],"description":"Where the decision stands."}},"required":["toolName","requestId","status"],"additionalProperties":false},"browserHelp":{"description":"The browser hand-over this row asked for, and how it ended.","type":"object","properties":{"requestId":{"type":"string"},"session":{"type":"string"},"account":{"type":"string"},"message":{"type":"string"},"status":{"type":"string","enum":["pending","helped","declined","cancelled"],"description":"How the hand-over ended."}},"required":["requestId","session","account","message","status"],"additionalProperties":false},"terminalHelp":{"description":"The terminal hand-over this row asked for, and how it ended.","type":"object","properties":{"requestId":{"type":"string"},"session":{"type":"string"},"message":{"type":"string"},"status":{"type":"string","enum":["pending","helped","declined","cancelled"],"description":"How the hand-over ended."}},"required":["requestId","session","message","status"],"additionalProperties":false},"capabilityOffer":{"description":"The capability setup this row asked for, the decision, and the outcome.","type":"object","properties":{"requestId":{"type":"string"},"offer":{"type":"object","properties":{"entry":{"type":"string","description":"Which catalog entry is being asked for."},"name":{"type":"string","description":"What it is called, as the catalogue titles it rather than as the agent named it."},"why":{"description":"The agent's case for connecting it, and the only words on this request that are the agent's.","type":"string"}},"required":["entry","name"],"additionalProperties":false},"status":{"type":"string","enum":["pending","connecting","skipped","cancelled"],"description":"Where the decision stands."},"outcome":{"description":"How an accepted ask's setup ended (the capability_outcome frame).","type":"object","properties":{"outcome":{"type":"string","enum":["connected","unfinished"]},"id":{"type":"string"}},"required":["outcome"],"additionalProperties":false}},"required":["requestId","offer","status"],"additionalProperties":false},"paymentOffer":{"description":"The payment this row asked for, the decision, and the receipt.","type":"object","properties":{"requestId":{"type":"string"},"offer":{"type":"object","properties":{"url":{"type":"string","description":"What is being paid for."},"description":{"description":"What the endpoint says it is.","type":"string"},"payTo":{"type":"string","description":"Where the money goes, taken verbatim from the endpoint's own demand."},"network":{"type":"string","description":"On which network."},"asset":{"type":"string","description":"In which token."},"assetName":{"type":"string","description":"That token's name. It is pegged to the dollar, which is what lets every amount here read as dollars."},"amountUsd":{"type":"string","description":"The exact price. Not a ceiling: this scheme has no ranges, so this is the whole spend."},"spentTodayUsd":{"type":"string","description":"What has already gone out today."},"dailyCapUsd":{"type":"string","description":"What may go out in a day."},"why":{"description":"The agent's case for paying, and the only words on this request that are the agent's.","type":"string"}},"required":["url","payTo","network","asset","assetName","amountUsd","spentTodayUsd","dailyCapUsd"],"additionalProperties":false},"status":{"type":"string","enum":["pending","approved","skipped","cancelled"],"description":"Where the decision stands."},"receipt":{"description":"How the approved payment ended (the payment_receipt frame).","type":"object","properties":{"outcome":{"type":"string","enum":["paid","failed"]},"amountUsd":{"type":"string"},"transaction":{"type":"string"},"network":{"type":"string"}},"required":["outcome","amountUsd"],"additionalProperties":false}},"required":["requestId","offer","status"],"additionalProperties":false},"watchWake":{"description":"The condition watch that woke this conversation, and the prompt it was woken with.","type":"object","properties":{"outcome":{"type":"string","enum":["met","timeout","restart-expired","broken"],"description":"How the watch ended: the condition held, the deadline passed, or a restart cut it short."},"note":{"type":"string","description":"The agent's own line on what it was waiting for."},"elapsed":{"type":"string","description":"How long the watch stood, already worded ('43m'): carried rather than recomputed, since the arming instant is not on the row."},"sent":{"type":"string","description":"The whole prompt the model was woken with, disclosed under the row."}},"required":["outcome","note","elapsed","sent"],"additionalProperties":false},"need":{"description":"Something the agent asked a person for, as it was when raised. Its live state (answered, met) is read by its id, since it outlives the turn.","type":"object","properties":{"id":{"type":"string","description":"The need's handle, the one `needs cancel` takes."},"conversationId":{"type":"string","description":"The conversation that asked, and the one its answer wakes."},"subject":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"capability"},"entry":{"type":"string","minLength":1,"description":"The catalog entry, as the catalog names it."},"name":{"type":"string","description":"What the catalog calls it, never the agent's spelling."},"mode":{"type":"string","enum":["connect","reconnect","change"],"description":"Connect something new, give a connected one a credential that works again, or change a setting on a connected one."},"instance":{"description":"The connection a reconnect or a change is about.","type":"string"},"target":{"description":"The site, host or address the connection is for, when the entry can hold several.","type":"string"},"prefill":{"description":"Settings the agent could fill in for a new connection, never a credential: the daemon keeps only the entry's own non-secret fields.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"changes":{"description":"For a change: each setting and the value it would take. Never a credential.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"reason":{"description":"The daemon's own sentence on why this is the ask, such as the refusal a connected credential keeps getting.","type":"string"},"reported":{"description":"The agent reported the credential refused while the connection still probes as working, so only a person's word that it is fixed meets it: the probe could not see the refusal in the first place.","type":"boolean"}},"required":["kind","entry","name","mode"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret"},"name":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,127}$","description":"The name it is stored under, and what `{{secret:NAME}}` will resolve."},"where":{"description":"How it will be used: the header, the command, the site it goes to.","type":"string","maxLength":200},"link":{"description":"Where a person gets one, shown as a link on the card.","type":"string","format":"uri"},"hint":{"description":"What a valid one looks like, so a wrong paste is caught by eye.","type":"string","maxLength":120},"replace":{"description":"One is stored under this name and is being refused: the ask is for a new value in its place.","type":"boolean"}},"required":["kind","name"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"grant"},"subject":{"type":"string","enum":["capability","folder","shelf","site"],"description":"A connected capability the persona leaves out, a folder outside the conversation's reach, a whole shelf of tools, or a site in the person's own browser, which only their browser extension can allow."},"what":{"type":"string","minLength":1,"description":"The capability's id, the folder, or the shelf."},"label":{"type":"string","description":"What it is, in the daemon's words: the account and its kind, the folder, the shelf's name."},"persona":{"description":"The persona that withholds it, when one does.","type":"string"},"scope":{"description":"How far the yes went, once there was one.","type":"string","enum":["conversation","persona"]}},"required":["kind","subject","what","label"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"release"},"subject":{"type":"string","minLength":1,"description":"The gated account or connector."},"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it. Anyone else's answer is refused and leaves it waiting."}},"required":["kind","subject","approvers"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"environment"},"tool":{"type":"string","minLength":1,"description":"What the steps install, the name the proposal is filed under."},"steps":{"type":"string","minLength":1,"description":"The Dockerfile steps proposed for the image's custom section: RUN and ENV lines only."},"approvedHash":{"description":"The overlay these steps were approved into; met once the running container was built from it.","type":"string"}},"required":["kind","tool","steps"],"additionalProperties":false}],"description":"What exactly is asked for."},"title":{"type":"string","description":"The one line it leads with, in the daemon's words."},"why":{"description":"The agent's case for it, and the only words on a need that are the agent's.","type":"string","maxLength":280},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."},"createdAt":{"type":"number","description":"When it was raised, in milliseconds."},"updatedAt":{"type":"number","description":"When it last moved, in milliseconds."},"answeredBy":{"description":"Who answered it, as the sandbox verified them.","type":"string"},"outcome":{"description":"How it ended, in the daemon's words, once it has.","type":"string"},"told":{"description":"How the agent heard the outcome, once it has.","type":"string","enum":["call","turn","queued"]},"unattended":{"description":"Raised by a turn nobody was watching, so the card waited for whoever came next.","type":"boolean"}},"required":["id","conversationId","subject","title","status","createdAt","updatedAt"],"additionalProperties":false},"needWake":{"description":"The answered need that reached this conversation, and the prompt it came as.","type":"object","properties":{"outcome":{"type":"string","enum":["met","declined"],"description":"How the need ended: a person gave it, or said no."},"title":{"type":"string","description":"The need, as its card leads with it."},"id":{"type":"string","description":"The need's handle, which its live card is keyed by."},"sent":{"type":"string","description":"The whole prompt the model received, disclosed under the row."}},"required":["outcome","title","id","sent"],"additionalProperties":false},"agentWords":{"description":"Another agent's words that reached this conversation, whose they are, and the prompt they came as.","type":"object","properties":{"kind":{"type":"string","enum":["peer","child"],"description":"Who sent it: another conversation in the workspace, or a subagent this one started."},"from":{"type":"string","description":"The sending conversation's id."},"title":{"description":"The sender's title, when it had one.","type":"string"},"failed":{"description":"A child's report on a turn that failed rather than finished.","type":"boolean"},"sent":{"type":"string","description":"The whole prompt the model received, disclosed under the row."}},"required":["kind","from","sent"],"additionalProperties":false},"backgroundJob":{"description":"The background job this row marks the start of.","type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for the job, which its live state on the conversation's card is keyed by."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"command":{"type":"string","description":"The command as the agent wrote it, folded to one line."},"startedAt":{"type":"number","description":"When it started, in milliseconds."}},"required":["id","label","command","startedAt"],"additionalProperties":false},"credentialOffer":{"description":"The gated credential this row asked to use, who may release it, and who did.","type":"object","properties":{"requestId":{"type":"string"},"offer":{"type":"object","properties":{"subject":{"type":"string","description":"Which credential is being asked for."},"kind":{"type":"string","enum":["secret","capability"],"description":"Whether this gate covers one stored secret, by the name a reference carries, or one whole connected capability, by its id."},"lane":{"type":"string","enum":["shell","code","browser","session","otp"],"description":"What the credential was about to be used for: a shell command, a script, typing into a page, mounting a connected account, or one one-time code."},"detail":{"description":"Where it would go: the start of the command, the site, or what is being mounted. Never a value: the command still reads as a reference at this point.","type":"string"},"why":{"description":"The agent's case for using it, and the only words on this request that are the agent's.","type":"string"},"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it. A click from anyone else is refused and leaves the request standing."},"scope":{"type":"string","enum":["use","conversation"],"description":"How far one release goes: `use` asks again every single time (one click releases exactly one use), `conversation` covers the rest of this conversation and is forgotten when the daemon restarts."}},"required":["subject","kind","lane","approvers","scope"],"additionalProperties":false},"status":{"type":"string","enum":["pending","approved","skipped","cancelled"],"description":"Where the decision stands."},"receipt":{"description":"Who released it, or that somebody refused (the credential_receipt frame).","type":"object","properties":{"outcome":{"type":"string","enum":["released","refused"]},"approvedBy":{"type":"string"}},"required":["outcome"],"additionalProperties":false}},"required":["requestId","offer","status"],"additionalProperties":false}},"required":["role","text"],"additionalProperties":false}},"required":["op","index","row"],"additionalProperties":false},{"type":"object","properties":{"op":{"type":"string","const":"drop","description":"The row at that index is gone: it was opened and never written into."},"index":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["op","index"],"additionalProperties":false},{"type":"object","properties":{"op":{"type":"string","const":"text","description":"More of the agent's prose, onto that row's text."},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"text":{"type":"string"}},"required":["op","index","text"],"additionalProperties":false},{"type":"object","properties":{"op":{"type":"string","const":"thinking","description":"More of the agent's reasoning, onto that row's thinking."},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"text":{"type":"string"}},"required":["op","index","text"],"additionalProperties":false},{"type":"object","properties":{"op":{"type":"string","const":"toolThinking","description":"More of a delegated subagent's reasoning, onto the thinking of the card that started it."},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"id":{"type":"string","description":"The card's id, matched wherever it nests."},"text":{"type":"string"}},"required":["op","index","id","text"],"additionalProperties":false},{"type":"object","properties":{"op":{"type":"string","const":"tool","description":"A tool card's own fields: new, or the latest state of one already there, matched by id wherever it nests. Carries no `children` or `thinking`; a card already there keeps its own."},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"tool":{"$ref":"#/$defs/__schema0"},"parent":{"description":"The card this one nests under, when it is a delegated subagent's own call.","type":"string"}},"required":["op","index","tool"],"additionalProperties":false}]}},"required":["kind","seq","patch"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"fact","description":"One thing about the turn that is not a row: its session, its branch, its cost, a failure."},"seq":{"type":"number","description":"Its position in the run, counting from one. At or below the head's number, it is being replayed."},"fact":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"session"},"sessionId":{"type":"string"},"account":{"description":"Which stored account this session belongs to, as the daemon resolved it for the turn.","type":"string"}},"required":["kind","sessionId"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"worktree"},"branch":{"type":"string"},"base":{"type":"string"},"unenforced":{"type":"boolean"},"sync":{"type":"object","properties":{"commits":{"type":"number"},"blocked":{"type":"array","items":{"type":"string"}}},"required":["commits","blocked"],"additionalProperties":false},"remote":{"type":"string"}},"required":["kind","branch","base"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"init"},"model":{"type":"string"},"prompt":{"type":"object","properties":{"hash":{"type":"string","description":"One short hash over every part."},"parts":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"Each part's own short hash or value, by name."}},"required":["hash","parts"],"additionalProperties":false}},"required":["kind","model"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"terminal"},"session":{"type":"string"}},"required":["kind","session"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"browser"},"session":{"type":"string"}},"required":["kind","session"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"commands"},"items":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"What to type, without the leading slash."},"description":{"type":"string","description":"What it does."},"hint":{"description":"What its argument should look like, shown after the name.","type":"string"}},"required":["name","description"],"additionalProperties":false}}},"required":["kind","items"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"usage"},"account":{"type":"string"},"costUsd":{"type":"number"},"inputTokens":{"type":"number"},"outputTokens":{"type":"number"},"cacheReadTokens":{"type":"number"},"cacheCreationTokens":{"type":"number"},"durationMs":{"type":"number"},"numTurns":{"type":"number"},"openingCacheReadTokens":{"type":"number"},"openingCacheCreationTokens":{"type":"number"},"promptFingerprint":{"type":"string"}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"status":{"type":"string","enum":["allowed","allowed_warning","rejected"]},"resetsAt":{"type":"number"},"rateLimitType":{"type":"string"},"utilization":{"type":"number"},"kind":{"type":"string","const":"rate_limit_info"},"account":{"type":"string"}},"required":["status","kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"fast_mode"},"state":{"type":"string","enum":["off","cooldown","on"]},"reason":{"type":"string"}},"required":["kind","state"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"provider_retry"},"attempt":{"type":"number"},"maxAttempts":{"type":"number"},"nextAttemptAt":{"type":"number"},"status":{"type":"number"}},"required":["kind","attempt"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"account_usage"},"account":{"type":"string"},"windows":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"label":{"type":"string"},"utilization":{"type":"number"},"resetsAt":{"type":"number"},"gates":{"anyOf":[{"type":"string","const":"all"},{"type":"string","const":"none"},{"type":"object","properties":{"models":{"minItems":1,"type":"array","items":{"type":"string","minLength":1}}},"required":["models"],"additionalProperties":false}]}},"required":["kind","utilization","gates"],"additionalProperties":false}}},"required":["kind","windows"],"additionalProperties":false},{"type":"object","properties":{"tokens":{"type":"number","description":"How much the latest request sent, all told."},"contextWindow":{"type":"number","description":"How much the model can hold. The gap between these two is how close the conversation is to being compacted."},"cachedAt":{"description":"When that request last touched the provider's prompt cache, in milliseconds. The cache's clock runs from here, since a read refreshes it as a write does.","type":"number"},"cacheTtlMs":{"description":"How long that cache entry lives from `cachedAt`, in milliseconds.","type":"number"},"kind":{"type":"string","const":"context_usage"}},"required":["tokens","contextWindow","kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"agent_status"},"key":{"type":"string","description":"Which entry: a later frame with the same key replaces it."},"text":{"description":"The entry's words, one or more lines; null clears it. Every entry is gone once the turn ends, whatever it last said.","type":["string","null"]},"source":{"description":"Who set it, when the runtime named one.","type":"string"}},"required":["kind","key","text"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"mode"},"mode":{"type":"string","enum":["default","plan","bypassPermissions"]}},"required":["kind","mode"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"error"},"message":{"type":"string"},"code":{"type":"string","enum":["session-not-found","rate_limit","codex-advisory","codex-reauth","acp-auth-required","claude-reauth","claude-token-refused","claude-not-entitled","provider-outage","trial-unavailable","trial-model-unavailable","trial-exhausted","unknown-command","grok-model-invalid","codex-model-invalid","model-unavailable","context-window-too-small","model-helper-only","privacy-unshielded","context-overflow","subscription-required","agent-busy","sandbox-memory-low","turn-cap","harness-incomplete","engine-version-floor","safeguard-flagged"]},"refusal":{"type":"object","properties":{"category":{"description":"The classifier's category as the provider named it (cyber, bio, reasoning_extraction, …), when it did.","type":"string"},"resumeAt":{"description":"The last session entry before the stopped response: a retry resumes the session there, so the model never sees what was stopped.","type":"string"}},"additionalProperties":false},"engine":{"type":"object","properties":{"id":{"type":"string","description":"Which engine (e.g. claude)."},"running":{"description":"The version that was refused, when the provider named it.","type":"string"},"floor":{"type":"string","description":"The lowest version the provider will accept."}},"required":["id","floor"],"additionalProperties":false},"resetsAt":{"type":"number"},"account":{"description":"Which of the provider's accounts served (or was refused for) the turn, where the sandbox holds it.","type":"string"},"autoResume":{"type":"string","enum":["scheduled","available"]},"nextAt":{"type":"number"},"held":{"type":"object","properties":{"ran":{"type":"boolean"},"contextTokens":{"type":"number"},"handoffTokens":{"type":"number"},"moving":{"type":"string"}},"required":["ran"],"additionalProperties":false},"outage":{"type":"object","properties":{"retryAt":{"type":"number"}},"required":["retryAt"],"additionalProperties":false},"retries":{"type":"object","properties":{"made":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Automatic re-runs already sent for this turn."},"max":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many the ladder may send before it stands down."}},"required":["made","max"],"additionalProperties":false},"memory":{"type":"object","properties":{"limitBytes":{"type":"number","description":"The cgroup's ceiling: what a raise would move."},"residentBytes":{"type":"number","description":"memory.current, the resident charge alone."},"swapBytes":{"type":"number","description":"memory.swap.current; 0 when swap is off or unaccounted."}},"required":["limitBytes","residentBytes","swapBytes"],"additionalProperties":false},"unattended":{"type":"boolean"}},"required":["kind","message"],"additionalProperties":false}]}},"required":["kind","seq","fact"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"end","description":"The run is over and every frame has been delivered. A stream that closes without this was dropped mid-run, so re-attach rather than assuming the turn finished."}},"required":["kind"],"additionalProperties":false}],"$defs":{"__schema0":{"type":"object","properties":{"id":{"type":"string","description":"The call's id."},"name":{"type":"string","description":"Which tool."},"category":{"type":"string","enum":["read","edit","delete","move","search","execute","think","fetch","other"],"description":"What kind of thing it does: read, edit, delete, move, search, run, think, fetch. Named the same way whatever the backend called the tool."},"status":{"type":"string","enum":["pending","in_progress","completed","failed"],"description":"How it went."},"target":{"description":"What it acted on, in one line: a file, a command, an address.","type":"string"},"locations":{"description":"The files it touched.","type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The file, as a workspace path, whatever directory the tool was run from."},"line":{"description":"Which line, counting from one.","type":"number"}},"required":["path"],"additionalProperties":false}},"content":{"description":"What it produced: text, a change to a file, or a picture.","type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"text","description":"Plain output."},"text":{"type":"string","description":"What the tool said."}},"required":["type","text"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"diff","description":"A change to a file."},"path":{"type":"string","description":"Which file, as a workspace path."},"oldText":{"description":"What was there. Absent for a new file, or where the previous contents are not known.","type":"string"},"newText":{"type":"string","description":"What is there now."},"truncated":{"description":"One of the two sides was too large to send whole.","type":"boolean"}},"required":["type","path","newText"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"image","description":"A picture the tool produced."},"path":{"type":"string","description":"Where it is, as a workspace path. A path rather than the bytes, because the workspace already serves it, sending it inline would bloat every stored record, and this way the picture stays openable afterwards."}},"required":["type","path"],"additionalProperties":false}]}},"children":{"description":"Calls a delegated subagent made, nested under the call that started it, so a reopened conversation redraws the delegation rather than collapsing it into one result.","type":"array","items":{"$ref":"#/$defs/__schema0"}},"nested":{"description":"How many calls sit under this one, present in place of `children` when they were left behind. A transcript page does that, since a settled delegation draws collapsed; ask for the call's own children to fill it in.","type":"integer","minimum":0,"maximum":9007199254740991},"thinking":{"description":"What the agent was reasoning about around this call.","type":"string"},"subagent":{"description":"The helper this call started, as the daemon's registry sees it: what it is, how it is going, what it has spent. What a card can say about a backgrounded child whose result is minutes away.","type":"object","properties":{"id":{"description":"Its own id, where that is not the card's: a spawned subagent is named by its own conversation, which is what the roster, `wait` and its own chat call it. Absent, the card's id is its id, as it is for one the runtime started in-process.","type":"string"},"kind":{"type":"string","enum":["subagent","spawned"]},"agentType":{"type":"string"},"description":{"type":"string"},"model":{"type":"string"},"provider":{"type":"string"},"background":{"type":"boolean"},"status":{"type":"string","enum":["pending","running","blocked","completed","failed","killed","paused"]},"tokens":{"type":"number"},"toolUses":{"type":"number"},"lastTool":{"type":"string"},"summary":{"type":"string"},"error":{"type":"string"},"verification":{"type":"object","properties":{"state":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Whether anything proved its work: a check passed after its last edit, it changed code and nothing checked it, a check ran and failed, or it changed no code at all."},"paths":{"description":"The code files it changed, most recent last. The first few; the record holds the rest.","type":"array","items":{"type":"string"}},"check":{"description":"The command that spoke: the one that cleared it, or the one that failed. Named rather than summarised, so a targeted test is not read as the whole suite.","type":"string"}},"required":["state"],"additionalProperties":false}},"required":["kind","status"],"additionalProperties":false}},"required":["id","name","category","status"],"additionalProperties":false}}},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event","data"]},{"type":"object","properties":{"event":{"const":"done"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"error"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]}]}}}}}}},"/agent/reply":{"post":{"operationId":"agent.reply","summary":"Answer a question the agent asked","description":"Un-parks a turn that is waiting on you: approving a plan, choosing between options, or permitting a tool. The turn picks up where it stopped.","tags":["One agent"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"plan","description":"Answering a plan the agent proposed."},"requestId":{"type":"string","minLength":1,"description":"Which card you are answering, from the frame that raised it."},"approve":{"type":"boolean","description":"Whether to go ahead. Approving means the plan then runs without a prompt per tool, because being asked whether a plan you just approved may run its first command is not a question worth having."},"feedback":{"description":"Why not, which goes back to the model as the reason.","type":"string"}},"required":["kind","requestId","approve"]},{"type":"object","properties":{"kind":{"type":"string","const":"question","description":"Answering a question the agent asked."},"requestId":{"type":"string","minLength":1,"description":"Which card you are answering."},"answers":{"description":"What you chose, keyed by the question, with the chosen labels or your own words.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"string"}}},"attachments":{"description":"Files that go with your own-words answer, keyed by the question, as workspace-relative paths of files already uploaded (a screenshot, a mock-up).","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"string"}}},"cancelled":{"description":"Dismissing it instead, which tells the agent to carry on using sensible defaults rather than leaving it waiting.","type":"boolean"}},"required":["kind","requestId"]},{"type":"object","properties":{"kind":{"type":"string","const":"permission","description":"Answering a request to use a tool."},"requestId":{"type":"string","minLength":1,"description":"Which card you are answering."},"decision":{"type":"string","enum":["once","always","everything","deny"],"description":"Once allows this call alone; always allows what the request's always-label names (a tool, a rule, a secret) for the rest of the conversation; everything allows this call and every later request in this conversation that an allow-once could settle, until it is taken back on Grants; no blocks it. A request that always asks (alwaysAsks) reads everything as once."},"feedback":{"description":"Why not, which goes back to the model as the reason.","type":"string"},"child":{"description":"For a request to start a subagent: what to start it on instead of what the agent asked for. It replaces the whole run (model, account, effort and the rest), not only the fields it names. Ignored with a no, and on any other request.","type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves it."},"model":{"type":"string","minLength":1,"description":"Which of that provider's models."},"harness":{"description":"Which agentic loop runs it. Absent is the provider's own.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which of that provider's connected accounts pays for it. Absent is whichever has the most room when it starts.","type":"string"},"effort":{"description":"How hard it thinks, where the model offers a choice. Absent is the model's own default.","type":"string"},"thinking":{"description":"Whether it reasons before it answers, where the model offers the choice.","type":"boolean"},"fast":{"description":"Whether it asks for the faster rate, at the higher price.","type":"boolean"}},"required":["provider","model"]}},"required":["kind","requestId","decision"]},{"type":"object","properties":{"kind":{"type":"string","const":"browser_help","description":"Answering a request for help in the agent's browser: a captcha, a password it does not hold, a check on your phone."},"requestId":{"type":"string","minLength":1,"description":"Which card you are answering."},"helped":{"type":"boolean","description":"Whether you cleared it. Yes means the turn carries on from the page as you left it; no tells the agent so, and it moves on rather than waiting for ever."},"note":{"description":"Anything the agent should know, which goes back to it either way.","type":"string"}},"required":["kind","requestId","helped"]},{"type":"object","properties":{"kind":{"type":"string","const":"terminal_help","description":"Answering a request for help at a terminal: a code to type, a confirmation only a person can give."},"requestId":{"type":"string","minLength":1,"description":"Which card you are answering."},"helped":{"type":"boolean","description":"Whether you did it. Yes also hands the agent what the terminal now says, because a person answering a prompt is exactly the moment the agent cannot see."},"note":{"description":"Anything the agent should know, which goes back to it either way.","type":"string"}},"required":["kind","requestId","helped"]},{"type":"object","properties":{"kind":{"type":"string","const":"capability_offer","description":"Answering a request to connect something the agent needs."},"requestId":{"type":"string","minLength":1,"description":"Which card you are answering."},"connect":{"type":"boolean","description":"Yes keeps the agent waiting while you set it up, and it carries on the moment the connection comes alive. No tells it to continue without. The reply itself connects nothing: setting it up is still your own doing."}},"required":["kind","requestId","connect"]},{"type":"object","properties":{"kind":{"type":"string","const":"payment_offer","description":"Answering a request to pay for something."},"requestId":{"type":"string","minLength":1,"description":"Which card you are answering."},"approve":{"type":"boolean","description":"Yes releases exactly one payment. Anything else spends nothing. This click is the only way the money can move."}},"required":["kind","requestId","approve"]},{"type":"object","properties":{"kind":{"type":"string","const":"credential_offer","description":"Releasing a credential the agent may only use once a named person says so."},"requestId":{"type":"string","minLength":1,"description":"Which card you are answering."},"approve":{"type":"boolean","description":"Yes releases it, as far as the card says (this one use, or the rest of the conversation). Only the people the card names can answer at all, yes or no."}},"required":["kind","requestId","approve"]}]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/agent/steer":{"post":{"operationId":"agent.steer","summary":"Interrupt a running turn","description":"Slips a message into a turn already under way, without stopping it. This is how you redirect an agent mid-thought rather than waiting for it to finish being wrong. Give the message an id, and sending it again after a lost answer is met with what became of it the first time rather than saying it twice.","tags":["One agent"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","minLength":1,"description":"Which running conversation to interrupt."},"text":{"type":"string","maxLength":20000,"description":"What to say to it. It arrives mid-turn without stopping the turn."},"messageId":{"description":"Your id for this message. Sending again under an id the sandbox already took is answered with what it did with it the first time, never a second delivery. Leave it out and the sandbox names the message itself.","type":"string","minLength":1,"maxLength":128},"attachments":{"description":"Files to send with it, as workspace paths. A screenshot dropped in mid-turn with no words is a legitimate thing to send.","maxItems":20,"type":"array","items":{"type":"string","minLength":1}},"mentions":{"description":"Workspace paths the message mentions with `@`. Unlike attachments, one that escapes the workspace or names no file is ignored rather than refused.","maxItems":20,"type":"array","items":{"type":"string","minLength":1}},"editorContext":{"description":"What you have open, folded in so that pointing words resolve.","type":"object","properties":{"file":{"type":"string","minLength":1,"description":"The file open in the editor, as a workspace path."},"startLine":{"description":"First line of the selection, counting from one. Leave both out when the whole file is the context.","type":"integer","minimum":1,"maximum":9007199254740991},"endLine":{"description":"Last line of the selection, counting from one.","type":"integer","minimum":1,"maximum":9007199254740991},"selection":{"description":"The selected text itself. Cut it down before sending if it is long: this is context, not an upload.","type":"string","maxLength":20000}},"required":["file"]}},"required":["conversationId","text"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"delivered":{"type":"string","enum":["started","steered","queued"],"description":"What became of the message: it started a turn, it was said into the turn already running, or it waits in the conversation's queue for the next one."},"run":{"description":"The run the message is in: the turn it started, or the one it was said into. Hand it back when you attach. Absent while the message waits in the queue.","type":"string"},"duplicate":{"description":"The sandbox had already taken a message under this id: this is what became of it, and nothing new happened.","type":"boolean","const":true}},"required":["delivered"],"additionalProperties":false}}}}}}},"/agent/stop":{"post":{"operationId":"agent.stop","summary":"Stop a turn now","description":"Cancels the running turn inside the sandbox. Whatever it had already written to disk stays written, and whatever waits in the conversation's queue is held there, for everyone, until somebody resumes it. Name the run you mean: a stop that arrives after that run has ended cancels nothing, rather than whatever turn started next.","tags":["One agent"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"conversationId":{"type":"string","minLength":1,"description":"Which conversation's running turn to cancel."},"run":{"type":"string","minLength":1,"description":"The run you mean to cancel, as starting or attaching to it named it. If another turn has started since, nothing is cancelled and the answer names the one running instead."}},"required":["conversationId","run"]},{"type":"object","properties":{"conversationId":{"type":"string","minLength":1,"description":"Which conversation's running turn to cancel."},"messageId":{"type":"string","minLength":1,"description":"The message you sent, while its run is not named yet: cancels the turn it is in. If none is, nothing is cancelled: the message has not become a turn, or its turn has already ended."}},"required":["conversationId","messageId"]},{"type":"object","properties":{"conversationId":{"type":"string","minLength":1,"description":"Which conversation's running turn to cancel."},"live":{"type":"boolean","const":true,"description":"Cancel whatever turn is running now, whichever that is. Only for a turn you cannot name: one that has no run to attach to."}},"required":["conversationId","live"]}]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"stopped":{"type":"boolean","description":"Whether a turn was cancelled."},"running":{"description":"The run that is live instead of the one you named, left running. Absent when nothing else runs.","type":"string"}},"required":["stopped"],"additionalProperties":false}}}}}}},"/agent/queue/edit":{"post":{"operationId":"agent.queueEdit","summary":"Reword a waiting message","description":"Changes what a message waiting in the conversation's queue says, keeping its place. Name the revision you read it at: if somebody changed it since, on this device or another, nothing is changed and you are told so.","tags":["One agent"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","minLength":1,"description":"Whose queue."},"id":{"type":"string","minLength":1,"description":"Which waiting message."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The message's revision as you read it. If it has been changed since, from this window or another, nothing happens."},"text":{"type":"string","description":"What the message should say instead. It keeps its place in the queue."}},"required":["conversationId","id","revision","text"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false}}}}}}},"/agent/queue/remove":{"post":{"operationId":"agent.queueRemove","summary":"Take back a waiting message","description":"Removes a message from the conversation's queue before the agent gets it. Name the revision you read it at: a message somebody reworded since is left alone, so you never take back words you have not seen.","tags":["One agent"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","minLength":1,"description":"Whose queue."},"id":{"type":"string","minLength":1,"description":"Which waiting message."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The message's revision as you read it. If it has been changed since, from this window or another, nothing happens."}},"required":["conversationId","id","revision"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false}}}}}}},"/agent/queue/resume":{"post":{"operationId":"agent.queueResume","summary":"Let waiting messages go","description":"Releases a queue held after a stop or a refusal: what waits goes out now as one turn when nothing is running, or after the running turn otherwise. Name who serves that turn when the conversation has been re-pointed since the messages were queued.","tags":["One agent"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","minLength":1,"description":"Whose queue to let go."},"routing":{"description":"Who serves the turn the waiting messages start, when the conversation has been re-pointed since they were queued: the usual answer to a refusal that held them. Leave it out to send them as they were queued.","type":"object","properties":{"agent":{"type":"string","minLength":1,"description":"Which provider serves the re-run."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop runs it."},"account":{"description":"Which of that provider's accounts pays for it. Leave it out to keep the account the conversation runs on, or, on another provider, to take whichever of its accounts can serve with the most room. Moving to another account of the same provider is `switchAccount`'s job; naming one here still works.","type":"string"},"model":{"description":"Which model. Leave it out to keep the one the refused turn named.","type":"string"},"carry":{"description":"When the account changes, keep the provider session (the model keeps everything, and re-reads all of it once on the other account) rather than opening a fresh one seeded from the record. Ignored when the provider changes, or when nothing changes.","type":"boolean"}},"required":["agent","harness"]}},"required":["conversationId"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"run":{"description":"The turn the waiting messages started. Absent when a turn was already running, and they go after it.","type":"string"},"queue":{"description":"The queue the release left: what still waits once the turn took what it could. Absent from sandboxes older than the field.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false}},"additionalProperties":false}}}}}}},"/agent/queue/schedule":{"post":{"operationId":"agent.queueSchedule","summary":"Reschedule waiting messages","description":"Books what waits in the conversation's queue to go out by itself at another instant, or once another conversation has finished and its work has landed, holding it until then. Works on a queue that is held for any reason, or on messages waiting behind a running turn. A time already past, or a conversation with nothing left to land, lets them go now.","tags":["One agent"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","minLength":1,"description":"Whose queue."},"sendAt":{"description":"Send what waits at this instant (epoch milliseconds) instead. At most a month ahead; an instant already past sends it now.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"sendAfter":{"description":"Send what waits once that conversation has finished and all of its work has landed in the workspace, instead. Sent now when it has nothing running and nothing left to land.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["conversationId"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false}}}}}}},"/agent/resume":{"post":{"operationId":"agent.resume","summary":"Run a refused turn again","description":"Sends the same turn again when the model provider's allowance refused it, with everything it originally carried except who serves it: the caller may name a different provider, harness or account, which is the usual answer to a spent allowance. It repeats the request rather than adding a new message to the conversation, so pressing it twice costs nothing and the agent is never told to continue work it has not started.","tags":["One agent"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","minLength":1,"description":"Which conversation's held turn to run again."},"routing":{"description":"Who serves the re-run, when the conversation has been re-pointed since it was refused. Leave it out to run it on whatever the turn carried.","type":"object","properties":{"agent":{"type":"string","minLength":1,"description":"Which provider serves the re-run."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop runs it."},"account":{"description":"Which of that provider's accounts pays for it. Leave it out to keep the account the conversation runs on, or, on another provider, to take whichever of its accounts can serve with the most room. Moving to another account of the same provider is `switchAccount`'s job; naming one here still works.","type":"string"},"model":{"description":"Which model. Leave it out to keep the one the refused turn named.","type":"string"},"carry":{"description":"When the account changes, keep the provider session (the model keeps everything, and re-reads all of it once on the other account) rather than opening a fresh one seeded from the record. Ignored when the provider changes, or when nothing changes.","type":"boolean"}},"required":["agent","harness"]}},"required":["conversationId"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"run":{"type":"string","description":"The id of the run that just started. Hand it back when you attach: the stream always opens on the conversation's newest run, so a different id there means another turn has started since."}},"required":["run"],"additionalProperties":false}}}}}}},"/agent/account":{"post":{"operationId":"agent.switchAccount","summary":"Move a conversation to another account","description":"Points the conversation at another connected account of the provider it runs on, for every turn from now on. It starts nothing by itself: with `run`, a turn held by a spent allowance or a stop runs again at once on that account, which is how a refused turn continues elsewhere. Without `carry` the next turn opens a fresh session seeded from the record.","tags":["One agent"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","minLength":1,"description":"Which conversation to move."},"account":{"type":"string","minLength":1,"description":"Which of the conversation's provider's connected accounts pays for its turns from now on."},"carry":{"description":"Keep the provider session across the move (the model keeps everything, and re-reads all of it once on the other account) rather than opening a fresh one seeded from the record.","type":"boolean"},"run":{"description":"Also run a turn that a spent allowance, a stop or a refusal is holding, at once on the new account. Leave it out to only move the conversation: a held turn stays held until something asks for it.","type":"boolean"}},"required":["conversationId","account"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"run":{"description":"The run that re-ran a held turn on the new account, when one was waiting: attach to it. Absent when nothing was held.","type":"string"}},"additionalProperties":false}}}}}}},"/agent/rewind":{"post":{"operationId":"agent.rewind","summary":"Go back to an earlier message","description":"Puts the files back as they stood at that point, drops every message after it, and forgets what the model remembered, so the next thing you say starts from there cleanly. Refused while a turn is running, because a restore cannot overwrite files an agent is editing; refused for a message with no saved state to return to; and refused when that position no longer holds the message you named, because the conversation moved since you read it.","tags":["One agent"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","minLength":1,"description":"Which conversation to rewind."},"index":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which message to go back to, counting from the start. It is also how many messages survive: rewinding to the first keeps none of them and puts the files back to before it ran."},"messageId":{"type":"string","minLength":1,"description":"The id of the message at that position, as its row names it. If that position now holds a different message, nothing is rewound: the transcript has moved since you read it."}},"required":["conversationId","index","messageId"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"snapshot":{"description":"The saved point the files were put back to. Absent for a conversation working in its own copy, whose rewind moved a branch rather than the shared timeline.","type":"string"},"dropped":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"How many messages were removed."}},"required":["dropped"],"additionalProperties":false}}}}}}},"/agent/commands":{"get":{"operationId":"agent.commands","summary":"Shortcut commands the agent knows","description":"The commands a provider published the last time one of its turns ran, so a composer can offer them before this conversation has run anything. A running turn's own list wins over this one.","tags":["One agent"],"parameters":[{"name":"agent","in":"query","schema":{"description":"Whose commands to read. Leave it out for Claude.","type":"string","minLength":1},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"commands":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"What to type, without the leading slash."},"description":{"type":"string","description":"What it does."},"hint":{"description":"What its argument should look like, shown after the name.","type":"string"}},"required":["name","description"],"additionalProperties":false},"description":"The shortcut commands, as the provider last published them."}},"required":["commands"],"additionalProperties":false}}}}}}},"/agent/route-chat":{"post":{"operationId":"agent.routeChat","summary":"Choose what a new chat opens on","description":"Reads a new chat's opening message once and answers whichever of two questions it still has: the model, effort and account that conversation should run on, from what is connected and still has allowance left, and which of this sandbox's personas should handle it. `model` and `persona` on the ask say which halves to answer, and only those are put to the model. Asked once per chat, on the message actually sent, and never again: every turn after it runs on what the chat is wearing, which you are free to change. Answers with nothing, and a reason, whenever it cannot choose — a chat is never held up by this.","tags":["One agent"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string","minLength":1,"maxLength":20000,"description":"The message a new chat is about to open with."},"paths":{"default":[],"description":"Workspace paths the message names: uploads, @-mentions, the editor's own file. How much real code the work touches, and which persona's ground it stands on.","maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":500}},"folder":{"description":"The workspace folder the chat was opened in, when it was opened in one.","type":"string","maxLength":200},"editorContext":{"description":"Whether the message carries a file and selection the user pointed at, so it is about real code.","type":"boolean"},"planMode":{"description":"Whether the chat opens in plan mode, which is a request to think before acting.","type":"boolean"},"model":{"type":"boolean","description":"Whether to choose the model, effort and account: true when the chat is on Auto and nothing has been picked by hand."},"persona":{"type":"boolean","description":"Whether to choose the persona: true when persona matching is on and the chat has not been pointed at one by hand."}},"required":["prompt","model","persona"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"persona":{"description":"The persona half's answer, present only when it was asked for.","type":"object","properties":{"id":{"description":"The persona this message belongs to, or absent when none does.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"reason":{"type":"string","description":"Why, in the one clause a chat can show. Present whether or not a persona was named."}},"required":["reason"],"additionalProperties":false},"model":{"description":"The model half's answer, present only when it was asked for.","type":"object","properties":{"pick":{"description":"What the conversation should run on, or absent when nothing could be chosen and the usual pick stands.","type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves the conversation."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"effort":{"description":"How hard it should think, where the model offers a choice. Absent takes the model's own default.","type":"string"},"account":{"description":"Which connected account pays, by its daemon-minted id. Absent leaves it to whichever account has the most headroom.","type":"string"}},"required":["provider","model"],"additionalProperties":false},"reason":{"type":"string","description":"Why, in the one clause a chat can show. Present whether or not a model was named."}},"required":["reason"],"additionalProperties":false},"judge":{"description":"Which model answered, as `provider:model`, so the chat can name what the reading cost. Absent when no model was reached at all.","type":"string"}},"additionalProperties":false}}}}}}},"/agent/refusals":{"get":{"operationId":"agent.refusals","summary":"The last time each provider said no","description":"What each model provider most recently refused and why. Read this alongside an account's usage: the usage says how full it was when last checked, this says whether it has since started turning work away.","tags":["One agent"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"refusals":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"object","properties":{"at":{"type":"number","description":"When it refused, in milliseconds."},"kind":{"type":"string","enum":["limit","auth","entitlement"],"description":"Three different noes, kept apart because what fixes each is different. A spent allowance is answered by waiting; a refused credential by signing in again; and an entitlement refusal, where somebody has switched this off for your seat, by neither of those. That last one authenticates fine and reports healthy limits the whole time it refuses everything."},"message":{"type":"string","description":"The provider's own words, verbatim. The only part that says which limit or which credential."},"account":{"description":"Which account was serving, where that is known.","type":"string"},"model":{"description":"Which model the refused turn was on, where that is known.","type":"string"},"resetsAt":{"description":"When the provider said to try again, in epoch seconds, for a spent allowance where it named one. Until then the refusal stands whatever a reading says; after it, it is over.","type":"number"}},"required":["at","kind","message"],"additionalProperties":false},"description":"The most recent refusal per provider. Read alongside an account's usage: that says how full it was when last checked, this says whether it has since started saying no."}},"required":["refusals"],"additionalProperties":false}}}}}}},"/agents":{"get":{"operationId":"agents.list","summary":"Every live conversation","description":"The fleet as the board draws it: each conversation with its title, what it is doing, when it last moved and whether anybody has read it since. Archived conversations are not in here.","tags":["The fleet"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"agents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The conversation id, which is how every other call addresses it."},"sessionId":{"description":"The provider session behind the last turn. It is retired whenever the model or account changes.","type":"string"},"title":{"description":"What to call it: the first prompt cut to one line, unless somebody renamed it.","type":"string"},"titleAction":{"description":"One word for the kind of work the naming pass read in it (fix, audit, redesign). Never part of the displayed name: a board tints and glyphs a card by it. Absent for a title nothing named an action for.","type":"string"},"status":{"type":"string","enum":["idle","running","awaiting","stopping","dismissing","stopped","resuming","landing","ready","landed","conflict","error","interrupted"],"description":"What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; dismissing is the same window for a question waved away, which ends the turn too but owes the user nothing; resuming means the sandbox is already putting right whatever killed the turn; landing means its work is being carried into the workspace right now, and nothing may act on its branch until that settles."},"failure":{"description":"Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.","type":"string"},"failureCode":{"description":"Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.","type":"string"},"limitResetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant.","type":"number"},"limitHeld":{"description":"Whether the refused turn is held whole, so sending again re-runs it instead of appending to it.","type":"boolean"},"limitScheduled":{"description":"Whether the held turn is already booked to go again at the reset, so nobody has to press anything.","type":"boolean"},"limitMoving":{"description":"The account the held turn is being moved to by the owner's policy, while that move is booked.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it runs on."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop it runs on."},"runner":{"description":"The runner this conversation runs on. Absent means this sandbox.","type":"string"},"startIn":{"description":"Which folder it opened in, relative to the workspace root. Absent means the root.","type":"string"},"actsAs":{"description":"Which persona its first turn acted as. Absent for an ordinary chat.","type":"string"},"lastActsAs":{"description":"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.","type":"string"},"model":{"description":"What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked.","type":"string"},"effort":{"description":"How hard that turn was told to think.","type":"string"},"thinking":{"description":"Whether that turn showed its reasoning.","type":"boolean"},"fast":{"description":"Whether that turn asked for higher speed. What was asked for, not what was served.","type":"boolean"},"account":{"description":"Which connected account paid for it.","type":"string"},"branch":{"description":"The branch its private copy works on. Absent for a conversation that works directly in the shared tree.","type":"string"},"autoLand":{"description":"This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case.","type":"boolean"},"limitPolicy":{"type":"string","enum":["wait","resend","move"]},"outagePolicy":{"type":"string","enum":["wait","retry"]},"stopPolicy":{"type":"string","enum":["wait","retry"]},"landRequested":{"description":"A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting.","type":"object","properties":{"email":{"type":"string","description":"Who asked."},"name":{"description":"Their display name.","type":"string"},"at":{"type":"number","description":"When they asked, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"reactions":{"description":"What people have marked this conversation with, one entry per emoji, in the order the emoji were first used. Absent means nobody has marked it, which is most conversations.","type":"array","items":{"type":"object","properties":{"emoji":{"type":"string","description":"The mark itself, one emoji, carried as the character rather than as a name that would need a table on both sides."},"by":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Who it was, as the sandbox verified them."},"name":{"description":"What to call them, when their sign-in carried a name. Absent leaves the address to stand for them.","type":"string"},"at":{"type":"number","description":"When they marked it, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"description":"Everyone wearing this mark, oldest first, each by name. Carried whole rather than as a count, because a chip reading 3 that cannot say whose is a number nobody can answer. Never empty: the last person taking theirs back takes the whole chip with it."}},"required":["emoji","by"],"additionalProperties":false}},"origin":{"description":"Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"startedBy":{"description":"Who asked for the first turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a child another conversation spawned. Absent when nothing was verified (a wake, a loopback caller).","type":"string"},"areas":{"description":"Which named areas of the workspace this conversation was started within, latched from whoever asked for the first turn. Absent means its starter held the whole workspace, which is why a fenced member is not shown it.","type":"array","items":{"type":"string"}},"owner":{"description":"The member answerable for this conversation: set from whoever started it, inherited from the parent by a spawned child, moved by handing it over. Absent means nobody has claimed it yet.","type":"object","properties":{"email":{"type":"string","description":"Who answers for this conversation, as the sandbox verified them."},"name":{"description":"What to call them, when the sign-in that made them its owner carried a name. Absent leaves the address to stand for them.","type":"string"},"since":{"type":"number","description":"When they became its owner, in milliseconds."}},"required":["email","since"],"additionalProperties":false},"forkedFrom":{"description":"The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"type":"string","enum":["then","now"]}},"required":["conversationId","index","files"],"additionalProperties":false},"base":{"description":"The commit its private copy started from, shortened.","type":"string"},"costUsd":{"description":"What it has cost so far, in dollars. A subagent's spend is its own and is not folded in here.","type":"number"},"inputTokens":{"description":"Uncached input tokens, excluding cache reads and cache writes.","type":"number"},"outputTokens":{"description":"Tokens received.","type":"number"},"contextTokens":{"description":"How much of the window the conversation currently fills.","type":"number"},"contextWindow":{"description":"How large that window is.","type":"number"},"promptCache":{"description":"When this conversation's prompt cache was last kept alive and how long it lasts, which together say when picking the conversation up stops being cheap. Absent when the provider publishes nothing to ground it on.","type":"object","properties":{"at":{"type":"number","description":"When its last request touched the provider's prompt cache, in milliseconds."},"ttlMs":{"type":"number","description":"How long that entry lives from `at`, in milliseconds."},"rollsAt":{"description":"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.","type":"number"},"keepableUntil":{"description":"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.","type":"number"}},"required":["at","ttlMs"],"additionalProperties":false},"keepWarm":{"description":"Whether the sandbox is keeping this conversation's prompt cache warm while it sits idle, how that is going, or why it stopped. Absent when nobody asked for it.","type":"object","properties":{"since":{"type":"number","description":"When keeping it warm started, in milliseconds."},"until":{"type":"number","description":"When it stops by itself, in milliseconds: the time asked for, shortened to what the sandbox can honestly keep, which is never past the point where refreshing costs more than re-reading, nor past the date change that rewrites the prompt."},"refreshes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Refreshes sent so far."},"readTokens":{"description":"How much the last refresh read back from the provider's cache, in tokens: the proof the cache was still there.","type":"number"},"ended":{"description":"Why keeping it warm stopped before anyone picked the conversation up. Absent while it is still being kept.","type":"object","properties":{"at":{"type":"number","description":"When it stopped, in milliseconds."},"reason":{"type":"string","enum":["elapsed","allowance","changed","cold","failed"],"description":"Why: `elapsed` the time asked for ran out; `allowance` the account reached the reserve kept for real work, or the provider refused a refresh for its limit; `changed` what the next turn would send no longer matches the cache (a part of the prompt, the date in it, or the conversation's session or account); `cold` the cache was gone, expired before a refresh could run or found missing by one; `failed` a refresh failed."},"detail":{"description":"The specifics, when there are any: which parts of the prompt changed, how full the account was, or the failure's own words.","type":"string"}},"required":["at","reason"],"additionalProperties":false}},"required":["since","until","refreshes"],"additionalProperties":false},"activity":{"description":"What it is doing at this moment.","type":"object","properties":{"tool":{"description":"The last tool it reached for.","type":"string"},"target":{"description":"What it reached for that tool with: a file, a command, a URL.","type":"string"},"todo":{"description":"The item on its own list that it is working through.","type":"string"}},"additionalProperties":false},"checklist":{"description":"How far it is through its own checklist. Absent for a conversation that kept no list, which is most short ones.","type":"object","properties":{"done":{"type":"number","description":"Items it has completed."},"total":{"type":"number","description":"Items on the list. Never zero: a conversation that kept no list carries no clause at all."}},"required":["done","total"],"additionalProperties":false},"landedMessageDraft":{"description":"The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes.","type":"object","properties":{"startedAt":{"type":"number","description":"When the drafting began, in milliseconds."},"steps":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider was asked."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"status":{"type":"string","enum":["asking","answered","refused","skipped"],"description":"How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."},"at":{"description":"When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time.","type":"number"},"ms":{"description":"How long it took. Absent while it is still being asked.","type":"number"},"reason":{"description":"Why it refused, in its own words.","type":"string"}},"required":["provider","model","status"],"additionalProperties":false},"description":"Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."},"outcome":{"description":"How it ended. Absent means it is still going.","type":"string","enum":["written","failed"]},"reason":{"description":"The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words.","type":"string"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"}},"required":["startedAt","steps"],"additionalProperties":false},"landedMessage":{"description":"What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false},"startedAt":{"description":"When the running turn started, in milliseconds. Absent when none is running.","type":"number"},"run":{"description":"The run under way right now: what a stop names, so it cannot cancel a turn that started after it was pressed. Absent when none is running, and for a turn with no run to attach to.","type":"string"},"queue":{"description":"Messages waiting for its next turn, and whether they are held. Absent for a conversation nothing has ever waited for.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false},"updatedAt":{"type":"number","description":"When it last did something, in milliseconds. Reading it does not count."},"seenAt":{"description":"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.","type":"number"},"unsentAt":{"description":"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.","type":"number"},"attention":{"type":"object","properties":{"plan":{"type":"boolean","description":"It has proposed a plan and is waiting for a yes."},"question":{"type":"boolean","description":"It has asked you something."},"permission":{"type":"boolean","description":"It wants to use a tool it needs permission for."},"capability":{"type":"boolean","description":"It needs something connected that is not connected yet."},"credential":{"type":"boolean","description":"It is waiting for a named person to release a credential. The one pause that may not be yours to clear, whatever your role."},"conflict":{"type":"boolean","description":"Its work cannot be merged without somebody resolving a clash."},"need":{"description":"It asked a person for something it still needs: a connection, a secret, wider reach, a tool. Absent from a daemon older than needs.","type":"boolean"}},"required":["plan","question","permission","capability","credential","conflict"],"additionalProperties":false,"description":"Which kinds of waiting-for-you it is doing."},"permissionAsk":{"description":"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.","type":"object","properties":{"requestId":{"type":"string","description":"Which request this is: the id an answer to it names."},"ask":{"type":"string","description":"What it asks to do, on one line: the runtime's own sentence, else the tool's short name."}},"required":["requestId","ask"],"additionalProperties":false},"landFailure":{"description":"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.","type":"object","properties":{"reason":{"type":"string","description":"Why it failed, in the words it failed with."},"code":{"description":"Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise.","type":"string"},"at":{"type":"number","description":"When it failed, in milliseconds."}},"required":["reason","at"],"additionalProperties":false},"delivery":{"description":"What became of its last land in the folder on the owner's computer, for a project folder attached to this computer's sandbox. Absent for any other land, and from a sandbox older than it.","type":"object","properties":{"state":{"type":"string","enum":["delivered","partial","waiting","failed","too-large"]},"at":{"type":"number","description":"When this state was reached, in milliseconds."},"project":{"type":"string"},"folder":{"description":"The folder on the owner's computer, as its machine reported it.","type":"string"},"applied":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files written, merged ones included."},"conflicts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"reason":{"type":"string","enum":["edited","link","outside","missing-git"]}},"required":["path","reason"],"additionalProperties":false}},"point":{"description":"The machine's restore point taken before writing.","type":"string"},"reason":{"description":"Why it failed or waits, in a sentence.","type":"string"}},"required":["state","at","project","applied","conflicts"],"additionalProperties":false},"conflictCauses":{"description":"Why its work will not merge, and so who can clear it: your own uncommitted edits, which only you can commit or stash, against a moved main line or an unmergeable binary, which the conversation can redo on its own copy. Absent unless it is refusing to merge.","type":"array","items":{"type":"string","enum":["workspace","diverged","binary"]}},"unfinished":{"description":"What its last turn left open: steps it never completed. Absent for a turn that finished what it started.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"steps":{"description":"The agent's own checklist where that turn left it. Absent for a conversation that kept no list.","type":"object","properties":{"open":{"type":"number","description":"Items on it that were never completed."},"total":{"type":"number","description":"Items on the whole list."},"next":{"description":"The one it would have done next: what it was working through, or the first still waiting.","type":"string"}},"required":["open","total"],"additionalProperties":false},"check":{"description":"The end-of-turn check that was still failing when the turn ended, by name. No longer written: what a check finds as a turn ends is said back to the model and never leaves its work unfinished.","type":"string"}},"required":["at"],"additionalProperties":false},"proof":{"description":"What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"verification":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to."},"check":{"description":"The command that spoke, when one did, so a targeted test is never read as the whole suite.","type":"string"},"unviewed":{"description":"How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none.","type":"number"}},"required":["at","verification"],"additionalProperties":false},"reach":{"description":"Where its last turn's work went when it went somewhere besides its own branch: files changed live outside it, clones of its own holding work that will not land, pushes it made itself. Absent when everything it did is on its branch.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"live":{"description":"Files it changed in place where every conversation reads them, outside its own branch: an installed extension's copy, the sandbox's shared state, the owner's own checkout. Live the moment they were written, never reviewed, and an installed extension's next update replaces them.","maxItems":10,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What it wrote, as it named it: relative to the workspace for the sandbox's shared state, absolute for the owner's own checkout."},"extension":{"description":"The extension, by its manifest id, when what it changed is the installed copy of one. Absent for anything else.","type":"string"}},"required":["path"],"additionalProperties":false}},"liveMore":{"description":"How many more such files there were beyond the ones listed. Absent when all are listed.","type":"number"},"stranded":{"description":"Repositories it cloned inside its own copy and left work in. A clone of its own never rides its land, so what is in one stays there.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"The clone, as a folder relative to its own copy of the workspace."},"uncommitted":{"type":"number","description":"Files changed in it and never committed."},"unpushed":{"type":"number","description":"Commits in it its upstream does not have, or that no remote has when it follows none."}},"required":["dir","uncommitted","unpushed"],"additionalProperties":false}},"strandedMore":{"description":"How many more such clones there were beyond the ones listed. Absent when all are listed.","type":"number"},"published":{"description":"Pushes it made with git that went through: work it sent out of the sandbox itself, never through a land.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"description":"The folder it pushed from, as the command named it. Absent for its own working folder.","type":"string"},"remote":{"description":"The remote it pushed to, as the command named it. Absent when the command named none.","type":"string"},"branch":{"description":"The branch it pushed to, as the command named it. Absent when the command named none.","type":"string"},"command":{"type":"string","description":"The push itself, trimmed to one line."}},"required":["command"],"additionalProperties":false}},"publishedMore":{"description":"How many more such pushes there were beyond the ones listed. Absent when all are listed.","type":"number"}},"required":["at"],"additionalProperties":false},"turns":{"description":"Turns it has finished.","type":"number"},"toolUses":{"description":"Tools it has used, over its whole life.","type":"number"},"subagents":{"description":"Subagents this one started, in-process and spawned alike. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost.","type":"object","properties":{"running":{"type":"number","description":"Subagents working right now."},"total":{"type":"number","description":"Subagents it has started over its whole life."}},"required":["running","total"],"additionalProperties":false},"diff":{"description":"Everything it has written, measured from where it started. Independent of how much has been merged.","type":"object","properties":{"files":{"type":"number","description":"Files touched."},"insertions":{"type":"number","description":"Lines added."},"deletions":{"type":"number","description":"Lines removed."}},"required":["files","insertions","deletions"],"additionalProperties":false},"landedPresence":{"description":"Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it.","type":"object","properties":{"landed":{"type":"number","description":"Paths this conversation merged in."},"present":{"type":"number","description":"How many of them are still there, either pending or committed."},"removedBy":{"description":"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"id":{"type":"string","description":"The conversation that took them out."},"title":{"description":"What that conversation is called.","type":"string"}},"required":["kind","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"description":"Who it was, as the sandbox verified them. Absent when the request carried no identity.","type":"string"},"name":{"description":"What to call them, when their sign-in carried a name.","type":"string"}},"required":["kind"],"additionalProperties":false}]}},"required":["landed","present"],"additionalProperties":false},"loop":{"description":"The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them.","type":"object","properties":{"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How the loop is going."},"iteration":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which round it is on."},"maxIterations":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many rounds it will attempt before giving up."},"goal":{"type":"string","description":"What it is looping towards."}},"required":["state","iteration","maxIterations","goal"],"additionalProperties":false},"workflow":{"description":"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.","type":"object","properties":{"runId":{"type":"string","description":"The run this belongs to, which is how a board groups its steps together."},"name":{"type":"string","description":"The workflow's name."},"step":{"type":"string","description":"Which step this conversation is on now. It moves when steps are chained."},"index":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"This step's place in the workflow, counting from one."},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many steps the workflow has."}},"required":["runId","name","step","index","total"],"additionalProperties":false},"awaitingWake":{"description":"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.","type":"boolean"},"watches":{"description":"Outside conditions this conversation is parked on, each of which will wake it. Absent means none, which is nearly every conversation: an armed watch is why a finished-looking agent starts working by itself, and why a hosted machine will not go idle.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this watch, the same one the agent was given when it armed it."},"note":{"type":"string","description":"The agent's own line on what it is waiting for."},"intervalSeconds":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How often the check runs."},"deadlineAt":{"type":"number","description":"When it gives up and wakes the conversation anyway, in milliseconds. Every watch has one."}},"required":["id","note","intervalSeconds","deadlineAt"],"additionalProperties":false}},"needs":{"description":"What it is waiting on people for and has not got yet, oldest first. Absent means nothing: an open need is why an idle-looking agent still needs you.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["capability","secret","grant","release","environment"],"description":"What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image."},"title":{"type":"string"},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."}},"required":["id","kind","title","status"],"additionalProperties":false}},"jobs":{"description":"Commands this conversation left running in the background, and how the most recent ones ended. Absent means none since the daemon started. A job is running exactly while it has no end.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this job, the one its transcript row names."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"session":{"type":"string","description":"The terminal session its pane runs in, which is what opening it focuses."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When the command exited, in milliseconds. Absent while it runs.","type":"number"},"exitCode":{"description":"The code it exited with. Absent while it runs, or when its exit left none.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"watch":{"description":"The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.","type":"string"},"handed":{"description":"Left running for the person: the agent kept it for them with the `keep` tool (or it holds a port this conversation already left them), so it outlives the turn, wakes nothing and is theirs to stop.","type":"boolean"},"ports":{"description":"The ports it was listening on when its turn ended.","type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"stoppedBy":{"description":"Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.","type":"string","enum":["turn","person","agent"]},"inputWait":{"description":"Present while it sits blocked reading its terminal with nothing moving: a prompt nobody in the sandbox will answer, which only stopping it or a person typing into its terminal ends. Absent while it runs on, and once it ends.","type":"object","properties":{"since":{"type":"number","description":"When it went still, in milliseconds."},"program":{"type":"string","description":"The process blocked reading the terminal, as its command line."}},"required":["since","program"],"additionalProperties":false}},"required":["id","label","session","startedAt"],"additionalProperties":false}},"archivedAt":{"description":"When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board.","type":"number"}},"required":["id","status","provider","harness","updatedAt","attention"],"additionalProperties":false},"description":"The conversations."},"rev":{"type":"number","description":"Which version of the fleet this is. The fleet is published as whole snapshots, so without a version a list read before a change but delivered after it would silently undo that change. Drop any list older than the newest you have already applied."},"held":{"default":[],"description":"Automations waiting at the door for a yes, put alongside the running conversations so needs-you sits beside working rather than on a page nobody opens.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"This waiting item's own id, which approving and rejecting take."},"automationId":{"type":"string","description":"Which automation it came from."},"payload":{"description":"What set it off, kept whole so an approved wake carries the same thing it would have had. Absent for one on a schedule, which carries nothing.","type":"string"},"origin":{"description":"Where the message came from, kept alongside the payload so an approved wake appears on the board exactly as an automatic one would have.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"title":{"description":"What the conversation would be called.","type":"string"},"conversationId":{"description":"The thread this belongs to, when it has one, so approving continues that conversation rather than opening a new one. Without it, one visitor's chat becomes a card per approved message and an agent that meets them again every turn.","type":"string"},"sessionId":{"description":"The provider session that thread last ran on.","type":"string"},"thread":{"description":"Which inbound thread this belongs to, so the approved run continues that thread's memory rather than a fresh one.","type":"string"},"actsAs":{"description":"Which persona the approved run speaks as, decided when it was held.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"createdAt":{"type":"number","description":"When it started waiting, in milliseconds."},"autoRunAt":{"description":"When it goes ahead on its own, in milliseconds, for a hold that is only a delay. Absent for one that genuinely waits on a person.","type":"number"}},"required":["id","automationId","createdAt"],"additionalProperties":false}}},"required":["agents","rev","held"],"additionalProperties":false}}}}}}},"/agents/archived":{"get":{"operationId":"agents.archived","summary":"Conversations put away","description":"The same shape as the live fleet, for the conversations somebody has decided are finished. Their work is kept, and any one of them can be brought back.","tags":["The fleet"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"agents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The conversation id, which is how every other call addresses it."},"sessionId":{"description":"The provider session behind the last turn. It is retired whenever the model or account changes.","type":"string"},"title":{"description":"What to call it: the first prompt cut to one line, unless somebody renamed it.","type":"string"},"titleAction":{"description":"One word for the kind of work the naming pass read in it (fix, audit, redesign). Never part of the displayed name: a board tints and glyphs a card by it. Absent for a title nothing named an action for.","type":"string"},"status":{"type":"string","enum":["idle","running","awaiting","stopping","dismissing","stopped","resuming","landing","ready","landed","conflict","error","interrupted"],"description":"What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; dismissing is the same window for a question waved away, which ends the turn too but owes the user nothing; resuming means the sandbox is already putting right whatever killed the turn; landing means its work is being carried into the workspace right now, and nothing may act on its branch until that settles."},"failure":{"description":"Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.","type":"string"},"failureCode":{"description":"Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.","type":"string"},"limitResetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant.","type":"number"},"limitHeld":{"description":"Whether the refused turn is held whole, so sending again re-runs it instead of appending to it.","type":"boolean"},"limitScheduled":{"description":"Whether the held turn is already booked to go again at the reset, so nobody has to press anything.","type":"boolean"},"limitMoving":{"description":"The account the held turn is being moved to by the owner's policy, while that move is booked.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it runs on."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop it runs on."},"runner":{"description":"The runner this conversation runs on. Absent means this sandbox.","type":"string"},"startIn":{"description":"Which folder it opened in, relative to the workspace root. Absent means the root.","type":"string"},"actsAs":{"description":"Which persona its first turn acted as. Absent for an ordinary chat.","type":"string"},"lastActsAs":{"description":"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.","type":"string"},"model":{"description":"What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked.","type":"string"},"effort":{"description":"How hard that turn was told to think.","type":"string"},"thinking":{"description":"Whether that turn showed its reasoning.","type":"boolean"},"fast":{"description":"Whether that turn asked for higher speed. What was asked for, not what was served.","type":"boolean"},"account":{"description":"Which connected account paid for it.","type":"string"},"branch":{"description":"The branch its private copy works on. Absent for a conversation that works directly in the shared tree.","type":"string"},"autoLand":{"description":"This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case.","type":"boolean"},"limitPolicy":{"type":"string","enum":["wait","resend","move"]},"outagePolicy":{"type":"string","enum":["wait","retry"]},"stopPolicy":{"type":"string","enum":["wait","retry"]},"landRequested":{"description":"A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting.","type":"object","properties":{"email":{"type":"string","description":"Who asked."},"name":{"description":"Their display name.","type":"string"},"at":{"type":"number","description":"When they asked, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"reactions":{"description":"What people have marked this conversation with, one entry per emoji, in the order the emoji were first used. Absent means nobody has marked it, which is most conversations.","type":"array","items":{"type":"object","properties":{"emoji":{"type":"string","description":"The mark itself, one emoji, carried as the character rather than as a name that would need a table on both sides."},"by":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Who it was, as the sandbox verified them."},"name":{"description":"What to call them, when their sign-in carried a name. Absent leaves the address to stand for them.","type":"string"},"at":{"type":"number","description":"When they marked it, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"description":"Everyone wearing this mark, oldest first, each by name. Carried whole rather than as a count, because a chip reading 3 that cannot say whose is a number nobody can answer. Never empty: the last person taking theirs back takes the whole chip with it."}},"required":["emoji","by"],"additionalProperties":false}},"origin":{"description":"Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"startedBy":{"description":"Who asked for the first turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a child another conversation spawned. Absent when nothing was verified (a wake, a loopback caller).","type":"string"},"areas":{"description":"Which named areas of the workspace this conversation was started within, latched from whoever asked for the first turn. Absent means its starter held the whole workspace, which is why a fenced member is not shown it.","type":"array","items":{"type":"string"}},"owner":{"description":"The member answerable for this conversation: set from whoever started it, inherited from the parent by a spawned child, moved by handing it over. Absent means nobody has claimed it yet.","type":"object","properties":{"email":{"type":"string","description":"Who answers for this conversation, as the sandbox verified them."},"name":{"description":"What to call them, when the sign-in that made them its owner carried a name. Absent leaves the address to stand for them.","type":"string"},"since":{"type":"number","description":"When they became its owner, in milliseconds."}},"required":["email","since"],"additionalProperties":false},"forkedFrom":{"description":"The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"type":"string","enum":["then","now"]}},"required":["conversationId","index","files"],"additionalProperties":false},"base":{"description":"The commit its private copy started from, shortened.","type":"string"},"costUsd":{"description":"What it has cost so far, in dollars. A subagent's spend is its own and is not folded in here.","type":"number"},"inputTokens":{"description":"Uncached input tokens, excluding cache reads and cache writes.","type":"number"},"outputTokens":{"description":"Tokens received.","type":"number"},"contextTokens":{"description":"How much of the window the conversation currently fills.","type":"number"},"contextWindow":{"description":"How large that window is.","type":"number"},"promptCache":{"description":"When this conversation's prompt cache was last kept alive and how long it lasts, which together say when picking the conversation up stops being cheap. Absent when the provider publishes nothing to ground it on.","type":"object","properties":{"at":{"type":"number","description":"When its last request touched the provider's prompt cache, in milliseconds."},"ttlMs":{"type":"number","description":"How long that entry lives from `at`, in milliseconds."},"rollsAt":{"description":"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.","type":"number"},"keepableUntil":{"description":"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.","type":"number"}},"required":["at","ttlMs"],"additionalProperties":false},"keepWarm":{"description":"Whether the sandbox is keeping this conversation's prompt cache warm while it sits idle, how that is going, or why it stopped. Absent when nobody asked for it.","type":"object","properties":{"since":{"type":"number","description":"When keeping it warm started, in milliseconds."},"until":{"type":"number","description":"When it stops by itself, in milliseconds: the time asked for, shortened to what the sandbox can honestly keep, which is never past the point where refreshing costs more than re-reading, nor past the date change that rewrites the prompt."},"refreshes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Refreshes sent so far."},"readTokens":{"description":"How much the last refresh read back from the provider's cache, in tokens: the proof the cache was still there.","type":"number"},"ended":{"description":"Why keeping it warm stopped before anyone picked the conversation up. Absent while it is still being kept.","type":"object","properties":{"at":{"type":"number","description":"When it stopped, in milliseconds."},"reason":{"type":"string","enum":["elapsed","allowance","changed","cold","failed"],"description":"Why: `elapsed` the time asked for ran out; `allowance` the account reached the reserve kept for real work, or the provider refused a refresh for its limit; `changed` what the next turn would send no longer matches the cache (a part of the prompt, the date in it, or the conversation's session or account); `cold` the cache was gone, expired before a refresh could run or found missing by one; `failed` a refresh failed."},"detail":{"description":"The specifics, when there are any: which parts of the prompt changed, how full the account was, or the failure's own words.","type":"string"}},"required":["at","reason"],"additionalProperties":false}},"required":["since","until","refreshes"],"additionalProperties":false},"activity":{"description":"What it is doing at this moment.","type":"object","properties":{"tool":{"description":"The last tool it reached for.","type":"string"},"target":{"description":"What it reached for that tool with: a file, a command, a URL.","type":"string"},"todo":{"description":"The item on its own list that it is working through.","type":"string"}},"additionalProperties":false},"checklist":{"description":"How far it is through its own checklist. Absent for a conversation that kept no list, which is most short ones.","type":"object","properties":{"done":{"type":"number","description":"Items it has completed."},"total":{"type":"number","description":"Items on the list. Never zero: a conversation that kept no list carries no clause at all."}},"required":["done","total"],"additionalProperties":false},"landedMessageDraft":{"description":"The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes.","type":"object","properties":{"startedAt":{"type":"number","description":"When the drafting began, in milliseconds."},"steps":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider was asked."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"status":{"type":"string","enum":["asking","answered","refused","skipped"],"description":"How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."},"at":{"description":"When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time.","type":"number"},"ms":{"description":"How long it took. Absent while it is still being asked.","type":"number"},"reason":{"description":"Why it refused, in its own words.","type":"string"}},"required":["provider","model","status"],"additionalProperties":false},"description":"Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."},"outcome":{"description":"How it ended. Absent means it is still going.","type":"string","enum":["written","failed"]},"reason":{"description":"The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words.","type":"string"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"}},"required":["startedAt","steps"],"additionalProperties":false},"landedMessage":{"description":"What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false},"startedAt":{"description":"When the running turn started, in milliseconds. Absent when none is running.","type":"number"},"run":{"description":"The run under way right now: what a stop names, so it cannot cancel a turn that started after it was pressed. Absent when none is running, and for a turn with no run to attach to.","type":"string"},"queue":{"description":"Messages waiting for its next turn, and whether they are held. Absent for a conversation nothing has ever waited for.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false},"updatedAt":{"type":"number","description":"When it last did something, in milliseconds. Reading it does not count."},"seenAt":{"description":"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.","type":"number"},"unsentAt":{"description":"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.","type":"number"},"attention":{"type":"object","properties":{"plan":{"type":"boolean","description":"It has proposed a plan and is waiting for a yes."},"question":{"type":"boolean","description":"It has asked you something."},"permission":{"type":"boolean","description":"It wants to use a tool it needs permission for."},"capability":{"type":"boolean","description":"It needs something connected that is not connected yet."},"credential":{"type":"boolean","description":"It is waiting for a named person to release a credential. The one pause that may not be yours to clear, whatever your role."},"conflict":{"type":"boolean","description":"Its work cannot be merged without somebody resolving a clash."},"need":{"description":"It asked a person for something it still needs: a connection, a secret, wider reach, a tool. Absent from a daemon older than needs.","type":"boolean"}},"required":["plan","question","permission","capability","credential","conflict"],"additionalProperties":false,"description":"Which kinds of waiting-for-you it is doing."},"permissionAsk":{"description":"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.","type":"object","properties":{"requestId":{"type":"string","description":"Which request this is: the id an answer to it names."},"ask":{"type":"string","description":"What it asks to do, on one line: the runtime's own sentence, else the tool's short name."}},"required":["requestId","ask"],"additionalProperties":false},"landFailure":{"description":"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.","type":"object","properties":{"reason":{"type":"string","description":"Why it failed, in the words it failed with."},"code":{"description":"Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise.","type":"string"},"at":{"type":"number","description":"When it failed, in milliseconds."}},"required":["reason","at"],"additionalProperties":false},"delivery":{"description":"What became of its last land in the folder on the owner's computer, for a project folder attached to this computer's sandbox. Absent for any other land, and from a sandbox older than it.","type":"object","properties":{"state":{"type":"string","enum":["delivered","partial","waiting","failed","too-large"]},"at":{"type":"number","description":"When this state was reached, in milliseconds."},"project":{"type":"string"},"folder":{"description":"The folder on the owner's computer, as its machine reported it.","type":"string"},"applied":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files written, merged ones included."},"conflicts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"reason":{"type":"string","enum":["edited","link","outside","missing-git"]}},"required":["path","reason"],"additionalProperties":false}},"point":{"description":"The machine's restore point taken before writing.","type":"string"},"reason":{"description":"Why it failed or waits, in a sentence.","type":"string"}},"required":["state","at","project","applied","conflicts"],"additionalProperties":false},"conflictCauses":{"description":"Why its work will not merge, and so who can clear it: your own uncommitted edits, which only you can commit or stash, against a moved main line or an unmergeable binary, which the conversation can redo on its own copy. Absent unless it is refusing to merge.","type":"array","items":{"type":"string","enum":["workspace","diverged","binary"]}},"unfinished":{"description":"What its last turn left open: steps it never completed. Absent for a turn that finished what it started.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"steps":{"description":"The agent's own checklist where that turn left it. Absent for a conversation that kept no list.","type":"object","properties":{"open":{"type":"number","description":"Items on it that were never completed."},"total":{"type":"number","description":"Items on the whole list."},"next":{"description":"The one it would have done next: what it was working through, or the first still waiting.","type":"string"}},"required":["open","total"],"additionalProperties":false},"check":{"description":"The end-of-turn check that was still failing when the turn ended, by name. No longer written: what a check finds as a turn ends is said back to the model and never leaves its work unfinished.","type":"string"}},"required":["at"],"additionalProperties":false},"proof":{"description":"What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"verification":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to."},"check":{"description":"The command that spoke, when one did, so a targeted test is never read as the whole suite.","type":"string"},"unviewed":{"description":"How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none.","type":"number"}},"required":["at","verification"],"additionalProperties":false},"reach":{"description":"Where its last turn's work went when it went somewhere besides its own branch: files changed live outside it, clones of its own holding work that will not land, pushes it made itself. Absent when everything it did is on its branch.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"live":{"description":"Files it changed in place where every conversation reads them, outside its own branch: an installed extension's copy, the sandbox's shared state, the owner's own checkout. Live the moment they were written, never reviewed, and an installed extension's next update replaces them.","maxItems":10,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What it wrote, as it named it: relative to the workspace for the sandbox's shared state, absolute for the owner's own checkout."},"extension":{"description":"The extension, by its manifest id, when what it changed is the installed copy of one. Absent for anything else.","type":"string"}},"required":["path"],"additionalProperties":false}},"liveMore":{"description":"How many more such files there were beyond the ones listed. Absent when all are listed.","type":"number"},"stranded":{"description":"Repositories it cloned inside its own copy and left work in. A clone of its own never rides its land, so what is in one stays there.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"The clone, as a folder relative to its own copy of the workspace."},"uncommitted":{"type":"number","description":"Files changed in it and never committed."},"unpushed":{"type":"number","description":"Commits in it its upstream does not have, or that no remote has when it follows none."}},"required":["dir","uncommitted","unpushed"],"additionalProperties":false}},"strandedMore":{"description":"How many more such clones there were beyond the ones listed. Absent when all are listed.","type":"number"},"published":{"description":"Pushes it made with git that went through: work it sent out of the sandbox itself, never through a land.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"description":"The folder it pushed from, as the command named it. Absent for its own working folder.","type":"string"},"remote":{"description":"The remote it pushed to, as the command named it. Absent when the command named none.","type":"string"},"branch":{"description":"The branch it pushed to, as the command named it. Absent when the command named none.","type":"string"},"command":{"type":"string","description":"The push itself, trimmed to one line."}},"required":["command"],"additionalProperties":false}},"publishedMore":{"description":"How many more such pushes there were beyond the ones listed. Absent when all are listed.","type":"number"}},"required":["at"],"additionalProperties":false},"turns":{"description":"Turns it has finished.","type":"number"},"toolUses":{"description":"Tools it has used, over its whole life.","type":"number"},"subagents":{"description":"Subagents this one started, in-process and spawned alike. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost.","type":"object","properties":{"running":{"type":"number","description":"Subagents working right now."},"total":{"type":"number","description":"Subagents it has started over its whole life."}},"required":["running","total"],"additionalProperties":false},"diff":{"description":"Everything it has written, measured from where it started. Independent of how much has been merged.","type":"object","properties":{"files":{"type":"number","description":"Files touched."},"insertions":{"type":"number","description":"Lines added."},"deletions":{"type":"number","description":"Lines removed."}},"required":["files","insertions","deletions"],"additionalProperties":false},"landedPresence":{"description":"Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it.","type":"object","properties":{"landed":{"type":"number","description":"Paths this conversation merged in."},"present":{"type":"number","description":"How many of them are still there, either pending or committed."},"removedBy":{"description":"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"id":{"type":"string","description":"The conversation that took them out."},"title":{"description":"What that conversation is called.","type":"string"}},"required":["kind","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"description":"Who it was, as the sandbox verified them. Absent when the request carried no identity.","type":"string"},"name":{"description":"What to call them, when their sign-in carried a name.","type":"string"}},"required":["kind"],"additionalProperties":false}]}},"required":["landed","present"],"additionalProperties":false},"loop":{"description":"The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them.","type":"object","properties":{"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How the loop is going."},"iteration":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which round it is on."},"maxIterations":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many rounds it will attempt before giving up."},"goal":{"type":"string","description":"What it is looping towards."}},"required":["state","iteration","maxIterations","goal"],"additionalProperties":false},"workflow":{"description":"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.","type":"object","properties":{"runId":{"type":"string","description":"The run this belongs to, which is how a board groups its steps together."},"name":{"type":"string","description":"The workflow's name."},"step":{"type":"string","description":"Which step this conversation is on now. It moves when steps are chained."},"index":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"This step's place in the workflow, counting from one."},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many steps the workflow has."}},"required":["runId","name","step","index","total"],"additionalProperties":false},"awaitingWake":{"description":"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.","type":"boolean"},"watches":{"description":"Outside conditions this conversation is parked on, each of which will wake it. Absent means none, which is nearly every conversation: an armed watch is why a finished-looking agent starts working by itself, and why a hosted machine will not go idle.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this watch, the same one the agent was given when it armed it."},"note":{"type":"string","description":"The agent's own line on what it is waiting for."},"intervalSeconds":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How often the check runs."},"deadlineAt":{"type":"number","description":"When it gives up and wakes the conversation anyway, in milliseconds. Every watch has one."}},"required":["id","note","intervalSeconds","deadlineAt"],"additionalProperties":false}},"needs":{"description":"What it is waiting on people for and has not got yet, oldest first. Absent means nothing: an open need is why an idle-looking agent still needs you.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["capability","secret","grant","release","environment"],"description":"What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image."},"title":{"type":"string"},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."}},"required":["id","kind","title","status"],"additionalProperties":false}},"jobs":{"description":"Commands this conversation left running in the background, and how the most recent ones ended. Absent means none since the daemon started. A job is running exactly while it has no end.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this job, the one its transcript row names."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"session":{"type":"string","description":"The terminal session its pane runs in, which is what opening it focuses."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When the command exited, in milliseconds. Absent while it runs.","type":"number"},"exitCode":{"description":"The code it exited with. Absent while it runs, or when its exit left none.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"watch":{"description":"The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.","type":"string"},"handed":{"description":"Left running for the person: the agent kept it for them with the `keep` tool (or it holds a port this conversation already left them), so it outlives the turn, wakes nothing and is theirs to stop.","type":"boolean"},"ports":{"description":"The ports it was listening on when its turn ended.","type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"stoppedBy":{"description":"Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.","type":"string","enum":["turn","person","agent"]},"inputWait":{"description":"Present while it sits blocked reading its terminal with nothing moving: a prompt nobody in the sandbox will answer, which only stopping it or a person typing into its terminal ends. Absent while it runs on, and once it ends.","type":"object","properties":{"since":{"type":"number","description":"When it went still, in milliseconds."},"program":{"type":"string","description":"The process blocked reading the terminal, as its command line."}},"required":["since","program"],"additionalProperties":false}},"required":["id","label","session","startedAt"],"additionalProperties":false}},"archivedAt":{"description":"When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board.","type":"number"}},"required":["id","status","provider","harness","updatedAt","attention"],"additionalProperties":false},"description":"The conversations."},"rev":{"type":"number","description":"Which version of the fleet this is. The fleet is published as whole snapshots, so without a version a list read before a change but delivered after it would silently undo that change. Drop any list older than the newest you have already applied."},"held":{"default":[],"description":"Automations waiting at the door for a yes, put alongside the running conversations so needs-you sits beside working rather than on a page nobody opens.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"This waiting item's own id, which approving and rejecting take."},"automationId":{"type":"string","description":"Which automation it came from."},"payload":{"description":"What set it off, kept whole so an approved wake carries the same thing it would have had. Absent for one on a schedule, which carries nothing.","type":"string"},"origin":{"description":"Where the message came from, kept alongside the payload so an approved wake appears on the board exactly as an automatic one would have.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"title":{"description":"What the conversation would be called.","type":"string"},"conversationId":{"description":"The thread this belongs to, when it has one, so approving continues that conversation rather than opening a new one. Without it, one visitor's chat becomes a card per approved message and an agent that meets them again every turn.","type":"string"},"sessionId":{"description":"The provider session that thread last ran on.","type":"string"},"thread":{"description":"Which inbound thread this belongs to, so the approved run continues that thread's memory rather than a fresh one.","type":"string"},"actsAs":{"description":"Which persona the approved run speaks as, decided when it was held.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"createdAt":{"type":"number","description":"When it started waiting, in milliseconds."},"autoRunAt":{"description":"When it goes ahead on its own, in milliseconds, for a hold that is only a delay. Absent for one that genuinely waits on a person.","type":"number"}},"required":["id","automationId","createdAt"],"additionalProperties":false}}},"required":["agents","rev","held"],"additionalProperties":false}}}}}}},"/agents/search":{"get":{"operationId":"agents.search","summary":"Find a conversation","description":"Searches the live fleet and the archive together. Both halves on purpose: the board hides finished work by design, and a filter that says it found nothing while the answer sits one click away is simply wrong.","tags":["The fleet"],"parameters":[{"name":"query","in":"query","required":true,"schema":{"type":"string","minLength":2,"description":"What to look for. Searched against what was said, both sides of the conversation, and nothing else: not the thinking, not the tool output, which between them name nearly every identifier in the workspace and would return most of the board."},"allowEmptyValue":true,"allowReserved":true},{"name":"caseSensitive","in":"query","required":false,"schema":{"description":"Whether capitals matter.","type":"string"},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"matches":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Which conversation matched."},"snippet":{"description":"Why, in its own words. Absent when the title was the match, which the card already shows: repeating it underneath is noise where evidence was wanted.","type":"object","properties":{"text":{"type":"string","description":"The matching line, with a little either side of it."},"speaker":{"type":"string","enum":["user","agent"],"description":"Who said it. Carried with the words rather than beside them, because a line of the agent's prose under a card reads as something you typed until the row says otherwise."}},"required":["text","speaker"],"additionalProperties":false}},"required":["id"],"additionalProperties":false},"description":"What matched, from the live fleet and the archive together."},"scanned":{"type":"number","description":"How many conversations were actually read, so a screen can say when a search saw less than everything rather than implying it saw all of it."},"indexing":{"type":"boolean","description":"Whether what was said is still being read in the background. True means this answer can still grow, so a screen must say it is incomplete rather than presenting it as the whole list."}},"required":["matches","scanned","indexing"],"additionalProperties":false}}}}}}},"/agents/{id}":{"get":{"operationId":"agents.get","summary":"One conversation's card","description":"Everything the board shows for a single conversation: its title, state, working branch, unread marker and timestamps.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The conversation id, which is how every other call addresses it."},"sessionId":{"description":"The provider session behind the last turn. It is retired whenever the model or account changes.","type":"string"},"title":{"description":"What to call it: the first prompt cut to one line, unless somebody renamed it.","type":"string"},"titleAction":{"description":"One word for the kind of work the naming pass read in it (fix, audit, redesign). Never part of the displayed name: a board tints and glyphs a card by it. Absent for a title nothing named an action for.","type":"string"},"status":{"type":"string","enum":["idle","running","awaiting","stopping","dismissing","stopped","resuming","landing","ready","landed","conflict","error","interrupted"],"description":"What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; dismissing is the same window for a question waved away, which ends the turn too but owes the user nothing; resuming means the sandbox is already putting right whatever killed the turn; landing means its work is being carried into the workspace right now, and nothing may act on its branch until that settles."},"failure":{"description":"Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.","type":"string"},"failureCode":{"description":"Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.","type":"string"},"limitResetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant.","type":"number"},"limitHeld":{"description":"Whether the refused turn is held whole, so sending again re-runs it instead of appending to it.","type":"boolean"},"limitScheduled":{"description":"Whether the held turn is already booked to go again at the reset, so nobody has to press anything.","type":"boolean"},"limitMoving":{"description":"The account the held turn is being moved to by the owner's policy, while that move is booked.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it runs on."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop it runs on."},"runner":{"description":"The runner this conversation runs on. Absent means this sandbox.","type":"string"},"startIn":{"description":"Which folder it opened in, relative to the workspace root. Absent means the root.","type":"string"},"actsAs":{"description":"Which persona its first turn acted as. Absent for an ordinary chat.","type":"string"},"lastActsAs":{"description":"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.","type":"string"},"model":{"description":"What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked.","type":"string"},"effort":{"description":"How hard that turn was told to think.","type":"string"},"thinking":{"description":"Whether that turn showed its reasoning.","type":"boolean"},"fast":{"description":"Whether that turn asked for higher speed. What was asked for, not what was served.","type":"boolean"},"account":{"description":"Which connected account paid for it.","type":"string"},"branch":{"description":"The branch its private copy works on. Absent for a conversation that works directly in the shared tree.","type":"string"},"autoLand":{"description":"This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case.","type":"boolean"},"limitPolicy":{"type":"string","enum":["wait","resend","move"]},"outagePolicy":{"type":"string","enum":["wait","retry"]},"stopPolicy":{"type":"string","enum":["wait","retry"]},"landRequested":{"description":"A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting.","type":"object","properties":{"email":{"type":"string","description":"Who asked."},"name":{"description":"Their display name.","type":"string"},"at":{"type":"number","description":"When they asked, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"reactions":{"description":"What people have marked this conversation with, one entry per emoji, in the order the emoji were first used. Absent means nobody has marked it, which is most conversations.","type":"array","items":{"type":"object","properties":{"emoji":{"type":"string","description":"The mark itself, one emoji, carried as the character rather than as a name that would need a table on both sides."},"by":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Who it was, as the sandbox verified them."},"name":{"description":"What to call them, when their sign-in carried a name. Absent leaves the address to stand for them.","type":"string"},"at":{"type":"number","description":"When they marked it, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"description":"Everyone wearing this mark, oldest first, each by name. Carried whole rather than as a count, because a chip reading 3 that cannot say whose is a number nobody can answer. Never empty: the last person taking theirs back takes the whole chip with it."}},"required":["emoji","by"],"additionalProperties":false}},"origin":{"description":"Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"startedBy":{"description":"Who asked for the first turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a child another conversation spawned. Absent when nothing was verified (a wake, a loopback caller).","type":"string"},"areas":{"description":"Which named areas of the workspace this conversation was started within, latched from whoever asked for the first turn. Absent means its starter held the whole workspace, which is why a fenced member is not shown it.","type":"array","items":{"type":"string"}},"owner":{"description":"The member answerable for this conversation: set from whoever started it, inherited from the parent by a spawned child, moved by handing it over. Absent means nobody has claimed it yet.","type":"object","properties":{"email":{"type":"string","description":"Who answers for this conversation, as the sandbox verified them."},"name":{"description":"What to call them, when the sign-in that made them its owner carried a name. Absent leaves the address to stand for them.","type":"string"},"since":{"type":"number","description":"When they became its owner, in milliseconds."}},"required":["email","since"],"additionalProperties":false},"forkedFrom":{"description":"The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"type":"string","enum":["then","now"]}},"required":["conversationId","index","files"],"additionalProperties":false},"base":{"description":"The commit its private copy started from, shortened.","type":"string"},"costUsd":{"description":"What it has cost so far, in dollars. A subagent's spend is its own and is not folded in here.","type":"number"},"inputTokens":{"description":"Uncached input tokens, excluding cache reads and cache writes.","type":"number"},"outputTokens":{"description":"Tokens received.","type":"number"},"contextTokens":{"description":"How much of the window the conversation currently fills.","type":"number"},"contextWindow":{"description":"How large that window is.","type":"number"},"promptCache":{"description":"When this conversation's prompt cache was last kept alive and how long it lasts, which together say when picking the conversation up stops being cheap. Absent when the provider publishes nothing to ground it on.","type":"object","properties":{"at":{"type":"number","description":"When its last request touched the provider's prompt cache, in milliseconds."},"ttlMs":{"type":"number","description":"How long that entry lives from `at`, in milliseconds."},"rollsAt":{"description":"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.","type":"number"},"keepableUntil":{"description":"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.","type":"number"}},"required":["at","ttlMs"],"additionalProperties":false},"keepWarm":{"description":"Whether the sandbox is keeping this conversation's prompt cache warm while it sits idle, how that is going, or why it stopped. Absent when nobody asked for it.","type":"object","properties":{"since":{"type":"number","description":"When keeping it warm started, in milliseconds."},"until":{"type":"number","description":"When it stops by itself, in milliseconds: the time asked for, shortened to what the sandbox can honestly keep, which is never past the point where refreshing costs more than re-reading, nor past the date change that rewrites the prompt."},"refreshes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Refreshes sent so far."},"readTokens":{"description":"How much the last refresh read back from the provider's cache, in tokens: the proof the cache was still there.","type":"number"},"ended":{"description":"Why keeping it warm stopped before anyone picked the conversation up. Absent while it is still being kept.","type":"object","properties":{"at":{"type":"number","description":"When it stopped, in milliseconds."},"reason":{"type":"string","enum":["elapsed","allowance","changed","cold","failed"],"description":"Why: `elapsed` the time asked for ran out; `allowance` the account reached the reserve kept for real work, or the provider refused a refresh for its limit; `changed` what the next turn would send no longer matches the cache (a part of the prompt, the date in it, or the conversation's session or account); `cold` the cache was gone, expired before a refresh could run or found missing by one; `failed` a refresh failed."},"detail":{"description":"The specifics, when there are any: which parts of the prompt changed, how full the account was, or the failure's own words.","type":"string"}},"required":["at","reason"],"additionalProperties":false}},"required":["since","until","refreshes"],"additionalProperties":false},"activity":{"description":"What it is doing at this moment.","type":"object","properties":{"tool":{"description":"The last tool it reached for.","type":"string"},"target":{"description":"What it reached for that tool with: a file, a command, a URL.","type":"string"},"todo":{"description":"The item on its own list that it is working through.","type":"string"}},"additionalProperties":false},"checklist":{"description":"How far it is through its own checklist. Absent for a conversation that kept no list, which is most short ones.","type":"object","properties":{"done":{"type":"number","description":"Items it has completed."},"total":{"type":"number","description":"Items on the list. Never zero: a conversation that kept no list carries no clause at all."}},"required":["done","total"],"additionalProperties":false},"landedMessageDraft":{"description":"The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes.","type":"object","properties":{"startedAt":{"type":"number","description":"When the drafting began, in milliseconds."},"steps":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider was asked."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"status":{"type":"string","enum":["asking","answered","refused","skipped"],"description":"How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."},"at":{"description":"When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time.","type":"number"},"ms":{"description":"How long it took. Absent while it is still being asked.","type":"number"},"reason":{"description":"Why it refused, in its own words.","type":"string"}},"required":["provider","model","status"],"additionalProperties":false},"description":"Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."},"outcome":{"description":"How it ended. Absent means it is still going.","type":"string","enum":["written","failed"]},"reason":{"description":"The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words.","type":"string"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"}},"required":["startedAt","steps"],"additionalProperties":false},"landedMessage":{"description":"What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false},"startedAt":{"description":"When the running turn started, in milliseconds. Absent when none is running.","type":"number"},"run":{"description":"The run under way right now: what a stop names, so it cannot cancel a turn that started after it was pressed. Absent when none is running, and for a turn with no run to attach to.","type":"string"},"queue":{"description":"Messages waiting for its next turn, and whether they are held. Absent for a conversation nothing has ever waited for.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false},"updatedAt":{"type":"number","description":"When it last did something, in milliseconds. Reading it does not count."},"seenAt":{"description":"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.","type":"number"},"unsentAt":{"description":"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.","type":"number"},"attention":{"type":"object","properties":{"plan":{"type":"boolean","description":"It has proposed a plan and is waiting for a yes."},"question":{"type":"boolean","description":"It has asked you something."},"permission":{"type":"boolean","description":"It wants to use a tool it needs permission for."},"capability":{"type":"boolean","description":"It needs something connected that is not connected yet."},"credential":{"type":"boolean","description":"It is waiting for a named person to release a credential. The one pause that may not be yours to clear, whatever your role."},"conflict":{"type":"boolean","description":"Its work cannot be merged without somebody resolving a clash."},"need":{"description":"It asked a person for something it still needs: a connection, a secret, wider reach, a tool. Absent from a daemon older than needs.","type":"boolean"}},"required":["plan","question","permission","capability","credential","conflict"],"additionalProperties":false,"description":"Which kinds of waiting-for-you it is doing."},"permissionAsk":{"description":"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.","type":"object","properties":{"requestId":{"type":"string","description":"Which request this is: the id an answer to it names."},"ask":{"type":"string","description":"What it asks to do, on one line: the runtime's own sentence, else the tool's short name."}},"required":["requestId","ask"],"additionalProperties":false},"landFailure":{"description":"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.","type":"object","properties":{"reason":{"type":"string","description":"Why it failed, in the words it failed with."},"code":{"description":"Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise.","type":"string"},"at":{"type":"number","description":"When it failed, in milliseconds."}},"required":["reason","at"],"additionalProperties":false},"delivery":{"description":"What became of its last land in the folder on the owner's computer, for a project folder attached to this computer's sandbox. Absent for any other land, and from a sandbox older than it.","type":"object","properties":{"state":{"type":"string","enum":["delivered","partial","waiting","failed","too-large"]},"at":{"type":"number","description":"When this state was reached, in milliseconds."},"project":{"type":"string"},"folder":{"description":"The folder on the owner's computer, as its machine reported it.","type":"string"},"applied":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files written, merged ones included."},"conflicts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"reason":{"type":"string","enum":["edited","link","outside","missing-git"]}},"required":["path","reason"],"additionalProperties":false}},"point":{"description":"The machine's restore point taken before writing.","type":"string"},"reason":{"description":"Why it failed or waits, in a sentence.","type":"string"}},"required":["state","at","project","applied","conflicts"],"additionalProperties":false},"conflictCauses":{"description":"Why its work will not merge, and so who can clear it: your own uncommitted edits, which only you can commit or stash, against a moved main line or an unmergeable binary, which the conversation can redo on its own copy. Absent unless it is refusing to merge.","type":"array","items":{"type":"string","enum":["workspace","diverged","binary"]}},"unfinished":{"description":"What its last turn left open: steps it never completed. Absent for a turn that finished what it started.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"steps":{"description":"The agent's own checklist where that turn left it. Absent for a conversation that kept no list.","type":"object","properties":{"open":{"type":"number","description":"Items on it that were never completed."},"total":{"type":"number","description":"Items on the whole list."},"next":{"description":"The one it would have done next: what it was working through, or the first still waiting.","type":"string"}},"required":["open","total"],"additionalProperties":false},"check":{"description":"The end-of-turn check that was still failing when the turn ended, by name. No longer written: what a check finds as a turn ends is said back to the model and never leaves its work unfinished.","type":"string"}},"required":["at"],"additionalProperties":false},"proof":{"description":"What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"verification":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to."},"check":{"description":"The command that spoke, when one did, so a targeted test is never read as the whole suite.","type":"string"},"unviewed":{"description":"How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none.","type":"number"}},"required":["at","verification"],"additionalProperties":false},"reach":{"description":"Where its last turn's work went when it went somewhere besides its own branch: files changed live outside it, clones of its own holding work that will not land, pushes it made itself. Absent when everything it did is on its branch.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"live":{"description":"Files it changed in place where every conversation reads them, outside its own branch: an installed extension's copy, the sandbox's shared state, the owner's own checkout. Live the moment they were written, never reviewed, and an installed extension's next update replaces them.","maxItems":10,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What it wrote, as it named it: relative to the workspace for the sandbox's shared state, absolute for the owner's own checkout."},"extension":{"description":"The extension, by its manifest id, when what it changed is the installed copy of one. Absent for anything else.","type":"string"}},"required":["path"],"additionalProperties":false}},"liveMore":{"description":"How many more such files there were beyond the ones listed. Absent when all are listed.","type":"number"},"stranded":{"description":"Repositories it cloned inside its own copy and left work in. A clone of its own never rides its land, so what is in one stays there.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"The clone, as a folder relative to its own copy of the workspace."},"uncommitted":{"type":"number","description":"Files changed in it and never committed."},"unpushed":{"type":"number","description":"Commits in it its upstream does not have, or that no remote has when it follows none."}},"required":["dir","uncommitted","unpushed"],"additionalProperties":false}},"strandedMore":{"description":"How many more such clones there were beyond the ones listed. Absent when all are listed.","type":"number"},"published":{"description":"Pushes it made with git that went through: work it sent out of the sandbox itself, never through a land.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"description":"The folder it pushed from, as the command named it. Absent for its own working folder.","type":"string"},"remote":{"description":"The remote it pushed to, as the command named it. Absent when the command named none.","type":"string"},"branch":{"description":"The branch it pushed to, as the command named it. Absent when the command named none.","type":"string"},"command":{"type":"string","description":"The push itself, trimmed to one line."}},"required":["command"],"additionalProperties":false}},"publishedMore":{"description":"How many more such pushes there were beyond the ones listed. Absent when all are listed.","type":"number"}},"required":["at"],"additionalProperties":false},"turns":{"description":"Turns it has finished.","type":"number"},"toolUses":{"description":"Tools it has used, over its whole life.","type":"number"},"subagents":{"description":"Subagents this one started, in-process and spawned alike. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost.","type":"object","properties":{"running":{"type":"number","description":"Subagents working right now."},"total":{"type":"number","description":"Subagents it has started over its whole life."}},"required":["running","total"],"additionalProperties":false},"diff":{"description":"Everything it has written, measured from where it started. Independent of how much has been merged.","type":"object","properties":{"files":{"type":"number","description":"Files touched."},"insertions":{"type":"number","description":"Lines added."},"deletions":{"type":"number","description":"Lines removed."}},"required":["files","insertions","deletions"],"additionalProperties":false},"landedPresence":{"description":"Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it.","type":"object","properties":{"landed":{"type":"number","description":"Paths this conversation merged in."},"present":{"type":"number","description":"How many of them are still there, either pending or committed."},"removedBy":{"description":"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"id":{"type":"string","description":"The conversation that took them out."},"title":{"description":"What that conversation is called.","type":"string"}},"required":["kind","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"description":"Who it was, as the sandbox verified them. Absent when the request carried no identity.","type":"string"},"name":{"description":"What to call them, when their sign-in carried a name.","type":"string"}},"required":["kind"],"additionalProperties":false}]}},"required":["landed","present"],"additionalProperties":false},"loop":{"description":"The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them.","type":"object","properties":{"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How the loop is going."},"iteration":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which round it is on."},"maxIterations":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many rounds it will attempt before giving up."},"goal":{"type":"string","description":"What it is looping towards."}},"required":["state","iteration","maxIterations","goal"],"additionalProperties":false},"workflow":{"description":"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.","type":"object","properties":{"runId":{"type":"string","description":"The run this belongs to, which is how a board groups its steps together."},"name":{"type":"string","description":"The workflow's name."},"step":{"type":"string","description":"Which step this conversation is on now. It moves when steps are chained."},"index":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"This step's place in the workflow, counting from one."},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many steps the workflow has."}},"required":["runId","name","step","index","total"],"additionalProperties":false},"awaitingWake":{"description":"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.","type":"boolean"},"watches":{"description":"Outside conditions this conversation is parked on, each of which will wake it. Absent means none, which is nearly every conversation: an armed watch is why a finished-looking agent starts working by itself, and why a hosted machine will not go idle.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this watch, the same one the agent was given when it armed it."},"note":{"type":"string","description":"The agent's own line on what it is waiting for."},"intervalSeconds":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How often the check runs."},"deadlineAt":{"type":"number","description":"When it gives up and wakes the conversation anyway, in milliseconds. Every watch has one."}},"required":["id","note","intervalSeconds","deadlineAt"],"additionalProperties":false}},"needs":{"description":"What it is waiting on people for and has not got yet, oldest first. Absent means nothing: an open need is why an idle-looking agent still needs you.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["capability","secret","grant","release","environment"],"description":"What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image."},"title":{"type":"string"},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."}},"required":["id","kind","title","status"],"additionalProperties":false}},"jobs":{"description":"Commands this conversation left running in the background, and how the most recent ones ended. Absent means none since the daemon started. A job is running exactly while it has no end.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this job, the one its transcript row names."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"session":{"type":"string","description":"The terminal session its pane runs in, which is what opening it focuses."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When the command exited, in milliseconds. Absent while it runs.","type":"number"},"exitCode":{"description":"The code it exited with. Absent while it runs, or when its exit left none.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"watch":{"description":"The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.","type":"string"},"handed":{"description":"Left running for the person: the agent kept it for them with the `keep` tool (or it holds a port this conversation already left them), so it outlives the turn, wakes nothing and is theirs to stop.","type":"boolean"},"ports":{"description":"The ports it was listening on when its turn ended.","type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"stoppedBy":{"description":"Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.","type":"string","enum":["turn","person","agent"]},"inputWait":{"description":"Present while it sits blocked reading its terminal with nothing moving: a prompt nobody in the sandbox will answer, which only stopping it or a person typing into its terminal ends. Absent while it runs on, and once it ends.","type":"object","properties":{"since":{"type":"number","description":"When it went still, in milliseconds."},"program":{"type":"string","description":"The process blocked reading the terminal, as its command line."}},"required":["since","program"],"additionalProperties":false}},"required":["id","label","session","startedAt"],"additionalProperties":false}},"archivedAt":{"description":"When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board.","type":"number"}},"required":["id","status","provider","harness","updatedAt","attention"],"additionalProperties":false}}}}}}},"/agents/{id}/transcript":{"get":{"operationId":"agents.transcript","summary":"One page of a conversation","description":"The most recent turns of one conversation, in order, including the tool calls and their results: what the chat replays and the next turn is seeded from. A page, not the whole record — pass the answer's `from` back as `before` to walk further back, until `more` reads false.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}},{"name":"before","in":"query","schema":{"description":"Return the messages before this position in the record: the `from` of the page below. Absent asks for the most recent turns.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"allowEmptyValue":true,"allowReserved":true},{"name":"turns","in":"query","schema":{"description":"How many of the user's turns to return, newest first. Absent takes the daemon's default.","type":"integer","minimum":1,"maximum":200},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"type":"object","properties":{"role":{"type":"string","enum":["user","assistant","notice"],"description":"Who said it. A notice is neither side: it is something that happened to the turn, recorded so a reopened conversation can say it. Without those, a turn a provider refused ends on the user's message and reads as broken."},"text":{"type":"string","description":"The words."},"run":{"description":"The run that produced this row. Present on everything a turn produced, absent on rows written outside one. A client draws a run's rows over whatever it already holds for that run, which is what this identifies; content cannot, because the last row of a live run keeps growing.","type":"string"},"sentAt":{"description":"When it was sent, in milliseconds. On the user's rows only, because that is the only moment actually known: a turn's own frames arrive with no clock, so stamping the agent's rows could only ever mean the whole turn's start or end.","type":"number"},"messageId":{"description":"The message's own id, on the rows of messages sent to the agent: what its sender named it, or what the sandbox did. A rewind names the message by it, and the same id sent again is recognised rather than delivered twice.","type":"string"},"attachments":{"description":"Files attached to this message, as workspace paths.","type":"array","items":{"type":"string"}},"checkpointId":{"description":"The saved point this message can be rewound to. Looked up on each read rather than stored, so what is offered is exactly what is still there to go back to.","type":"string"},"rewindIndex":{"description":"This message's position in the conversation's record, which is how a rewind names it. Present only beside a checkpoint.","type":"integer","minimum":0,"maximum":9007199254740991},"thinking":{"description":"What the agent was reasoning about.","type":"string"},"tools":{"description":"The tool calls this part of the turn made.","type":"array","items":{"$ref":"#/$defs/__schema0"}},"todos":{"description":"The agent's task checklist, as of this bubble.","type":"array","items":{"type":"object","properties":{"content":{"type":"string","description":"The item, as the agent wrote it."},"status":{"type":"string","enum":["pending","in_progress","completed"],"description":"Where it is."},"activeForm":{"description":"How to phrase it while it is happening, so a screen can say what the agent is doing rather than what it plans to do.","type":"string"}},"required":["content","status"],"additionalProperties":false}},"usage":{"description":"What the turn cost, on the bubble its answer ended in.","type":"object","properties":{"costUsd":{"type":"number"},"inputTokens":{"type":"number"},"outputTokens":{"type":"number"},"durationMs":{"type":"number"},"numTurns":{"type":"number"}},"additionalProperties":false},"notes":{"description":"What the sandbox added to this message before the model saw it. Carried on the message rather than as rows of their own, because they genuinely were part of what was sent.","type":"array","items":{"type":"object","properties":{"title":{"type":"string","description":"The one line a reader sees, on a row that opens to the text below."},"text":{"type":"string","description":"The note itself, which is also exactly what the model was told."}},"required":["title","text"],"additionalProperties":false}},"speaker":{"description":"Who sent this message, as the sandbox verified it. Absent where it does not say.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"type":"string","description":"The signed-in member, as the sandbox verified them."},"name":{"description":"Their display name, where the sign-in carries one.","type":"string"}},"required":["kind","email"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"program"},"token":{"type":"string","description":"The label of the control token a person minted and handed to this program."}},"required":["kind","token"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"conversationId":{"type":"string","description":"The conversation whose agent is speaking: a child reporting back, a peer's message."}},"required":["kind","conversationId"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"sandbox"},"source":{"description":"What in the sandbox spoke: an automation, a watch that fired, a job that ended, a repair. Absent when it does not say.","type":"string"}},"required":["kind"],"additionalProperties":false}]},"errand":{"description":"What this message is for, when the sandbox or the app composed it rather than a person typing it. Absent on a person's own words.","type":"string","enum":["land-conflict","verify-nudge","land-breakage","land-fix","land-fix-nudge","land-held","push-fix","push-fix-nudge","ci-fix","ci-fix-nudge"]},"placed":{"description":"A person wrote this in the agent's voice, with no turn behind it. Marked for the human re-reading the conversation months later, so their own words do not pass as the agent's. The agent itself never sees the mark.","type":"boolean"},"noticeAction":{"description":"A one-press follow-up this notice offers, by name. The chat decides what it does and whether it still applies.","type":"string","enum":["landHold","depsInstall","watchStop","sandboxMemory","sendAnyway","sendAgain"]},"sandboxHeld":{"description":"The sandbox kept this refused turn whole, its message still above, so the notice's press runs that turn again instead of letting the conversation's queue go, which never held these words.","type":"boolean"},"noticeWait":{"description":"The wait this notice describes, by name, so a reader can say whether it is still on.","type":"string","enum":["credentialRenewal","chatRoute","watch"]},"noticeWaitId":{"description":"Which instance of the wait this notice names, for a kind that can have several running at once.","type":"string"},"noticeCode":{"description":"Which of the sandbox's own notices this row is, and the facts it was worded from, so a reader can say it in the reader's own language. The text stays the English sentence.","type":"object","properties":{"code":{"type":"string","description":"Which of the sandbox's notices this row is, by name. A reader that does not know the name shows the row's text."},"params":{"description":"The facts the notice was worded from, by name: counts, names, and the provider's own sentence where the notice quotes one.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number","boolean"]}}},"required":["code"],"additionalProperties":false},"agentNotice":{"description":"This notice was said by the agent's runtime or one of its extensions rather than by the sandbox: how loud, and who said it.","type":"object","properties":{"level":{"type":"string","enum":["info","warning","error"],"description":"How loud it was said: a muted line, a warning or an error."},"source":{"description":"Who said it, when the runtime named one: the extension or plugin, as the runtime spells it. Absent when the runtime's loop said it itself.","type":"string"}},"required":["level"],"additionalProperties":false},"plan":{"description":"The plan this row asked approval for, and the answer.","type":"object","properties":{"requestId":{"type":"string","description":"What to send back when you answer."},"text":{"type":"string","description":"The plan itself."},"document":{"description":"The write-up this plan refers to, when the plan itself is a pointer to one.","type":"object","properties":{"path":{"type":"string","description":"Where it lives, as a workspace path."},"title":{"type":"string","description":"What it is called: its opening heading, or its file name."},"markdown":{"type":"string","description":"The document itself."},"truncated":{"description":"It was clipped at the wire cap; the file on disk has more.","type":"boolean"},"plan":{"description":"It is one of the CLI's plan files, written to be approved rather than merely read.","type":"boolean"}},"required":["path","title","markdown"],"additionalProperties":false},"status":{"type":"string","enum":["pending","approved","rejected","cancelled"],"description":"Where the decision stands."}},"required":["requestId","text","status"],"additionalProperties":false},"question":{"description":"The questions this row asked, and the picks that answered them.","type":"object","properties":{"requestId":{"type":"string","description":"What to send back when you answer."},"questions":{"type":"array","items":{"type":"object","properties":{"question":{"type":"string","description":"What the agent is asking."},"header":{"type":"string","description":"A short label for the question."},"multiSelect":{"type":"boolean","description":"Whether more than one answer can be picked."},"options":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","description":"The choice, in a few words."},"description":{"type":"string","description":"What picking it means."},"preview":{"description":"Something to look at while deciding: a mock-up, a snippet, a layout.","type":"string"}},"required":["label","description"],"additionalProperties":false},"description":"The choices offered. A free-text answer is always possible as well."}},"required":["question","header","multiSelect","options"],"additionalProperties":false},"description":"What it wants to know."},"document":{"description":"The document this turn wrote and is asking about, so the choice can be read beside it.","type":"object","properties":{"path":{"type":"string","description":"Where it lives, as a workspace path."},"title":{"type":"string","description":"What it is called: its opening heading, or its file name."},"markdown":{"type":"string","description":"The document itself."},"truncated":{"description":"It was clipped at the wire cap; the file on disk has more.","type":"boolean"},"plan":{"description":"It is one of the CLI's plan files, written to be approved rather than merely read.","type":"boolean"}},"required":["path","title","markdown"],"additionalProperties":false},"status":{"type":"string","enum":["pending","answered","cancelled"],"description":"Where the answer stands."},"answers":{"description":"What was chosen, keyed by the question, with the chosen labels or the user's own words.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"string"}}},"attachments":{"description":"Files the user attached to their own-words answer, keyed by the question, as workspace-relative paths.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"string"}}}},"required":["requestId","questions","status"],"additionalProperties":false},"permission":{"description":"The tool this row asked permission for, and the decision.","type":"object","properties":{"toolName":{"type":"string","description":"Which tool it wants to use."},"title":{"description":"The whole question, as a sentence, exactly as the runtime words it.","type":"string"},"displayName":{"description":"A short phrase for the button, such as read file.","type":"string"},"description":{"description":"More about what it is asking for.","type":"string"},"reason":{"description":"Why it is asking at all: a rule, the current mode, something that looked risky.","type":"string"},"path":{"description":"Which file it concerns, when it concerns one.","type":"string"},"alwaysLabel":{"description":"The wording for an always-allow answer. Present only when there is something an always could actually remember; without it the only answers are once and no.","type":"string"},"alwaysAsks":{"description":"This request asks every time: an owner's hard rule, a sandbox restart other conversations would feel, or a change only the owner may make. Allow everything in this conversation is not offered on it and never answers it.","type":"boolean","const":true},"program":{"description":"The program this request is holding, when the request is about one. Present on a command gate's request and absent on every other permission ask.","type":"object","properties":{"text":{"type":"string","description":"What would run."},"language":{"type":"string","enum":["bash","javascript"],"description":"Which of the two backends it is written for, named as the grammar that colours it."},"truncated":{"type":"boolean","description":"Whether this is an excerpt of a longer program, so the request can say so instead of ending mid-word. An excerpt always carries the flagged fragment: the beginning, then a window around the fragment, with any skipped middle written into the text as a bracketed count."},"spans":{"type":"array","items":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"maximum":9007199254740991},"end":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["start","end"],"additionalProperties":false},"description":"Which fragments of the text the pattern match fired on: every matched class's, or, under the hard rule, only the class the title names. Offsets into text, in order, never overlapping."}},"required":["text","language","truncated","spans"],"additionalProperties":false},"child":{"description":"The subagent this request would start or reach, and what it runs on. Present on the request to start or reach a subagent and absent on every other permission ask.","type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves it."},"model":{"type":"string","minLength":1,"description":"Which of that provider's models."},"harness":{"description":"Which agentic loop runs it. Absent is the provider's own.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which of that provider's connected accounts pays for it. Absent is whichever has the most room when it starts.","type":"string"},"effort":{"description":"How hard it thinks, where the model offers a choice. Absent is the model's own default.","type":"string"},"thinking":{"description":"Whether it reasons before it answers, where the model offers the choice.","type":"boolean"},"fast":{"description":"Whether it asks for the faster rate, at the higher price.","type":"boolean"},"move":{"type":"string","enum":["spawn","send","answer"],"description":"What the parent asks to do: start a new child, say something to one it started, or answer one's question."},"child":{"description":"The child's id, for one that already exists.","type":"string"},"task":{"description":"What the child is for, in a line.","type":"string"},"message":{"description":"What the parent would say to it: the message it sends, or the answers it gives. Clipped for the request.","type":"string"},"on":{"description":"Which machine it runs on, when one is named: a runner, or \"here\" for this sandbox.","type":"string"},"proposed":{"description":"What the agent asked to start it on, when the owner started it on something else instead. Present only once the request has settled that way.","type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves it."},"model":{"type":"string","minLength":1,"description":"Which of that provider's models."},"harness":{"description":"Which agentic loop runs it. Absent is the provider's own.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which of that provider's connected accounts pays for it. Absent is whichever has the most room when it starts.","type":"string"},"effort":{"description":"How hard it thinks, where the model offers a choice. Absent is the model's own default.","type":"string"},"thinking":{"description":"Whether it reasons before it answers, where the model offers the choice.","type":"boolean"},"fast":{"description":"Whether it asks for the faster rate, at the higher price.","type":"boolean"}},"required":["provider","model"],"additionalProperties":false}},"required":["provider","model","move"],"additionalProperties":false},"explain":{"description":"One plain sentence saying what the program does and why it is being asked about, where the title says something else. Written by the judge that read your safety policy, never by the agent being gated.","type":"string"},"requestId":{"type":"string","description":"What to send back when you answer."},"status":{"type":"string","enum":["pending","allowed","always","everything","denied","cancelled"],"description":"Where the decision stands."}},"required":["toolName","requestId","status"],"additionalProperties":false},"browserHelp":{"description":"The browser hand-over this row asked for, and how it ended.","type":"object","properties":{"requestId":{"type":"string"},"session":{"type":"string"},"account":{"type":"string"},"message":{"type":"string"},"status":{"type":"string","enum":["pending","helped","declined","cancelled"],"description":"How the hand-over ended."}},"required":["requestId","session","account","message","status"],"additionalProperties":false},"terminalHelp":{"description":"The terminal hand-over this row asked for, and how it ended.","type":"object","properties":{"requestId":{"type":"string"},"session":{"type":"string"},"message":{"type":"string"},"status":{"type":"string","enum":["pending","helped","declined","cancelled"],"description":"How the hand-over ended."}},"required":["requestId","session","message","status"],"additionalProperties":false},"capabilityOffer":{"description":"The capability setup this row asked for, the decision, and the outcome.","type":"object","properties":{"requestId":{"type":"string"},"offer":{"type":"object","properties":{"entry":{"type":"string","description":"Which catalog entry is being asked for."},"name":{"type":"string","description":"What it is called, as the catalogue titles it rather than as the agent named it."},"why":{"description":"The agent's case for connecting it, and the only words on this request that are the agent's.","type":"string"}},"required":["entry","name"],"additionalProperties":false},"status":{"type":"string","enum":["pending","connecting","skipped","cancelled"],"description":"Where the decision stands."},"outcome":{"description":"How an accepted ask's setup ended (the capability_outcome frame).","type":"object","properties":{"outcome":{"type":"string","enum":["connected","unfinished"]},"id":{"type":"string"}},"required":["outcome"],"additionalProperties":false}},"required":["requestId","offer","status"],"additionalProperties":false},"paymentOffer":{"description":"The payment this row asked for, the decision, and the receipt.","type":"object","properties":{"requestId":{"type":"string"},"offer":{"type":"object","properties":{"url":{"type":"string","description":"What is being paid for."},"description":{"description":"What the endpoint says it is.","type":"string"},"payTo":{"type":"string","description":"Where the money goes, taken verbatim from the endpoint's own demand."},"network":{"type":"string","description":"On which network."},"asset":{"type":"string","description":"In which token."},"assetName":{"type":"string","description":"That token's name. It is pegged to the dollar, which is what lets every amount here read as dollars."},"amountUsd":{"type":"string","description":"The exact price. Not a ceiling: this scheme has no ranges, so this is the whole spend."},"spentTodayUsd":{"type":"string","description":"What has already gone out today."},"dailyCapUsd":{"type":"string","description":"What may go out in a day."},"why":{"description":"The agent's case for paying, and the only words on this request that are the agent's.","type":"string"}},"required":["url","payTo","network","asset","assetName","amountUsd","spentTodayUsd","dailyCapUsd"],"additionalProperties":false},"status":{"type":"string","enum":["pending","approved","skipped","cancelled"],"description":"Where the decision stands."},"receipt":{"description":"How the approved payment ended (the payment_receipt frame).","type":"object","properties":{"outcome":{"type":"string","enum":["paid","failed"]},"amountUsd":{"type":"string"},"transaction":{"type":"string"},"network":{"type":"string"}},"required":["outcome","amountUsd"],"additionalProperties":false}},"required":["requestId","offer","status"],"additionalProperties":false},"watchWake":{"description":"The condition watch that woke this conversation, and the prompt it was woken with.","type":"object","properties":{"outcome":{"type":"string","enum":["met","timeout","restart-expired","broken"],"description":"How the watch ended: the condition held, the deadline passed, or a restart cut it short."},"note":{"type":"string","description":"The agent's own line on what it was waiting for."},"elapsed":{"type":"string","description":"How long the watch stood, already worded ('43m'): carried rather than recomputed, since the arming instant is not on the row."},"sent":{"type":"string","description":"The whole prompt the model was woken with, disclosed under the row."}},"required":["outcome","note","elapsed","sent"],"additionalProperties":false},"need":{"description":"Something the agent asked a person for, as it was when raised. Its live state (answered, met) is read by its id, since it outlives the turn.","type":"object","properties":{"id":{"type":"string","description":"The need's handle, the one `needs cancel` takes."},"conversationId":{"type":"string","description":"The conversation that asked, and the one its answer wakes."},"subject":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"capability"},"entry":{"type":"string","minLength":1,"description":"The catalog entry, as the catalog names it."},"name":{"type":"string","description":"What the catalog calls it, never the agent's spelling."},"mode":{"type":"string","enum":["connect","reconnect","change"],"description":"Connect something new, give a connected one a credential that works again, or change a setting on a connected one."},"instance":{"description":"The connection a reconnect or a change is about.","type":"string"},"target":{"description":"The site, host or address the connection is for, when the entry can hold several.","type":"string"},"prefill":{"description":"Settings the agent could fill in for a new connection, never a credential: the daemon keeps only the entry's own non-secret fields.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"changes":{"description":"For a change: each setting and the value it would take. Never a credential.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"reason":{"description":"The daemon's own sentence on why this is the ask, such as the refusal a connected credential keeps getting.","type":"string"},"reported":{"description":"The agent reported the credential refused while the connection still probes as working, so only a person's word that it is fixed meets it: the probe could not see the refusal in the first place.","type":"boolean"}},"required":["kind","entry","name","mode"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret"},"name":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,127}$","description":"The name it is stored under, and what `{{secret:NAME}}` will resolve."},"where":{"description":"How it will be used: the header, the command, the site it goes to.","type":"string","maxLength":200},"link":{"description":"Where a person gets one, shown as a link on the card.","type":"string","format":"uri"},"hint":{"description":"What a valid one looks like, so a wrong paste is caught by eye.","type":"string","maxLength":120},"replace":{"description":"One is stored under this name and is being refused: the ask is for a new value in its place.","type":"boolean"}},"required":["kind","name"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"grant"},"subject":{"type":"string","enum":["capability","folder","shelf","site"],"description":"A connected capability the persona leaves out, a folder outside the conversation's reach, a whole shelf of tools, or a site in the person's own browser, which only their browser extension can allow."},"what":{"type":"string","minLength":1,"description":"The capability's id, the folder, or the shelf."},"label":{"type":"string","description":"What it is, in the daemon's words: the account and its kind, the folder, the shelf's name."},"persona":{"description":"The persona that withholds it, when one does.","type":"string"},"scope":{"description":"How far the yes went, once there was one.","type":"string","enum":["conversation","persona"]}},"required":["kind","subject","what","label"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"release"},"subject":{"type":"string","minLength":1,"description":"The gated account or connector."},"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it. Anyone else's answer is refused and leaves it waiting."}},"required":["kind","subject","approvers"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"environment"},"tool":{"type":"string","minLength":1,"description":"What the steps install, the name the proposal is filed under."},"steps":{"type":"string","minLength":1,"description":"The Dockerfile steps proposed for the image's custom section: RUN and ENV lines only."},"approvedHash":{"description":"The overlay these steps were approved into; met once the running container was built from it.","type":"string"}},"required":["kind","tool","steps"],"additionalProperties":false}],"description":"What exactly is asked for."},"title":{"type":"string","description":"The one line it leads with, in the daemon's words."},"why":{"description":"The agent's case for it, and the only words on a need that are the agent's.","type":"string","maxLength":280},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."},"createdAt":{"type":"number","description":"When it was raised, in milliseconds."},"updatedAt":{"type":"number","description":"When it last moved, in milliseconds."},"answeredBy":{"description":"Who answered it, as the sandbox verified them.","type":"string"},"outcome":{"description":"How it ended, in the daemon's words, once it has.","type":"string"},"told":{"description":"How the agent heard the outcome, once it has.","type":"string","enum":["call","turn","queued"]},"unattended":{"description":"Raised by a turn nobody was watching, so the card waited for whoever came next.","type":"boolean"}},"required":["id","conversationId","subject","title","status","createdAt","updatedAt"],"additionalProperties":false},"needWake":{"description":"The answered need that reached this conversation, and the prompt it came as.","type":"object","properties":{"outcome":{"type":"string","enum":["met","declined"],"description":"How the need ended: a person gave it, or said no."},"title":{"type":"string","description":"The need, as its card leads with it."},"id":{"type":"string","description":"The need's handle, which its live card is keyed by."},"sent":{"type":"string","description":"The whole prompt the model received, disclosed under the row."}},"required":["outcome","title","id","sent"],"additionalProperties":false},"agentWords":{"description":"Another agent's words that reached this conversation, whose they are, and the prompt they came as.","type":"object","properties":{"kind":{"type":"string","enum":["peer","child"],"description":"Who sent it: another conversation in the workspace, or a subagent this one started."},"from":{"type":"string","description":"The sending conversation's id."},"title":{"description":"The sender's title, when it had one.","type":"string"},"failed":{"description":"A child's report on a turn that failed rather than finished.","type":"boolean"},"sent":{"type":"string","description":"The whole prompt the model received, disclosed under the row."}},"required":["kind","from","sent"],"additionalProperties":false},"backgroundJob":{"description":"The background job this row marks the start of.","type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for the job, which its live state on the conversation's card is keyed by."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"command":{"type":"string","description":"The command as the agent wrote it, folded to one line."},"startedAt":{"type":"number","description":"When it started, in milliseconds."}},"required":["id","label","command","startedAt"],"additionalProperties":false},"credentialOffer":{"description":"The gated credential this row asked to use, who may release it, and who did.","type":"object","properties":{"requestId":{"type":"string"},"offer":{"type":"object","properties":{"subject":{"type":"string","description":"Which credential is being asked for."},"kind":{"type":"string","enum":["secret","capability"],"description":"Whether this gate covers one stored secret, by the name a reference carries, or one whole connected capability, by its id."},"lane":{"type":"string","enum":["shell","code","browser","session","otp"],"description":"What the credential was about to be used for: a shell command, a script, typing into a page, mounting a connected account, or one one-time code."},"detail":{"description":"Where it would go: the start of the command, the site, or what is being mounted. Never a value: the command still reads as a reference at this point.","type":"string"},"why":{"description":"The agent's case for using it, and the only words on this request that are the agent's.","type":"string"},"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it. A click from anyone else is refused and leaves the request standing."},"scope":{"type":"string","enum":["use","conversation"],"description":"How far one release goes: `use` asks again every single time (one click releases exactly one use), `conversation` covers the rest of this conversation and is forgotten when the daemon restarts."}},"required":["subject","kind","lane","approvers","scope"],"additionalProperties":false},"status":{"type":"string","enum":["pending","approved","skipped","cancelled"],"description":"Where the decision stands."},"receipt":{"description":"Who released it, or that somebody refused (the credential_receipt frame).","type":"object","properties":{"outcome":{"type":"string","enum":["released","refused"]},"approvedBy":{"type":"string"}},"required":["outcome"],"additionalProperties":false}},"required":["requestId","offer","status"],"additionalProperties":false}},"required":["role","text"],"additionalProperties":false},"description":"The conversation, in order. Each block of the agent's prose is its own message with the tools that block introduced, which is what reproduces the way it actually unfolded."},"sessionId":{"description":"The provider session behind the last turn, when there is one.","type":"string"},"provider":{"description":"Which provider minted that session.","type":"string","minLength":1},"harness":{"description":"Which runtime minted it: a session resumes only on the loop that opened it.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which stored account it belongs to, as the daemon resolved it. Absent when no stored account paid for the turn.","type":"string"},"ending":{"description":"How the last turn ended, when it left work behind that one press finishes. Absent for a conversation whose last turn ended on its own, and for the failures that name something to repair first.","type":"object","properties":{"reason":{"type":"string","enum":["limit","outage","stopped","flagged"],"description":"Which ending left the work here: a Stop or a daemon killed under the turn, a spent usage allowance, a provider that refused it, or the provider's safety classifier stopping it partway."},"resetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent for every ending that names no instant, and for a provider that publishes none.","type":"number"},"held":{"description":"Present when the daemon still holds the refused turn whole, so a press re-runs it rather than appending a message after it.","type":"object","properties":{"ran":{"type":"boolean","description":"Whether the held turn got anywhere before it was refused, which is a different sentence from one refused at the door."},"contextTokens":{"description":"How much context a press that keeps the session re-reads once, on this account at the reset or carried to another. Absent when no usage frame measured it.","type":"number"},"handoffTokens":{"description":"What a press that opens a fresh session pays instead: the capped record plus the sandbox's measured brief, counted at the failure.","type":"number"},"moving":{"description":"The account the owner's policy is already moving this turn to, when it is; the surface then reports the move rather than offering a press.","type":"string"}},"required":["ran"],"additionalProperties":false},"scheduled":{"description":"Whether something other than the user is already booked to send this turn again, so the surface reports the wait instead of offering a press.","type":"boolean"},"nextAt":{"description":"When the booked send actually fires, in epoch seconds. Present only with `scheduled`; absent for a booking that fires on the next pass, which is 'now' to a reader.","type":"number"},"retries":{"description":"How many automatic re-runs a stopped turn has already had, of how many. Absent before its first; equal counts mean the ladder is spent and only a press sends it again.","type":"object","properties":{"made":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Automatic re-runs already sent for this turn."},"max":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many the ladder may send before it stands down."}},"required":["made","max"],"additionalProperties":false}},"required":["reason"],"additionalProperties":false},"from":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Where the first message sits in the whole record, and the `before` that asks for the page above this one."},"more":{"type":"boolean","description":"Whether older messages precede this page."}},"required":["messages","from","more"],"additionalProperties":false,"$defs":{"__schema0":{"type":"object","properties":{"id":{"type":"string","description":"The call's id."},"name":{"type":"string","description":"Which tool."},"category":{"type":"string","enum":["read","edit","delete","move","search","execute","think","fetch","other"],"description":"What kind of thing it does: read, edit, delete, move, search, run, think, fetch. Named the same way whatever the backend called the tool."},"status":{"type":"string","enum":["pending","in_progress","completed","failed"],"description":"How it went."},"target":{"description":"What it acted on, in one line: a file, a command, an address.","type":"string"},"locations":{"description":"The files it touched.","type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The file, as a workspace path, whatever directory the tool was run from."},"line":{"description":"Which line, counting from one.","type":"number"}},"required":["path"],"additionalProperties":false}},"content":{"description":"What it produced: text, a change to a file, or a picture.","type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"text","description":"Plain output."},"text":{"type":"string","description":"What the tool said."}},"required":["type","text"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"diff","description":"A change to a file."},"path":{"type":"string","description":"Which file, as a workspace path."},"oldText":{"description":"What was there. Absent for a new file, or where the previous contents are not known.","type":"string"},"newText":{"type":"string","description":"What is there now."},"truncated":{"description":"One of the two sides was too large to send whole.","type":"boolean"}},"required":["type","path","newText"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"image","description":"A picture the tool produced."},"path":{"type":"string","description":"Where it is, as a workspace path. A path rather than the bytes, because the workspace already serves it, sending it inline would bloat every stored record, and this way the picture stays openable afterwards."}},"required":["type","path"],"additionalProperties":false}]}},"children":{"description":"Calls a delegated subagent made, nested under the call that started it, so a reopened conversation redraws the delegation rather than collapsing it into one result.","type":"array","items":{"$ref":"#/$defs/__schema0"}},"nested":{"description":"How many calls sit under this one, present in place of `children` when they were left behind. A transcript page does that, since a settled delegation draws collapsed; ask for the call's own children to fill it in.","type":"integer","minimum":0,"maximum":9007199254740991},"thinking":{"description":"What the agent was reasoning about around this call.","type":"string"},"subagent":{"description":"The helper this call started, as the daemon's registry sees it: what it is, how it is going, what it has spent. What a card can say about a backgrounded child whose result is minutes away.","type":"object","properties":{"id":{"description":"Its own id, where that is not the card's: a spawned subagent is named by its own conversation, which is what the roster, `wait` and its own chat call it. Absent, the card's id is its id, as it is for one the runtime started in-process.","type":"string"},"kind":{"type":"string","enum":["subagent","spawned"]},"agentType":{"type":"string"},"description":{"type":"string"},"model":{"type":"string"},"provider":{"type":"string"},"background":{"type":"boolean"},"status":{"type":"string","enum":["pending","running","blocked","completed","failed","killed","paused"]},"tokens":{"type":"number"},"toolUses":{"type":"number"},"lastTool":{"type":"string"},"summary":{"type":"string"},"error":{"type":"string"},"verification":{"type":"object","properties":{"state":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Whether anything proved its work: a check passed after its last edit, it changed code and nothing checked it, a check ran and failed, or it changed no code at all."},"paths":{"description":"The code files it changed, most recent last. The first few; the record holds the rest.","type":"array","items":{"type":"string"}},"check":{"description":"The command that spoke: the one that cleared it, or the one that failed. Named rather than summarised, so a targeted test is not read as the whole suite.","type":"string"}},"required":["state"],"additionalProperties":false}},"required":["kind","status"],"additionalProperties":false}},"required":["id","name","category","status"],"additionalProperties":false}}}}}}}}},"/agents/{id}/system-prompt":{"get":{"operationId":"agents.systemPrompt","summary":"What this conversation is told before it is asked anything","description":"The system prompt the most recent turn of this conversation actually ran on: which base it was, and every piece the sandbox added to it — this product's guidance, the persona, the field notes, the workspace's own standing rules — each with the exact words the model received. None of this appears in the transcript, so this is the only way to read it.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"description":"What the most recent turn of this conversation was told, if one has been recorded.","type":"object","properties":{"at":{"type":"number","description":"When the turn that was told this was sent (epoch ms)."},"runtime":{"type":"string","description":"Which runtime served that turn."},"mode":{"type":"string","enum":["intentic","claude","custom"],"description":"Which base the turn was configured to run on."},"base":{"type":"object","properties":{"kind":{"type":"string","enum":["intentic","claude","custom","runtime","trimmed"],"description":"Which prompt the additions ride on."},"text":{"description":"The base's own words, when they can be read here. Absent for a runtime that keeps its prompt to itself.","type":"string"},"model":{"description":"The model the turn ran on, for a built-in base: Claude Code renders a different preset for each.","type":"string"}},"required":["kind"],"additionalProperties":false},"sections":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string","enum":["guidance","persona","field-notes","memory"],"description":"Which mechanism added this."},"title":{"type":"string","description":"The one line a reader sees on the row that opens to the text below."},"text":{"type":"string","description":"The section's exact words, as the model received them."}},"required":["source","title","text"],"additionalProperties":false},"description":"What the daemon added to that base, in the order the model reads them."}},"required":["at","runtime","mode","base","sections"],"additionalProperties":false}},"additionalProperties":false}}}}}}},"/agents/{id}/transcript/tools/{toolId}":{"get":{"operationId":"agents.toolChildren","summary":"One delegation's own calls","description":"The calls a delegated agent made under one tool card. A transcript page leaves them behind and reports their count as `nested`, since a settled delegation draws collapsed; this is what fills the card in when it is opened. Empty when the record no longer holds that call.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}},{"name":"toolId","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which tool call, by the id its card carries."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"children":{"type":"array","items":{"$ref":"#/$defs/__schema0"},"description":"The calls the delegated agent made, in the order it made them."}},"required":["children"],"additionalProperties":false,"$defs":{"__schema0":{"type":"object","properties":{"id":{"type":"string","description":"The call's id."},"name":{"type":"string","description":"Which tool."},"category":{"type":"string","enum":["read","edit","delete","move","search","execute","think","fetch","other"],"description":"What kind of thing it does: read, edit, delete, move, search, run, think, fetch. Named the same way whatever the backend called the tool."},"status":{"type":"string","enum":["pending","in_progress","completed","failed"],"description":"How it went."},"target":{"description":"What it acted on, in one line: a file, a command, an address.","type":"string"},"locations":{"description":"The files it touched.","type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The file, as a workspace path, whatever directory the tool was run from."},"line":{"description":"Which line, counting from one.","type":"number"}},"required":["path"],"additionalProperties":false}},"content":{"description":"What it produced: text, a change to a file, or a picture.","type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"text","description":"Plain output."},"text":{"type":"string","description":"What the tool said."}},"required":["type","text"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"diff","description":"A change to a file."},"path":{"type":"string","description":"Which file, as a workspace path."},"oldText":{"description":"What was there. Absent for a new file, or where the previous contents are not known.","type":"string"},"newText":{"type":"string","description":"What is there now."},"truncated":{"description":"One of the two sides was too large to send whole.","type":"boolean"}},"required":["type","path","newText"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"image","description":"A picture the tool produced."},"path":{"type":"string","description":"Where it is, as a workspace path. A path rather than the bytes, because the workspace already serves it, sending it inline would bloat every stored record, and this way the picture stays openable afterwards."}},"required":["type","path"],"additionalProperties":false}]}},"children":{"description":"Calls a delegated subagent made, nested under the call that started it, so a reopened conversation redraws the delegation rather than collapsing it into one result.","type":"array","items":{"$ref":"#/$defs/__schema0"}},"nested":{"description":"How many calls sit under this one, present in place of `children` when they were left behind. A transcript page does that, since a settled delegation draws collapsed; ask for the call's own children to fill it in.","type":"integer","minimum":0,"maximum":9007199254740991},"thinking":{"description":"What the agent was reasoning about around this call.","type":"string"},"subagent":{"description":"The helper this call started, as the daemon's registry sees it: what it is, how it is going, what it has spent. What a card can say about a backgrounded child whose result is minutes away.","type":"object","properties":{"id":{"description":"Its own id, where that is not the card's: a spawned subagent is named by its own conversation, which is what the roster, `wait` and its own chat call it. Absent, the card's id is its id, as it is for one the runtime started in-process.","type":"string"},"kind":{"type":"string","enum":["subagent","spawned"]},"agentType":{"type":"string"},"description":{"type":"string"},"model":{"type":"string"},"provider":{"type":"string"},"background":{"type":"boolean"},"status":{"type":"string","enum":["pending","running","blocked","completed","failed","killed","paused"]},"tokens":{"type":"number"},"toolUses":{"type":"number"},"lastTool":{"type":"string"},"summary":{"type":"string"},"error":{"type":"string"},"verification":{"type":"object","properties":{"state":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Whether anything proved its work: a check passed after its last edit, it changed code and nothing checked it, a check ran and failed, or it changed no code at all."},"paths":{"description":"The code files it changed, most recent last. The first few; the record holds the rest.","type":"array","items":{"type":"string"}},"check":{"description":"The command that spoke: the one that cleared it, or the one that failed. Named rather than summarised, so a targeted test is not read as the whole suite.","type":"string"}},"required":["state"],"additionalProperties":false}},"required":["kind","status"],"additionalProperties":false}},"required":["id","name","category","status"],"additionalProperties":false}}}}}}}}},"/agents/{id}/subagents/{subagentId}/transcript":{"get":{"operationId":"agents.subagentTranscript","summary":"One in-process subagent's own record","description":"What a subagent this conversation's runtime ran in-process said and did, as a transcript of its own: its ask, its thinking, its calls and its words. Read from the runtime's own record of the subagent where it keeps one, which it writes as the subagent works, else from the calls the delegation's card holds in this conversation's record. A subagent spawned as a conversation of its own is read through `transcript` instead.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}},{"name":"subagentId","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which subagent this conversation's runtime ran in-process, by the id of the call that started it."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"type":"object","properties":{"role":{"type":"string","enum":["user","assistant","notice"],"description":"Who said it. A notice is neither side: it is something that happened to the turn, recorded so a reopened conversation can say it. Without those, a turn a provider refused ends on the user's message and reads as broken."},"text":{"type":"string","description":"The words."},"run":{"description":"The run that produced this row. Present on everything a turn produced, absent on rows written outside one. A client draws a run's rows over whatever it already holds for that run, which is what this identifies; content cannot, because the last row of a live run keeps growing.","type":"string"},"sentAt":{"description":"When it was sent, in milliseconds. On the user's rows only, because that is the only moment actually known: a turn's own frames arrive with no clock, so stamping the agent's rows could only ever mean the whole turn's start or end.","type":"number"},"messageId":{"description":"The message's own id, on the rows of messages sent to the agent: what its sender named it, or what the sandbox did. A rewind names the message by it, and the same id sent again is recognised rather than delivered twice.","type":"string"},"attachments":{"description":"Files attached to this message, as workspace paths.","type":"array","items":{"type":"string"}},"checkpointId":{"description":"The saved point this message can be rewound to. Looked up on each read rather than stored, so what is offered is exactly what is still there to go back to.","type":"string"},"rewindIndex":{"description":"This message's position in the conversation's record, which is how a rewind names it. Present only beside a checkpoint.","type":"integer","minimum":0,"maximum":9007199254740991},"thinking":{"description":"What the agent was reasoning about.","type":"string"},"tools":{"description":"The tool calls this part of the turn made.","type":"array","items":{"$ref":"#/$defs/__schema0"}},"todos":{"description":"The agent's task checklist, as of this bubble.","type":"array","items":{"type":"object","properties":{"content":{"type":"string","description":"The item, as the agent wrote it."},"status":{"type":"string","enum":["pending","in_progress","completed"],"description":"Where it is."},"activeForm":{"description":"How to phrase it while it is happening, so a screen can say what the agent is doing rather than what it plans to do.","type":"string"}},"required":["content","status"],"additionalProperties":false}},"usage":{"description":"What the turn cost, on the bubble its answer ended in.","type":"object","properties":{"costUsd":{"type":"number"},"inputTokens":{"type":"number"},"outputTokens":{"type":"number"},"durationMs":{"type":"number"},"numTurns":{"type":"number"}},"additionalProperties":false},"notes":{"description":"What the sandbox added to this message before the model saw it. Carried on the message rather than as rows of their own, because they genuinely were part of what was sent.","type":"array","items":{"type":"object","properties":{"title":{"type":"string","description":"The one line a reader sees, on a row that opens to the text below."},"text":{"type":"string","description":"The note itself, which is also exactly what the model was told."}},"required":["title","text"],"additionalProperties":false}},"speaker":{"description":"Who sent this message, as the sandbox verified it. Absent where it does not say.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"type":"string","description":"The signed-in member, as the sandbox verified them."},"name":{"description":"Their display name, where the sign-in carries one.","type":"string"}},"required":["kind","email"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"program"},"token":{"type":"string","description":"The label of the control token a person minted and handed to this program."}},"required":["kind","token"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"conversationId":{"type":"string","description":"The conversation whose agent is speaking: a child reporting back, a peer's message."}},"required":["kind","conversationId"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"sandbox"},"source":{"description":"What in the sandbox spoke: an automation, a watch that fired, a job that ended, a repair. Absent when it does not say.","type":"string"}},"required":["kind"],"additionalProperties":false}]},"errand":{"description":"What this message is for, when the sandbox or the app composed it rather than a person typing it. Absent on a person's own words.","type":"string","enum":["land-conflict","verify-nudge","land-breakage","land-fix","land-fix-nudge","land-held","push-fix","push-fix-nudge","ci-fix","ci-fix-nudge"]},"placed":{"description":"A person wrote this in the agent's voice, with no turn behind it. Marked for the human re-reading the conversation months later, so their own words do not pass as the agent's. The agent itself never sees the mark.","type":"boolean"},"noticeAction":{"description":"A one-press follow-up this notice offers, by name. The chat decides what it does and whether it still applies.","type":"string","enum":["landHold","depsInstall","watchStop","sandboxMemory","sendAnyway","sendAgain"]},"sandboxHeld":{"description":"The sandbox kept this refused turn whole, its message still above, so the notice's press runs that turn again instead of letting the conversation's queue go, which never held these words.","type":"boolean"},"noticeWait":{"description":"The wait this notice describes, by name, so a reader can say whether it is still on.","type":"string","enum":["credentialRenewal","chatRoute","watch"]},"noticeWaitId":{"description":"Which instance of the wait this notice names, for a kind that can have several running at once.","type":"string"},"noticeCode":{"description":"Which of the sandbox's own notices this row is, and the facts it was worded from, so a reader can say it in the reader's own language. The text stays the English sentence.","type":"object","properties":{"code":{"type":"string","description":"Which of the sandbox's notices this row is, by name. A reader that does not know the name shows the row's text."},"params":{"description":"The facts the notice was worded from, by name: counts, names, and the provider's own sentence where the notice quotes one.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number","boolean"]}}},"required":["code"],"additionalProperties":false},"agentNotice":{"description":"This notice was said by the agent's runtime or one of its extensions rather than by the sandbox: how loud, and who said it.","type":"object","properties":{"level":{"type":"string","enum":["info","warning","error"],"description":"How loud it was said: a muted line, a warning or an error."},"source":{"description":"Who said it, when the runtime named one: the extension or plugin, as the runtime spells it. Absent when the runtime's loop said it itself.","type":"string"}},"required":["level"],"additionalProperties":false},"plan":{"description":"The plan this row asked approval for, and the answer.","type":"object","properties":{"requestId":{"type":"string","description":"What to send back when you answer."},"text":{"type":"string","description":"The plan itself."},"document":{"description":"The write-up this plan refers to, when the plan itself is a pointer to one.","type":"object","properties":{"path":{"type":"string","description":"Where it lives, as a workspace path."},"title":{"type":"string","description":"What it is called: its opening heading, or its file name."},"markdown":{"type":"string","description":"The document itself."},"truncated":{"description":"It was clipped at the wire cap; the file on disk has more.","type":"boolean"},"plan":{"description":"It is one of the CLI's plan files, written to be approved rather than merely read.","type":"boolean"}},"required":["path","title","markdown"],"additionalProperties":false},"status":{"type":"string","enum":["pending","approved","rejected","cancelled"],"description":"Where the decision stands."}},"required":["requestId","text","status"],"additionalProperties":false},"question":{"description":"The questions this row asked, and the picks that answered them.","type":"object","properties":{"requestId":{"type":"string","description":"What to send back when you answer."},"questions":{"type":"array","items":{"type":"object","properties":{"question":{"type":"string","description":"What the agent is asking."},"header":{"type":"string","description":"A short label for the question."},"multiSelect":{"type":"boolean","description":"Whether more than one answer can be picked."},"options":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","description":"The choice, in a few words."},"description":{"type":"string","description":"What picking it means."},"preview":{"description":"Something to look at while deciding: a mock-up, a snippet, a layout.","type":"string"}},"required":["label","description"],"additionalProperties":false},"description":"The choices offered. A free-text answer is always possible as well."}},"required":["question","header","multiSelect","options"],"additionalProperties":false},"description":"What it wants to know."},"document":{"description":"The document this turn wrote and is asking about, so the choice can be read beside it.","type":"object","properties":{"path":{"type":"string","description":"Where it lives, as a workspace path."},"title":{"type":"string","description":"What it is called: its opening heading, or its file name."},"markdown":{"type":"string","description":"The document itself."},"truncated":{"description":"It was clipped at the wire cap; the file on disk has more.","type":"boolean"},"plan":{"description":"It is one of the CLI's plan files, written to be approved rather than merely read.","type":"boolean"}},"required":["path","title","markdown"],"additionalProperties":false},"status":{"type":"string","enum":["pending","answered","cancelled"],"description":"Where the answer stands."},"answers":{"description":"What was chosen, keyed by the question, with the chosen labels or the user's own words.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"string"}}},"attachments":{"description":"Files the user attached to their own-words answer, keyed by the question, as workspace-relative paths.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"string"}}}},"required":["requestId","questions","status"],"additionalProperties":false},"permission":{"description":"The tool this row asked permission for, and the decision.","type":"object","properties":{"toolName":{"type":"string","description":"Which tool it wants to use."},"title":{"description":"The whole question, as a sentence, exactly as the runtime words it.","type":"string"},"displayName":{"description":"A short phrase for the button, such as read file.","type":"string"},"description":{"description":"More about what it is asking for.","type":"string"},"reason":{"description":"Why it is asking at all: a rule, the current mode, something that looked risky.","type":"string"},"path":{"description":"Which file it concerns, when it concerns one.","type":"string"},"alwaysLabel":{"description":"The wording for an always-allow answer. Present only when there is something an always could actually remember; without it the only answers are once and no.","type":"string"},"alwaysAsks":{"description":"This request asks every time: an owner's hard rule, a sandbox restart other conversations would feel, or a change only the owner may make. Allow everything in this conversation is not offered on it and never answers it.","type":"boolean","const":true},"program":{"description":"The program this request is holding, when the request is about one. Present on a command gate's request and absent on every other permission ask.","type":"object","properties":{"text":{"type":"string","description":"What would run."},"language":{"type":"string","enum":["bash","javascript"],"description":"Which of the two backends it is written for, named as the grammar that colours it."},"truncated":{"type":"boolean","description":"Whether this is an excerpt of a longer program, so the request can say so instead of ending mid-word. An excerpt always carries the flagged fragment: the beginning, then a window around the fragment, with any skipped middle written into the text as a bracketed count."},"spans":{"type":"array","items":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"maximum":9007199254740991},"end":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["start","end"],"additionalProperties":false},"description":"Which fragments of the text the pattern match fired on: every matched class's, or, under the hard rule, only the class the title names. Offsets into text, in order, never overlapping."}},"required":["text","language","truncated","spans"],"additionalProperties":false},"child":{"description":"The subagent this request would start or reach, and what it runs on. Present on the request to start or reach a subagent and absent on every other permission ask.","type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves it."},"model":{"type":"string","minLength":1,"description":"Which of that provider's models."},"harness":{"description":"Which agentic loop runs it. Absent is the provider's own.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which of that provider's connected accounts pays for it. Absent is whichever has the most room when it starts.","type":"string"},"effort":{"description":"How hard it thinks, where the model offers a choice. Absent is the model's own default.","type":"string"},"thinking":{"description":"Whether it reasons before it answers, where the model offers the choice.","type":"boolean"},"fast":{"description":"Whether it asks for the faster rate, at the higher price.","type":"boolean"},"move":{"type":"string","enum":["spawn","send","answer"],"description":"What the parent asks to do: start a new child, say something to one it started, or answer one's question."},"child":{"description":"The child's id, for one that already exists.","type":"string"},"task":{"description":"What the child is for, in a line.","type":"string"},"message":{"description":"What the parent would say to it: the message it sends, or the answers it gives. Clipped for the request.","type":"string"},"on":{"description":"Which machine it runs on, when one is named: a runner, or \"here\" for this sandbox.","type":"string"},"proposed":{"description":"What the agent asked to start it on, when the owner started it on something else instead. Present only once the request has settled that way.","type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves it."},"model":{"type":"string","minLength":1,"description":"Which of that provider's models."},"harness":{"description":"Which agentic loop runs it. Absent is the provider's own.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which of that provider's connected accounts pays for it. Absent is whichever has the most room when it starts.","type":"string"},"effort":{"description":"How hard it thinks, where the model offers a choice. Absent is the model's own default.","type":"string"},"thinking":{"description":"Whether it reasons before it answers, where the model offers the choice.","type":"boolean"},"fast":{"description":"Whether it asks for the faster rate, at the higher price.","type":"boolean"}},"required":["provider","model"],"additionalProperties":false}},"required":["provider","model","move"],"additionalProperties":false},"explain":{"description":"One plain sentence saying what the program does and why it is being asked about, where the title says something else. Written by the judge that read your safety policy, never by the agent being gated.","type":"string"},"requestId":{"type":"string","description":"What to send back when you answer."},"status":{"type":"string","enum":["pending","allowed","always","everything","denied","cancelled"],"description":"Where the decision stands."}},"required":["toolName","requestId","status"],"additionalProperties":false},"browserHelp":{"description":"The browser hand-over this row asked for, and how it ended.","type":"object","properties":{"requestId":{"type":"string"},"session":{"type":"string"},"account":{"type":"string"},"message":{"type":"string"},"status":{"type":"string","enum":["pending","helped","declined","cancelled"],"description":"How the hand-over ended."}},"required":["requestId","session","account","message","status"],"additionalProperties":false},"terminalHelp":{"description":"The terminal hand-over this row asked for, and how it ended.","type":"object","properties":{"requestId":{"type":"string"},"session":{"type":"string"},"message":{"type":"string"},"status":{"type":"string","enum":["pending","helped","declined","cancelled"],"description":"How the hand-over ended."}},"required":["requestId","session","message","status"],"additionalProperties":false},"capabilityOffer":{"description":"The capability setup this row asked for, the decision, and the outcome.","type":"object","properties":{"requestId":{"type":"string"},"offer":{"type":"object","properties":{"entry":{"type":"string","description":"Which catalog entry is being asked for."},"name":{"type":"string","description":"What it is called, as the catalogue titles it rather than as the agent named it."},"why":{"description":"The agent's case for connecting it, and the only words on this request that are the agent's.","type":"string"}},"required":["entry","name"],"additionalProperties":false},"status":{"type":"string","enum":["pending","connecting","skipped","cancelled"],"description":"Where the decision stands."},"outcome":{"description":"How an accepted ask's setup ended (the capability_outcome frame).","type":"object","properties":{"outcome":{"type":"string","enum":["connected","unfinished"]},"id":{"type":"string"}},"required":["outcome"],"additionalProperties":false}},"required":["requestId","offer","status"],"additionalProperties":false},"paymentOffer":{"description":"The payment this row asked for, the decision, and the receipt.","type":"object","properties":{"requestId":{"type":"string"},"offer":{"type":"object","properties":{"url":{"type":"string","description":"What is being paid for."},"description":{"description":"What the endpoint says it is.","type":"string"},"payTo":{"type":"string","description":"Where the money goes, taken verbatim from the endpoint's own demand."},"network":{"type":"string","description":"On which network."},"asset":{"type":"string","description":"In which token."},"assetName":{"type":"string","description":"That token's name. It is pegged to the dollar, which is what lets every amount here read as dollars."},"amountUsd":{"type":"string","description":"The exact price. Not a ceiling: this scheme has no ranges, so this is the whole spend."},"spentTodayUsd":{"type":"string","description":"What has already gone out today."},"dailyCapUsd":{"type":"string","description":"What may go out in a day."},"why":{"description":"The agent's case for paying, and the only words on this request that are the agent's.","type":"string"}},"required":["url","payTo","network","asset","assetName","amountUsd","spentTodayUsd","dailyCapUsd"],"additionalProperties":false},"status":{"type":"string","enum":["pending","approved","skipped","cancelled"],"description":"Where the decision stands."},"receipt":{"description":"How the approved payment ended (the payment_receipt frame).","type":"object","properties":{"outcome":{"type":"string","enum":["paid","failed"]},"amountUsd":{"type":"string"},"transaction":{"type":"string"},"network":{"type":"string"}},"required":["outcome","amountUsd"],"additionalProperties":false}},"required":["requestId","offer","status"],"additionalProperties":false},"watchWake":{"description":"The condition watch that woke this conversation, and the prompt it was woken with.","type":"object","properties":{"outcome":{"type":"string","enum":["met","timeout","restart-expired","broken"],"description":"How the watch ended: the condition held, the deadline passed, or a restart cut it short."},"note":{"type":"string","description":"The agent's own line on what it was waiting for."},"elapsed":{"type":"string","description":"How long the watch stood, already worded ('43m'): carried rather than recomputed, since the arming instant is not on the row."},"sent":{"type":"string","description":"The whole prompt the model was woken with, disclosed under the row."}},"required":["outcome","note","elapsed","sent"],"additionalProperties":false},"need":{"description":"Something the agent asked a person for, as it was when raised. Its live state (answered, met) is read by its id, since it outlives the turn.","type":"object","properties":{"id":{"type":"string","description":"The need's handle, the one `needs cancel` takes."},"conversationId":{"type":"string","description":"The conversation that asked, and the one its answer wakes."},"subject":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"capability"},"entry":{"type":"string","minLength":1,"description":"The catalog entry, as the catalog names it."},"name":{"type":"string","description":"What the catalog calls it, never the agent's spelling."},"mode":{"type":"string","enum":["connect","reconnect","change"],"description":"Connect something new, give a connected one a credential that works again, or change a setting on a connected one."},"instance":{"description":"The connection a reconnect or a change is about.","type":"string"},"target":{"description":"The site, host or address the connection is for, when the entry can hold several.","type":"string"},"prefill":{"description":"Settings the agent could fill in for a new connection, never a credential: the daemon keeps only the entry's own non-secret fields.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"changes":{"description":"For a change: each setting and the value it would take. Never a credential.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"reason":{"description":"The daemon's own sentence on why this is the ask, such as the refusal a connected credential keeps getting.","type":"string"},"reported":{"description":"The agent reported the credential refused while the connection still probes as working, so only a person's word that it is fixed meets it: the probe could not see the refusal in the first place.","type":"boolean"}},"required":["kind","entry","name","mode"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret"},"name":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,127}$","description":"The name it is stored under, and what `{{secret:NAME}}` will resolve."},"where":{"description":"How it will be used: the header, the command, the site it goes to.","type":"string","maxLength":200},"link":{"description":"Where a person gets one, shown as a link on the card.","type":"string","format":"uri"},"hint":{"description":"What a valid one looks like, so a wrong paste is caught by eye.","type":"string","maxLength":120},"replace":{"description":"One is stored under this name and is being refused: the ask is for a new value in its place.","type":"boolean"}},"required":["kind","name"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"grant"},"subject":{"type":"string","enum":["capability","folder","shelf","site"],"description":"A connected capability the persona leaves out, a folder outside the conversation's reach, a whole shelf of tools, or a site in the person's own browser, which only their browser extension can allow."},"what":{"type":"string","minLength":1,"description":"The capability's id, the folder, or the shelf."},"label":{"type":"string","description":"What it is, in the daemon's words: the account and its kind, the folder, the shelf's name."},"persona":{"description":"The persona that withholds it, when one does.","type":"string"},"scope":{"description":"How far the yes went, once there was one.","type":"string","enum":["conversation","persona"]}},"required":["kind","subject","what","label"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"release"},"subject":{"type":"string","minLength":1,"description":"The gated account or connector."},"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it. Anyone else's answer is refused and leaves it waiting."}},"required":["kind","subject","approvers"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"environment"},"tool":{"type":"string","minLength":1,"description":"What the steps install, the name the proposal is filed under."},"steps":{"type":"string","minLength":1,"description":"The Dockerfile steps proposed for the image's custom section: RUN and ENV lines only."},"approvedHash":{"description":"The overlay these steps were approved into; met once the running container was built from it.","type":"string"}},"required":["kind","tool","steps"],"additionalProperties":false}],"description":"What exactly is asked for."},"title":{"type":"string","description":"The one line it leads with, in the daemon's words."},"why":{"description":"The agent's case for it, and the only words on a need that are the agent's.","type":"string","maxLength":280},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."},"createdAt":{"type":"number","description":"When it was raised, in milliseconds."},"updatedAt":{"type":"number","description":"When it last moved, in milliseconds."},"answeredBy":{"description":"Who answered it, as the sandbox verified them.","type":"string"},"outcome":{"description":"How it ended, in the daemon's words, once it has.","type":"string"},"told":{"description":"How the agent heard the outcome, once it has.","type":"string","enum":["call","turn","queued"]},"unattended":{"description":"Raised by a turn nobody was watching, so the card waited for whoever came next.","type":"boolean"}},"required":["id","conversationId","subject","title","status","createdAt","updatedAt"],"additionalProperties":false},"needWake":{"description":"The answered need that reached this conversation, and the prompt it came as.","type":"object","properties":{"outcome":{"type":"string","enum":["met","declined"],"description":"How the need ended: a person gave it, or said no."},"title":{"type":"string","description":"The need, as its card leads with it."},"id":{"type":"string","description":"The need's handle, which its live card is keyed by."},"sent":{"type":"string","description":"The whole prompt the model received, disclosed under the row."}},"required":["outcome","title","id","sent"],"additionalProperties":false},"agentWords":{"description":"Another agent's words that reached this conversation, whose they are, and the prompt they came as.","type":"object","properties":{"kind":{"type":"string","enum":["peer","child"],"description":"Who sent it: another conversation in the workspace, or a subagent this one started."},"from":{"type":"string","description":"The sending conversation's id."},"title":{"description":"The sender's title, when it had one.","type":"string"},"failed":{"description":"A child's report on a turn that failed rather than finished.","type":"boolean"},"sent":{"type":"string","description":"The whole prompt the model received, disclosed under the row."}},"required":["kind","from","sent"],"additionalProperties":false},"backgroundJob":{"description":"The background job this row marks the start of.","type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for the job, which its live state on the conversation's card is keyed by."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"command":{"type":"string","description":"The command as the agent wrote it, folded to one line."},"startedAt":{"type":"number","description":"When it started, in milliseconds."}},"required":["id","label","command","startedAt"],"additionalProperties":false},"credentialOffer":{"description":"The gated credential this row asked to use, who may release it, and who did.","type":"object","properties":{"requestId":{"type":"string"},"offer":{"type":"object","properties":{"subject":{"type":"string","description":"Which credential is being asked for."},"kind":{"type":"string","enum":["secret","capability"],"description":"Whether this gate covers one stored secret, by the name a reference carries, or one whole connected capability, by its id."},"lane":{"type":"string","enum":["shell","code","browser","session","otp"],"description":"What the credential was about to be used for: a shell command, a script, typing into a page, mounting a connected account, or one one-time code."},"detail":{"description":"Where it would go: the start of the command, the site, or what is being mounted. Never a value: the command still reads as a reference at this point.","type":"string"},"why":{"description":"The agent's case for using it, and the only words on this request that are the agent's.","type":"string"},"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it. A click from anyone else is refused and leaves the request standing."},"scope":{"type":"string","enum":["use","conversation"],"description":"How far one release goes: `use` asks again every single time (one click releases exactly one use), `conversation` covers the rest of this conversation and is forgotten when the daemon restarts."}},"required":["subject","kind","lane","approvers","scope"],"additionalProperties":false},"status":{"type":"string","enum":["pending","approved","skipped","cancelled"],"description":"Where the decision stands."},"receipt":{"description":"Who released it, or that somebody refused (the credential_receipt frame).","type":"object","properties":{"outcome":{"type":"string","enum":["released","refused"]},"approvedBy":{"type":"string"}},"required":["outcome"],"additionalProperties":false}},"required":["requestId","offer","status"],"additionalProperties":false}},"required":["role","text"],"additionalProperties":false},"description":"The conversation, in order. Each block of the agent's prose is its own message with the tools that block introduced, which is what reproduces the way it actually unfolded."}},"required":["messages"],"additionalProperties":false,"$defs":{"__schema0":{"type":"object","properties":{"id":{"type":"string","description":"The call's id."},"name":{"type":"string","description":"Which tool."},"category":{"type":"string","enum":["read","edit","delete","move","search","execute","think","fetch","other"],"description":"What kind of thing it does: read, edit, delete, move, search, run, think, fetch. Named the same way whatever the backend called the tool."},"status":{"type":"string","enum":["pending","in_progress","completed","failed"],"description":"How it went."},"target":{"description":"What it acted on, in one line: a file, a command, an address.","type":"string"},"locations":{"description":"The files it touched.","type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The file, as a workspace path, whatever directory the tool was run from."},"line":{"description":"Which line, counting from one.","type":"number"}},"required":["path"],"additionalProperties":false}},"content":{"description":"What it produced: text, a change to a file, or a picture.","type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"text","description":"Plain output."},"text":{"type":"string","description":"What the tool said."}},"required":["type","text"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"diff","description":"A change to a file."},"path":{"type":"string","description":"Which file, as a workspace path."},"oldText":{"description":"What was there. Absent for a new file, or where the previous contents are not known.","type":"string"},"newText":{"type":"string","description":"What is there now."},"truncated":{"description":"One of the two sides was too large to send whole.","type":"boolean"}},"required":["type","path","newText"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"image","description":"A picture the tool produced."},"path":{"type":"string","description":"Where it is, as a workspace path. A path rather than the bytes, because the workspace already serves it, sending it inline would bloat every stored record, and this way the picture stays openable afterwards."}},"required":["type","path"],"additionalProperties":false}]}},"children":{"description":"Calls a delegated subagent made, nested under the call that started it, so a reopened conversation redraws the delegation rather than collapsing it into one result.","type":"array","items":{"$ref":"#/$defs/__schema0"}},"nested":{"description":"How many calls sit under this one, present in place of `children` when they were left behind. A transcript page does that, since a settled delegation draws collapsed; ask for the call's own children to fill it in.","type":"integer","minimum":0,"maximum":9007199254740991},"thinking":{"description":"What the agent was reasoning about around this call.","type":"string"},"subagent":{"description":"The helper this call started, as the daemon's registry sees it: what it is, how it is going, what it has spent. What a card can say about a backgrounded child whose result is minutes away.","type":"object","properties":{"id":{"description":"Its own id, where that is not the card's: a spawned subagent is named by its own conversation, which is what the roster, `wait` and its own chat call it. Absent, the card's id is its id, as it is for one the runtime started in-process.","type":"string"},"kind":{"type":"string","enum":["subagent","spawned"]},"agentType":{"type":"string"},"description":{"type":"string"},"model":{"type":"string"},"provider":{"type":"string"},"background":{"type":"boolean"},"status":{"type":"string","enum":["pending","running","blocked","completed","failed","killed","paused"]},"tokens":{"type":"number"},"toolUses":{"type":"number"},"lastTool":{"type":"string"},"summary":{"type":"string"},"error":{"type":"string"},"verification":{"type":"object","properties":{"state":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Whether anything proved its work: a check passed after its last edit, it changed code and nothing checked it, a check ran and failed, or it changed no code at all."},"paths":{"description":"The code files it changed, most recent last. The first few; the record holds the rest.","type":"array","items":{"type":"string"}},"check":{"description":"The command that spoke: the one that cleared it, or the one that failed. Named rather than summarised, so a targeted test is not read as the whole suite.","type":"string"}},"required":["state"],"additionalProperties":false}},"required":["kind","status"],"additionalProperties":false}},"required":["id","name","category","status"],"additionalProperties":false}}}}}}}}},"/agents/{id}/place":{"post":{"operationId":"agents.place","summary":"Put words in the agent's mouth","description":"Writes a line into the record as though the agent had said it, with no turn behind it and no reply. Human readers see it marked as placed. The next real turn starts fresh from the record, where the line reads as the agent's own. Refused while a turn is running.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":8000,"description":"The words to put in the agent's mouth. Bounded just above what the next turn can carry whole, because a line too long to be handed over intact would reach the agent truncated and quietly break the very thing this is for."}},"required":["text"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/agents/{id}/rename":{"post":{"operationId":"agents.rename","summary":"Retitle a conversation","description":"Sets the title a person chose, replacing the one that was generated. Allowed while the conversation is working, and it does not count as activity.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":80,"description":"What to call it from now on."}},"required":["title"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The conversation id, which is how every other call addresses it."},"sessionId":{"description":"The provider session behind the last turn. It is retired whenever the model or account changes.","type":"string"},"title":{"description":"What to call it: the first prompt cut to one line, unless somebody renamed it.","type":"string"},"titleAction":{"description":"One word for the kind of work the naming pass read in it (fix, audit, redesign). Never part of the displayed name: a board tints and glyphs a card by it. Absent for a title nothing named an action for.","type":"string"},"status":{"type":"string","enum":["idle","running","awaiting","stopping","dismissing","stopped","resuming","landing","ready","landed","conflict","error","interrupted"],"description":"What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; dismissing is the same window for a question waved away, which ends the turn too but owes the user nothing; resuming means the sandbox is already putting right whatever killed the turn; landing means its work is being carried into the workspace right now, and nothing may act on its branch until that settles."},"failure":{"description":"Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.","type":"string"},"failureCode":{"description":"Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.","type":"string"},"limitResetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant.","type":"number"},"limitHeld":{"description":"Whether the refused turn is held whole, so sending again re-runs it instead of appending to it.","type":"boolean"},"limitScheduled":{"description":"Whether the held turn is already booked to go again at the reset, so nobody has to press anything.","type":"boolean"},"limitMoving":{"description":"The account the held turn is being moved to by the owner's policy, while that move is booked.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it runs on."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop it runs on."},"runner":{"description":"The runner this conversation runs on. Absent means this sandbox.","type":"string"},"startIn":{"description":"Which folder it opened in, relative to the workspace root. Absent means the root.","type":"string"},"actsAs":{"description":"Which persona its first turn acted as. Absent for an ordinary chat.","type":"string"},"lastActsAs":{"description":"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.","type":"string"},"model":{"description":"What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked.","type":"string"},"effort":{"description":"How hard that turn was told to think.","type":"string"},"thinking":{"description":"Whether that turn showed its reasoning.","type":"boolean"},"fast":{"description":"Whether that turn asked for higher speed. What was asked for, not what was served.","type":"boolean"},"account":{"description":"Which connected account paid for it.","type":"string"},"branch":{"description":"The branch its private copy works on. Absent for a conversation that works directly in the shared tree.","type":"string"},"autoLand":{"description":"This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case.","type":"boolean"},"limitPolicy":{"type":"string","enum":["wait","resend","move"]},"outagePolicy":{"type":"string","enum":["wait","retry"]},"stopPolicy":{"type":"string","enum":["wait","retry"]},"landRequested":{"description":"A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting.","type":"object","properties":{"email":{"type":"string","description":"Who asked."},"name":{"description":"Their display name.","type":"string"},"at":{"type":"number","description":"When they asked, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"reactions":{"description":"What people have marked this conversation with, one entry per emoji, in the order the emoji were first used. Absent means nobody has marked it, which is most conversations.","type":"array","items":{"type":"object","properties":{"emoji":{"type":"string","description":"The mark itself, one emoji, carried as the character rather than as a name that would need a table on both sides."},"by":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Who it was, as the sandbox verified them."},"name":{"description":"What to call them, when their sign-in carried a name. Absent leaves the address to stand for them.","type":"string"},"at":{"type":"number","description":"When they marked it, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"description":"Everyone wearing this mark, oldest first, each by name. Carried whole rather than as a count, because a chip reading 3 that cannot say whose is a number nobody can answer. Never empty: the last person taking theirs back takes the whole chip with it."}},"required":["emoji","by"],"additionalProperties":false}},"origin":{"description":"Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"startedBy":{"description":"Who asked for the first turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a child another conversation spawned. Absent when nothing was verified (a wake, a loopback caller).","type":"string"},"areas":{"description":"Which named areas of the workspace this conversation was started within, latched from whoever asked for the first turn. Absent means its starter held the whole workspace, which is why a fenced member is not shown it.","type":"array","items":{"type":"string"}},"owner":{"description":"The member answerable for this conversation: set from whoever started it, inherited from the parent by a spawned child, moved by handing it over. Absent means nobody has claimed it yet.","type":"object","properties":{"email":{"type":"string","description":"Who answers for this conversation, as the sandbox verified them."},"name":{"description":"What to call them, when the sign-in that made them its owner carried a name. Absent leaves the address to stand for them.","type":"string"},"since":{"type":"number","description":"When they became its owner, in milliseconds."}},"required":["email","since"],"additionalProperties":false},"forkedFrom":{"description":"The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"type":"string","enum":["then","now"]}},"required":["conversationId","index","files"],"additionalProperties":false},"base":{"description":"The commit its private copy started from, shortened.","type":"string"},"costUsd":{"description":"What it has cost so far, in dollars. A subagent's spend is its own and is not folded in here.","type":"number"},"inputTokens":{"description":"Uncached input tokens, excluding cache reads and cache writes.","type":"number"},"outputTokens":{"description":"Tokens received.","type":"number"},"contextTokens":{"description":"How much of the window the conversation currently fills.","type":"number"},"contextWindow":{"description":"How large that window is.","type":"number"},"promptCache":{"description":"When this conversation's prompt cache was last kept alive and how long it lasts, which together say when picking the conversation up stops being cheap. Absent when the provider publishes nothing to ground it on.","type":"object","properties":{"at":{"type":"number","description":"When its last request touched the provider's prompt cache, in milliseconds."},"ttlMs":{"type":"number","description":"How long that entry lives from `at`, in milliseconds."},"rollsAt":{"description":"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.","type":"number"},"keepableUntil":{"description":"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.","type":"number"}},"required":["at","ttlMs"],"additionalProperties":false},"keepWarm":{"description":"Whether the sandbox is keeping this conversation's prompt cache warm while it sits idle, how that is going, or why it stopped. Absent when nobody asked for it.","type":"object","properties":{"since":{"type":"number","description":"When keeping it warm started, in milliseconds."},"until":{"type":"number","description":"When it stops by itself, in milliseconds: the time asked for, shortened to what the sandbox can honestly keep, which is never past the point where refreshing costs more than re-reading, nor past the date change that rewrites the prompt."},"refreshes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Refreshes sent so far."},"readTokens":{"description":"How much the last refresh read back from the provider's cache, in tokens: the proof the cache was still there.","type":"number"},"ended":{"description":"Why keeping it warm stopped before anyone picked the conversation up. Absent while it is still being kept.","type":"object","properties":{"at":{"type":"number","description":"When it stopped, in milliseconds."},"reason":{"type":"string","enum":["elapsed","allowance","changed","cold","failed"],"description":"Why: `elapsed` the time asked for ran out; `allowance` the account reached the reserve kept for real work, or the provider refused a refresh for its limit; `changed` what the next turn would send no longer matches the cache (a part of the prompt, the date in it, or the conversation's session or account); `cold` the cache was gone, expired before a refresh could run or found missing by one; `failed` a refresh failed."},"detail":{"description":"The specifics, when there are any: which parts of the prompt changed, how full the account was, or the failure's own words.","type":"string"}},"required":["at","reason"],"additionalProperties":false}},"required":["since","until","refreshes"],"additionalProperties":false},"activity":{"description":"What it is doing at this moment.","type":"object","properties":{"tool":{"description":"The last tool it reached for.","type":"string"},"target":{"description":"What it reached for that tool with: a file, a command, a URL.","type":"string"},"todo":{"description":"The item on its own list that it is working through.","type":"string"}},"additionalProperties":false},"checklist":{"description":"How far it is through its own checklist. Absent for a conversation that kept no list, which is most short ones.","type":"object","properties":{"done":{"type":"number","description":"Items it has completed."},"total":{"type":"number","description":"Items on the list. Never zero: a conversation that kept no list carries no clause at all."}},"required":["done","total"],"additionalProperties":false},"landedMessageDraft":{"description":"The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes.","type":"object","properties":{"startedAt":{"type":"number","description":"When the drafting began, in milliseconds."},"steps":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider was asked."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"status":{"type":"string","enum":["asking","answered","refused","skipped"],"description":"How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."},"at":{"description":"When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time.","type":"number"},"ms":{"description":"How long it took. Absent while it is still being asked.","type":"number"},"reason":{"description":"Why it refused, in its own words.","type":"string"}},"required":["provider","model","status"],"additionalProperties":false},"description":"Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."},"outcome":{"description":"How it ended. Absent means it is still going.","type":"string","enum":["written","failed"]},"reason":{"description":"The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words.","type":"string"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"}},"required":["startedAt","steps"],"additionalProperties":false},"landedMessage":{"description":"What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false},"startedAt":{"description":"When the running turn started, in milliseconds. Absent when none is running.","type":"number"},"run":{"description":"The run under way right now: what a stop names, so it cannot cancel a turn that started after it was pressed. Absent when none is running, and for a turn with no run to attach to.","type":"string"},"queue":{"description":"Messages waiting for its next turn, and whether they are held. Absent for a conversation nothing has ever waited for.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false},"updatedAt":{"type":"number","description":"When it last did something, in milliseconds. Reading it does not count."},"seenAt":{"description":"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.","type":"number"},"unsentAt":{"description":"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.","type":"number"},"attention":{"type":"object","properties":{"plan":{"type":"boolean","description":"It has proposed a plan and is waiting for a yes."},"question":{"type":"boolean","description":"It has asked you something."},"permission":{"type":"boolean","description":"It wants to use a tool it needs permission for."},"capability":{"type":"boolean","description":"It needs something connected that is not connected yet."},"credential":{"type":"boolean","description":"It is waiting for a named person to release a credential. The one pause that may not be yours to clear, whatever your role."},"conflict":{"type":"boolean","description":"Its work cannot be merged without somebody resolving a clash."},"need":{"description":"It asked a person for something it still needs: a connection, a secret, wider reach, a tool. Absent from a daemon older than needs.","type":"boolean"}},"required":["plan","question","permission","capability","credential","conflict"],"additionalProperties":false,"description":"Which kinds of waiting-for-you it is doing."},"permissionAsk":{"description":"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.","type":"object","properties":{"requestId":{"type":"string","description":"Which request this is: the id an answer to it names."},"ask":{"type":"string","description":"What it asks to do, on one line: the runtime's own sentence, else the tool's short name."}},"required":["requestId","ask"],"additionalProperties":false},"landFailure":{"description":"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.","type":"object","properties":{"reason":{"type":"string","description":"Why it failed, in the words it failed with."},"code":{"description":"Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise.","type":"string"},"at":{"type":"number","description":"When it failed, in milliseconds."}},"required":["reason","at"],"additionalProperties":false},"delivery":{"description":"What became of its last land in the folder on the owner's computer, for a project folder attached to this computer's sandbox. Absent for any other land, and from a sandbox older than it.","type":"object","properties":{"state":{"type":"string","enum":["delivered","partial","waiting","failed","too-large"]},"at":{"type":"number","description":"When this state was reached, in milliseconds."},"project":{"type":"string"},"folder":{"description":"The folder on the owner's computer, as its machine reported it.","type":"string"},"applied":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files written, merged ones included."},"conflicts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"reason":{"type":"string","enum":["edited","link","outside","missing-git"]}},"required":["path","reason"],"additionalProperties":false}},"point":{"description":"The machine's restore point taken before writing.","type":"string"},"reason":{"description":"Why it failed or waits, in a sentence.","type":"string"}},"required":["state","at","project","applied","conflicts"],"additionalProperties":false},"conflictCauses":{"description":"Why its work will not merge, and so who can clear it: your own uncommitted edits, which only you can commit or stash, against a moved main line or an unmergeable binary, which the conversation can redo on its own copy. Absent unless it is refusing to merge.","type":"array","items":{"type":"string","enum":["workspace","diverged","binary"]}},"unfinished":{"description":"What its last turn left open: steps it never completed. Absent for a turn that finished what it started.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"steps":{"description":"The agent's own checklist where that turn left it. Absent for a conversation that kept no list.","type":"object","properties":{"open":{"type":"number","description":"Items on it that were never completed."},"total":{"type":"number","description":"Items on the whole list."},"next":{"description":"The one it would have done next: what it was working through, or the first still waiting.","type":"string"}},"required":["open","total"],"additionalProperties":false},"check":{"description":"The end-of-turn check that was still failing when the turn ended, by name. No longer written: what a check finds as a turn ends is said back to the model and never leaves its work unfinished.","type":"string"}},"required":["at"],"additionalProperties":false},"proof":{"description":"What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"verification":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to."},"check":{"description":"The command that spoke, when one did, so a targeted test is never read as the whole suite.","type":"string"},"unviewed":{"description":"How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none.","type":"number"}},"required":["at","verification"],"additionalProperties":false},"reach":{"description":"Where its last turn's work went when it went somewhere besides its own branch: files changed live outside it, clones of its own holding work that will not land, pushes it made itself. Absent when everything it did is on its branch.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"live":{"description":"Files it changed in place where every conversation reads them, outside its own branch: an installed extension's copy, the sandbox's shared state, the owner's own checkout. Live the moment they were written, never reviewed, and an installed extension's next update replaces them.","maxItems":10,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What it wrote, as it named it: relative to the workspace for the sandbox's shared state, absolute for the owner's own checkout."},"extension":{"description":"The extension, by its manifest id, when what it changed is the installed copy of one. Absent for anything else.","type":"string"}},"required":["path"],"additionalProperties":false}},"liveMore":{"description":"How many more such files there were beyond the ones listed. Absent when all are listed.","type":"number"},"stranded":{"description":"Repositories it cloned inside its own copy and left work in. A clone of its own never rides its land, so what is in one stays there.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"The clone, as a folder relative to its own copy of the workspace."},"uncommitted":{"type":"number","description":"Files changed in it and never committed."},"unpushed":{"type":"number","description":"Commits in it its upstream does not have, or that no remote has when it follows none."}},"required":["dir","uncommitted","unpushed"],"additionalProperties":false}},"strandedMore":{"description":"How many more such clones there were beyond the ones listed. Absent when all are listed.","type":"number"},"published":{"description":"Pushes it made with git that went through: work it sent out of the sandbox itself, never through a land.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"description":"The folder it pushed from, as the command named it. Absent for its own working folder.","type":"string"},"remote":{"description":"The remote it pushed to, as the command named it. Absent when the command named none.","type":"string"},"branch":{"description":"The branch it pushed to, as the command named it. Absent when the command named none.","type":"string"},"command":{"type":"string","description":"The push itself, trimmed to one line."}},"required":["command"],"additionalProperties":false}},"publishedMore":{"description":"How many more such pushes there were beyond the ones listed. Absent when all are listed.","type":"number"}},"required":["at"],"additionalProperties":false},"turns":{"description":"Turns it has finished.","type":"number"},"toolUses":{"description":"Tools it has used, over its whole life.","type":"number"},"subagents":{"description":"Subagents this one started, in-process and spawned alike. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost.","type":"object","properties":{"running":{"type":"number","description":"Subagents working right now."},"total":{"type":"number","description":"Subagents it has started over its whole life."}},"required":["running","total"],"additionalProperties":false},"diff":{"description":"Everything it has written, measured from where it started. Independent of how much has been merged.","type":"object","properties":{"files":{"type":"number","description":"Files touched."},"insertions":{"type":"number","description":"Lines added."},"deletions":{"type":"number","description":"Lines removed."}},"required":["files","insertions","deletions"],"additionalProperties":false},"landedPresence":{"description":"Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it.","type":"object","properties":{"landed":{"type":"number","description":"Paths this conversation merged in."},"present":{"type":"number","description":"How many of them are still there, either pending or committed."},"removedBy":{"description":"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"id":{"type":"string","description":"The conversation that took them out."},"title":{"description":"What that conversation is called.","type":"string"}},"required":["kind","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"description":"Who it was, as the sandbox verified them. Absent when the request carried no identity.","type":"string"},"name":{"description":"What to call them, when their sign-in carried a name.","type":"string"}},"required":["kind"],"additionalProperties":false}]}},"required":["landed","present"],"additionalProperties":false},"loop":{"description":"The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them.","type":"object","properties":{"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How the loop is going."},"iteration":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which round it is on."},"maxIterations":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many rounds it will attempt before giving up."},"goal":{"type":"string","description":"What it is looping towards."}},"required":["state","iteration","maxIterations","goal"],"additionalProperties":false},"workflow":{"description":"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.","type":"object","properties":{"runId":{"type":"string","description":"The run this belongs to, which is how a board groups its steps together."},"name":{"type":"string","description":"The workflow's name."},"step":{"type":"string","description":"Which step this conversation is on now. It moves when steps are chained."},"index":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"This step's place in the workflow, counting from one."},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many steps the workflow has."}},"required":["runId","name","step","index","total"],"additionalProperties":false},"awaitingWake":{"description":"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.","type":"boolean"},"watches":{"description":"Outside conditions this conversation is parked on, each of which will wake it. Absent means none, which is nearly every conversation: an armed watch is why a finished-looking agent starts working by itself, and why a hosted machine will not go idle.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this watch, the same one the agent was given when it armed it."},"note":{"type":"string","description":"The agent's own line on what it is waiting for."},"intervalSeconds":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How often the check runs."},"deadlineAt":{"type":"number","description":"When it gives up and wakes the conversation anyway, in milliseconds. Every watch has one."}},"required":["id","note","intervalSeconds","deadlineAt"],"additionalProperties":false}},"needs":{"description":"What it is waiting on people for and has not got yet, oldest first. Absent means nothing: an open need is why an idle-looking agent still needs you.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["capability","secret","grant","release","environment"],"description":"What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image."},"title":{"type":"string"},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."}},"required":["id","kind","title","status"],"additionalProperties":false}},"jobs":{"description":"Commands this conversation left running in the background, and how the most recent ones ended. Absent means none since the daemon started. A job is running exactly while it has no end.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this job, the one its transcript row names."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"session":{"type":"string","description":"The terminal session its pane runs in, which is what opening it focuses."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When the command exited, in milliseconds. Absent while it runs.","type":"number"},"exitCode":{"description":"The code it exited with. Absent while it runs, or when its exit left none.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"watch":{"description":"The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.","type":"string"},"handed":{"description":"Left running for the person: the agent kept it for them with the `keep` tool (or it holds a port this conversation already left them), so it outlives the turn, wakes nothing and is theirs to stop.","type":"boolean"},"ports":{"description":"The ports it was listening on when its turn ended.","type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"stoppedBy":{"description":"Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.","type":"string","enum":["turn","person","agent"]},"inputWait":{"description":"Present while it sits blocked reading its terminal with nothing moving: a prompt nobody in the sandbox will answer, which only stopping it or a person typing into its terminal ends. Absent while it runs on, and once it ends.","type":"object","properties":{"since":{"type":"number","description":"When it went still, in milliseconds."},"program":{"type":"string","description":"The process blocked reading the terminal, as its command line."}},"required":["since","program"],"additionalProperties":false}},"required":["id","label","session","startedAt"],"additionalProperties":false}},"archivedAt":{"description":"When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board.","type":"number"}},"required":["id","status","provider","harness","updatedAt","attention"],"additionalProperties":false}}}}}}},"/agents/{id}/auto-land":{"post":{"operationId":"agents.autoLand","summary":"Whether this conversation merges its work automatically","description":"Overrides the sandbox-wide setting for one conversation; clear it to go back to following the default. Deliberately allowed mid-turn, because the setting is read when the turn finishes, so flipping it while the agent works means exactly hold this piece of work for review.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"autoLand":{"description":"Whether its work merges automatically when a turn finishes. Null clears the override and goes back to following the sandbox-wide setting, so a conversation does not sit holding a frozen copy of a default it has quietly stopped following.","type":["boolean","null"]}},"required":["autoLand"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The conversation id, which is how every other call addresses it."},"sessionId":{"description":"The provider session behind the last turn. It is retired whenever the model or account changes.","type":"string"},"title":{"description":"What to call it: the first prompt cut to one line, unless somebody renamed it.","type":"string"},"titleAction":{"description":"One word for the kind of work the naming pass read in it (fix, audit, redesign). Never part of the displayed name: a board tints and glyphs a card by it. Absent for a title nothing named an action for.","type":"string"},"status":{"type":"string","enum":["idle","running","awaiting","stopping","dismissing","stopped","resuming","landing","ready","landed","conflict","error","interrupted"],"description":"What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; dismissing is the same window for a question waved away, which ends the turn too but owes the user nothing; resuming means the sandbox is already putting right whatever killed the turn; landing means its work is being carried into the workspace right now, and nothing may act on its branch until that settles."},"failure":{"description":"Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.","type":"string"},"failureCode":{"description":"Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.","type":"string"},"limitResetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant.","type":"number"},"limitHeld":{"description":"Whether the refused turn is held whole, so sending again re-runs it instead of appending to it.","type":"boolean"},"limitScheduled":{"description":"Whether the held turn is already booked to go again at the reset, so nobody has to press anything.","type":"boolean"},"limitMoving":{"description":"The account the held turn is being moved to by the owner's policy, while that move is booked.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it runs on."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop it runs on."},"runner":{"description":"The runner this conversation runs on. Absent means this sandbox.","type":"string"},"startIn":{"description":"Which folder it opened in, relative to the workspace root. Absent means the root.","type":"string"},"actsAs":{"description":"Which persona its first turn acted as. Absent for an ordinary chat.","type":"string"},"lastActsAs":{"description":"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.","type":"string"},"model":{"description":"What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked.","type":"string"},"effort":{"description":"How hard that turn was told to think.","type":"string"},"thinking":{"description":"Whether that turn showed its reasoning.","type":"boolean"},"fast":{"description":"Whether that turn asked for higher speed. What was asked for, not what was served.","type":"boolean"},"account":{"description":"Which connected account paid for it.","type":"string"},"branch":{"description":"The branch its private copy works on. Absent for a conversation that works directly in the shared tree.","type":"string"},"autoLand":{"description":"This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case.","type":"boolean"},"limitPolicy":{"type":"string","enum":["wait","resend","move"]},"outagePolicy":{"type":"string","enum":["wait","retry"]},"stopPolicy":{"type":"string","enum":["wait","retry"]},"landRequested":{"description":"A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting.","type":"object","properties":{"email":{"type":"string","description":"Who asked."},"name":{"description":"Their display name.","type":"string"},"at":{"type":"number","description":"When they asked, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"reactions":{"description":"What people have marked this conversation with, one entry per emoji, in the order the emoji were first used. Absent means nobody has marked it, which is most conversations.","type":"array","items":{"type":"object","properties":{"emoji":{"type":"string","description":"The mark itself, one emoji, carried as the character rather than as a name that would need a table on both sides."},"by":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Who it was, as the sandbox verified them."},"name":{"description":"What to call them, when their sign-in carried a name. Absent leaves the address to stand for them.","type":"string"},"at":{"type":"number","description":"When they marked it, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"description":"Everyone wearing this mark, oldest first, each by name. Carried whole rather than as a count, because a chip reading 3 that cannot say whose is a number nobody can answer. Never empty: the last person taking theirs back takes the whole chip with it."}},"required":["emoji","by"],"additionalProperties":false}},"origin":{"description":"Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"startedBy":{"description":"Who asked for the first turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a child another conversation spawned. Absent when nothing was verified (a wake, a loopback caller).","type":"string"},"areas":{"description":"Which named areas of the workspace this conversation was started within, latched from whoever asked for the first turn. Absent means its starter held the whole workspace, which is why a fenced member is not shown it.","type":"array","items":{"type":"string"}},"owner":{"description":"The member answerable for this conversation: set from whoever started it, inherited from the parent by a spawned child, moved by handing it over. Absent means nobody has claimed it yet.","type":"object","properties":{"email":{"type":"string","description":"Who answers for this conversation, as the sandbox verified them."},"name":{"description":"What to call them, when the sign-in that made them its owner carried a name. Absent leaves the address to stand for them.","type":"string"},"since":{"type":"number","description":"When they became its owner, in milliseconds."}},"required":["email","since"],"additionalProperties":false},"forkedFrom":{"description":"The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"type":"string","enum":["then","now"]}},"required":["conversationId","index","files"],"additionalProperties":false},"base":{"description":"The commit its private copy started from, shortened.","type":"string"},"costUsd":{"description":"What it has cost so far, in dollars. A subagent's spend is its own and is not folded in here.","type":"number"},"inputTokens":{"description":"Uncached input tokens, excluding cache reads and cache writes.","type":"number"},"outputTokens":{"description":"Tokens received.","type":"number"},"contextTokens":{"description":"How much of the window the conversation currently fills.","type":"number"},"contextWindow":{"description":"How large that window is.","type":"number"},"promptCache":{"description":"When this conversation's prompt cache was last kept alive and how long it lasts, which together say when picking the conversation up stops being cheap. Absent when the provider publishes nothing to ground it on.","type":"object","properties":{"at":{"type":"number","description":"When its last request touched the provider's prompt cache, in milliseconds."},"ttlMs":{"type":"number","description":"How long that entry lives from `at`, in milliseconds."},"rollsAt":{"description":"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.","type":"number"},"keepableUntil":{"description":"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.","type":"number"}},"required":["at","ttlMs"],"additionalProperties":false},"keepWarm":{"description":"Whether the sandbox is keeping this conversation's prompt cache warm while it sits idle, how that is going, or why it stopped. Absent when nobody asked for it.","type":"object","properties":{"since":{"type":"number","description":"When keeping it warm started, in milliseconds."},"until":{"type":"number","description":"When it stops by itself, in milliseconds: the time asked for, shortened to what the sandbox can honestly keep, which is never past the point where refreshing costs more than re-reading, nor past the date change that rewrites the prompt."},"refreshes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Refreshes sent so far."},"readTokens":{"description":"How much the last refresh read back from the provider's cache, in tokens: the proof the cache was still there.","type":"number"},"ended":{"description":"Why keeping it warm stopped before anyone picked the conversation up. Absent while it is still being kept.","type":"object","properties":{"at":{"type":"number","description":"When it stopped, in milliseconds."},"reason":{"type":"string","enum":["elapsed","allowance","changed","cold","failed"],"description":"Why: `elapsed` the time asked for ran out; `allowance` the account reached the reserve kept for real work, or the provider refused a refresh for its limit; `changed` what the next turn would send no longer matches the cache (a part of the prompt, the date in it, or the conversation's session or account); `cold` the cache was gone, expired before a refresh could run or found missing by one; `failed` a refresh failed."},"detail":{"description":"The specifics, when there are any: which parts of the prompt changed, how full the account was, or the failure's own words.","type":"string"}},"required":["at","reason"],"additionalProperties":false}},"required":["since","until","refreshes"],"additionalProperties":false},"activity":{"description":"What it is doing at this moment.","type":"object","properties":{"tool":{"description":"The last tool it reached for.","type":"string"},"target":{"description":"What it reached for that tool with: a file, a command, a URL.","type":"string"},"todo":{"description":"The item on its own list that it is working through.","type":"string"}},"additionalProperties":false},"checklist":{"description":"How far it is through its own checklist. Absent for a conversation that kept no list, which is most short ones.","type":"object","properties":{"done":{"type":"number","description":"Items it has completed."},"total":{"type":"number","description":"Items on the list. Never zero: a conversation that kept no list carries no clause at all."}},"required":["done","total"],"additionalProperties":false},"landedMessageDraft":{"description":"The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes.","type":"object","properties":{"startedAt":{"type":"number","description":"When the drafting began, in milliseconds."},"steps":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider was asked."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"status":{"type":"string","enum":["asking","answered","refused","skipped"],"description":"How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."},"at":{"description":"When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time.","type":"number"},"ms":{"description":"How long it took. Absent while it is still being asked.","type":"number"},"reason":{"description":"Why it refused, in its own words.","type":"string"}},"required":["provider","model","status"],"additionalProperties":false},"description":"Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."},"outcome":{"description":"How it ended. Absent means it is still going.","type":"string","enum":["written","failed"]},"reason":{"description":"The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words.","type":"string"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"}},"required":["startedAt","steps"],"additionalProperties":false},"landedMessage":{"description":"What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false},"startedAt":{"description":"When the running turn started, in milliseconds. Absent when none is running.","type":"number"},"run":{"description":"The run under way right now: what a stop names, so it cannot cancel a turn that started after it was pressed. Absent when none is running, and for a turn with no run to attach to.","type":"string"},"queue":{"description":"Messages waiting for its next turn, and whether they are held. Absent for a conversation nothing has ever waited for.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false},"updatedAt":{"type":"number","description":"When it last did something, in milliseconds. Reading it does not count."},"seenAt":{"description":"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.","type":"number"},"unsentAt":{"description":"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.","type":"number"},"attention":{"type":"object","properties":{"plan":{"type":"boolean","description":"It has proposed a plan and is waiting for a yes."},"question":{"type":"boolean","description":"It has asked you something."},"permission":{"type":"boolean","description":"It wants to use a tool it needs permission for."},"capability":{"type":"boolean","description":"It needs something connected that is not connected yet."},"credential":{"type":"boolean","description":"It is waiting for a named person to release a credential. The one pause that may not be yours to clear, whatever your role."},"conflict":{"type":"boolean","description":"Its work cannot be merged without somebody resolving a clash."},"need":{"description":"It asked a person for something it still needs: a connection, a secret, wider reach, a tool. Absent from a daemon older than needs.","type":"boolean"}},"required":["plan","question","permission","capability","credential","conflict"],"additionalProperties":false,"description":"Which kinds of waiting-for-you it is doing."},"permissionAsk":{"description":"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.","type":"object","properties":{"requestId":{"type":"string","description":"Which request this is: the id an answer to it names."},"ask":{"type":"string","description":"What it asks to do, on one line: the runtime's own sentence, else the tool's short name."}},"required":["requestId","ask"],"additionalProperties":false},"landFailure":{"description":"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.","type":"object","properties":{"reason":{"type":"string","description":"Why it failed, in the words it failed with."},"code":{"description":"Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise.","type":"string"},"at":{"type":"number","description":"When it failed, in milliseconds."}},"required":["reason","at"],"additionalProperties":false},"delivery":{"description":"What became of its last land in the folder on the owner's computer, for a project folder attached to this computer's sandbox. Absent for any other land, and from a sandbox older than it.","type":"object","properties":{"state":{"type":"string","enum":["delivered","partial","waiting","failed","too-large"]},"at":{"type":"number","description":"When this state was reached, in milliseconds."},"project":{"type":"string"},"folder":{"description":"The folder on the owner's computer, as its machine reported it.","type":"string"},"applied":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files written, merged ones included."},"conflicts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"reason":{"type":"string","enum":["edited","link","outside","missing-git"]}},"required":["path","reason"],"additionalProperties":false}},"point":{"description":"The machine's restore point taken before writing.","type":"string"},"reason":{"description":"Why it failed or waits, in a sentence.","type":"string"}},"required":["state","at","project","applied","conflicts"],"additionalProperties":false},"conflictCauses":{"description":"Why its work will not merge, and so who can clear it: your own uncommitted edits, which only you can commit or stash, against a moved main line or an unmergeable binary, which the conversation can redo on its own copy. Absent unless it is refusing to merge.","type":"array","items":{"type":"string","enum":["workspace","diverged","binary"]}},"unfinished":{"description":"What its last turn left open: steps it never completed. Absent for a turn that finished what it started.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"steps":{"description":"The agent's own checklist where that turn left it. Absent for a conversation that kept no list.","type":"object","properties":{"open":{"type":"number","description":"Items on it that were never completed."},"total":{"type":"number","description":"Items on the whole list."},"next":{"description":"The one it would have done next: what it was working through, or the first still waiting.","type":"string"}},"required":["open","total"],"additionalProperties":false},"check":{"description":"The end-of-turn check that was still failing when the turn ended, by name. No longer written: what a check finds as a turn ends is said back to the model and never leaves its work unfinished.","type":"string"}},"required":["at"],"additionalProperties":false},"proof":{"description":"What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"verification":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to."},"check":{"description":"The command that spoke, when one did, so a targeted test is never read as the whole suite.","type":"string"},"unviewed":{"description":"How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none.","type":"number"}},"required":["at","verification"],"additionalProperties":false},"reach":{"description":"Where its last turn's work went when it went somewhere besides its own branch: files changed live outside it, clones of its own holding work that will not land, pushes it made itself. Absent when everything it did is on its branch.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"live":{"description":"Files it changed in place where every conversation reads them, outside its own branch: an installed extension's copy, the sandbox's shared state, the owner's own checkout. Live the moment they were written, never reviewed, and an installed extension's next update replaces them.","maxItems":10,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What it wrote, as it named it: relative to the workspace for the sandbox's shared state, absolute for the owner's own checkout."},"extension":{"description":"The extension, by its manifest id, when what it changed is the installed copy of one. Absent for anything else.","type":"string"}},"required":["path"],"additionalProperties":false}},"liveMore":{"description":"How many more such files there were beyond the ones listed. Absent when all are listed.","type":"number"},"stranded":{"description":"Repositories it cloned inside its own copy and left work in. A clone of its own never rides its land, so what is in one stays there.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"The clone, as a folder relative to its own copy of the workspace."},"uncommitted":{"type":"number","description":"Files changed in it and never committed."},"unpushed":{"type":"number","description":"Commits in it its upstream does not have, or that no remote has when it follows none."}},"required":["dir","uncommitted","unpushed"],"additionalProperties":false}},"strandedMore":{"description":"How many more such clones there were beyond the ones listed. Absent when all are listed.","type":"number"},"published":{"description":"Pushes it made with git that went through: work it sent out of the sandbox itself, never through a land.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"description":"The folder it pushed from, as the command named it. Absent for its own working folder.","type":"string"},"remote":{"description":"The remote it pushed to, as the command named it. Absent when the command named none.","type":"string"},"branch":{"description":"The branch it pushed to, as the command named it. Absent when the command named none.","type":"string"},"command":{"type":"string","description":"The push itself, trimmed to one line."}},"required":["command"],"additionalProperties":false}},"publishedMore":{"description":"How many more such pushes there were beyond the ones listed. Absent when all are listed.","type":"number"}},"required":["at"],"additionalProperties":false},"turns":{"description":"Turns it has finished.","type":"number"},"toolUses":{"description":"Tools it has used, over its whole life.","type":"number"},"subagents":{"description":"Subagents this one started, in-process and spawned alike. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost.","type":"object","properties":{"running":{"type":"number","description":"Subagents working right now."},"total":{"type":"number","description":"Subagents it has started over its whole life."}},"required":["running","total"],"additionalProperties":false},"diff":{"description":"Everything it has written, measured from where it started. Independent of how much has been merged.","type":"object","properties":{"files":{"type":"number","description":"Files touched."},"insertions":{"type":"number","description":"Lines added."},"deletions":{"type":"number","description":"Lines removed."}},"required":["files","insertions","deletions"],"additionalProperties":false},"landedPresence":{"description":"Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it.","type":"object","properties":{"landed":{"type":"number","description":"Paths this conversation merged in."},"present":{"type":"number","description":"How many of them are still there, either pending or committed."},"removedBy":{"description":"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"id":{"type":"string","description":"The conversation that took them out."},"title":{"description":"What that conversation is called.","type":"string"}},"required":["kind","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"description":"Who it was, as the sandbox verified them. Absent when the request carried no identity.","type":"string"},"name":{"description":"What to call them, when their sign-in carried a name.","type":"string"}},"required":["kind"],"additionalProperties":false}]}},"required":["landed","present"],"additionalProperties":false},"loop":{"description":"The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them.","type":"object","properties":{"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How the loop is going."},"iteration":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which round it is on."},"maxIterations":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many rounds it will attempt before giving up."},"goal":{"type":"string","description":"What it is looping towards."}},"required":["state","iteration","maxIterations","goal"],"additionalProperties":false},"workflow":{"description":"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.","type":"object","properties":{"runId":{"type":"string","description":"The run this belongs to, which is how a board groups its steps together."},"name":{"type":"string","description":"The workflow's name."},"step":{"type":"string","description":"Which step this conversation is on now. It moves when steps are chained."},"index":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"This step's place in the workflow, counting from one."},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many steps the workflow has."}},"required":["runId","name","step","index","total"],"additionalProperties":false},"awaitingWake":{"description":"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.","type":"boolean"},"watches":{"description":"Outside conditions this conversation is parked on, each of which will wake it. Absent means none, which is nearly every conversation: an armed watch is why a finished-looking agent starts working by itself, and why a hosted machine will not go idle.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this watch, the same one the agent was given when it armed it."},"note":{"type":"string","description":"The agent's own line on what it is waiting for."},"intervalSeconds":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How often the check runs."},"deadlineAt":{"type":"number","description":"When it gives up and wakes the conversation anyway, in milliseconds. Every watch has one."}},"required":["id","note","intervalSeconds","deadlineAt"],"additionalProperties":false}},"needs":{"description":"What it is waiting on people for and has not got yet, oldest first. Absent means nothing: an open need is why an idle-looking agent still needs you.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["capability","secret","grant","release","environment"],"description":"What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image."},"title":{"type":"string"},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."}},"required":["id","kind","title","status"],"additionalProperties":false}},"jobs":{"description":"Commands this conversation left running in the background, and how the most recent ones ended. Absent means none since the daemon started. A job is running exactly while it has no end.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this job, the one its transcript row names."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"session":{"type":"string","description":"The terminal session its pane runs in, which is what opening it focuses."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When the command exited, in milliseconds. Absent while it runs.","type":"number"},"exitCode":{"description":"The code it exited with. Absent while it runs, or when its exit left none.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"watch":{"description":"The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.","type":"string"},"handed":{"description":"Left running for the person: the agent kept it for them with the `keep` tool (or it holds a port this conversation already left them), so it outlives the turn, wakes nothing and is theirs to stop.","type":"boolean"},"ports":{"description":"The ports it was listening on when its turn ended.","type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"stoppedBy":{"description":"Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.","type":"string","enum":["turn","person","agent"]},"inputWait":{"description":"Present while it sits blocked reading its terminal with nothing moving: a prompt nobody in the sandbox will answer, which only stopping it or a person typing into its terminal ends. Absent while it runs on, and once it ends.","type":"object","properties":{"since":{"type":"number","description":"When it went still, in milliseconds."},"program":{"type":"string","description":"The process blocked reading the terminal, as its command line."}},"required":["since","program"],"additionalProperties":false}},"required":["id","label","session","startedAt"],"additionalProperties":false}},"archivedAt":{"description":"When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board.","type":"number"}},"required":["id","status","provider","harness","updatedAt","attention"],"additionalProperties":false}}}}}}},"/agents/{id}/break-policy":{"post":{"operationId":"agents.breakPolicy","summary":"What this conversation does when a turn stops before it finished","description":"One answer per ending — a spent usage limit, a provider outage, a turn that stopped short — overriding the sandbox-wide policy for one conversation; clear it to follow the default again. The answers are mutually exclusive by construction, so nothing here can arm two automations over the same wall. Every ending starts at `wait` unless asked otherwise, because a re-run spends the user's own allowance on a turn they sent once.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ending":{"type":"string","enum":["limit","outage","stopped"],"description":"Which wall this answers for: a spent usage limit, a provider outage, or a turn that stopped short."},"policy":{"anyOf":[{"type":"string","enum":["wait","resend","move","retry"]},{"type":"null"}],"description":"What happens next for that ending. `wait` holds the turn for a press; `retry` re-runs it on a bounded ladder (outage, stop); `resend` sends it again at the published reset and `move` also tries another account with room (limit only). An answer the ending does not allow is refused. Null clears the override and goes back to following the sandbox-wide policy, so a conversation does not sit holding a frozen copy of a default it has quietly stopped following."}},"required":["ending","policy"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The conversation id, which is how every other call addresses it."},"sessionId":{"description":"The provider session behind the last turn. It is retired whenever the model or account changes.","type":"string"},"title":{"description":"What to call it: the first prompt cut to one line, unless somebody renamed it.","type":"string"},"titleAction":{"description":"One word for the kind of work the naming pass read in it (fix, audit, redesign). Never part of the displayed name: a board tints and glyphs a card by it. Absent for a title nothing named an action for.","type":"string"},"status":{"type":"string","enum":["idle","running","awaiting","stopping","dismissing","stopped","resuming","landing","ready","landed","conflict","error","interrupted"],"description":"What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; dismissing is the same window for a question waved away, which ends the turn too but owes the user nothing; resuming means the sandbox is already putting right whatever killed the turn; landing means its work is being carried into the workspace right now, and nothing may act on its branch until that settles."},"failure":{"description":"Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.","type":"string"},"failureCode":{"description":"Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.","type":"string"},"limitResetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant.","type":"number"},"limitHeld":{"description":"Whether the refused turn is held whole, so sending again re-runs it instead of appending to it.","type":"boolean"},"limitScheduled":{"description":"Whether the held turn is already booked to go again at the reset, so nobody has to press anything.","type":"boolean"},"limitMoving":{"description":"The account the held turn is being moved to by the owner's policy, while that move is booked.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it runs on."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop it runs on."},"runner":{"description":"The runner this conversation runs on. Absent means this sandbox.","type":"string"},"startIn":{"description":"Which folder it opened in, relative to the workspace root. Absent means the root.","type":"string"},"actsAs":{"description":"Which persona its first turn acted as. Absent for an ordinary chat.","type":"string"},"lastActsAs":{"description":"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.","type":"string"},"model":{"description":"What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked.","type":"string"},"effort":{"description":"How hard that turn was told to think.","type":"string"},"thinking":{"description":"Whether that turn showed its reasoning.","type":"boolean"},"fast":{"description":"Whether that turn asked for higher speed. What was asked for, not what was served.","type":"boolean"},"account":{"description":"Which connected account paid for it.","type":"string"},"branch":{"description":"The branch its private copy works on. Absent for a conversation that works directly in the shared tree.","type":"string"},"autoLand":{"description":"This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case.","type":"boolean"},"limitPolicy":{"type":"string","enum":["wait","resend","move"]},"outagePolicy":{"type":"string","enum":["wait","retry"]},"stopPolicy":{"type":"string","enum":["wait","retry"]},"landRequested":{"description":"A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting.","type":"object","properties":{"email":{"type":"string","description":"Who asked."},"name":{"description":"Their display name.","type":"string"},"at":{"type":"number","description":"When they asked, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"reactions":{"description":"What people have marked this conversation with, one entry per emoji, in the order the emoji were first used. Absent means nobody has marked it, which is most conversations.","type":"array","items":{"type":"object","properties":{"emoji":{"type":"string","description":"The mark itself, one emoji, carried as the character rather than as a name that would need a table on both sides."},"by":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Who it was, as the sandbox verified them."},"name":{"description":"What to call them, when their sign-in carried a name. Absent leaves the address to stand for them.","type":"string"},"at":{"type":"number","description":"When they marked it, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"description":"Everyone wearing this mark, oldest first, each by name. Carried whole rather than as a count, because a chip reading 3 that cannot say whose is a number nobody can answer. Never empty: the last person taking theirs back takes the whole chip with it."}},"required":["emoji","by"],"additionalProperties":false}},"origin":{"description":"Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"startedBy":{"description":"Who asked for the first turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a child another conversation spawned. Absent when nothing was verified (a wake, a loopback caller).","type":"string"},"areas":{"description":"Which named areas of the workspace this conversation was started within, latched from whoever asked for the first turn. Absent means its starter held the whole workspace, which is why a fenced member is not shown it.","type":"array","items":{"type":"string"}},"owner":{"description":"The member answerable for this conversation: set from whoever started it, inherited from the parent by a spawned child, moved by handing it over. Absent means nobody has claimed it yet.","type":"object","properties":{"email":{"type":"string","description":"Who answers for this conversation, as the sandbox verified them."},"name":{"description":"What to call them, when the sign-in that made them its owner carried a name. Absent leaves the address to stand for them.","type":"string"},"since":{"type":"number","description":"When they became its owner, in milliseconds."}},"required":["email","since"],"additionalProperties":false},"forkedFrom":{"description":"The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"type":"string","enum":["then","now"]}},"required":["conversationId","index","files"],"additionalProperties":false},"base":{"description":"The commit its private copy started from, shortened.","type":"string"},"costUsd":{"description":"What it has cost so far, in dollars. A subagent's spend is its own and is not folded in here.","type":"number"},"inputTokens":{"description":"Uncached input tokens, excluding cache reads and cache writes.","type":"number"},"outputTokens":{"description":"Tokens received.","type":"number"},"contextTokens":{"description":"How much of the window the conversation currently fills.","type":"number"},"contextWindow":{"description":"How large that window is.","type":"number"},"promptCache":{"description":"When this conversation's prompt cache was last kept alive and how long it lasts, which together say when picking the conversation up stops being cheap. Absent when the provider publishes nothing to ground it on.","type":"object","properties":{"at":{"type":"number","description":"When its last request touched the provider's prompt cache, in milliseconds."},"ttlMs":{"type":"number","description":"How long that entry lives from `at`, in milliseconds."},"rollsAt":{"description":"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.","type":"number"},"keepableUntil":{"description":"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.","type":"number"}},"required":["at","ttlMs"],"additionalProperties":false},"keepWarm":{"description":"Whether the sandbox is keeping this conversation's prompt cache warm while it sits idle, how that is going, or why it stopped. Absent when nobody asked for it.","type":"object","properties":{"since":{"type":"number","description":"When keeping it warm started, in milliseconds."},"until":{"type":"number","description":"When it stops by itself, in milliseconds: the time asked for, shortened to what the sandbox can honestly keep, which is never past the point where refreshing costs more than re-reading, nor past the date change that rewrites the prompt."},"refreshes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Refreshes sent so far."},"readTokens":{"description":"How much the last refresh read back from the provider's cache, in tokens: the proof the cache was still there.","type":"number"},"ended":{"description":"Why keeping it warm stopped before anyone picked the conversation up. Absent while it is still being kept.","type":"object","properties":{"at":{"type":"number","description":"When it stopped, in milliseconds."},"reason":{"type":"string","enum":["elapsed","allowance","changed","cold","failed"],"description":"Why: `elapsed` the time asked for ran out; `allowance` the account reached the reserve kept for real work, or the provider refused a refresh for its limit; `changed` what the next turn would send no longer matches the cache (a part of the prompt, the date in it, or the conversation's session or account); `cold` the cache was gone, expired before a refresh could run or found missing by one; `failed` a refresh failed."},"detail":{"description":"The specifics, when there are any: which parts of the prompt changed, how full the account was, or the failure's own words.","type":"string"}},"required":["at","reason"],"additionalProperties":false}},"required":["since","until","refreshes"],"additionalProperties":false},"activity":{"description":"What it is doing at this moment.","type":"object","properties":{"tool":{"description":"The last tool it reached for.","type":"string"},"target":{"description":"What it reached for that tool with: a file, a command, a URL.","type":"string"},"todo":{"description":"The item on its own list that it is working through.","type":"string"}},"additionalProperties":false},"checklist":{"description":"How far it is through its own checklist. Absent for a conversation that kept no list, which is most short ones.","type":"object","properties":{"done":{"type":"number","description":"Items it has completed."},"total":{"type":"number","description":"Items on the list. Never zero: a conversation that kept no list carries no clause at all."}},"required":["done","total"],"additionalProperties":false},"landedMessageDraft":{"description":"The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes.","type":"object","properties":{"startedAt":{"type":"number","description":"When the drafting began, in milliseconds."},"steps":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider was asked."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"status":{"type":"string","enum":["asking","answered","refused","skipped"],"description":"How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."},"at":{"description":"When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time.","type":"number"},"ms":{"description":"How long it took. Absent while it is still being asked.","type":"number"},"reason":{"description":"Why it refused, in its own words.","type":"string"}},"required":["provider","model","status"],"additionalProperties":false},"description":"Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."},"outcome":{"description":"How it ended. Absent means it is still going.","type":"string","enum":["written","failed"]},"reason":{"description":"The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words.","type":"string"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"}},"required":["startedAt","steps"],"additionalProperties":false},"landedMessage":{"description":"What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false},"startedAt":{"description":"When the running turn started, in milliseconds. Absent when none is running.","type":"number"},"run":{"description":"The run under way right now: what a stop names, so it cannot cancel a turn that started after it was pressed. Absent when none is running, and for a turn with no run to attach to.","type":"string"},"queue":{"description":"Messages waiting for its next turn, and whether they are held. Absent for a conversation nothing has ever waited for.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false},"updatedAt":{"type":"number","description":"When it last did something, in milliseconds. Reading it does not count."},"seenAt":{"description":"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.","type":"number"},"unsentAt":{"description":"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.","type":"number"},"attention":{"type":"object","properties":{"plan":{"type":"boolean","description":"It has proposed a plan and is waiting for a yes."},"question":{"type":"boolean","description":"It has asked you something."},"permission":{"type":"boolean","description":"It wants to use a tool it needs permission for."},"capability":{"type":"boolean","description":"It needs something connected that is not connected yet."},"credential":{"type":"boolean","description":"It is waiting for a named person to release a credential. The one pause that may not be yours to clear, whatever your role."},"conflict":{"type":"boolean","description":"Its work cannot be merged without somebody resolving a clash."},"need":{"description":"It asked a person for something it still needs: a connection, a secret, wider reach, a tool. Absent from a daemon older than needs.","type":"boolean"}},"required":["plan","question","permission","capability","credential","conflict"],"additionalProperties":false,"description":"Which kinds of waiting-for-you it is doing."},"permissionAsk":{"description":"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.","type":"object","properties":{"requestId":{"type":"string","description":"Which request this is: the id an answer to it names."},"ask":{"type":"string","description":"What it asks to do, on one line: the runtime's own sentence, else the tool's short name."}},"required":["requestId","ask"],"additionalProperties":false},"landFailure":{"description":"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.","type":"object","properties":{"reason":{"type":"string","description":"Why it failed, in the words it failed with."},"code":{"description":"Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise.","type":"string"},"at":{"type":"number","description":"When it failed, in milliseconds."}},"required":["reason","at"],"additionalProperties":false},"delivery":{"description":"What became of its last land in the folder on the owner's computer, for a project folder attached to this computer's sandbox. Absent for any other land, and from a sandbox older than it.","type":"object","properties":{"state":{"type":"string","enum":["delivered","partial","waiting","failed","too-large"]},"at":{"type":"number","description":"When this state was reached, in milliseconds."},"project":{"type":"string"},"folder":{"description":"The folder on the owner's computer, as its machine reported it.","type":"string"},"applied":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files written, merged ones included."},"conflicts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"reason":{"type":"string","enum":["edited","link","outside","missing-git"]}},"required":["path","reason"],"additionalProperties":false}},"point":{"description":"The machine's restore point taken before writing.","type":"string"},"reason":{"description":"Why it failed or waits, in a sentence.","type":"string"}},"required":["state","at","project","applied","conflicts"],"additionalProperties":false},"conflictCauses":{"description":"Why its work will not merge, and so who can clear it: your own uncommitted edits, which only you can commit or stash, against a moved main line or an unmergeable binary, which the conversation can redo on its own copy. Absent unless it is refusing to merge.","type":"array","items":{"type":"string","enum":["workspace","diverged","binary"]}},"unfinished":{"description":"What its last turn left open: steps it never completed. Absent for a turn that finished what it started.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"steps":{"description":"The agent's own checklist where that turn left it. Absent for a conversation that kept no list.","type":"object","properties":{"open":{"type":"number","description":"Items on it that were never completed."},"total":{"type":"number","description":"Items on the whole list."},"next":{"description":"The one it would have done next: what it was working through, or the first still waiting.","type":"string"}},"required":["open","total"],"additionalProperties":false},"check":{"description":"The end-of-turn check that was still failing when the turn ended, by name. No longer written: what a check finds as a turn ends is said back to the model and never leaves its work unfinished.","type":"string"}},"required":["at"],"additionalProperties":false},"proof":{"description":"What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"verification":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to."},"check":{"description":"The command that spoke, when one did, so a targeted test is never read as the whole suite.","type":"string"},"unviewed":{"description":"How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none.","type":"number"}},"required":["at","verification"],"additionalProperties":false},"reach":{"description":"Where its last turn's work went when it went somewhere besides its own branch: files changed live outside it, clones of its own holding work that will not land, pushes it made itself. Absent when everything it did is on its branch.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"live":{"description":"Files it changed in place where every conversation reads them, outside its own branch: an installed extension's copy, the sandbox's shared state, the owner's own checkout. Live the moment they were written, never reviewed, and an installed extension's next update replaces them.","maxItems":10,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What it wrote, as it named it: relative to the workspace for the sandbox's shared state, absolute for the owner's own checkout."},"extension":{"description":"The extension, by its manifest id, when what it changed is the installed copy of one. Absent for anything else.","type":"string"}},"required":["path"],"additionalProperties":false}},"liveMore":{"description":"How many more such files there were beyond the ones listed. Absent when all are listed.","type":"number"},"stranded":{"description":"Repositories it cloned inside its own copy and left work in. A clone of its own never rides its land, so what is in one stays there.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"The clone, as a folder relative to its own copy of the workspace."},"uncommitted":{"type":"number","description":"Files changed in it and never committed."},"unpushed":{"type":"number","description":"Commits in it its upstream does not have, or that no remote has when it follows none."}},"required":["dir","uncommitted","unpushed"],"additionalProperties":false}},"strandedMore":{"description":"How many more such clones there were beyond the ones listed. Absent when all are listed.","type":"number"},"published":{"description":"Pushes it made with git that went through: work it sent out of the sandbox itself, never through a land.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"description":"The folder it pushed from, as the command named it. Absent for its own working folder.","type":"string"},"remote":{"description":"The remote it pushed to, as the command named it. Absent when the command named none.","type":"string"},"branch":{"description":"The branch it pushed to, as the command named it. Absent when the command named none.","type":"string"},"command":{"type":"string","description":"The push itself, trimmed to one line."}},"required":["command"],"additionalProperties":false}},"publishedMore":{"description":"How many more such pushes there were beyond the ones listed. Absent when all are listed.","type":"number"}},"required":["at"],"additionalProperties":false},"turns":{"description":"Turns it has finished.","type":"number"},"toolUses":{"description":"Tools it has used, over its whole life.","type":"number"},"subagents":{"description":"Subagents this one started, in-process and spawned alike. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost.","type":"object","properties":{"running":{"type":"number","description":"Subagents working right now."},"total":{"type":"number","description":"Subagents it has started over its whole life."}},"required":["running","total"],"additionalProperties":false},"diff":{"description":"Everything it has written, measured from where it started. Independent of how much has been merged.","type":"object","properties":{"files":{"type":"number","description":"Files touched."},"insertions":{"type":"number","description":"Lines added."},"deletions":{"type":"number","description":"Lines removed."}},"required":["files","insertions","deletions"],"additionalProperties":false},"landedPresence":{"description":"Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it.","type":"object","properties":{"landed":{"type":"number","description":"Paths this conversation merged in."},"present":{"type":"number","description":"How many of them are still there, either pending or committed."},"removedBy":{"description":"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"id":{"type":"string","description":"The conversation that took them out."},"title":{"description":"What that conversation is called.","type":"string"}},"required":["kind","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"description":"Who it was, as the sandbox verified them. Absent when the request carried no identity.","type":"string"},"name":{"description":"What to call them, when their sign-in carried a name.","type":"string"}},"required":["kind"],"additionalProperties":false}]}},"required":["landed","present"],"additionalProperties":false},"loop":{"description":"The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them.","type":"object","properties":{"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How the loop is going."},"iteration":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which round it is on."},"maxIterations":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many rounds it will attempt before giving up."},"goal":{"type":"string","description":"What it is looping towards."}},"required":["state","iteration","maxIterations","goal"],"additionalProperties":false},"workflow":{"description":"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.","type":"object","properties":{"runId":{"type":"string","description":"The run this belongs to, which is how a board groups its steps together."},"name":{"type":"string","description":"The workflow's name."},"step":{"type":"string","description":"Which step this conversation is on now. It moves when steps are chained."},"index":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"This step's place in the workflow, counting from one."},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many steps the workflow has."}},"required":["runId","name","step","index","total"],"additionalProperties":false},"awaitingWake":{"description":"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.","type":"boolean"},"watches":{"description":"Outside conditions this conversation is parked on, each of which will wake it. Absent means none, which is nearly every conversation: an armed watch is why a finished-looking agent starts working by itself, and why a hosted machine will not go idle.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this watch, the same one the agent was given when it armed it."},"note":{"type":"string","description":"The agent's own line on what it is waiting for."},"intervalSeconds":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How often the check runs."},"deadlineAt":{"type":"number","description":"When it gives up and wakes the conversation anyway, in milliseconds. Every watch has one."}},"required":["id","note","intervalSeconds","deadlineAt"],"additionalProperties":false}},"needs":{"description":"What it is waiting on people for and has not got yet, oldest first. Absent means nothing: an open need is why an idle-looking agent still needs you.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["capability","secret","grant","release","environment"],"description":"What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image."},"title":{"type":"string"},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."}},"required":["id","kind","title","status"],"additionalProperties":false}},"jobs":{"description":"Commands this conversation left running in the background, and how the most recent ones ended. Absent means none since the daemon started. A job is running exactly while it has no end.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this job, the one its transcript row names."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"session":{"type":"string","description":"The terminal session its pane runs in, which is what opening it focuses."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When the command exited, in milliseconds. Absent while it runs.","type":"number"},"exitCode":{"description":"The code it exited with. Absent while it runs, or when its exit left none.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"watch":{"description":"The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.","type":"string"},"handed":{"description":"Left running for the person: the agent kept it for them with the `keep` tool (or it holds a port this conversation already left them), so it outlives the turn, wakes nothing and is theirs to stop.","type":"boolean"},"ports":{"description":"The ports it was listening on when its turn ended.","type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"stoppedBy":{"description":"Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.","type":"string","enum":["turn","person","agent"]},"inputWait":{"description":"Present while it sits blocked reading its terminal with nothing moving: a prompt nobody in the sandbox will answer, which only stopping it or a person typing into its terminal ends. Absent while it runs on, and once it ends.","type":"object","properties":{"since":{"type":"number","description":"When it went still, in milliseconds."},"program":{"type":"string","description":"The process blocked reading the terminal, as its command line."}},"required":["since","program"],"additionalProperties":false}},"required":["id","label","session","startedAt"],"additionalProperties":false}},"archivedAt":{"description":"When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board.","type":"number"}},"required":["id","status","provider","harness","updatedAt","attention"],"additionalProperties":false}}}}}}},"/agents/{id}/keep-warm":{"post":{"operationId":"agents.keepWarm","summary":"Keep this conversation's prompt cache warm while it sits idle","description":"Re-reads the conversation's cached context shortly before the provider would drop it, until the time asked for, so picking it back up costs a cache read instead of re-sending everything. Each refresh is a forked, unsaved request that adds nothing to the conversation. Stops by itself when the time runs out, when a turn starts, when the account nears its limit, or when the prompt the next turn would send has changed. Refused for a conversation whose cache is already cold or that this sandbox cannot replay. Null stops it.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"until":{"description":"Keep its prompt cache warm until this instant, in milliseconds; shortened to what the sandbox can honestly keep. Null stops keeping it warm.","type":["number","null"]}},"required":["until"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The conversation id, which is how every other call addresses it."},"sessionId":{"description":"The provider session behind the last turn. It is retired whenever the model or account changes.","type":"string"},"title":{"description":"What to call it: the first prompt cut to one line, unless somebody renamed it.","type":"string"},"titleAction":{"description":"One word for the kind of work the naming pass read in it (fix, audit, redesign). Never part of the displayed name: a board tints and glyphs a card by it. Absent for a title nothing named an action for.","type":"string"},"status":{"type":"string","enum":["idle","running","awaiting","stopping","dismissing","stopped","resuming","landing","ready","landed","conflict","error","interrupted"],"description":"What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; dismissing is the same window for a question waved away, which ends the turn too but owes the user nothing; resuming means the sandbox is already putting right whatever killed the turn; landing means its work is being carried into the workspace right now, and nothing may act on its branch until that settles."},"failure":{"description":"Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.","type":"string"},"failureCode":{"description":"Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.","type":"string"},"limitResetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant.","type":"number"},"limitHeld":{"description":"Whether the refused turn is held whole, so sending again re-runs it instead of appending to it.","type":"boolean"},"limitScheduled":{"description":"Whether the held turn is already booked to go again at the reset, so nobody has to press anything.","type":"boolean"},"limitMoving":{"description":"The account the held turn is being moved to by the owner's policy, while that move is booked.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it runs on."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop it runs on."},"runner":{"description":"The runner this conversation runs on. Absent means this sandbox.","type":"string"},"startIn":{"description":"Which folder it opened in, relative to the workspace root. Absent means the root.","type":"string"},"actsAs":{"description":"Which persona its first turn acted as. Absent for an ordinary chat.","type":"string"},"lastActsAs":{"description":"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.","type":"string"},"model":{"description":"What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked.","type":"string"},"effort":{"description":"How hard that turn was told to think.","type":"string"},"thinking":{"description":"Whether that turn showed its reasoning.","type":"boolean"},"fast":{"description":"Whether that turn asked for higher speed. What was asked for, not what was served.","type":"boolean"},"account":{"description":"Which connected account paid for it.","type":"string"},"branch":{"description":"The branch its private copy works on. Absent for a conversation that works directly in the shared tree.","type":"string"},"autoLand":{"description":"This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case.","type":"boolean"},"limitPolicy":{"type":"string","enum":["wait","resend","move"]},"outagePolicy":{"type":"string","enum":["wait","retry"]},"stopPolicy":{"type":"string","enum":["wait","retry"]},"landRequested":{"description":"A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting.","type":"object","properties":{"email":{"type":"string","description":"Who asked."},"name":{"description":"Their display name.","type":"string"},"at":{"type":"number","description":"When they asked, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"reactions":{"description":"What people have marked this conversation with, one entry per emoji, in the order the emoji were first used. Absent means nobody has marked it, which is most conversations.","type":"array","items":{"type":"object","properties":{"emoji":{"type":"string","description":"The mark itself, one emoji, carried as the character rather than as a name that would need a table on both sides."},"by":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Who it was, as the sandbox verified them."},"name":{"description":"What to call them, when their sign-in carried a name. Absent leaves the address to stand for them.","type":"string"},"at":{"type":"number","description":"When they marked it, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"description":"Everyone wearing this mark, oldest first, each by name. Carried whole rather than as a count, because a chip reading 3 that cannot say whose is a number nobody can answer. Never empty: the last person taking theirs back takes the whole chip with it."}},"required":["emoji","by"],"additionalProperties":false}},"origin":{"description":"Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"startedBy":{"description":"Who asked for the first turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a child another conversation spawned. Absent when nothing was verified (a wake, a loopback caller).","type":"string"},"areas":{"description":"Which named areas of the workspace this conversation was started within, latched from whoever asked for the first turn. Absent means its starter held the whole workspace, which is why a fenced member is not shown it.","type":"array","items":{"type":"string"}},"owner":{"description":"The member answerable for this conversation: set from whoever started it, inherited from the parent by a spawned child, moved by handing it over. Absent means nobody has claimed it yet.","type":"object","properties":{"email":{"type":"string","description":"Who answers for this conversation, as the sandbox verified them."},"name":{"description":"What to call them, when the sign-in that made them its owner carried a name. Absent leaves the address to stand for them.","type":"string"},"since":{"type":"number","description":"When they became its owner, in milliseconds."}},"required":["email","since"],"additionalProperties":false},"forkedFrom":{"description":"The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"type":"string","enum":["then","now"]}},"required":["conversationId","index","files"],"additionalProperties":false},"base":{"description":"The commit its private copy started from, shortened.","type":"string"},"costUsd":{"description":"What it has cost so far, in dollars. A subagent's spend is its own and is not folded in here.","type":"number"},"inputTokens":{"description":"Uncached input tokens, excluding cache reads and cache writes.","type":"number"},"outputTokens":{"description":"Tokens received.","type":"number"},"contextTokens":{"description":"How much of the window the conversation currently fills.","type":"number"},"contextWindow":{"description":"How large that window is.","type":"number"},"promptCache":{"description":"When this conversation's prompt cache was last kept alive and how long it lasts, which together say when picking the conversation up stops being cheap. Absent when the provider publishes nothing to ground it on.","type":"object","properties":{"at":{"type":"number","description":"When its last request touched the provider's prompt cache, in milliseconds."},"ttlMs":{"type":"number","description":"How long that entry lives from `at`, in milliseconds."},"rollsAt":{"description":"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.","type":"number"},"keepableUntil":{"description":"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.","type":"number"}},"required":["at","ttlMs"],"additionalProperties":false},"keepWarm":{"description":"Whether the sandbox is keeping this conversation's prompt cache warm while it sits idle, how that is going, or why it stopped. Absent when nobody asked for it.","type":"object","properties":{"since":{"type":"number","description":"When keeping it warm started, in milliseconds."},"until":{"type":"number","description":"When it stops by itself, in milliseconds: the time asked for, shortened to what the sandbox can honestly keep, which is never past the point where refreshing costs more than re-reading, nor past the date change that rewrites the prompt."},"refreshes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Refreshes sent so far."},"readTokens":{"description":"How much the last refresh read back from the provider's cache, in tokens: the proof the cache was still there.","type":"number"},"ended":{"description":"Why keeping it warm stopped before anyone picked the conversation up. Absent while it is still being kept.","type":"object","properties":{"at":{"type":"number","description":"When it stopped, in milliseconds."},"reason":{"type":"string","enum":["elapsed","allowance","changed","cold","failed"],"description":"Why: `elapsed` the time asked for ran out; `allowance` the account reached the reserve kept for real work, or the provider refused a refresh for its limit; `changed` what the next turn would send no longer matches the cache (a part of the prompt, the date in it, or the conversation's session or account); `cold` the cache was gone, expired before a refresh could run or found missing by one; `failed` a refresh failed."},"detail":{"description":"The specifics, when there are any: which parts of the prompt changed, how full the account was, or the failure's own words.","type":"string"}},"required":["at","reason"],"additionalProperties":false}},"required":["since","until","refreshes"],"additionalProperties":false},"activity":{"description":"What it is doing at this moment.","type":"object","properties":{"tool":{"description":"The last tool it reached for.","type":"string"},"target":{"description":"What it reached for that tool with: a file, a command, a URL.","type":"string"},"todo":{"description":"The item on its own list that it is working through.","type":"string"}},"additionalProperties":false},"checklist":{"description":"How far it is through its own checklist. Absent for a conversation that kept no list, which is most short ones.","type":"object","properties":{"done":{"type":"number","description":"Items it has completed."},"total":{"type":"number","description":"Items on the list. Never zero: a conversation that kept no list carries no clause at all."}},"required":["done","total"],"additionalProperties":false},"landedMessageDraft":{"description":"The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes.","type":"object","properties":{"startedAt":{"type":"number","description":"When the drafting began, in milliseconds."},"steps":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider was asked."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"status":{"type":"string","enum":["asking","answered","refused","skipped"],"description":"How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."},"at":{"description":"When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time.","type":"number"},"ms":{"description":"How long it took. Absent while it is still being asked.","type":"number"},"reason":{"description":"Why it refused, in its own words.","type":"string"}},"required":["provider","model","status"],"additionalProperties":false},"description":"Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."},"outcome":{"description":"How it ended. Absent means it is still going.","type":"string","enum":["written","failed"]},"reason":{"description":"The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words.","type":"string"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"}},"required":["startedAt","steps"],"additionalProperties":false},"landedMessage":{"description":"What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false},"startedAt":{"description":"When the running turn started, in milliseconds. Absent when none is running.","type":"number"},"run":{"description":"The run under way right now: what a stop names, so it cannot cancel a turn that started after it was pressed. Absent when none is running, and for a turn with no run to attach to.","type":"string"},"queue":{"description":"Messages waiting for its next turn, and whether they are held. Absent for a conversation nothing has ever waited for.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false},"updatedAt":{"type":"number","description":"When it last did something, in milliseconds. Reading it does not count."},"seenAt":{"description":"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.","type":"number"},"unsentAt":{"description":"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.","type":"number"},"attention":{"type":"object","properties":{"plan":{"type":"boolean","description":"It has proposed a plan and is waiting for a yes."},"question":{"type":"boolean","description":"It has asked you something."},"permission":{"type":"boolean","description":"It wants to use a tool it needs permission for."},"capability":{"type":"boolean","description":"It needs something connected that is not connected yet."},"credential":{"type":"boolean","description":"It is waiting for a named person to release a credential. The one pause that may not be yours to clear, whatever your role."},"conflict":{"type":"boolean","description":"Its work cannot be merged without somebody resolving a clash."},"need":{"description":"It asked a person for something it still needs: a connection, a secret, wider reach, a tool. Absent from a daemon older than needs.","type":"boolean"}},"required":["plan","question","permission","capability","credential","conflict"],"additionalProperties":false,"description":"Which kinds of waiting-for-you it is doing."},"permissionAsk":{"description":"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.","type":"object","properties":{"requestId":{"type":"string","description":"Which request this is: the id an answer to it names."},"ask":{"type":"string","description":"What it asks to do, on one line: the runtime's own sentence, else the tool's short name."}},"required":["requestId","ask"],"additionalProperties":false},"landFailure":{"description":"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.","type":"object","properties":{"reason":{"type":"string","description":"Why it failed, in the words it failed with."},"code":{"description":"Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise.","type":"string"},"at":{"type":"number","description":"When it failed, in milliseconds."}},"required":["reason","at"],"additionalProperties":false},"delivery":{"description":"What became of its last land in the folder on the owner's computer, for a project folder attached to this computer's sandbox. Absent for any other land, and from a sandbox older than it.","type":"object","properties":{"state":{"type":"string","enum":["delivered","partial","waiting","failed","too-large"]},"at":{"type":"number","description":"When this state was reached, in milliseconds."},"project":{"type":"string"},"folder":{"description":"The folder on the owner's computer, as its machine reported it.","type":"string"},"applied":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files written, merged ones included."},"conflicts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"reason":{"type":"string","enum":["edited","link","outside","missing-git"]}},"required":["path","reason"],"additionalProperties":false}},"point":{"description":"The machine's restore point taken before writing.","type":"string"},"reason":{"description":"Why it failed or waits, in a sentence.","type":"string"}},"required":["state","at","project","applied","conflicts"],"additionalProperties":false},"conflictCauses":{"description":"Why its work will not merge, and so who can clear it: your own uncommitted edits, which only you can commit or stash, against a moved main line or an unmergeable binary, which the conversation can redo on its own copy. Absent unless it is refusing to merge.","type":"array","items":{"type":"string","enum":["workspace","diverged","binary"]}},"unfinished":{"description":"What its last turn left open: steps it never completed. Absent for a turn that finished what it started.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"steps":{"description":"The agent's own checklist where that turn left it. Absent for a conversation that kept no list.","type":"object","properties":{"open":{"type":"number","description":"Items on it that were never completed."},"total":{"type":"number","description":"Items on the whole list."},"next":{"description":"The one it would have done next: what it was working through, or the first still waiting.","type":"string"}},"required":["open","total"],"additionalProperties":false},"check":{"description":"The end-of-turn check that was still failing when the turn ended, by name. No longer written: what a check finds as a turn ends is said back to the model and never leaves its work unfinished.","type":"string"}},"required":["at"],"additionalProperties":false},"proof":{"description":"What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"verification":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to."},"check":{"description":"The command that spoke, when one did, so a targeted test is never read as the whole suite.","type":"string"},"unviewed":{"description":"How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none.","type":"number"}},"required":["at","verification"],"additionalProperties":false},"reach":{"description":"Where its last turn's work went when it went somewhere besides its own branch: files changed live outside it, clones of its own holding work that will not land, pushes it made itself. Absent when everything it did is on its branch.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"live":{"description":"Files it changed in place where every conversation reads them, outside its own branch: an installed extension's copy, the sandbox's shared state, the owner's own checkout. Live the moment they were written, never reviewed, and an installed extension's next update replaces them.","maxItems":10,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What it wrote, as it named it: relative to the workspace for the sandbox's shared state, absolute for the owner's own checkout."},"extension":{"description":"The extension, by its manifest id, when what it changed is the installed copy of one. Absent for anything else.","type":"string"}},"required":["path"],"additionalProperties":false}},"liveMore":{"description":"How many more such files there were beyond the ones listed. Absent when all are listed.","type":"number"},"stranded":{"description":"Repositories it cloned inside its own copy and left work in. A clone of its own never rides its land, so what is in one stays there.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"The clone, as a folder relative to its own copy of the workspace."},"uncommitted":{"type":"number","description":"Files changed in it and never committed."},"unpushed":{"type":"number","description":"Commits in it its upstream does not have, or that no remote has when it follows none."}},"required":["dir","uncommitted","unpushed"],"additionalProperties":false}},"strandedMore":{"description":"How many more such clones there were beyond the ones listed. Absent when all are listed.","type":"number"},"published":{"description":"Pushes it made with git that went through: work it sent out of the sandbox itself, never through a land.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"description":"The folder it pushed from, as the command named it. Absent for its own working folder.","type":"string"},"remote":{"description":"The remote it pushed to, as the command named it. Absent when the command named none.","type":"string"},"branch":{"description":"The branch it pushed to, as the command named it. Absent when the command named none.","type":"string"},"command":{"type":"string","description":"The push itself, trimmed to one line."}},"required":["command"],"additionalProperties":false}},"publishedMore":{"description":"How many more such pushes there were beyond the ones listed. Absent when all are listed.","type":"number"}},"required":["at"],"additionalProperties":false},"turns":{"description":"Turns it has finished.","type":"number"},"toolUses":{"description":"Tools it has used, over its whole life.","type":"number"},"subagents":{"description":"Subagents this one started, in-process and spawned alike. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost.","type":"object","properties":{"running":{"type":"number","description":"Subagents working right now."},"total":{"type":"number","description":"Subagents it has started over its whole life."}},"required":["running","total"],"additionalProperties":false},"diff":{"description":"Everything it has written, measured from where it started. Independent of how much has been merged.","type":"object","properties":{"files":{"type":"number","description":"Files touched."},"insertions":{"type":"number","description":"Lines added."},"deletions":{"type":"number","description":"Lines removed."}},"required":["files","insertions","deletions"],"additionalProperties":false},"landedPresence":{"description":"Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it.","type":"object","properties":{"landed":{"type":"number","description":"Paths this conversation merged in."},"present":{"type":"number","description":"How many of them are still there, either pending or committed."},"removedBy":{"description":"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"id":{"type":"string","description":"The conversation that took them out."},"title":{"description":"What that conversation is called.","type":"string"}},"required":["kind","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"description":"Who it was, as the sandbox verified them. Absent when the request carried no identity.","type":"string"},"name":{"description":"What to call them, when their sign-in carried a name.","type":"string"}},"required":["kind"],"additionalProperties":false}]}},"required":["landed","present"],"additionalProperties":false},"loop":{"description":"The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them.","type":"object","properties":{"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How the loop is going."},"iteration":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which round it is on."},"maxIterations":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many rounds it will attempt before giving up."},"goal":{"type":"string","description":"What it is looping towards."}},"required":["state","iteration","maxIterations","goal"],"additionalProperties":false},"workflow":{"description":"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.","type":"object","properties":{"runId":{"type":"string","description":"The run this belongs to, which is how a board groups its steps together."},"name":{"type":"string","description":"The workflow's name."},"step":{"type":"string","description":"Which step this conversation is on now. It moves when steps are chained."},"index":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"This step's place in the workflow, counting from one."},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many steps the workflow has."}},"required":["runId","name","step","index","total"],"additionalProperties":false},"awaitingWake":{"description":"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.","type":"boolean"},"watches":{"description":"Outside conditions this conversation is parked on, each of which will wake it. Absent means none, which is nearly every conversation: an armed watch is why a finished-looking agent starts working by itself, and why a hosted machine will not go idle.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this watch, the same one the agent was given when it armed it."},"note":{"type":"string","description":"The agent's own line on what it is waiting for."},"intervalSeconds":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How often the check runs."},"deadlineAt":{"type":"number","description":"When it gives up and wakes the conversation anyway, in milliseconds. Every watch has one."}},"required":["id","note","intervalSeconds","deadlineAt"],"additionalProperties":false}},"needs":{"description":"What it is waiting on people for and has not got yet, oldest first. Absent means nothing: an open need is why an idle-looking agent still needs you.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["capability","secret","grant","release","environment"],"description":"What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image."},"title":{"type":"string"},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."}},"required":["id","kind","title","status"],"additionalProperties":false}},"jobs":{"description":"Commands this conversation left running in the background, and how the most recent ones ended. Absent means none since the daemon started. A job is running exactly while it has no end.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this job, the one its transcript row names."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"session":{"type":"string","description":"The terminal session its pane runs in, which is what opening it focuses."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When the command exited, in milliseconds. Absent while it runs.","type":"number"},"exitCode":{"description":"The code it exited with. Absent while it runs, or when its exit left none.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"watch":{"description":"The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.","type":"string"},"handed":{"description":"Left running for the person: the agent kept it for them with the `keep` tool (or it holds a port this conversation already left them), so it outlives the turn, wakes nothing and is theirs to stop.","type":"boolean"},"ports":{"description":"The ports it was listening on when its turn ended.","type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"stoppedBy":{"description":"Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.","type":"string","enum":["turn","person","agent"]},"inputWait":{"description":"Present while it sits blocked reading its terminal with nothing moving: a prompt nobody in the sandbox will answer, which only stopping it or a person typing into its terminal ends. Absent while it runs on, and once it ends.","type":"object","properties":{"since":{"type":"number","description":"When it went still, in milliseconds."},"program":{"type":"string","description":"The process blocked reading the terminal, as its command line."}},"required":["since","program"],"additionalProperties":false}},"required":["id","label","session","startedAt"],"additionalProperties":false}},"archivedAt":{"description":"When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board.","type":"number"}},"required":["id","status","provider","harness","updatedAt","attention"],"additionalProperties":false}}}}}}},"/agents/{id}/seen":{"post":{"operationId":"agents.seen","summary":"Mark a conversation read","description":"Stamps the read marker behind the unread badge on one card. Allowed while the conversation is working, and reading never counts as activity.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The conversation id, which is how every other call addresses it."},"sessionId":{"description":"The provider session behind the last turn. It is retired whenever the model or account changes.","type":"string"},"title":{"description":"What to call it: the first prompt cut to one line, unless somebody renamed it.","type":"string"},"titleAction":{"description":"One word for the kind of work the naming pass read in it (fix, audit, redesign). Never part of the displayed name: a board tints and glyphs a card by it. Absent for a title nothing named an action for.","type":"string"},"status":{"type":"string","enum":["idle","running","awaiting","stopping","dismissing","stopped","resuming","landing","ready","landed","conflict","error","interrupted"],"description":"What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; dismissing is the same window for a question waved away, which ends the turn too but owes the user nothing; resuming means the sandbox is already putting right whatever killed the turn; landing means its work is being carried into the workspace right now, and nothing may act on its branch until that settles."},"failure":{"description":"Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.","type":"string"},"failureCode":{"description":"Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.","type":"string"},"limitResetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant.","type":"number"},"limitHeld":{"description":"Whether the refused turn is held whole, so sending again re-runs it instead of appending to it.","type":"boolean"},"limitScheduled":{"description":"Whether the held turn is already booked to go again at the reset, so nobody has to press anything.","type":"boolean"},"limitMoving":{"description":"The account the held turn is being moved to by the owner's policy, while that move is booked.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it runs on."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop it runs on."},"runner":{"description":"The runner this conversation runs on. Absent means this sandbox.","type":"string"},"startIn":{"description":"Which folder it opened in, relative to the workspace root. Absent means the root.","type":"string"},"actsAs":{"description":"Which persona its first turn acted as. Absent for an ordinary chat.","type":"string"},"lastActsAs":{"description":"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.","type":"string"},"model":{"description":"What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked.","type":"string"},"effort":{"description":"How hard that turn was told to think.","type":"string"},"thinking":{"description":"Whether that turn showed its reasoning.","type":"boolean"},"fast":{"description":"Whether that turn asked for higher speed. What was asked for, not what was served.","type":"boolean"},"account":{"description":"Which connected account paid for it.","type":"string"},"branch":{"description":"The branch its private copy works on. Absent for a conversation that works directly in the shared tree.","type":"string"},"autoLand":{"description":"This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case.","type":"boolean"},"limitPolicy":{"type":"string","enum":["wait","resend","move"]},"outagePolicy":{"type":"string","enum":["wait","retry"]},"stopPolicy":{"type":"string","enum":["wait","retry"]},"landRequested":{"description":"A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting.","type":"object","properties":{"email":{"type":"string","description":"Who asked."},"name":{"description":"Their display name.","type":"string"},"at":{"type":"number","description":"When they asked, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"reactions":{"description":"What people have marked this conversation with, one entry per emoji, in the order the emoji were first used. Absent means nobody has marked it, which is most conversations.","type":"array","items":{"type":"object","properties":{"emoji":{"type":"string","description":"The mark itself, one emoji, carried as the character rather than as a name that would need a table on both sides."},"by":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Who it was, as the sandbox verified them."},"name":{"description":"What to call them, when their sign-in carried a name. Absent leaves the address to stand for them.","type":"string"},"at":{"type":"number","description":"When they marked it, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"description":"Everyone wearing this mark, oldest first, each by name. Carried whole rather than as a count, because a chip reading 3 that cannot say whose is a number nobody can answer. Never empty: the last person taking theirs back takes the whole chip with it."}},"required":["emoji","by"],"additionalProperties":false}},"origin":{"description":"Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"startedBy":{"description":"Who asked for the first turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a child another conversation spawned. Absent when nothing was verified (a wake, a loopback caller).","type":"string"},"areas":{"description":"Which named areas of the workspace this conversation was started within, latched from whoever asked for the first turn. Absent means its starter held the whole workspace, which is why a fenced member is not shown it.","type":"array","items":{"type":"string"}},"owner":{"description":"The member answerable for this conversation: set from whoever started it, inherited from the parent by a spawned child, moved by handing it over. Absent means nobody has claimed it yet.","type":"object","properties":{"email":{"type":"string","description":"Who answers for this conversation, as the sandbox verified them."},"name":{"description":"What to call them, when the sign-in that made them its owner carried a name. Absent leaves the address to stand for them.","type":"string"},"since":{"type":"number","description":"When they became its owner, in milliseconds."}},"required":["email","since"],"additionalProperties":false},"forkedFrom":{"description":"The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"type":"string","enum":["then","now"]}},"required":["conversationId","index","files"],"additionalProperties":false},"base":{"description":"The commit its private copy started from, shortened.","type":"string"},"costUsd":{"description":"What it has cost so far, in dollars. A subagent's spend is its own and is not folded in here.","type":"number"},"inputTokens":{"description":"Uncached input tokens, excluding cache reads and cache writes.","type":"number"},"outputTokens":{"description":"Tokens received.","type":"number"},"contextTokens":{"description":"How much of the window the conversation currently fills.","type":"number"},"contextWindow":{"description":"How large that window is.","type":"number"},"promptCache":{"description":"When this conversation's prompt cache was last kept alive and how long it lasts, which together say when picking the conversation up stops being cheap. Absent when the provider publishes nothing to ground it on.","type":"object","properties":{"at":{"type":"number","description":"When its last request touched the provider's prompt cache, in milliseconds."},"ttlMs":{"type":"number","description":"How long that entry lives from `at`, in milliseconds."},"rollsAt":{"description":"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.","type":"number"},"keepableUntil":{"description":"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.","type":"number"}},"required":["at","ttlMs"],"additionalProperties":false},"keepWarm":{"description":"Whether the sandbox is keeping this conversation's prompt cache warm while it sits idle, how that is going, or why it stopped. Absent when nobody asked for it.","type":"object","properties":{"since":{"type":"number","description":"When keeping it warm started, in milliseconds."},"until":{"type":"number","description":"When it stops by itself, in milliseconds: the time asked for, shortened to what the sandbox can honestly keep, which is never past the point where refreshing costs more than re-reading, nor past the date change that rewrites the prompt."},"refreshes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Refreshes sent so far."},"readTokens":{"description":"How much the last refresh read back from the provider's cache, in tokens: the proof the cache was still there.","type":"number"},"ended":{"description":"Why keeping it warm stopped before anyone picked the conversation up. Absent while it is still being kept.","type":"object","properties":{"at":{"type":"number","description":"When it stopped, in milliseconds."},"reason":{"type":"string","enum":["elapsed","allowance","changed","cold","failed"],"description":"Why: `elapsed` the time asked for ran out; `allowance` the account reached the reserve kept for real work, or the provider refused a refresh for its limit; `changed` what the next turn would send no longer matches the cache (a part of the prompt, the date in it, or the conversation's session or account); `cold` the cache was gone, expired before a refresh could run or found missing by one; `failed` a refresh failed."},"detail":{"description":"The specifics, when there are any: which parts of the prompt changed, how full the account was, or the failure's own words.","type":"string"}},"required":["at","reason"],"additionalProperties":false}},"required":["since","until","refreshes"],"additionalProperties":false},"activity":{"description":"What it is doing at this moment.","type":"object","properties":{"tool":{"description":"The last tool it reached for.","type":"string"},"target":{"description":"What it reached for that tool with: a file, a command, a URL.","type":"string"},"todo":{"description":"The item on its own list that it is working through.","type":"string"}},"additionalProperties":false},"checklist":{"description":"How far it is through its own checklist. Absent for a conversation that kept no list, which is most short ones.","type":"object","properties":{"done":{"type":"number","description":"Items it has completed."},"total":{"type":"number","description":"Items on the list. Never zero: a conversation that kept no list carries no clause at all."}},"required":["done","total"],"additionalProperties":false},"landedMessageDraft":{"description":"The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes.","type":"object","properties":{"startedAt":{"type":"number","description":"When the drafting began, in milliseconds."},"steps":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider was asked."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"status":{"type":"string","enum":["asking","answered","refused","skipped"],"description":"How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."},"at":{"description":"When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time.","type":"number"},"ms":{"description":"How long it took. Absent while it is still being asked.","type":"number"},"reason":{"description":"Why it refused, in its own words.","type":"string"}},"required":["provider","model","status"],"additionalProperties":false},"description":"Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."},"outcome":{"description":"How it ended. Absent means it is still going.","type":"string","enum":["written","failed"]},"reason":{"description":"The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words.","type":"string"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"}},"required":["startedAt","steps"],"additionalProperties":false},"landedMessage":{"description":"What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false},"startedAt":{"description":"When the running turn started, in milliseconds. Absent when none is running.","type":"number"},"run":{"description":"The run under way right now: what a stop names, so it cannot cancel a turn that started after it was pressed. Absent when none is running, and for a turn with no run to attach to.","type":"string"},"queue":{"description":"Messages waiting for its next turn, and whether they are held. Absent for a conversation nothing has ever waited for.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false},"updatedAt":{"type":"number","description":"When it last did something, in milliseconds. Reading it does not count."},"seenAt":{"description":"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.","type":"number"},"unsentAt":{"description":"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.","type":"number"},"attention":{"type":"object","properties":{"plan":{"type":"boolean","description":"It has proposed a plan and is waiting for a yes."},"question":{"type":"boolean","description":"It has asked you something."},"permission":{"type":"boolean","description":"It wants to use a tool it needs permission for."},"capability":{"type":"boolean","description":"It needs something connected that is not connected yet."},"credential":{"type":"boolean","description":"It is waiting for a named person to release a credential. The one pause that may not be yours to clear, whatever your role."},"conflict":{"type":"boolean","description":"Its work cannot be merged without somebody resolving a clash."},"need":{"description":"It asked a person for something it still needs: a connection, a secret, wider reach, a tool. Absent from a daemon older than needs.","type":"boolean"}},"required":["plan","question","permission","capability","credential","conflict"],"additionalProperties":false,"description":"Which kinds of waiting-for-you it is doing."},"permissionAsk":{"description":"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.","type":"object","properties":{"requestId":{"type":"string","description":"Which request this is: the id an answer to it names."},"ask":{"type":"string","description":"What it asks to do, on one line: the runtime's own sentence, else the tool's short name."}},"required":["requestId","ask"],"additionalProperties":false},"landFailure":{"description":"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.","type":"object","properties":{"reason":{"type":"string","description":"Why it failed, in the words it failed with."},"code":{"description":"Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise.","type":"string"},"at":{"type":"number","description":"When it failed, in milliseconds."}},"required":["reason","at"],"additionalProperties":false},"delivery":{"description":"What became of its last land in the folder on the owner's computer, for a project folder attached to this computer's sandbox. Absent for any other land, and from a sandbox older than it.","type":"object","properties":{"state":{"type":"string","enum":["delivered","partial","waiting","failed","too-large"]},"at":{"type":"number","description":"When this state was reached, in milliseconds."},"project":{"type":"string"},"folder":{"description":"The folder on the owner's computer, as its machine reported it.","type":"string"},"applied":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files written, merged ones included."},"conflicts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"reason":{"type":"string","enum":["edited","link","outside","missing-git"]}},"required":["path","reason"],"additionalProperties":false}},"point":{"description":"The machine's restore point taken before writing.","type":"string"},"reason":{"description":"Why it failed or waits, in a sentence.","type":"string"}},"required":["state","at","project","applied","conflicts"],"additionalProperties":false},"conflictCauses":{"description":"Why its work will not merge, and so who can clear it: your own uncommitted edits, which only you can commit or stash, against a moved main line or an unmergeable binary, which the conversation can redo on its own copy. Absent unless it is refusing to merge.","type":"array","items":{"type":"string","enum":["workspace","diverged","binary"]}},"unfinished":{"description":"What its last turn left open: steps it never completed. Absent for a turn that finished what it started.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"steps":{"description":"The agent's own checklist where that turn left it. Absent for a conversation that kept no list.","type":"object","properties":{"open":{"type":"number","description":"Items on it that were never completed."},"total":{"type":"number","description":"Items on the whole list."},"next":{"description":"The one it would have done next: what it was working through, or the first still waiting.","type":"string"}},"required":["open","total"],"additionalProperties":false},"check":{"description":"The end-of-turn check that was still failing when the turn ended, by name. No longer written: what a check finds as a turn ends is said back to the model and never leaves its work unfinished.","type":"string"}},"required":["at"],"additionalProperties":false},"proof":{"description":"What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"verification":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to."},"check":{"description":"The command that spoke, when one did, so a targeted test is never read as the whole suite.","type":"string"},"unviewed":{"description":"How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none.","type":"number"}},"required":["at","verification"],"additionalProperties":false},"reach":{"description":"Where its last turn's work went when it went somewhere besides its own branch: files changed live outside it, clones of its own holding work that will not land, pushes it made itself. Absent when everything it did is on its branch.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"live":{"description":"Files it changed in place where every conversation reads them, outside its own branch: an installed extension's copy, the sandbox's shared state, the owner's own checkout. Live the moment they were written, never reviewed, and an installed extension's next update replaces them.","maxItems":10,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What it wrote, as it named it: relative to the workspace for the sandbox's shared state, absolute for the owner's own checkout."},"extension":{"description":"The extension, by its manifest id, when what it changed is the installed copy of one. Absent for anything else.","type":"string"}},"required":["path"],"additionalProperties":false}},"liveMore":{"description":"How many more such files there were beyond the ones listed. Absent when all are listed.","type":"number"},"stranded":{"description":"Repositories it cloned inside its own copy and left work in. A clone of its own never rides its land, so what is in one stays there.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"The clone, as a folder relative to its own copy of the workspace."},"uncommitted":{"type":"number","description":"Files changed in it and never committed."},"unpushed":{"type":"number","description":"Commits in it its upstream does not have, or that no remote has when it follows none."}},"required":["dir","uncommitted","unpushed"],"additionalProperties":false}},"strandedMore":{"description":"How many more such clones there were beyond the ones listed. Absent when all are listed.","type":"number"},"published":{"description":"Pushes it made with git that went through: work it sent out of the sandbox itself, never through a land.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"description":"The folder it pushed from, as the command named it. Absent for its own working folder.","type":"string"},"remote":{"description":"The remote it pushed to, as the command named it. Absent when the command named none.","type":"string"},"branch":{"description":"The branch it pushed to, as the command named it. Absent when the command named none.","type":"string"},"command":{"type":"string","description":"The push itself, trimmed to one line."}},"required":["command"],"additionalProperties":false}},"publishedMore":{"description":"How many more such pushes there were beyond the ones listed. Absent when all are listed.","type":"number"}},"required":["at"],"additionalProperties":false},"turns":{"description":"Turns it has finished.","type":"number"},"toolUses":{"description":"Tools it has used, over its whole life.","type":"number"},"subagents":{"description":"Subagents this one started, in-process and spawned alike. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost.","type":"object","properties":{"running":{"type":"number","description":"Subagents working right now."},"total":{"type":"number","description":"Subagents it has started over its whole life."}},"required":["running","total"],"additionalProperties":false},"diff":{"description":"Everything it has written, measured from where it started. Independent of how much has been merged.","type":"object","properties":{"files":{"type":"number","description":"Files touched."},"insertions":{"type":"number","description":"Lines added."},"deletions":{"type":"number","description":"Lines removed."}},"required":["files","insertions","deletions"],"additionalProperties":false},"landedPresence":{"description":"Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it.","type":"object","properties":{"landed":{"type":"number","description":"Paths this conversation merged in."},"present":{"type":"number","description":"How many of them are still there, either pending or committed."},"removedBy":{"description":"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"id":{"type":"string","description":"The conversation that took them out."},"title":{"description":"What that conversation is called.","type":"string"}},"required":["kind","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"description":"Who it was, as the sandbox verified them. Absent when the request carried no identity.","type":"string"},"name":{"description":"What to call them, when their sign-in carried a name.","type":"string"}},"required":["kind"],"additionalProperties":false}]}},"required":["landed","present"],"additionalProperties":false},"loop":{"description":"The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them.","type":"object","properties":{"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How the loop is going."},"iteration":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which round it is on."},"maxIterations":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many rounds it will attempt before giving up."},"goal":{"type":"string","description":"What it is looping towards."}},"required":["state","iteration","maxIterations","goal"],"additionalProperties":false},"workflow":{"description":"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.","type":"object","properties":{"runId":{"type":"string","description":"The run this belongs to, which is how a board groups its steps together."},"name":{"type":"string","description":"The workflow's name."},"step":{"type":"string","description":"Which step this conversation is on now. It moves when steps are chained."},"index":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"This step's place in the workflow, counting from one."},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many steps the workflow has."}},"required":["runId","name","step","index","total"],"additionalProperties":false},"awaitingWake":{"description":"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.","type":"boolean"},"watches":{"description":"Outside conditions this conversation is parked on, each of which will wake it. Absent means none, which is nearly every conversation: an armed watch is why a finished-looking agent starts working by itself, and why a hosted machine will not go idle.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this watch, the same one the agent was given when it armed it."},"note":{"type":"string","description":"The agent's own line on what it is waiting for."},"intervalSeconds":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How often the check runs."},"deadlineAt":{"type":"number","description":"When it gives up and wakes the conversation anyway, in milliseconds. Every watch has one."}},"required":["id","note","intervalSeconds","deadlineAt"],"additionalProperties":false}},"needs":{"description":"What it is waiting on people for and has not got yet, oldest first. Absent means nothing: an open need is why an idle-looking agent still needs you.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["capability","secret","grant","release","environment"],"description":"What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image."},"title":{"type":"string"},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."}},"required":["id","kind","title","status"],"additionalProperties":false}},"jobs":{"description":"Commands this conversation left running in the background, and how the most recent ones ended. Absent means none since the daemon started. A job is running exactly while it has no end.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this job, the one its transcript row names."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"session":{"type":"string","description":"The terminal session its pane runs in, which is what opening it focuses."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When the command exited, in milliseconds. Absent while it runs.","type":"number"},"exitCode":{"description":"The code it exited with. Absent while it runs, or when its exit left none.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"watch":{"description":"The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.","type":"string"},"handed":{"description":"Left running for the person: the agent kept it for them with the `keep` tool (or it holds a port this conversation already left them), so it outlives the turn, wakes nothing and is theirs to stop.","type":"boolean"},"ports":{"description":"The ports it was listening on when its turn ended.","type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"stoppedBy":{"description":"Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.","type":"string","enum":["turn","person","agent"]},"inputWait":{"description":"Present while it sits blocked reading its terminal with nothing moving: a prompt nobody in the sandbox will answer, which only stopping it or a person typing into its terminal ends. Absent while it runs on, and once it ends.","type":"object","properties":{"since":{"type":"number","description":"When it went still, in milliseconds."},"program":{"type":"string","description":"The process blocked reading the terminal, as its command line."}},"required":["since","program"],"additionalProperties":false}},"required":["id","label","session","startedAt"],"additionalProperties":false}},"archivedAt":{"description":"When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board.","type":"number"}},"required":["id","status","provider","harness","updatedAt","attention"],"additionalProperties":false}}}}}}},"/agents/{id}/unsent":{"post":{"operationId":"agents.unsent","summary":"Report that a composer holds an unsent message for a conversation","description":"The words stay in the browser; the sandbox only records since when some composer has held them, so a conversation waiting on a message nobody has sent yet is never archived for being idle. Null clears it once the message is sent or deleted. Does not count as activity.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"at":{"description":"When the composer started holding the unsent message, in milliseconds. Null says it no longer holds one: it was sent or cleared.","type":["number","null"]}},"required":["at"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The conversation id, which is how every other call addresses it."},"sessionId":{"description":"The provider session behind the last turn. It is retired whenever the model or account changes.","type":"string"},"title":{"description":"What to call it: the first prompt cut to one line, unless somebody renamed it.","type":"string"},"titleAction":{"description":"One word for the kind of work the naming pass read in it (fix, audit, redesign). Never part of the displayed name: a board tints and glyphs a card by it. Absent for a title nothing named an action for.","type":"string"},"status":{"type":"string","enum":["idle","running","awaiting","stopping","dismissing","stopped","resuming","landing","ready","landed","conflict","error","interrupted"],"description":"What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; dismissing is the same window for a question waved away, which ends the turn too but owes the user nothing; resuming means the sandbox is already putting right whatever killed the turn; landing means its work is being carried into the workspace right now, and nothing may act on its branch until that settles."},"failure":{"description":"Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.","type":"string"},"failureCode":{"description":"Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.","type":"string"},"limitResetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant.","type":"number"},"limitHeld":{"description":"Whether the refused turn is held whole, so sending again re-runs it instead of appending to it.","type":"boolean"},"limitScheduled":{"description":"Whether the held turn is already booked to go again at the reset, so nobody has to press anything.","type":"boolean"},"limitMoving":{"description":"The account the held turn is being moved to by the owner's policy, while that move is booked.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it runs on."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop it runs on."},"runner":{"description":"The runner this conversation runs on. Absent means this sandbox.","type":"string"},"startIn":{"description":"Which folder it opened in, relative to the workspace root. Absent means the root.","type":"string"},"actsAs":{"description":"Which persona its first turn acted as. Absent for an ordinary chat.","type":"string"},"lastActsAs":{"description":"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.","type":"string"},"model":{"description":"What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked.","type":"string"},"effort":{"description":"How hard that turn was told to think.","type":"string"},"thinking":{"description":"Whether that turn showed its reasoning.","type":"boolean"},"fast":{"description":"Whether that turn asked for higher speed. What was asked for, not what was served.","type":"boolean"},"account":{"description":"Which connected account paid for it.","type":"string"},"branch":{"description":"The branch its private copy works on. Absent for a conversation that works directly in the shared tree.","type":"string"},"autoLand":{"description":"This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case.","type":"boolean"},"limitPolicy":{"type":"string","enum":["wait","resend","move"]},"outagePolicy":{"type":"string","enum":["wait","retry"]},"stopPolicy":{"type":"string","enum":["wait","retry"]},"landRequested":{"description":"A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting.","type":"object","properties":{"email":{"type":"string","description":"Who asked."},"name":{"description":"Their display name.","type":"string"},"at":{"type":"number","description":"When they asked, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"reactions":{"description":"What people have marked this conversation with, one entry per emoji, in the order the emoji were first used. Absent means nobody has marked it, which is most conversations.","type":"array","items":{"type":"object","properties":{"emoji":{"type":"string","description":"The mark itself, one emoji, carried as the character rather than as a name that would need a table on both sides."},"by":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Who it was, as the sandbox verified them."},"name":{"description":"What to call them, when their sign-in carried a name. Absent leaves the address to stand for them.","type":"string"},"at":{"type":"number","description":"When they marked it, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"description":"Everyone wearing this mark, oldest first, each by name. Carried whole rather than as a count, because a chip reading 3 that cannot say whose is a number nobody can answer. Never empty: the last person taking theirs back takes the whole chip with it."}},"required":["emoji","by"],"additionalProperties":false}},"origin":{"description":"Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"startedBy":{"description":"Who asked for the first turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a child another conversation spawned. Absent when nothing was verified (a wake, a loopback caller).","type":"string"},"areas":{"description":"Which named areas of the workspace this conversation was started within, latched from whoever asked for the first turn. Absent means its starter held the whole workspace, which is why a fenced member is not shown it.","type":"array","items":{"type":"string"}},"owner":{"description":"The member answerable for this conversation: set from whoever started it, inherited from the parent by a spawned child, moved by handing it over. Absent means nobody has claimed it yet.","type":"object","properties":{"email":{"type":"string","description":"Who answers for this conversation, as the sandbox verified them."},"name":{"description":"What to call them, when the sign-in that made them its owner carried a name. Absent leaves the address to stand for them.","type":"string"},"since":{"type":"number","description":"When they became its owner, in milliseconds."}},"required":["email","since"],"additionalProperties":false},"forkedFrom":{"description":"The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"type":"string","enum":["then","now"]}},"required":["conversationId","index","files"],"additionalProperties":false},"base":{"description":"The commit its private copy started from, shortened.","type":"string"},"costUsd":{"description":"What it has cost so far, in dollars. A subagent's spend is its own and is not folded in here.","type":"number"},"inputTokens":{"description":"Uncached input tokens, excluding cache reads and cache writes.","type":"number"},"outputTokens":{"description":"Tokens received.","type":"number"},"contextTokens":{"description":"How much of the window the conversation currently fills.","type":"number"},"contextWindow":{"description":"How large that window is.","type":"number"},"promptCache":{"description":"When this conversation's prompt cache was last kept alive and how long it lasts, which together say when picking the conversation up stops being cheap. Absent when the provider publishes nothing to ground it on.","type":"object","properties":{"at":{"type":"number","description":"When its last request touched the provider's prompt cache, in milliseconds."},"ttlMs":{"type":"number","description":"How long that entry lives from `at`, in milliseconds."},"rollsAt":{"description":"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.","type":"number"},"keepableUntil":{"description":"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.","type":"number"}},"required":["at","ttlMs"],"additionalProperties":false},"keepWarm":{"description":"Whether the sandbox is keeping this conversation's prompt cache warm while it sits idle, how that is going, or why it stopped. Absent when nobody asked for it.","type":"object","properties":{"since":{"type":"number","description":"When keeping it warm started, in milliseconds."},"until":{"type":"number","description":"When it stops by itself, in milliseconds: the time asked for, shortened to what the sandbox can honestly keep, which is never past the point where refreshing costs more than re-reading, nor past the date change that rewrites the prompt."},"refreshes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Refreshes sent so far."},"readTokens":{"description":"How much the last refresh read back from the provider's cache, in tokens: the proof the cache was still there.","type":"number"},"ended":{"description":"Why keeping it warm stopped before anyone picked the conversation up. Absent while it is still being kept.","type":"object","properties":{"at":{"type":"number","description":"When it stopped, in milliseconds."},"reason":{"type":"string","enum":["elapsed","allowance","changed","cold","failed"],"description":"Why: `elapsed` the time asked for ran out; `allowance` the account reached the reserve kept for real work, or the provider refused a refresh for its limit; `changed` what the next turn would send no longer matches the cache (a part of the prompt, the date in it, or the conversation's session or account); `cold` the cache was gone, expired before a refresh could run or found missing by one; `failed` a refresh failed."},"detail":{"description":"The specifics, when there are any: which parts of the prompt changed, how full the account was, or the failure's own words.","type":"string"}},"required":["at","reason"],"additionalProperties":false}},"required":["since","until","refreshes"],"additionalProperties":false},"activity":{"description":"What it is doing at this moment.","type":"object","properties":{"tool":{"description":"The last tool it reached for.","type":"string"},"target":{"description":"What it reached for that tool with: a file, a command, a URL.","type":"string"},"todo":{"description":"The item on its own list that it is working through.","type":"string"}},"additionalProperties":false},"checklist":{"description":"How far it is through its own checklist. Absent for a conversation that kept no list, which is most short ones.","type":"object","properties":{"done":{"type":"number","description":"Items it has completed."},"total":{"type":"number","description":"Items on the list. Never zero: a conversation that kept no list carries no clause at all."}},"required":["done","total"],"additionalProperties":false},"landedMessageDraft":{"description":"The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes.","type":"object","properties":{"startedAt":{"type":"number","description":"When the drafting began, in milliseconds."},"steps":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider was asked."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"status":{"type":"string","enum":["asking","answered","refused","skipped"],"description":"How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."},"at":{"description":"When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time.","type":"number"},"ms":{"description":"How long it took. Absent while it is still being asked.","type":"number"},"reason":{"description":"Why it refused, in its own words.","type":"string"}},"required":["provider","model","status"],"additionalProperties":false},"description":"Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."},"outcome":{"description":"How it ended. Absent means it is still going.","type":"string","enum":["written","failed"]},"reason":{"description":"The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words.","type":"string"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"}},"required":["startedAt","steps"],"additionalProperties":false},"landedMessage":{"description":"What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false},"startedAt":{"description":"When the running turn started, in milliseconds. Absent when none is running.","type":"number"},"run":{"description":"The run under way right now: what a stop names, so it cannot cancel a turn that started after it was pressed. Absent when none is running, and for a turn with no run to attach to.","type":"string"},"queue":{"description":"Messages waiting for its next turn, and whether they are held. Absent for a conversation nothing has ever waited for.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false},"updatedAt":{"type":"number","description":"When it last did something, in milliseconds. Reading it does not count."},"seenAt":{"description":"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.","type":"number"},"unsentAt":{"description":"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.","type":"number"},"attention":{"type":"object","properties":{"plan":{"type":"boolean","description":"It has proposed a plan and is waiting for a yes."},"question":{"type":"boolean","description":"It has asked you something."},"permission":{"type":"boolean","description":"It wants to use a tool it needs permission for."},"capability":{"type":"boolean","description":"It needs something connected that is not connected yet."},"credential":{"type":"boolean","description":"It is waiting for a named person to release a credential. The one pause that may not be yours to clear, whatever your role."},"conflict":{"type":"boolean","description":"Its work cannot be merged without somebody resolving a clash."},"need":{"description":"It asked a person for something it still needs: a connection, a secret, wider reach, a tool. Absent from a daemon older than needs.","type":"boolean"}},"required":["plan","question","permission","capability","credential","conflict"],"additionalProperties":false,"description":"Which kinds of waiting-for-you it is doing."},"permissionAsk":{"description":"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.","type":"object","properties":{"requestId":{"type":"string","description":"Which request this is: the id an answer to it names."},"ask":{"type":"string","description":"What it asks to do, on one line: the runtime's own sentence, else the tool's short name."}},"required":["requestId","ask"],"additionalProperties":false},"landFailure":{"description":"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.","type":"object","properties":{"reason":{"type":"string","description":"Why it failed, in the words it failed with."},"code":{"description":"Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise.","type":"string"},"at":{"type":"number","description":"When it failed, in milliseconds."}},"required":["reason","at"],"additionalProperties":false},"delivery":{"description":"What became of its last land in the folder on the owner's computer, for a project folder attached to this computer's sandbox. Absent for any other land, and from a sandbox older than it.","type":"object","properties":{"state":{"type":"string","enum":["delivered","partial","waiting","failed","too-large"]},"at":{"type":"number","description":"When this state was reached, in milliseconds."},"project":{"type":"string"},"folder":{"description":"The folder on the owner's computer, as its machine reported it.","type":"string"},"applied":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files written, merged ones included."},"conflicts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"reason":{"type":"string","enum":["edited","link","outside","missing-git"]}},"required":["path","reason"],"additionalProperties":false}},"point":{"description":"The machine's restore point taken before writing.","type":"string"},"reason":{"description":"Why it failed or waits, in a sentence.","type":"string"}},"required":["state","at","project","applied","conflicts"],"additionalProperties":false},"conflictCauses":{"description":"Why its work will not merge, and so who can clear it: your own uncommitted edits, which only you can commit or stash, against a moved main line or an unmergeable binary, which the conversation can redo on its own copy. Absent unless it is refusing to merge.","type":"array","items":{"type":"string","enum":["workspace","diverged","binary"]}},"unfinished":{"description":"What its last turn left open: steps it never completed. Absent for a turn that finished what it started.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"steps":{"description":"The agent's own checklist where that turn left it. Absent for a conversation that kept no list.","type":"object","properties":{"open":{"type":"number","description":"Items on it that were never completed."},"total":{"type":"number","description":"Items on the whole list."},"next":{"description":"The one it would have done next: what it was working through, or the first still waiting.","type":"string"}},"required":["open","total"],"additionalProperties":false},"check":{"description":"The end-of-turn check that was still failing when the turn ended, by name. No longer written: what a check finds as a turn ends is said back to the model and never leaves its work unfinished.","type":"string"}},"required":["at"],"additionalProperties":false},"proof":{"description":"What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"verification":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to."},"check":{"description":"The command that spoke, when one did, so a targeted test is never read as the whole suite.","type":"string"},"unviewed":{"description":"How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none.","type":"number"}},"required":["at","verification"],"additionalProperties":false},"reach":{"description":"Where its last turn's work went when it went somewhere besides its own branch: files changed live outside it, clones of its own holding work that will not land, pushes it made itself. Absent when everything it did is on its branch.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"live":{"description":"Files it changed in place where every conversation reads them, outside its own branch: an installed extension's copy, the sandbox's shared state, the owner's own checkout. Live the moment they were written, never reviewed, and an installed extension's next update replaces them.","maxItems":10,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What it wrote, as it named it: relative to the workspace for the sandbox's shared state, absolute for the owner's own checkout."},"extension":{"description":"The extension, by its manifest id, when what it changed is the installed copy of one. Absent for anything else.","type":"string"}},"required":["path"],"additionalProperties":false}},"liveMore":{"description":"How many more such files there were beyond the ones listed. Absent when all are listed.","type":"number"},"stranded":{"description":"Repositories it cloned inside its own copy and left work in. A clone of its own never rides its land, so what is in one stays there.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"The clone, as a folder relative to its own copy of the workspace."},"uncommitted":{"type":"number","description":"Files changed in it and never committed."},"unpushed":{"type":"number","description":"Commits in it its upstream does not have, or that no remote has when it follows none."}},"required":["dir","uncommitted","unpushed"],"additionalProperties":false}},"strandedMore":{"description":"How many more such clones there were beyond the ones listed. Absent when all are listed.","type":"number"},"published":{"description":"Pushes it made with git that went through: work it sent out of the sandbox itself, never through a land.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"description":"The folder it pushed from, as the command named it. Absent for its own working folder.","type":"string"},"remote":{"description":"The remote it pushed to, as the command named it. Absent when the command named none.","type":"string"},"branch":{"description":"The branch it pushed to, as the command named it. Absent when the command named none.","type":"string"},"command":{"type":"string","description":"The push itself, trimmed to one line."}},"required":["command"],"additionalProperties":false}},"publishedMore":{"description":"How many more such pushes there were beyond the ones listed. Absent when all are listed.","type":"number"}},"required":["at"],"additionalProperties":false},"turns":{"description":"Turns it has finished.","type":"number"},"toolUses":{"description":"Tools it has used, over its whole life.","type":"number"},"subagents":{"description":"Subagents this one started, in-process and spawned alike. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost.","type":"object","properties":{"running":{"type":"number","description":"Subagents working right now."},"total":{"type":"number","description":"Subagents it has started over its whole life."}},"required":["running","total"],"additionalProperties":false},"diff":{"description":"Everything it has written, measured from where it started. Independent of how much has been merged.","type":"object","properties":{"files":{"type":"number","description":"Files touched."},"insertions":{"type":"number","description":"Lines added."},"deletions":{"type":"number","description":"Lines removed."}},"required":["files","insertions","deletions"],"additionalProperties":false},"landedPresence":{"description":"Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it.","type":"object","properties":{"landed":{"type":"number","description":"Paths this conversation merged in."},"present":{"type":"number","description":"How many of them are still there, either pending or committed."},"removedBy":{"description":"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"id":{"type":"string","description":"The conversation that took them out."},"title":{"description":"What that conversation is called.","type":"string"}},"required":["kind","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"description":"Who it was, as the sandbox verified them. Absent when the request carried no identity.","type":"string"},"name":{"description":"What to call them, when their sign-in carried a name.","type":"string"}},"required":["kind"],"additionalProperties":false}]}},"required":["landed","present"],"additionalProperties":false},"loop":{"description":"The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them.","type":"object","properties":{"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How the loop is going."},"iteration":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which round it is on."},"maxIterations":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many rounds it will attempt before giving up."},"goal":{"type":"string","description":"What it is looping towards."}},"required":["state","iteration","maxIterations","goal"],"additionalProperties":false},"workflow":{"description":"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.","type":"object","properties":{"runId":{"type":"string","description":"The run this belongs to, which is how a board groups its steps together."},"name":{"type":"string","description":"The workflow's name."},"step":{"type":"string","description":"Which step this conversation is on now. It moves when steps are chained."},"index":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"This step's place in the workflow, counting from one."},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many steps the workflow has."}},"required":["runId","name","step","index","total"],"additionalProperties":false},"awaitingWake":{"description":"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.","type":"boolean"},"watches":{"description":"Outside conditions this conversation is parked on, each of which will wake it. Absent means none, which is nearly every conversation: an armed watch is why a finished-looking agent starts working by itself, and why a hosted machine will not go idle.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this watch, the same one the agent was given when it armed it."},"note":{"type":"string","description":"The agent's own line on what it is waiting for."},"intervalSeconds":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How often the check runs."},"deadlineAt":{"type":"number","description":"When it gives up and wakes the conversation anyway, in milliseconds. Every watch has one."}},"required":["id","note","intervalSeconds","deadlineAt"],"additionalProperties":false}},"needs":{"description":"What it is waiting on people for and has not got yet, oldest first. Absent means nothing: an open need is why an idle-looking agent still needs you.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["capability","secret","grant","release","environment"],"description":"What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image."},"title":{"type":"string"},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."}},"required":["id","kind","title","status"],"additionalProperties":false}},"jobs":{"description":"Commands this conversation left running in the background, and how the most recent ones ended. Absent means none since the daemon started. A job is running exactly while it has no end.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this job, the one its transcript row names."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"session":{"type":"string","description":"The terminal session its pane runs in, which is what opening it focuses."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When the command exited, in milliseconds. Absent while it runs.","type":"number"},"exitCode":{"description":"The code it exited with. Absent while it runs, or when its exit left none.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"watch":{"description":"The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.","type":"string"},"handed":{"description":"Left running for the person: the agent kept it for them with the `keep` tool (or it holds a port this conversation already left them), so it outlives the turn, wakes nothing and is theirs to stop.","type":"boolean"},"ports":{"description":"The ports it was listening on when its turn ended.","type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"stoppedBy":{"description":"Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.","type":"string","enum":["turn","person","agent"]},"inputWait":{"description":"Present while it sits blocked reading its terminal with nothing moving: a prompt nobody in the sandbox will answer, which only stopping it or a person typing into its terminal ends. Absent while it runs on, and once it ends.","type":"object","properties":{"since":{"type":"number","description":"When it went still, in milliseconds."},"program":{"type":"string","description":"The process blocked reading the terminal, as its command line."}},"required":["since","program"],"additionalProperties":false}},"required":["id","label","session","startedAt"],"additionalProperties":false}},"archivedAt":{"description":"When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board.","type":"number"}},"required":["id","status","provider","harness","updatedAt","attention"],"additionalProperties":false}}}}}}},"/agents/{id}/stop-watching":{"post":{"operationId":"agents.stopWatching","summary":"Stop a conversation's condition watches","description":"Disarms this conversation's outside-condition watches, so they will not wake it. Named without a watch id it disarms all of them, because that is what the press means when it is made about a card; a press made about one watch's own row names that watch and leaves the rest armed. Nothing else about the conversation changes.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"watchId":{"description":"Which watch to disarm. Absent disarms every watch this conversation is parked on.","type":"string"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The conversation id, which is how every other call addresses it."},"sessionId":{"description":"The provider session behind the last turn. It is retired whenever the model or account changes.","type":"string"},"title":{"description":"What to call it: the first prompt cut to one line, unless somebody renamed it.","type":"string"},"titleAction":{"description":"One word for the kind of work the naming pass read in it (fix, audit, redesign). Never part of the displayed name: a board tints and glyphs a card by it. Absent for a title nothing named an action for.","type":"string"},"status":{"type":"string","enum":["idle","running","awaiting","stopping","dismissing","stopped","resuming","landing","ready","landed","conflict","error","interrupted"],"description":"What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; dismissing is the same window for a question waved away, which ends the turn too but owes the user nothing; resuming means the sandbox is already putting right whatever killed the turn; landing means its work is being carried into the workspace right now, and nothing may act on its branch until that settles."},"failure":{"description":"Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.","type":"string"},"failureCode":{"description":"Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.","type":"string"},"limitResetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant.","type":"number"},"limitHeld":{"description":"Whether the refused turn is held whole, so sending again re-runs it instead of appending to it.","type":"boolean"},"limitScheduled":{"description":"Whether the held turn is already booked to go again at the reset, so nobody has to press anything.","type":"boolean"},"limitMoving":{"description":"The account the held turn is being moved to by the owner's policy, while that move is booked.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it runs on."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop it runs on."},"runner":{"description":"The runner this conversation runs on. Absent means this sandbox.","type":"string"},"startIn":{"description":"Which folder it opened in, relative to the workspace root. Absent means the root.","type":"string"},"actsAs":{"description":"Which persona its first turn acted as. Absent for an ordinary chat.","type":"string"},"lastActsAs":{"description":"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.","type":"string"},"model":{"description":"What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked.","type":"string"},"effort":{"description":"How hard that turn was told to think.","type":"string"},"thinking":{"description":"Whether that turn showed its reasoning.","type":"boolean"},"fast":{"description":"Whether that turn asked for higher speed. What was asked for, not what was served.","type":"boolean"},"account":{"description":"Which connected account paid for it.","type":"string"},"branch":{"description":"The branch its private copy works on. Absent for a conversation that works directly in the shared tree.","type":"string"},"autoLand":{"description":"This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case.","type":"boolean"},"limitPolicy":{"type":"string","enum":["wait","resend","move"]},"outagePolicy":{"type":"string","enum":["wait","retry"]},"stopPolicy":{"type":"string","enum":["wait","retry"]},"landRequested":{"description":"A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting.","type":"object","properties":{"email":{"type":"string","description":"Who asked."},"name":{"description":"Their display name.","type":"string"},"at":{"type":"number","description":"When they asked, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"reactions":{"description":"What people have marked this conversation with, one entry per emoji, in the order the emoji were first used. Absent means nobody has marked it, which is most conversations.","type":"array","items":{"type":"object","properties":{"emoji":{"type":"string","description":"The mark itself, one emoji, carried as the character rather than as a name that would need a table on both sides."},"by":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Who it was, as the sandbox verified them."},"name":{"description":"What to call them, when their sign-in carried a name. Absent leaves the address to stand for them.","type":"string"},"at":{"type":"number","description":"When they marked it, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"description":"Everyone wearing this mark, oldest first, each by name. Carried whole rather than as a count, because a chip reading 3 that cannot say whose is a number nobody can answer. Never empty: the last person taking theirs back takes the whole chip with it."}},"required":["emoji","by"],"additionalProperties":false}},"origin":{"description":"Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"startedBy":{"description":"Who asked for the first turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a child another conversation spawned. Absent when nothing was verified (a wake, a loopback caller).","type":"string"},"areas":{"description":"Which named areas of the workspace this conversation was started within, latched from whoever asked for the first turn. Absent means its starter held the whole workspace, which is why a fenced member is not shown it.","type":"array","items":{"type":"string"}},"owner":{"description":"The member answerable for this conversation: set from whoever started it, inherited from the parent by a spawned child, moved by handing it over. Absent means nobody has claimed it yet.","type":"object","properties":{"email":{"type":"string","description":"Who answers for this conversation, as the sandbox verified them."},"name":{"description":"What to call them, when the sign-in that made them its owner carried a name. Absent leaves the address to stand for them.","type":"string"},"since":{"type":"number","description":"When they became its owner, in milliseconds."}},"required":["email","since"],"additionalProperties":false},"forkedFrom":{"description":"The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"type":"string","enum":["then","now"]}},"required":["conversationId","index","files"],"additionalProperties":false},"base":{"description":"The commit its private copy started from, shortened.","type":"string"},"costUsd":{"description":"What it has cost so far, in dollars. A subagent's spend is its own and is not folded in here.","type":"number"},"inputTokens":{"description":"Uncached input tokens, excluding cache reads and cache writes.","type":"number"},"outputTokens":{"description":"Tokens received.","type":"number"},"contextTokens":{"description":"How much of the window the conversation currently fills.","type":"number"},"contextWindow":{"description":"How large that window is.","type":"number"},"promptCache":{"description":"When this conversation's prompt cache was last kept alive and how long it lasts, which together say when picking the conversation up stops being cheap. Absent when the provider publishes nothing to ground it on.","type":"object","properties":{"at":{"type":"number","description":"When its last request touched the provider's prompt cache, in milliseconds."},"ttlMs":{"type":"number","description":"How long that entry lives from `at`, in milliseconds."},"rollsAt":{"description":"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.","type":"number"},"keepableUntil":{"description":"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.","type":"number"}},"required":["at","ttlMs"],"additionalProperties":false},"keepWarm":{"description":"Whether the sandbox is keeping this conversation's prompt cache warm while it sits idle, how that is going, or why it stopped. Absent when nobody asked for it.","type":"object","properties":{"since":{"type":"number","description":"When keeping it warm started, in milliseconds."},"until":{"type":"number","description":"When it stops by itself, in milliseconds: the time asked for, shortened to what the sandbox can honestly keep, which is never past the point where refreshing costs more than re-reading, nor past the date change that rewrites the prompt."},"refreshes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Refreshes sent so far."},"readTokens":{"description":"How much the last refresh read back from the provider's cache, in tokens: the proof the cache was still there.","type":"number"},"ended":{"description":"Why keeping it warm stopped before anyone picked the conversation up. Absent while it is still being kept.","type":"object","properties":{"at":{"type":"number","description":"When it stopped, in milliseconds."},"reason":{"type":"string","enum":["elapsed","allowance","changed","cold","failed"],"description":"Why: `elapsed` the time asked for ran out; `allowance` the account reached the reserve kept for real work, or the provider refused a refresh for its limit; `changed` what the next turn would send no longer matches the cache (a part of the prompt, the date in it, or the conversation's session or account); `cold` the cache was gone, expired before a refresh could run or found missing by one; `failed` a refresh failed."},"detail":{"description":"The specifics, when there are any: which parts of the prompt changed, how full the account was, or the failure's own words.","type":"string"}},"required":["at","reason"],"additionalProperties":false}},"required":["since","until","refreshes"],"additionalProperties":false},"activity":{"description":"What it is doing at this moment.","type":"object","properties":{"tool":{"description":"The last tool it reached for.","type":"string"},"target":{"description":"What it reached for that tool with: a file, a command, a URL.","type":"string"},"todo":{"description":"The item on its own list that it is working through.","type":"string"}},"additionalProperties":false},"checklist":{"description":"How far it is through its own checklist. Absent for a conversation that kept no list, which is most short ones.","type":"object","properties":{"done":{"type":"number","description":"Items it has completed."},"total":{"type":"number","description":"Items on the list. Never zero: a conversation that kept no list carries no clause at all."}},"required":["done","total"],"additionalProperties":false},"landedMessageDraft":{"description":"The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes.","type":"object","properties":{"startedAt":{"type":"number","description":"When the drafting began, in milliseconds."},"steps":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider was asked."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"status":{"type":"string","enum":["asking","answered","refused","skipped"],"description":"How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."},"at":{"description":"When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time.","type":"number"},"ms":{"description":"How long it took. Absent while it is still being asked.","type":"number"},"reason":{"description":"Why it refused, in its own words.","type":"string"}},"required":["provider","model","status"],"additionalProperties":false},"description":"Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."},"outcome":{"description":"How it ended. Absent means it is still going.","type":"string","enum":["written","failed"]},"reason":{"description":"The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words.","type":"string"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"}},"required":["startedAt","steps"],"additionalProperties":false},"landedMessage":{"description":"What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false},"startedAt":{"description":"When the running turn started, in milliseconds. Absent when none is running.","type":"number"},"run":{"description":"The run under way right now: what a stop names, so it cannot cancel a turn that started after it was pressed. Absent when none is running, and for a turn with no run to attach to.","type":"string"},"queue":{"description":"Messages waiting for its next turn, and whether they are held. Absent for a conversation nothing has ever waited for.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false},"updatedAt":{"type":"number","description":"When it last did something, in milliseconds. Reading it does not count."},"seenAt":{"description":"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.","type":"number"},"unsentAt":{"description":"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.","type":"number"},"attention":{"type":"object","properties":{"plan":{"type":"boolean","description":"It has proposed a plan and is waiting for a yes."},"question":{"type":"boolean","description":"It has asked you something."},"permission":{"type":"boolean","description":"It wants to use a tool it needs permission for."},"capability":{"type":"boolean","description":"It needs something connected that is not connected yet."},"credential":{"type":"boolean","description":"It is waiting for a named person to release a credential. The one pause that may not be yours to clear, whatever your role."},"conflict":{"type":"boolean","description":"Its work cannot be merged without somebody resolving a clash."},"need":{"description":"It asked a person for something it still needs: a connection, a secret, wider reach, a tool. Absent from a daemon older than needs.","type":"boolean"}},"required":["plan","question","permission","capability","credential","conflict"],"additionalProperties":false,"description":"Which kinds of waiting-for-you it is doing."},"permissionAsk":{"description":"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.","type":"object","properties":{"requestId":{"type":"string","description":"Which request this is: the id an answer to it names."},"ask":{"type":"string","description":"What it asks to do, on one line: the runtime's own sentence, else the tool's short name."}},"required":["requestId","ask"],"additionalProperties":false},"landFailure":{"description":"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.","type":"object","properties":{"reason":{"type":"string","description":"Why it failed, in the words it failed with."},"code":{"description":"Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise.","type":"string"},"at":{"type":"number","description":"When it failed, in milliseconds."}},"required":["reason","at"],"additionalProperties":false},"delivery":{"description":"What became of its last land in the folder on the owner's computer, for a project folder attached to this computer's sandbox. Absent for any other land, and from a sandbox older than it.","type":"object","properties":{"state":{"type":"string","enum":["delivered","partial","waiting","failed","too-large"]},"at":{"type":"number","description":"When this state was reached, in milliseconds."},"project":{"type":"string"},"folder":{"description":"The folder on the owner's computer, as its machine reported it.","type":"string"},"applied":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files written, merged ones included."},"conflicts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"reason":{"type":"string","enum":["edited","link","outside","missing-git"]}},"required":["path","reason"],"additionalProperties":false}},"point":{"description":"The machine's restore point taken before writing.","type":"string"},"reason":{"description":"Why it failed or waits, in a sentence.","type":"string"}},"required":["state","at","project","applied","conflicts"],"additionalProperties":false},"conflictCauses":{"description":"Why its work will not merge, and so who can clear it: your own uncommitted edits, which only you can commit or stash, against a moved main line or an unmergeable binary, which the conversation can redo on its own copy. Absent unless it is refusing to merge.","type":"array","items":{"type":"string","enum":["workspace","diverged","binary"]}},"unfinished":{"description":"What its last turn left open: steps it never completed. Absent for a turn that finished what it started.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"steps":{"description":"The agent's own checklist where that turn left it. Absent for a conversation that kept no list.","type":"object","properties":{"open":{"type":"number","description":"Items on it that were never completed."},"total":{"type":"number","description":"Items on the whole list."},"next":{"description":"The one it would have done next: what it was working through, or the first still waiting.","type":"string"}},"required":["open","total"],"additionalProperties":false},"check":{"description":"The end-of-turn check that was still failing when the turn ended, by name. No longer written: what a check finds as a turn ends is said back to the model and never leaves its work unfinished.","type":"string"}},"required":["at"],"additionalProperties":false},"proof":{"description":"What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"verification":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to."},"check":{"description":"The command that spoke, when one did, so a targeted test is never read as the whole suite.","type":"string"},"unviewed":{"description":"How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none.","type":"number"}},"required":["at","verification"],"additionalProperties":false},"reach":{"description":"Where its last turn's work went when it went somewhere besides its own branch: files changed live outside it, clones of its own holding work that will not land, pushes it made itself. Absent when everything it did is on its branch.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"live":{"description":"Files it changed in place where every conversation reads them, outside its own branch: an installed extension's copy, the sandbox's shared state, the owner's own checkout. Live the moment they were written, never reviewed, and an installed extension's next update replaces them.","maxItems":10,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What it wrote, as it named it: relative to the workspace for the sandbox's shared state, absolute for the owner's own checkout."},"extension":{"description":"The extension, by its manifest id, when what it changed is the installed copy of one. Absent for anything else.","type":"string"}},"required":["path"],"additionalProperties":false}},"liveMore":{"description":"How many more such files there were beyond the ones listed. Absent when all are listed.","type":"number"},"stranded":{"description":"Repositories it cloned inside its own copy and left work in. A clone of its own never rides its land, so what is in one stays there.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"The clone, as a folder relative to its own copy of the workspace."},"uncommitted":{"type":"number","description":"Files changed in it and never committed."},"unpushed":{"type":"number","description":"Commits in it its upstream does not have, or that no remote has when it follows none."}},"required":["dir","uncommitted","unpushed"],"additionalProperties":false}},"strandedMore":{"description":"How many more such clones there were beyond the ones listed. Absent when all are listed.","type":"number"},"published":{"description":"Pushes it made with git that went through: work it sent out of the sandbox itself, never through a land.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"description":"The folder it pushed from, as the command named it. Absent for its own working folder.","type":"string"},"remote":{"description":"The remote it pushed to, as the command named it. Absent when the command named none.","type":"string"},"branch":{"description":"The branch it pushed to, as the command named it. Absent when the command named none.","type":"string"},"command":{"type":"string","description":"The push itself, trimmed to one line."}},"required":["command"],"additionalProperties":false}},"publishedMore":{"description":"How many more such pushes there were beyond the ones listed. Absent when all are listed.","type":"number"}},"required":["at"],"additionalProperties":false},"turns":{"description":"Turns it has finished.","type":"number"},"toolUses":{"description":"Tools it has used, over its whole life.","type":"number"},"subagents":{"description":"Subagents this one started, in-process and spawned alike. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost.","type":"object","properties":{"running":{"type":"number","description":"Subagents working right now."},"total":{"type":"number","description":"Subagents it has started over its whole life."}},"required":["running","total"],"additionalProperties":false},"diff":{"description":"Everything it has written, measured from where it started. Independent of how much has been merged.","type":"object","properties":{"files":{"type":"number","description":"Files touched."},"insertions":{"type":"number","description":"Lines added."},"deletions":{"type":"number","description":"Lines removed."}},"required":["files","insertions","deletions"],"additionalProperties":false},"landedPresence":{"description":"Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it.","type":"object","properties":{"landed":{"type":"number","description":"Paths this conversation merged in."},"present":{"type":"number","description":"How many of them are still there, either pending or committed."},"removedBy":{"description":"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"id":{"type":"string","description":"The conversation that took them out."},"title":{"description":"What that conversation is called.","type":"string"}},"required":["kind","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"description":"Who it was, as the sandbox verified them. Absent when the request carried no identity.","type":"string"},"name":{"description":"What to call them, when their sign-in carried a name.","type":"string"}},"required":["kind"],"additionalProperties":false}]}},"required":["landed","present"],"additionalProperties":false},"loop":{"description":"The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them.","type":"object","properties":{"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How the loop is going."},"iteration":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which round it is on."},"maxIterations":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many rounds it will attempt before giving up."},"goal":{"type":"string","description":"What it is looping towards."}},"required":["state","iteration","maxIterations","goal"],"additionalProperties":false},"workflow":{"description":"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.","type":"object","properties":{"runId":{"type":"string","description":"The run this belongs to, which is how a board groups its steps together."},"name":{"type":"string","description":"The workflow's name."},"step":{"type":"string","description":"Which step this conversation is on now. It moves when steps are chained."},"index":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"This step's place in the workflow, counting from one."},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many steps the workflow has."}},"required":["runId","name","step","index","total"],"additionalProperties":false},"awaitingWake":{"description":"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.","type":"boolean"},"watches":{"description":"Outside conditions this conversation is parked on, each of which will wake it. Absent means none, which is nearly every conversation: an armed watch is why a finished-looking agent starts working by itself, and why a hosted machine will not go idle.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this watch, the same one the agent was given when it armed it."},"note":{"type":"string","description":"The agent's own line on what it is waiting for."},"intervalSeconds":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How often the check runs."},"deadlineAt":{"type":"number","description":"When it gives up and wakes the conversation anyway, in milliseconds. Every watch has one."}},"required":["id","note","intervalSeconds","deadlineAt"],"additionalProperties":false}},"needs":{"description":"What it is waiting on people for and has not got yet, oldest first. Absent means nothing: an open need is why an idle-looking agent still needs you.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["capability","secret","grant","release","environment"],"description":"What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image."},"title":{"type":"string"},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."}},"required":["id","kind","title","status"],"additionalProperties":false}},"jobs":{"description":"Commands this conversation left running in the background, and how the most recent ones ended. Absent means none since the daemon started. A job is running exactly while it has no end.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this job, the one its transcript row names."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"session":{"type":"string","description":"The terminal session its pane runs in, which is what opening it focuses."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When the command exited, in milliseconds. Absent while it runs.","type":"number"},"exitCode":{"description":"The code it exited with. Absent while it runs, or when its exit left none.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"watch":{"description":"The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.","type":"string"},"handed":{"description":"Left running for the person: the agent kept it for them with the `keep` tool (or it holds a port this conversation already left them), so it outlives the turn, wakes nothing and is theirs to stop.","type":"boolean"},"ports":{"description":"The ports it was listening on when its turn ended.","type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"stoppedBy":{"description":"Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.","type":"string","enum":["turn","person","agent"]},"inputWait":{"description":"Present while it sits blocked reading its terminal with nothing moving: a prompt nobody in the sandbox will answer, which only stopping it or a person typing into its terminal ends. Absent while it runs on, and once it ends.","type":"object","properties":{"since":{"type":"number","description":"When it went still, in milliseconds."},"program":{"type":"string","description":"The process blocked reading the terminal, as its command line."}},"required":["since","program"],"additionalProperties":false}},"required":["id","label","session","startedAt"],"additionalProperties":false}},"archivedAt":{"description":"When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board.","type":"number"}},"required":["id","status","provider","harness","updatedAt","attention"],"additionalProperties":false}}}}}}},"/agents/{id}/stop-job":{"post":{"operationId":"agents.stopJob","summary":"Stop one of a conversation's background jobs","description":"Ends a command this conversation left running: the server it handed over, or the build it is waiting on. The watch that would have woken the conversation on its exit is disarmed first, so stopping it wakes nothing. Stopping a job that already ended is not an error.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"jobId":{"type":"string","minLength":1,"description":"Which of its background jobs, by the id its card and transcript row carry."}},"required":["jobId"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The conversation id, which is how every other call addresses it."},"sessionId":{"description":"The provider session behind the last turn. It is retired whenever the model or account changes.","type":"string"},"title":{"description":"What to call it: the first prompt cut to one line, unless somebody renamed it.","type":"string"},"titleAction":{"description":"One word for the kind of work the naming pass read in it (fix, audit, redesign). Never part of the displayed name: a board tints and glyphs a card by it. Absent for a title nothing named an action for.","type":"string"},"status":{"type":"string","enum":["idle","running","awaiting","stopping","dismissing","stopped","resuming","landing","ready","landed","conflict","error","interrupted"],"description":"What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; dismissing is the same window for a question waved away, which ends the turn too but owes the user nothing; resuming means the sandbox is already putting right whatever killed the turn; landing means its work is being carried into the workspace right now, and nothing may act on its branch until that settles."},"failure":{"description":"Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.","type":"string"},"failureCode":{"description":"Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.","type":"string"},"limitResetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant.","type":"number"},"limitHeld":{"description":"Whether the refused turn is held whole, so sending again re-runs it instead of appending to it.","type":"boolean"},"limitScheduled":{"description":"Whether the held turn is already booked to go again at the reset, so nobody has to press anything.","type":"boolean"},"limitMoving":{"description":"The account the held turn is being moved to by the owner's policy, while that move is booked.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it runs on."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop it runs on."},"runner":{"description":"The runner this conversation runs on. Absent means this sandbox.","type":"string"},"startIn":{"description":"Which folder it opened in, relative to the workspace root. Absent means the root.","type":"string"},"actsAs":{"description":"Which persona its first turn acted as. Absent for an ordinary chat.","type":"string"},"lastActsAs":{"description":"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.","type":"string"},"model":{"description":"What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked.","type":"string"},"effort":{"description":"How hard that turn was told to think.","type":"string"},"thinking":{"description":"Whether that turn showed its reasoning.","type":"boolean"},"fast":{"description":"Whether that turn asked for higher speed. What was asked for, not what was served.","type":"boolean"},"account":{"description":"Which connected account paid for it.","type":"string"},"branch":{"description":"The branch its private copy works on. Absent for a conversation that works directly in the shared tree.","type":"string"},"autoLand":{"description":"This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case.","type":"boolean"},"limitPolicy":{"type":"string","enum":["wait","resend","move"]},"outagePolicy":{"type":"string","enum":["wait","retry"]},"stopPolicy":{"type":"string","enum":["wait","retry"]},"landRequested":{"description":"A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting.","type":"object","properties":{"email":{"type":"string","description":"Who asked."},"name":{"description":"Their display name.","type":"string"},"at":{"type":"number","description":"When they asked, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"reactions":{"description":"What people have marked this conversation with, one entry per emoji, in the order the emoji were first used. Absent means nobody has marked it, which is most conversations.","type":"array","items":{"type":"object","properties":{"emoji":{"type":"string","description":"The mark itself, one emoji, carried as the character rather than as a name that would need a table on both sides."},"by":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Who it was, as the sandbox verified them."},"name":{"description":"What to call them, when their sign-in carried a name. Absent leaves the address to stand for them.","type":"string"},"at":{"type":"number","description":"When they marked it, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"description":"Everyone wearing this mark, oldest first, each by name. Carried whole rather than as a count, because a chip reading 3 that cannot say whose is a number nobody can answer. Never empty: the last person taking theirs back takes the whole chip with it."}},"required":["emoji","by"],"additionalProperties":false}},"origin":{"description":"Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"startedBy":{"description":"Who asked for the first turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a child another conversation spawned. Absent when nothing was verified (a wake, a loopback caller).","type":"string"},"areas":{"description":"Which named areas of the workspace this conversation was started within, latched from whoever asked for the first turn. Absent means its starter held the whole workspace, which is why a fenced member is not shown it.","type":"array","items":{"type":"string"}},"owner":{"description":"The member answerable for this conversation: set from whoever started it, inherited from the parent by a spawned child, moved by handing it over. Absent means nobody has claimed it yet.","type":"object","properties":{"email":{"type":"string","description":"Who answers for this conversation, as the sandbox verified them."},"name":{"description":"What to call them, when the sign-in that made them its owner carried a name. Absent leaves the address to stand for them.","type":"string"},"since":{"type":"number","description":"When they became its owner, in milliseconds."}},"required":["email","since"],"additionalProperties":false},"forkedFrom":{"description":"The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"type":"string","enum":["then","now"]}},"required":["conversationId","index","files"],"additionalProperties":false},"base":{"description":"The commit its private copy started from, shortened.","type":"string"},"costUsd":{"description":"What it has cost so far, in dollars. A subagent's spend is its own and is not folded in here.","type":"number"},"inputTokens":{"description":"Uncached input tokens, excluding cache reads and cache writes.","type":"number"},"outputTokens":{"description":"Tokens received.","type":"number"},"contextTokens":{"description":"How much of the window the conversation currently fills.","type":"number"},"contextWindow":{"description":"How large that window is.","type":"number"},"promptCache":{"description":"When this conversation's prompt cache was last kept alive and how long it lasts, which together say when picking the conversation up stops being cheap. Absent when the provider publishes nothing to ground it on.","type":"object","properties":{"at":{"type":"number","description":"When its last request touched the provider's prompt cache, in milliseconds."},"ttlMs":{"type":"number","description":"How long that entry lives from `at`, in milliseconds."},"rollsAt":{"description":"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.","type":"number"},"keepableUntil":{"description":"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.","type":"number"}},"required":["at","ttlMs"],"additionalProperties":false},"keepWarm":{"description":"Whether the sandbox is keeping this conversation's prompt cache warm while it sits idle, how that is going, or why it stopped. Absent when nobody asked for it.","type":"object","properties":{"since":{"type":"number","description":"When keeping it warm started, in milliseconds."},"until":{"type":"number","description":"When it stops by itself, in milliseconds: the time asked for, shortened to what the sandbox can honestly keep, which is never past the point where refreshing costs more than re-reading, nor past the date change that rewrites the prompt."},"refreshes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Refreshes sent so far."},"readTokens":{"description":"How much the last refresh read back from the provider's cache, in tokens: the proof the cache was still there.","type":"number"},"ended":{"description":"Why keeping it warm stopped before anyone picked the conversation up. Absent while it is still being kept.","type":"object","properties":{"at":{"type":"number","description":"When it stopped, in milliseconds."},"reason":{"type":"string","enum":["elapsed","allowance","changed","cold","failed"],"description":"Why: `elapsed` the time asked for ran out; `allowance` the account reached the reserve kept for real work, or the provider refused a refresh for its limit; `changed` what the next turn would send no longer matches the cache (a part of the prompt, the date in it, or the conversation's session or account); `cold` the cache was gone, expired before a refresh could run or found missing by one; `failed` a refresh failed."},"detail":{"description":"The specifics, when there are any: which parts of the prompt changed, how full the account was, or the failure's own words.","type":"string"}},"required":["at","reason"],"additionalProperties":false}},"required":["since","until","refreshes"],"additionalProperties":false},"activity":{"description":"What it is doing at this moment.","type":"object","properties":{"tool":{"description":"The last tool it reached for.","type":"string"},"target":{"description":"What it reached for that tool with: a file, a command, a URL.","type":"string"},"todo":{"description":"The item on its own list that it is working through.","type":"string"}},"additionalProperties":false},"checklist":{"description":"How far it is through its own checklist. Absent for a conversation that kept no list, which is most short ones.","type":"object","properties":{"done":{"type":"number","description":"Items it has completed."},"total":{"type":"number","description":"Items on the list. Never zero: a conversation that kept no list carries no clause at all."}},"required":["done","total"],"additionalProperties":false},"landedMessageDraft":{"description":"The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes.","type":"object","properties":{"startedAt":{"type":"number","description":"When the drafting began, in milliseconds."},"steps":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider was asked."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"status":{"type":"string","enum":["asking","answered","refused","skipped"],"description":"How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."},"at":{"description":"When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time.","type":"number"},"ms":{"description":"How long it took. Absent while it is still being asked.","type":"number"},"reason":{"description":"Why it refused, in its own words.","type":"string"}},"required":["provider","model","status"],"additionalProperties":false},"description":"Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."},"outcome":{"description":"How it ended. Absent means it is still going.","type":"string","enum":["written","failed"]},"reason":{"description":"The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words.","type":"string"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"}},"required":["startedAt","steps"],"additionalProperties":false},"landedMessage":{"description":"What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false},"startedAt":{"description":"When the running turn started, in milliseconds. Absent when none is running.","type":"number"},"run":{"description":"The run under way right now: what a stop names, so it cannot cancel a turn that started after it was pressed. Absent when none is running, and for a turn with no run to attach to.","type":"string"},"queue":{"description":"Messages waiting for its next turn, and whether they are held. Absent for a conversation nothing has ever waited for.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false},"updatedAt":{"type":"number","description":"When it last did something, in milliseconds. Reading it does not count."},"seenAt":{"description":"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.","type":"number"},"unsentAt":{"description":"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.","type":"number"},"attention":{"type":"object","properties":{"plan":{"type":"boolean","description":"It has proposed a plan and is waiting for a yes."},"question":{"type":"boolean","description":"It has asked you something."},"permission":{"type":"boolean","description":"It wants to use a tool it needs permission for."},"capability":{"type":"boolean","description":"It needs something connected that is not connected yet."},"credential":{"type":"boolean","description":"It is waiting for a named person to release a credential. The one pause that may not be yours to clear, whatever your role."},"conflict":{"type":"boolean","description":"Its work cannot be merged without somebody resolving a clash."},"need":{"description":"It asked a person for something it still needs: a connection, a secret, wider reach, a tool. Absent from a daemon older than needs.","type":"boolean"}},"required":["plan","question","permission","capability","credential","conflict"],"additionalProperties":false,"description":"Which kinds of waiting-for-you it is doing."},"permissionAsk":{"description":"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.","type":"object","properties":{"requestId":{"type":"string","description":"Which request this is: the id an answer to it names."},"ask":{"type":"string","description":"What it asks to do, on one line: the runtime's own sentence, else the tool's short name."}},"required":["requestId","ask"],"additionalProperties":false},"landFailure":{"description":"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.","type":"object","properties":{"reason":{"type":"string","description":"Why it failed, in the words it failed with."},"code":{"description":"Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise.","type":"string"},"at":{"type":"number","description":"When it failed, in milliseconds."}},"required":["reason","at"],"additionalProperties":false},"delivery":{"description":"What became of its last land in the folder on the owner's computer, for a project folder attached to this computer's sandbox. Absent for any other land, and from a sandbox older than it.","type":"object","properties":{"state":{"type":"string","enum":["delivered","partial","waiting","failed","too-large"]},"at":{"type":"number","description":"When this state was reached, in milliseconds."},"project":{"type":"string"},"folder":{"description":"The folder on the owner's computer, as its machine reported it.","type":"string"},"applied":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files written, merged ones included."},"conflicts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"reason":{"type":"string","enum":["edited","link","outside","missing-git"]}},"required":["path","reason"],"additionalProperties":false}},"point":{"description":"The machine's restore point taken before writing.","type":"string"},"reason":{"description":"Why it failed or waits, in a sentence.","type":"string"}},"required":["state","at","project","applied","conflicts"],"additionalProperties":false},"conflictCauses":{"description":"Why its work will not merge, and so who can clear it: your own uncommitted edits, which only you can commit or stash, against a moved main line or an unmergeable binary, which the conversation can redo on its own copy. Absent unless it is refusing to merge.","type":"array","items":{"type":"string","enum":["workspace","diverged","binary"]}},"unfinished":{"description":"What its last turn left open: steps it never completed. Absent for a turn that finished what it started.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"steps":{"description":"The agent's own checklist where that turn left it. Absent for a conversation that kept no list.","type":"object","properties":{"open":{"type":"number","description":"Items on it that were never completed."},"total":{"type":"number","description":"Items on the whole list."},"next":{"description":"The one it would have done next: what it was working through, or the first still waiting.","type":"string"}},"required":["open","total"],"additionalProperties":false},"check":{"description":"The end-of-turn check that was still failing when the turn ended, by name. No longer written: what a check finds as a turn ends is said back to the model and never leaves its work unfinished.","type":"string"}},"required":["at"],"additionalProperties":false},"proof":{"description":"What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"verification":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to."},"check":{"description":"The command that spoke, when one did, so a targeted test is never read as the whole suite.","type":"string"},"unviewed":{"description":"How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none.","type":"number"}},"required":["at","verification"],"additionalProperties":false},"reach":{"description":"Where its last turn's work went when it went somewhere besides its own branch: files changed live outside it, clones of its own holding work that will not land, pushes it made itself. Absent when everything it did is on its branch.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"live":{"description":"Files it changed in place where every conversation reads them, outside its own branch: an installed extension's copy, the sandbox's shared state, the owner's own checkout. Live the moment they were written, never reviewed, and an installed extension's next update replaces them.","maxItems":10,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What it wrote, as it named it: relative to the workspace for the sandbox's shared state, absolute for the owner's own checkout."},"extension":{"description":"The extension, by its manifest id, when what it changed is the installed copy of one. Absent for anything else.","type":"string"}},"required":["path"],"additionalProperties":false}},"liveMore":{"description":"How many more such files there were beyond the ones listed. Absent when all are listed.","type":"number"},"stranded":{"description":"Repositories it cloned inside its own copy and left work in. A clone of its own never rides its land, so what is in one stays there.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"The clone, as a folder relative to its own copy of the workspace."},"uncommitted":{"type":"number","description":"Files changed in it and never committed."},"unpushed":{"type":"number","description":"Commits in it its upstream does not have, or that no remote has when it follows none."}},"required":["dir","uncommitted","unpushed"],"additionalProperties":false}},"strandedMore":{"description":"How many more such clones there were beyond the ones listed. Absent when all are listed.","type":"number"},"published":{"description":"Pushes it made with git that went through: work it sent out of the sandbox itself, never through a land.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"description":"The folder it pushed from, as the command named it. Absent for its own working folder.","type":"string"},"remote":{"description":"The remote it pushed to, as the command named it. Absent when the command named none.","type":"string"},"branch":{"description":"The branch it pushed to, as the command named it. Absent when the command named none.","type":"string"},"command":{"type":"string","description":"The push itself, trimmed to one line."}},"required":["command"],"additionalProperties":false}},"publishedMore":{"description":"How many more such pushes there were beyond the ones listed. Absent when all are listed.","type":"number"}},"required":["at"],"additionalProperties":false},"turns":{"description":"Turns it has finished.","type":"number"},"toolUses":{"description":"Tools it has used, over its whole life.","type":"number"},"subagents":{"description":"Subagents this one started, in-process and spawned alike. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost.","type":"object","properties":{"running":{"type":"number","description":"Subagents working right now."},"total":{"type":"number","description":"Subagents it has started over its whole life."}},"required":["running","total"],"additionalProperties":false},"diff":{"description":"Everything it has written, measured from where it started. Independent of how much has been merged.","type":"object","properties":{"files":{"type":"number","description":"Files touched."},"insertions":{"type":"number","description":"Lines added."},"deletions":{"type":"number","description":"Lines removed."}},"required":["files","insertions","deletions"],"additionalProperties":false},"landedPresence":{"description":"Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it.","type":"object","properties":{"landed":{"type":"number","description":"Paths this conversation merged in."},"present":{"type":"number","description":"How many of them are still there, either pending or committed."},"removedBy":{"description":"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"id":{"type":"string","description":"The conversation that took them out."},"title":{"description":"What that conversation is called.","type":"string"}},"required":["kind","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"description":"Who it was, as the sandbox verified them. Absent when the request carried no identity.","type":"string"},"name":{"description":"What to call them, when their sign-in carried a name.","type":"string"}},"required":["kind"],"additionalProperties":false}]}},"required":["landed","present"],"additionalProperties":false},"loop":{"description":"The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them.","type":"object","properties":{"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How the loop is going."},"iteration":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which round it is on."},"maxIterations":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many rounds it will attempt before giving up."},"goal":{"type":"string","description":"What it is looping towards."}},"required":["state","iteration","maxIterations","goal"],"additionalProperties":false},"workflow":{"description":"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.","type":"object","properties":{"runId":{"type":"string","description":"The run this belongs to, which is how a board groups its steps together."},"name":{"type":"string","description":"The workflow's name."},"step":{"type":"string","description":"Which step this conversation is on now. It moves when steps are chained."},"index":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"This step's place in the workflow, counting from one."},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many steps the workflow has."}},"required":["runId","name","step","index","total"],"additionalProperties":false},"awaitingWake":{"description":"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.","type":"boolean"},"watches":{"description":"Outside conditions this conversation is parked on, each of which will wake it. Absent means none, which is nearly every conversation: an armed watch is why a finished-looking agent starts working by itself, and why a hosted machine will not go idle.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this watch, the same one the agent was given when it armed it."},"note":{"type":"string","description":"The agent's own line on what it is waiting for."},"intervalSeconds":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How often the check runs."},"deadlineAt":{"type":"number","description":"When it gives up and wakes the conversation anyway, in milliseconds. Every watch has one."}},"required":["id","note","intervalSeconds","deadlineAt"],"additionalProperties":false}},"needs":{"description":"What it is waiting on people for and has not got yet, oldest first. Absent means nothing: an open need is why an idle-looking agent still needs you.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["capability","secret","grant","release","environment"],"description":"What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image."},"title":{"type":"string"},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."}},"required":["id","kind","title","status"],"additionalProperties":false}},"jobs":{"description":"Commands this conversation left running in the background, and how the most recent ones ended. Absent means none since the daemon started. A job is running exactly while it has no end.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this job, the one its transcript row names."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"session":{"type":"string","description":"The terminal session its pane runs in, which is what opening it focuses."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When the command exited, in milliseconds. Absent while it runs.","type":"number"},"exitCode":{"description":"The code it exited with. Absent while it runs, or when its exit left none.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"watch":{"description":"The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.","type":"string"},"handed":{"description":"Left running for the person: the agent kept it for them with the `keep` tool (or it holds a port this conversation already left them), so it outlives the turn, wakes nothing and is theirs to stop.","type":"boolean"},"ports":{"description":"The ports it was listening on when its turn ended.","type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"stoppedBy":{"description":"Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.","type":"string","enum":["turn","person","agent"]},"inputWait":{"description":"Present while it sits blocked reading its terminal with nothing moving: a prompt nobody in the sandbox will answer, which only stopping it or a person typing into its terminal ends. Absent while it runs on, and once it ends.","type":"object","properties":{"since":{"type":"number","description":"When it went still, in milliseconds."},"program":{"type":"string","description":"The process blocked reading the terminal, as its command line."}},"required":["since","program"],"additionalProperties":false}},"required":["id","label","session","startedAt"],"additionalProperties":false}},"archivedAt":{"description":"When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board.","type":"number"}},"required":["id","status","provider","harness","updatedAt","attention"],"additionalProperties":false}}}}}}},"/agents/seen":{"post":{"operationId":"agents.seenAll","summary":"Mark every conversation read","description":"Clears the unread badge across the whole fleet at once, and hands the refreshed list back.","tags":["The fleet"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"agents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The conversation id, which is how every other call addresses it."},"sessionId":{"description":"The provider session behind the last turn. It is retired whenever the model or account changes.","type":"string"},"title":{"description":"What to call it: the first prompt cut to one line, unless somebody renamed it.","type":"string"},"titleAction":{"description":"One word for the kind of work the naming pass read in it (fix, audit, redesign). Never part of the displayed name: a board tints and glyphs a card by it. Absent for a title nothing named an action for.","type":"string"},"status":{"type":"string","enum":["idle","running","awaiting","stopping","dismissing","stopped","resuming","landing","ready","landed","conflict","error","interrupted"],"description":"What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; dismissing is the same window for a question waved away, which ends the turn too but owes the user nothing; resuming means the sandbox is already putting right whatever killed the turn; landing means its work is being carried into the workspace right now, and nothing may act on its branch until that settles."},"failure":{"description":"Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.","type":"string"},"failureCode":{"description":"Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.","type":"string"},"limitResetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant.","type":"number"},"limitHeld":{"description":"Whether the refused turn is held whole, so sending again re-runs it instead of appending to it.","type":"boolean"},"limitScheduled":{"description":"Whether the held turn is already booked to go again at the reset, so nobody has to press anything.","type":"boolean"},"limitMoving":{"description":"The account the held turn is being moved to by the owner's policy, while that move is booked.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it runs on."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop it runs on."},"runner":{"description":"The runner this conversation runs on. Absent means this sandbox.","type":"string"},"startIn":{"description":"Which folder it opened in, relative to the workspace root. Absent means the root.","type":"string"},"actsAs":{"description":"Which persona its first turn acted as. Absent for an ordinary chat.","type":"string"},"lastActsAs":{"description":"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.","type":"string"},"model":{"description":"What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked.","type":"string"},"effort":{"description":"How hard that turn was told to think.","type":"string"},"thinking":{"description":"Whether that turn showed its reasoning.","type":"boolean"},"fast":{"description":"Whether that turn asked for higher speed. What was asked for, not what was served.","type":"boolean"},"account":{"description":"Which connected account paid for it.","type":"string"},"branch":{"description":"The branch its private copy works on. Absent for a conversation that works directly in the shared tree.","type":"string"},"autoLand":{"description":"This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case.","type":"boolean"},"limitPolicy":{"type":"string","enum":["wait","resend","move"]},"outagePolicy":{"type":"string","enum":["wait","retry"]},"stopPolicy":{"type":"string","enum":["wait","retry"]},"landRequested":{"description":"A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting.","type":"object","properties":{"email":{"type":"string","description":"Who asked."},"name":{"description":"Their display name.","type":"string"},"at":{"type":"number","description":"When they asked, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"reactions":{"description":"What people have marked this conversation with, one entry per emoji, in the order the emoji were first used. Absent means nobody has marked it, which is most conversations.","type":"array","items":{"type":"object","properties":{"emoji":{"type":"string","description":"The mark itself, one emoji, carried as the character rather than as a name that would need a table on both sides."},"by":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Who it was, as the sandbox verified them."},"name":{"description":"What to call them, when their sign-in carried a name. Absent leaves the address to stand for them.","type":"string"},"at":{"type":"number","description":"When they marked it, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"description":"Everyone wearing this mark, oldest first, each by name. Carried whole rather than as a count, because a chip reading 3 that cannot say whose is a number nobody can answer. Never empty: the last person taking theirs back takes the whole chip with it."}},"required":["emoji","by"],"additionalProperties":false}},"origin":{"description":"Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"startedBy":{"description":"Who asked for the first turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a child another conversation spawned. Absent when nothing was verified (a wake, a loopback caller).","type":"string"},"areas":{"description":"Which named areas of the workspace this conversation was started within, latched from whoever asked for the first turn. Absent means its starter held the whole workspace, which is why a fenced member is not shown it.","type":"array","items":{"type":"string"}},"owner":{"description":"The member answerable for this conversation: set from whoever started it, inherited from the parent by a spawned child, moved by handing it over. Absent means nobody has claimed it yet.","type":"object","properties":{"email":{"type":"string","description":"Who answers for this conversation, as the sandbox verified them."},"name":{"description":"What to call them, when the sign-in that made them its owner carried a name. Absent leaves the address to stand for them.","type":"string"},"since":{"type":"number","description":"When they became its owner, in milliseconds."}},"required":["email","since"],"additionalProperties":false},"forkedFrom":{"description":"The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"type":"string","enum":["then","now"]}},"required":["conversationId","index","files"],"additionalProperties":false},"base":{"description":"The commit its private copy started from, shortened.","type":"string"},"costUsd":{"description":"What it has cost so far, in dollars. A subagent's spend is its own and is not folded in here.","type":"number"},"inputTokens":{"description":"Uncached input tokens, excluding cache reads and cache writes.","type":"number"},"outputTokens":{"description":"Tokens received.","type":"number"},"contextTokens":{"description":"How much of the window the conversation currently fills.","type":"number"},"contextWindow":{"description":"How large that window is.","type":"number"},"promptCache":{"description":"When this conversation's prompt cache was last kept alive and how long it lasts, which together say when picking the conversation up stops being cheap. Absent when the provider publishes nothing to ground it on.","type":"object","properties":{"at":{"type":"number","description":"When its last request touched the provider's prompt cache, in milliseconds."},"ttlMs":{"type":"number","description":"How long that entry lives from `at`, in milliseconds."},"rollsAt":{"description":"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.","type":"number"},"keepableUntil":{"description":"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.","type":"number"}},"required":["at","ttlMs"],"additionalProperties":false},"keepWarm":{"description":"Whether the sandbox is keeping this conversation's prompt cache warm while it sits idle, how that is going, or why it stopped. Absent when nobody asked for it.","type":"object","properties":{"since":{"type":"number","description":"When keeping it warm started, in milliseconds."},"until":{"type":"number","description":"When it stops by itself, in milliseconds: the time asked for, shortened to what the sandbox can honestly keep, which is never past the point where refreshing costs more than re-reading, nor past the date change that rewrites the prompt."},"refreshes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Refreshes sent so far."},"readTokens":{"description":"How much the last refresh read back from the provider's cache, in tokens: the proof the cache was still there.","type":"number"},"ended":{"description":"Why keeping it warm stopped before anyone picked the conversation up. Absent while it is still being kept.","type":"object","properties":{"at":{"type":"number","description":"When it stopped, in milliseconds."},"reason":{"type":"string","enum":["elapsed","allowance","changed","cold","failed"],"description":"Why: `elapsed` the time asked for ran out; `allowance` the account reached the reserve kept for real work, or the provider refused a refresh for its limit; `changed` what the next turn would send no longer matches the cache (a part of the prompt, the date in it, or the conversation's session or account); `cold` the cache was gone, expired before a refresh could run or found missing by one; `failed` a refresh failed."},"detail":{"description":"The specifics, when there are any: which parts of the prompt changed, how full the account was, or the failure's own words.","type":"string"}},"required":["at","reason"],"additionalProperties":false}},"required":["since","until","refreshes"],"additionalProperties":false},"activity":{"description":"What it is doing at this moment.","type":"object","properties":{"tool":{"description":"The last tool it reached for.","type":"string"},"target":{"description":"What it reached for that tool with: a file, a command, a URL.","type":"string"},"todo":{"description":"The item on its own list that it is working through.","type":"string"}},"additionalProperties":false},"checklist":{"description":"How far it is through its own checklist. Absent for a conversation that kept no list, which is most short ones.","type":"object","properties":{"done":{"type":"number","description":"Items it has completed."},"total":{"type":"number","description":"Items on the list. Never zero: a conversation that kept no list carries no clause at all."}},"required":["done","total"],"additionalProperties":false},"landedMessageDraft":{"description":"The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes.","type":"object","properties":{"startedAt":{"type":"number","description":"When the drafting began, in milliseconds."},"steps":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider was asked."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"status":{"type":"string","enum":["asking","answered","refused","skipped"],"description":"How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."},"at":{"description":"When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time.","type":"number"},"ms":{"description":"How long it took. Absent while it is still being asked.","type":"number"},"reason":{"description":"Why it refused, in its own words.","type":"string"}},"required":["provider","model","status"],"additionalProperties":false},"description":"Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."},"outcome":{"description":"How it ended. Absent means it is still going.","type":"string","enum":["written","failed"]},"reason":{"description":"The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words.","type":"string"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"}},"required":["startedAt","steps"],"additionalProperties":false},"landedMessage":{"description":"What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false},"startedAt":{"description":"When the running turn started, in milliseconds. Absent when none is running.","type":"number"},"run":{"description":"The run under way right now: what a stop names, so it cannot cancel a turn that started after it was pressed. Absent when none is running, and for a turn with no run to attach to.","type":"string"},"queue":{"description":"Messages waiting for its next turn, and whether they are held. Absent for a conversation nothing has ever waited for.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false},"updatedAt":{"type":"number","description":"When it last did something, in milliseconds. Reading it does not count."},"seenAt":{"description":"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.","type":"number"},"unsentAt":{"description":"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.","type":"number"},"attention":{"type":"object","properties":{"plan":{"type":"boolean","description":"It has proposed a plan and is waiting for a yes."},"question":{"type":"boolean","description":"It has asked you something."},"permission":{"type":"boolean","description":"It wants to use a tool it needs permission for."},"capability":{"type":"boolean","description":"It needs something connected that is not connected yet."},"credential":{"type":"boolean","description":"It is waiting for a named person to release a credential. The one pause that may not be yours to clear, whatever your role."},"conflict":{"type":"boolean","description":"Its work cannot be merged without somebody resolving a clash."},"need":{"description":"It asked a person for something it still needs: a connection, a secret, wider reach, a tool. Absent from a daemon older than needs.","type":"boolean"}},"required":["plan","question","permission","capability","credential","conflict"],"additionalProperties":false,"description":"Which kinds of waiting-for-you it is doing."},"permissionAsk":{"description":"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.","type":"object","properties":{"requestId":{"type":"string","description":"Which request this is: the id an answer to it names."},"ask":{"type":"string","description":"What it asks to do, on one line: the runtime's own sentence, else the tool's short name."}},"required":["requestId","ask"],"additionalProperties":false},"landFailure":{"description":"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.","type":"object","properties":{"reason":{"type":"string","description":"Why it failed, in the words it failed with."},"code":{"description":"Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise.","type":"string"},"at":{"type":"number","description":"When it failed, in milliseconds."}},"required":["reason","at"],"additionalProperties":false},"delivery":{"description":"What became of its last land in the folder on the owner's computer, for a project folder attached to this computer's sandbox. Absent for any other land, and from a sandbox older than it.","type":"object","properties":{"state":{"type":"string","enum":["delivered","partial","waiting","failed","too-large"]},"at":{"type":"number","description":"When this state was reached, in milliseconds."},"project":{"type":"string"},"folder":{"description":"The folder on the owner's computer, as its machine reported it.","type":"string"},"applied":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files written, merged ones included."},"conflicts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"reason":{"type":"string","enum":["edited","link","outside","missing-git"]}},"required":["path","reason"],"additionalProperties":false}},"point":{"description":"The machine's restore point taken before writing.","type":"string"},"reason":{"description":"Why it failed or waits, in a sentence.","type":"string"}},"required":["state","at","project","applied","conflicts"],"additionalProperties":false},"conflictCauses":{"description":"Why its work will not merge, and so who can clear it: your own uncommitted edits, which only you can commit or stash, against a moved main line or an unmergeable binary, which the conversation can redo on its own copy. Absent unless it is refusing to merge.","type":"array","items":{"type":"string","enum":["workspace","diverged","binary"]}},"unfinished":{"description":"What its last turn left open: steps it never completed. Absent for a turn that finished what it started.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"steps":{"description":"The agent's own checklist where that turn left it. Absent for a conversation that kept no list.","type":"object","properties":{"open":{"type":"number","description":"Items on it that were never completed."},"total":{"type":"number","description":"Items on the whole list."},"next":{"description":"The one it would have done next: what it was working through, or the first still waiting.","type":"string"}},"required":["open","total"],"additionalProperties":false},"check":{"description":"The end-of-turn check that was still failing when the turn ended, by name. No longer written: what a check finds as a turn ends is said back to the model and never leaves its work unfinished.","type":"string"}},"required":["at"],"additionalProperties":false},"proof":{"description":"What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"verification":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to."},"check":{"description":"The command that spoke, when one did, so a targeted test is never read as the whole suite.","type":"string"},"unviewed":{"description":"How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none.","type":"number"}},"required":["at","verification"],"additionalProperties":false},"reach":{"description":"Where its last turn's work went when it went somewhere besides its own branch: files changed live outside it, clones of its own holding work that will not land, pushes it made itself. Absent when everything it did is on its branch.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"live":{"description":"Files it changed in place where every conversation reads them, outside its own branch: an installed extension's copy, the sandbox's shared state, the owner's own checkout. Live the moment they were written, never reviewed, and an installed extension's next update replaces them.","maxItems":10,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What it wrote, as it named it: relative to the workspace for the sandbox's shared state, absolute for the owner's own checkout."},"extension":{"description":"The extension, by its manifest id, when what it changed is the installed copy of one. Absent for anything else.","type":"string"}},"required":["path"],"additionalProperties":false}},"liveMore":{"description":"How many more such files there were beyond the ones listed. Absent when all are listed.","type":"number"},"stranded":{"description":"Repositories it cloned inside its own copy and left work in. A clone of its own never rides its land, so what is in one stays there.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"The clone, as a folder relative to its own copy of the workspace."},"uncommitted":{"type":"number","description":"Files changed in it and never committed."},"unpushed":{"type":"number","description":"Commits in it its upstream does not have, or that no remote has when it follows none."}},"required":["dir","uncommitted","unpushed"],"additionalProperties":false}},"strandedMore":{"description":"How many more such clones there were beyond the ones listed. Absent when all are listed.","type":"number"},"published":{"description":"Pushes it made with git that went through: work it sent out of the sandbox itself, never through a land.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"description":"The folder it pushed from, as the command named it. Absent for its own working folder.","type":"string"},"remote":{"description":"The remote it pushed to, as the command named it. Absent when the command named none.","type":"string"},"branch":{"description":"The branch it pushed to, as the command named it. Absent when the command named none.","type":"string"},"command":{"type":"string","description":"The push itself, trimmed to one line."}},"required":["command"],"additionalProperties":false}},"publishedMore":{"description":"How many more such pushes there were beyond the ones listed. Absent when all are listed.","type":"number"}},"required":["at"],"additionalProperties":false},"turns":{"description":"Turns it has finished.","type":"number"},"toolUses":{"description":"Tools it has used, over its whole life.","type":"number"},"subagents":{"description":"Subagents this one started, in-process and spawned alike. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost.","type":"object","properties":{"running":{"type":"number","description":"Subagents working right now."},"total":{"type":"number","description":"Subagents it has started over its whole life."}},"required":["running","total"],"additionalProperties":false},"diff":{"description":"Everything it has written, measured from where it started. Independent of how much has been merged.","type":"object","properties":{"files":{"type":"number","description":"Files touched."},"insertions":{"type":"number","description":"Lines added."},"deletions":{"type":"number","description":"Lines removed."}},"required":["files","insertions","deletions"],"additionalProperties":false},"landedPresence":{"description":"Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it.","type":"object","properties":{"landed":{"type":"number","description":"Paths this conversation merged in."},"present":{"type":"number","description":"How many of them are still there, either pending or committed."},"removedBy":{"description":"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"id":{"type":"string","description":"The conversation that took them out."},"title":{"description":"What that conversation is called.","type":"string"}},"required":["kind","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"description":"Who it was, as the sandbox verified them. Absent when the request carried no identity.","type":"string"},"name":{"description":"What to call them, when their sign-in carried a name.","type":"string"}},"required":["kind"],"additionalProperties":false}]}},"required":["landed","present"],"additionalProperties":false},"loop":{"description":"The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them.","type":"object","properties":{"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How the loop is going."},"iteration":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which round it is on."},"maxIterations":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many rounds it will attempt before giving up."},"goal":{"type":"string","description":"What it is looping towards."}},"required":["state","iteration","maxIterations","goal"],"additionalProperties":false},"workflow":{"description":"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.","type":"object","properties":{"runId":{"type":"string","description":"The run this belongs to, which is how a board groups its steps together."},"name":{"type":"string","description":"The workflow's name."},"step":{"type":"string","description":"Which step this conversation is on now. It moves when steps are chained."},"index":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"This step's place in the workflow, counting from one."},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many steps the workflow has."}},"required":["runId","name","step","index","total"],"additionalProperties":false},"awaitingWake":{"description":"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.","type":"boolean"},"watches":{"description":"Outside conditions this conversation is parked on, each of which will wake it. Absent means none, which is nearly every conversation: an armed watch is why a finished-looking agent starts working by itself, and why a hosted machine will not go idle.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this watch, the same one the agent was given when it armed it."},"note":{"type":"string","description":"The agent's own line on what it is waiting for."},"intervalSeconds":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How often the check runs."},"deadlineAt":{"type":"number","description":"When it gives up and wakes the conversation anyway, in milliseconds. Every watch has one."}},"required":["id","note","intervalSeconds","deadlineAt"],"additionalProperties":false}},"needs":{"description":"What it is waiting on people for and has not got yet, oldest first. Absent means nothing: an open need is why an idle-looking agent still needs you.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["capability","secret","grant","release","environment"],"description":"What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image."},"title":{"type":"string"},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."}},"required":["id","kind","title","status"],"additionalProperties":false}},"jobs":{"description":"Commands this conversation left running in the background, and how the most recent ones ended. Absent means none since the daemon started. A job is running exactly while it has no end.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this job, the one its transcript row names."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"session":{"type":"string","description":"The terminal session its pane runs in, which is what opening it focuses."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When the command exited, in milliseconds. Absent while it runs.","type":"number"},"exitCode":{"description":"The code it exited with. Absent while it runs, or when its exit left none.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"watch":{"description":"The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.","type":"string"},"handed":{"description":"Left running for the person: the agent kept it for them with the `keep` tool (or it holds a port this conversation already left them), so it outlives the turn, wakes nothing and is theirs to stop.","type":"boolean"},"ports":{"description":"The ports it was listening on when its turn ended.","type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"stoppedBy":{"description":"Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.","type":"string","enum":["turn","person","agent"]},"inputWait":{"description":"Present while it sits blocked reading its terminal with nothing moving: a prompt nobody in the sandbox will answer, which only stopping it or a person typing into its terminal ends. Absent while it runs on, and once it ends.","type":"object","properties":{"since":{"type":"number","description":"When it went still, in milliseconds."},"program":{"type":"string","description":"The process blocked reading the terminal, as its command line."}},"required":["since","program"],"additionalProperties":false}},"required":["id","label","session","startedAt"],"additionalProperties":false}},"archivedAt":{"description":"When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board.","type":"number"}},"required":["id","status","provider","harness","updatedAt","attention"],"additionalProperties":false},"description":"The conversations."},"rev":{"type":"number","description":"Which version of the fleet this is. The fleet is published as whole snapshots, so without a version a list read before a change but delivered after it would silently undo that change. Drop any list older than the newest you have already applied."},"held":{"default":[],"description":"Automations waiting at the door for a yes, put alongside the running conversations so needs-you sits beside working rather than on a page nobody opens.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"This waiting item's own id, which approving and rejecting take."},"automationId":{"type":"string","description":"Which automation it came from."},"payload":{"description":"What set it off, kept whole so an approved wake carries the same thing it would have had. Absent for one on a schedule, which carries nothing.","type":"string"},"origin":{"description":"Where the message came from, kept alongside the payload so an approved wake appears on the board exactly as an automatic one would have.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"title":{"description":"What the conversation would be called.","type":"string"},"conversationId":{"description":"The thread this belongs to, when it has one, so approving continues that conversation rather than opening a new one. Without it, one visitor's chat becomes a card per approved message and an agent that meets them again every turn.","type":"string"},"sessionId":{"description":"The provider session that thread last ran on.","type":"string"},"thread":{"description":"Which inbound thread this belongs to, so the approved run continues that thread's memory rather than a fresh one.","type":"string"},"actsAs":{"description":"Which persona the approved run speaks as, decided when it was held.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"createdAt":{"type":"number","description":"When it started waiting, in milliseconds."},"autoRunAt":{"description":"When it goes ahead on its own, in milliseconds, for a hold that is only a delay. Absent for one that genuinely waits on a person.","type":"number"}},"required":["id","automationId","createdAt"],"additionalProperties":false}}},"required":["agents","rev","held"],"additionalProperties":false}}}}}}},"/agents/{id}/diff":{"get":{"operationId":"agents.diff","summary":"Everything a conversation has changed","description":"One flat set of changed files per repo, measured against where each repo stood when the conversation started, with every file flagged as already merged or not. Not the staged-and-unstaged shape a working copy has, because nobody ever checks this branch out to stage into it.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"repos":{"type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository."},"branch":{"description":"The branch this conversation's work sits on.","type":"string"},"changes":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The path, relative to the repository root. For a rename this is the new one."},"status":{"type":"string","enum":["added","modified","deleted","renamed","type-changed","conflicted"],"description":"What happened to it. Conflicted is not a kind of edit: nothing can be committed anywhere in the repository while one exists."},"from":{"description":"Where a renamed file came from.","type":"string"},"additions":{"description":"Lines added. Absent for a binary file, and for an untracked one, which has nothing to compare against.","type":"number"},"deletions":{"description":"Lines removed. Absent for the same reasons additions is.","type":"number"},"code":{"description":"The same +/− with every comment stripped from both sides, which is what a review shows beside a diff that opens on code alone. Absent when the file cannot be read that way (binary, too large, or a language this build ships no grammar for): git's own counts above are then the reading.","type":"object","properties":{"additions":{"type":"number"},"deletions":{"type":"number"}},"required":["additions","deletions"],"additionalProperties":false},"landed":{"type":"boolean","description":"Whether your workspace already holds this content. Read from the tree at request time, not from what a land recorded: discard a landed file in the Changes panel and this goes back to false, which is what puts it back under Land now."}},"required":["path","status","landed"],"additionalProperties":false},"description":"What it changed there."},"modules":{"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"Where the package lives, relative to its repository. Empty when the repository is itself one package."},"name":{"type":"string","description":"The name the package declares for itself."}},"required":["dir","name"],"additionalProperties":false},"description":"The packages of the tree these changes came from, so a review can group by package. Carried with the changes rather than looked up separately, because a package the conversation has just created exists only in its own copy and the shared tree has never heard of it."},"addedDependencies":{"description":"Dependencies the changed manifests here declare that they did not before (package.json, pyproject.toml, requirements.txt), one entry per manifest that gained any. The review is where a new dependency is approved: a conversation installs freely in its own copy, and this is what the project takes on if the work lands. Absent when none was added.","type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The manifest, relative to the repository root, such as video/package.json."},"added":{"type":"array","items":{"type":"string"},"description":"The names it declares now and did not declare before, sorted. Only new names: a version bump of a dependency already there is not listed."}},"required":["path","added"],"additionalProperties":false}}},"required":["repo","changes","modules"],"additionalProperties":false},"description":"One entry per repository the conversation touched."},"absorbed":{"type":"number","description":"How many of this conversation's files your own history already carries, and which are therefore not listed as differences any more."},"conflicts":{"description":"Why the last merge refused, when one did. Carried here as well as in the merge's own answer, because a conflict is found the moment a turn ends and dealt with hours later on this surface, which would otherwise open with nothing to explain what it promised to resolve.","type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository."},"paths":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"Which file."},"reason":{"type":"string","enum":["workspace","diverged","binary"],"description":"Why it would not merge, and the three have nothing in common but the symptom. Your own uncommitted edits on that path, where yours is the copy at risk. The shared tree having moved under the conversation since it started, where nothing of yours is at risk. Or a file git cannot merge at all, where no automatic answer exists."}},"required":["path","reason"],"additionalProperties":false},"description":"The files that genuinely would not apply. Not the whole change: reporting everything whenever the cause could not be pinned down turned four real conflicts into a wall of fourteen."},"clean":{"type":"number","description":"How many files in this repository passed but remain held with the refused composition. Zero alongside an empty list means the repository could not be reached at all."},"mainBranch":{"description":"The branch your own checkout is on, which is what the conversation has to rebase onto. Carried because only the sandbox can see it. Absent where there is no name to give.","type":"string"}},"required":["repo","paths","clean"],"additionalProperties":false}},"elsewhere":{"description":"Repositories whose copy the conversation left standing on a different branch of its own. What is listed for them is this conversation's own branch, onto which each turn copies what it committed on the other branch unless `carried` says it could not.","type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository."},"branch":{"description":"The branch its copy is standing on. Absent where it stands on no branch at all, which is a state git allows.","type":"string"},"carried":{"description":"Whether everything the conversation committed there has also been copied onto its own branch, so what is listed here and what a merge brings include it. False where a commit would not copy over cleanly. Absent from a sandbox too old to copy it, which never did.","type":"boolean"},"uncommitted":{"description":"Whether its copy there holds uncommitted changes to tracked files, which no merge brings until they are committed there.","type":"boolean"}},"required":["repo"],"additionalProperties":false}},"scratch":{"description":"Untracked files the conversation left in its copy that look like scratch: logs, probe scripts, dumps, a checkout of its own. They are not in the list above and no merge carries them. They stay in its copy until they are included or deleted, and go with the copy when it is archived or retired. Absent when there are none.","type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository."},"paths":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"Relative to its repository. A directory ends in a slash and stands for everything inside it."},"reason":{"type":"string","enum":["hidden","byproduct","checkout","oversized","root"],"description":"Why it looks like scratch. A new hidden directory that is not one a project keeps on purpose (like `.github`). A log, dump, backup or editor leftover. A git checkout of its own. A new file past the size source code reaches. Or a new dotfile at the top of a workspace whose projects are the repositories inside it."},"files":{"description":"How many files it holds. Absent for a checkout of its own, whose contents are not walked.","type":"number"},"bytes":{"description":"Their total size in bytes. Absent exactly when `files` is.","type":"number"}},"required":["path","reason"],"additionalProperties":false},"description":"What it keeps out there."}},"required":["repo","paths"],"additionalProperties":false}}},"required":["repos","absorbed"],"additionalProperties":false}}}}}}},"/agents/{id}/conflicts":{"get":{"operationId":"agents.conflicts","summary":"Why a conversation's last merge refused","description":"What still blocks the conversation's last refused merge, checked again against your workspace as it stands now. Returns the same `conflicts` the full change list carries, without the change list itself. Empty when nothing refused, or when what refused before has since stopped being in the way.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"conflicts":{"description":"Why the last merge refused, when one did. Carried here as well as in the merge's own answer, because a conflict is found the moment a turn ends and dealt with hours later on this surface, which would otherwise open with nothing to explain what it promised to resolve.","type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository."},"paths":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"Which file."},"reason":{"type":"string","enum":["workspace","diverged","binary"],"description":"Why it would not merge, and the three have nothing in common but the symptom. Your own uncommitted edits on that path, where yours is the copy at risk. The shared tree having moved under the conversation since it started, where nothing of yours is at risk. Or a file git cannot merge at all, where no automatic answer exists."}},"required":["path","reason"],"additionalProperties":false},"description":"The files that genuinely would not apply. Not the whole change: reporting everything whenever the cause could not be pinned down turned four real conflicts into a wall of fourteen."},"clean":{"type":"number","description":"How many files in this repository passed but remain held with the refused composition. Zero alongside an empty list means the repository could not be reached at all."},"mainBranch":{"description":"The branch your own checkout is on, which is what the conversation has to rebase onto. Carried because only the sandbox can see it. Absent where there is no name to give.","type":"string"}},"required":["repo","paths","clean"],"additionalProperties":false}}},"additionalProperties":false}}}}}}},"/agents/{id}/history":{"get":{"operationId":"agents.history","summary":"Where a conversation's committed work lives","description":"The commits in your own history that carry this conversation's work, with the files each one brought. Use it when the change list is empty or short because you already committed what it wrote: those files are not differences against the main line any more, so they are not in the review, and this is where they went.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"repos":{"type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository."},"commits":{"type":"array","items":{"type":"object","properties":{"sha":{"type":"string","description":"The commit."},"short":{"type":"string","description":"Its abbreviated hash, which is what a reader recognises it by."},"subject":{"type":"string","description":"Its first line."},"author":{"type":"string","description":"Who committed it."},"at":{"type":"number","description":"When it was authored, in milliseconds."},"changes":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The path, relative to the repository root. For a rename this is the new one."},"status":{"type":"string","enum":["added","modified","deleted","renamed","type-changed","conflicted"],"description":"What happened to it. Conflicted is not a kind of edit: nothing can be committed anywhere in the repository while one exists."},"from":{"description":"Where a renamed file came from.","type":"string"},"additions":{"description":"Lines added. Absent for a binary file, and for an untracked one, which has nothing to compare against.","type":"number"},"deletions":{"description":"Lines removed. Absent for the same reasons additions is.","type":"number"},"code":{"description":"The same +/− with every comment stripped from both sides, which is what a review shows beside a diff that opens on code alone. Absent when the file cannot be read that way (binary, too large, or a language this build ships no grammar for): git's own counts above are then the reading.","type":"object","properties":{"additions":{"type":"number"},"deletions":{"type":"number"}},"required":["additions","deletions"],"additionalProperties":false}},"required":["path","status"],"additionalProperties":false},"description":"The conversation's files that this commit is the newest carrier of, as the conversation changed them. Every file appears under exactly one commit, so these counts add up to the work rather than over-counting a file that history touched twice."}},"required":["sha","short","subject","author","at","changes"],"additionalProperties":false},"description":"The commits carrying this conversation's work there, newest first."},"modules":{"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"Where the package lives, relative to its repository. Empty when the repository is itself one package."},"name":{"type":"string","description":"The name the package declares for itself."}},"required":["dir","name"],"additionalProperties":false},"description":"The packages of the tree these files came from, so a review can group them by package."}},"required":["repo","commits","modules"],"additionalProperties":false},"description":"One entry per repository holding committed work of this conversation."},"unaccounted":{"type":"number","description":"How many of the conversation's absorbed files none of these commits carries. Above zero means its content reached your main line by some other road, so the commits listed are not the whole story."}},"required":["repos","unaccounted"],"additionalProperties":false}}}}}}},"/agents/{id}/{repo}/file-diff":{"get":{"operationId":"agents.fileDiff","summary":"One file's before and after in a conversation's work","description":"Both sides of a single file: what it held when the conversation started and what it holds on its branch now.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}},{"name":"repo","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which repository."}},{"name":"path","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"Which file, relative to that repository."},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"before":{"description":"The whole file as it was. Absent when it did not exist yet, or when `partial` is set.","type":"string"},"after":{"description":"The whole file as it is now. Absent when it was deleted, or when `partial` is set.","type":"string"},"binary":{"description":"The file is not text, so neither side is sent.","type":"boolean"},"partial":{"description":"Set when the file was too large to send whole: what is sent instead of the two sides.","type":"object","properties":{"beforeBytes":{"description":"How big the before side is, in bytes. Absent when the file did not exist yet.","type":"integer","minimum":0,"maximum":9007199254740991},"afterBytes":{"description":"How big the after side is, in bytes. Absent when the file was deleted.","type":"integer","minimum":0,"maximum":9007199254740991},"patch":{"description":"The changed regions as unified-diff hunks (`@@` sections only). Absent when the change was too large to render even as a patch.","type":"string"},"more":{"description":"There were more changed regions than fit; the patch stops at a region boundary.","type":"boolean"}},"additionalProperties":false}},"additionalProperties":false}}}}}}},"/agents/{id}/scratch/include":{"post":{"operationId":"agents.includeScratch","summary":"Carry files set aside as scratch with a conversation's work","description":"Takes files the review lists as scratch and adds them to the conversation's work, so the next merge carries them like any other file. For the file that only looked like scratch. Refused for a checkout of its own, which no merge can carry, while a turn is running, and for a path that is not scratch right now.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"repo":{"type":"string","minLength":1,"description":"Which repository of its composition."},"paths":{"minItems":1,"maxItems":1000,"type":"array","items":{"type":"string","minLength":1},"description":"Paths exactly as the review lists them under scratch, a directory with its trailing slash."}},"required":["repo","paths"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/agents/{id}/scratch/delete":{"post":{"operationId":"agents.deleteScratch","summary":"Delete a conversation's scratch","description":"Removes files the review lists as scratch from the conversation's copy. Nothing else is touched, and nothing of it was ever merged. Refused while a turn is running, and for a path that is not scratch right now.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"repo":{"type":"string","minLength":1,"description":"Which repository of its composition."},"paths":{"minItems":1,"maxItems":1000,"type":"array","items":{"type":"string","minLength":1},"description":"Paths exactly as the review lists them under scratch, a directory with its trailing slash."}},"required":["repo","paths"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/agents/{id}/land":{"post":{"operationId":"agents.land","summary":"Merge a conversation's work into the workspace","description":"Brings the conversation's branches into the main tree, one repo at a time. A conflict is reported rather than raised and nothing is lost when it fails. Refused while a turn is running, and refused for a conversation that works directly in the shared tree, which has nothing to merge.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation's work to merge."}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"description":"How to apply it. The default applies every repository or none, so a refusal leaves the workspace exactly as it was. The other carries the whole composition and leaves conflicted paths with markers to resolve by hand.","type":"string","enum":["check","merge","measure"]},"span":{"description":"How much of the work to take. Leave it out for everything not yet merged.","type":"string","enum":["cumulative","outstanding"]},"force":{"description":"Go ahead despite a check that would otherwise refuse.","type":"boolean"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"landed":{"type":"boolean","description":"Whether the entire composed change was applied."},"changed":{"type":"boolean","description":"Whether anything actually moved. False alongside merged means there was nothing on the branch to apply: the work is already in your tree, or the branch never carried any."},"conflicts":{"description":"What stopped the whole composed change, grouped per repository.","type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository."},"paths":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"Which file."},"reason":{"type":"string","enum":["workspace","diverged","binary"],"description":"Why it would not merge, and the three have nothing in common but the symptom. Your own uncommitted edits on that path, where yours is the copy at risk. The shared tree having moved under the conversation since it started, where nothing of yours is at risk. Or a file git cannot merge at all, where no automatic answer exists."}},"required":["path","reason"],"additionalProperties":false},"description":"The files that genuinely would not apply. Not the whole change: reporting everything whenever the cause could not be pinned down turned four real conflicts into a wall of fourteen."},"clean":{"type":"number","description":"How many files in this repository passed but remain held with the refused composition. Zero alongside an empty list means the repository could not be reached at all."},"mainBranch":{"description":"The branch your own checkout is on, which is what the conversation has to rebase onto. Carried because only the sandbox can see it. Absent where there is no name to give.","type":"string"}},"required":["repo","paths","clean"],"additionalProperties":false}},"resolving":{"description":"Files left half-merged when you asked to carry the whole composition with its conflicts marked for resolution.","type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository."},"paths":{"type":"array","items":{"type":"string"},"description":"Which files now hold conflict markers to sort out by hand."}},"required":["repo","paths"],"additionalProperties":false}},"held":{"description":"Nothing was applied and nothing failed: there is work waiting on the branch for a deliberate merge. Not merged on its own cannot say that, because on its own it means refused.","type":"boolean"}},"required":["landed","changed"],"additionalProperties":false}}}}}}},"/agents/{id}/request-land":{"post":{"operationId":"agents.requestLand","summary":"Ask a maintainer to merge this work","description":"For a collaborator who is not allowed to merge: marks the conversation as waiting for review, with who asked. The request shows on every maintainer's board and clears when somebody merges or discards it.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The conversation id, which is how every other call addresses it."},"sessionId":{"description":"The provider session behind the last turn. It is retired whenever the model or account changes.","type":"string"},"title":{"description":"What to call it: the first prompt cut to one line, unless somebody renamed it.","type":"string"},"titleAction":{"description":"One word for the kind of work the naming pass read in it (fix, audit, redesign). Never part of the displayed name: a board tints and glyphs a card by it. Absent for a title nothing named an action for.","type":"string"},"status":{"type":"string","enum":["idle","running","awaiting","stopping","dismissing","stopped","resuming","landing","ready","landed","conflict","error","interrupted"],"description":"What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; dismissing is the same window for a question waved away, which ends the turn too but owes the user nothing; resuming means the sandbox is already putting right whatever killed the turn; landing means its work is being carried into the workspace right now, and nothing may act on its branch until that settles."},"failure":{"description":"Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.","type":"string"},"failureCode":{"description":"Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.","type":"string"},"limitResetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant.","type":"number"},"limitHeld":{"description":"Whether the refused turn is held whole, so sending again re-runs it instead of appending to it.","type":"boolean"},"limitScheduled":{"description":"Whether the held turn is already booked to go again at the reset, so nobody has to press anything.","type":"boolean"},"limitMoving":{"description":"The account the held turn is being moved to by the owner's policy, while that move is booked.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it runs on."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop it runs on."},"runner":{"description":"The runner this conversation runs on. Absent means this sandbox.","type":"string"},"startIn":{"description":"Which folder it opened in, relative to the workspace root. Absent means the root.","type":"string"},"actsAs":{"description":"Which persona its first turn acted as. Absent for an ordinary chat.","type":"string"},"lastActsAs":{"description":"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.","type":"string"},"model":{"description":"What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked.","type":"string"},"effort":{"description":"How hard that turn was told to think.","type":"string"},"thinking":{"description":"Whether that turn showed its reasoning.","type":"boolean"},"fast":{"description":"Whether that turn asked for higher speed. What was asked for, not what was served.","type":"boolean"},"account":{"description":"Which connected account paid for it.","type":"string"},"branch":{"description":"The branch its private copy works on. Absent for a conversation that works directly in the shared tree.","type":"string"},"autoLand":{"description":"This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case.","type":"boolean"},"limitPolicy":{"type":"string","enum":["wait","resend","move"]},"outagePolicy":{"type":"string","enum":["wait","retry"]},"stopPolicy":{"type":"string","enum":["wait","retry"]},"landRequested":{"description":"A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting.","type":"object","properties":{"email":{"type":"string","description":"Who asked."},"name":{"description":"Their display name.","type":"string"},"at":{"type":"number","description":"When they asked, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"reactions":{"description":"What people have marked this conversation with, one entry per emoji, in the order the emoji were first used. Absent means nobody has marked it, which is most conversations.","type":"array","items":{"type":"object","properties":{"emoji":{"type":"string","description":"The mark itself, one emoji, carried as the character rather than as a name that would need a table on both sides."},"by":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Who it was, as the sandbox verified them."},"name":{"description":"What to call them, when their sign-in carried a name. Absent leaves the address to stand for them.","type":"string"},"at":{"type":"number","description":"When they marked it, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"description":"Everyone wearing this mark, oldest first, each by name. Carried whole rather than as a count, because a chip reading 3 that cannot say whose is a number nobody can answer. Never empty: the last person taking theirs back takes the whole chip with it."}},"required":["emoji","by"],"additionalProperties":false}},"origin":{"description":"Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"startedBy":{"description":"Who asked for the first turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a child another conversation spawned. Absent when nothing was verified (a wake, a loopback caller).","type":"string"},"areas":{"description":"Which named areas of the workspace this conversation was started within, latched from whoever asked for the first turn. Absent means its starter held the whole workspace, which is why a fenced member is not shown it.","type":"array","items":{"type":"string"}},"owner":{"description":"The member answerable for this conversation: set from whoever started it, inherited from the parent by a spawned child, moved by handing it over. Absent means nobody has claimed it yet.","type":"object","properties":{"email":{"type":"string","description":"Who answers for this conversation, as the sandbox verified them."},"name":{"description":"What to call them, when the sign-in that made them its owner carried a name. Absent leaves the address to stand for them.","type":"string"},"since":{"type":"number","description":"When they became its owner, in milliseconds."}},"required":["email","since"],"additionalProperties":false},"forkedFrom":{"description":"The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"type":"string","enum":["then","now"]}},"required":["conversationId","index","files"],"additionalProperties":false},"base":{"description":"The commit its private copy started from, shortened.","type":"string"},"costUsd":{"description":"What it has cost so far, in dollars. A subagent's spend is its own and is not folded in here.","type":"number"},"inputTokens":{"description":"Uncached input tokens, excluding cache reads and cache writes.","type":"number"},"outputTokens":{"description":"Tokens received.","type":"number"},"contextTokens":{"description":"How much of the window the conversation currently fills.","type":"number"},"contextWindow":{"description":"How large that window is.","type":"number"},"promptCache":{"description":"When this conversation's prompt cache was last kept alive and how long it lasts, which together say when picking the conversation up stops being cheap. Absent when the provider publishes nothing to ground it on.","type":"object","properties":{"at":{"type":"number","description":"When its last request touched the provider's prompt cache, in milliseconds."},"ttlMs":{"type":"number","description":"How long that entry lives from `at`, in milliseconds."},"rollsAt":{"description":"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.","type":"number"},"keepableUntil":{"description":"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.","type":"number"}},"required":["at","ttlMs"],"additionalProperties":false},"keepWarm":{"description":"Whether the sandbox is keeping this conversation's prompt cache warm while it sits idle, how that is going, or why it stopped. Absent when nobody asked for it.","type":"object","properties":{"since":{"type":"number","description":"When keeping it warm started, in milliseconds."},"until":{"type":"number","description":"When it stops by itself, in milliseconds: the time asked for, shortened to what the sandbox can honestly keep, which is never past the point where refreshing costs more than re-reading, nor past the date change that rewrites the prompt."},"refreshes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Refreshes sent so far."},"readTokens":{"description":"How much the last refresh read back from the provider's cache, in tokens: the proof the cache was still there.","type":"number"},"ended":{"description":"Why keeping it warm stopped before anyone picked the conversation up. Absent while it is still being kept.","type":"object","properties":{"at":{"type":"number","description":"When it stopped, in milliseconds."},"reason":{"type":"string","enum":["elapsed","allowance","changed","cold","failed"],"description":"Why: `elapsed` the time asked for ran out; `allowance` the account reached the reserve kept for real work, or the provider refused a refresh for its limit; `changed` what the next turn would send no longer matches the cache (a part of the prompt, the date in it, or the conversation's session or account); `cold` the cache was gone, expired before a refresh could run or found missing by one; `failed` a refresh failed."},"detail":{"description":"The specifics, when there are any: which parts of the prompt changed, how full the account was, or the failure's own words.","type":"string"}},"required":["at","reason"],"additionalProperties":false}},"required":["since","until","refreshes"],"additionalProperties":false},"activity":{"description":"What it is doing at this moment.","type":"object","properties":{"tool":{"description":"The last tool it reached for.","type":"string"},"target":{"description":"What it reached for that tool with: a file, a command, a URL.","type":"string"},"todo":{"description":"The item on its own list that it is working through.","type":"string"}},"additionalProperties":false},"checklist":{"description":"How far it is through its own checklist. Absent for a conversation that kept no list, which is most short ones.","type":"object","properties":{"done":{"type":"number","description":"Items it has completed."},"total":{"type":"number","description":"Items on the list. Never zero: a conversation that kept no list carries no clause at all."}},"required":["done","total"],"additionalProperties":false},"landedMessageDraft":{"description":"The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes.","type":"object","properties":{"startedAt":{"type":"number","description":"When the drafting began, in milliseconds."},"steps":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider was asked."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"status":{"type":"string","enum":["asking","answered","refused","skipped"],"description":"How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."},"at":{"description":"When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time.","type":"number"},"ms":{"description":"How long it took. Absent while it is still being asked.","type":"number"},"reason":{"description":"Why it refused, in its own words.","type":"string"}},"required":["provider","model","status"],"additionalProperties":false},"description":"Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."},"outcome":{"description":"How it ended. Absent means it is still going.","type":"string","enum":["written","failed"]},"reason":{"description":"The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words.","type":"string"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"}},"required":["startedAt","steps"],"additionalProperties":false},"landedMessage":{"description":"What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false},"startedAt":{"description":"When the running turn started, in milliseconds. Absent when none is running.","type":"number"},"run":{"description":"The run under way right now: what a stop names, so it cannot cancel a turn that started after it was pressed. Absent when none is running, and for a turn with no run to attach to.","type":"string"},"queue":{"description":"Messages waiting for its next turn, and whether they are held. Absent for a conversation nothing has ever waited for.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false},"updatedAt":{"type":"number","description":"When it last did something, in milliseconds. Reading it does not count."},"seenAt":{"description":"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.","type":"number"},"unsentAt":{"description":"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.","type":"number"},"attention":{"type":"object","properties":{"plan":{"type":"boolean","description":"It has proposed a plan and is waiting for a yes."},"question":{"type":"boolean","description":"It has asked you something."},"permission":{"type":"boolean","description":"It wants to use a tool it needs permission for."},"capability":{"type":"boolean","description":"It needs something connected that is not connected yet."},"credential":{"type":"boolean","description":"It is waiting for a named person to release a credential. The one pause that may not be yours to clear, whatever your role."},"conflict":{"type":"boolean","description":"Its work cannot be merged without somebody resolving a clash."},"need":{"description":"It asked a person for something it still needs: a connection, a secret, wider reach, a tool. Absent from a daemon older than needs.","type":"boolean"}},"required":["plan","question","permission","capability","credential","conflict"],"additionalProperties":false,"description":"Which kinds of waiting-for-you it is doing."},"permissionAsk":{"description":"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.","type":"object","properties":{"requestId":{"type":"string","description":"Which request this is: the id an answer to it names."},"ask":{"type":"string","description":"What it asks to do, on one line: the runtime's own sentence, else the tool's short name."}},"required":["requestId","ask"],"additionalProperties":false},"landFailure":{"description":"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.","type":"object","properties":{"reason":{"type":"string","description":"Why it failed, in the words it failed with."},"code":{"description":"Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise.","type":"string"},"at":{"type":"number","description":"When it failed, in milliseconds."}},"required":["reason","at"],"additionalProperties":false},"delivery":{"description":"What became of its last land in the folder on the owner's computer, for a project folder attached to this computer's sandbox. Absent for any other land, and from a sandbox older than it.","type":"object","properties":{"state":{"type":"string","enum":["delivered","partial","waiting","failed","too-large"]},"at":{"type":"number","description":"When this state was reached, in milliseconds."},"project":{"type":"string"},"folder":{"description":"The folder on the owner's computer, as its machine reported it.","type":"string"},"applied":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files written, merged ones included."},"conflicts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"reason":{"type":"string","enum":["edited","link","outside","missing-git"]}},"required":["path","reason"],"additionalProperties":false}},"point":{"description":"The machine's restore point taken before writing.","type":"string"},"reason":{"description":"Why it failed or waits, in a sentence.","type":"string"}},"required":["state","at","project","applied","conflicts"],"additionalProperties":false},"conflictCauses":{"description":"Why its work will not merge, and so who can clear it: your own uncommitted edits, which only you can commit or stash, against a moved main line or an unmergeable binary, which the conversation can redo on its own copy. Absent unless it is refusing to merge.","type":"array","items":{"type":"string","enum":["workspace","diverged","binary"]}},"unfinished":{"description":"What its last turn left open: steps it never completed. Absent for a turn that finished what it started.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"steps":{"description":"The agent's own checklist where that turn left it. Absent for a conversation that kept no list.","type":"object","properties":{"open":{"type":"number","description":"Items on it that were never completed."},"total":{"type":"number","description":"Items on the whole list."},"next":{"description":"The one it would have done next: what it was working through, or the first still waiting.","type":"string"}},"required":["open","total"],"additionalProperties":false},"check":{"description":"The end-of-turn check that was still failing when the turn ended, by name. No longer written: what a check finds as a turn ends is said back to the model and never leaves its work unfinished.","type":"string"}},"required":["at"],"additionalProperties":false},"proof":{"description":"What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"verification":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to."},"check":{"description":"The command that spoke, when one did, so a targeted test is never read as the whole suite.","type":"string"},"unviewed":{"description":"How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none.","type":"number"}},"required":["at","verification"],"additionalProperties":false},"reach":{"description":"Where its last turn's work went when it went somewhere besides its own branch: files changed live outside it, clones of its own holding work that will not land, pushes it made itself. Absent when everything it did is on its branch.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"live":{"description":"Files it changed in place where every conversation reads them, outside its own branch: an installed extension's copy, the sandbox's shared state, the owner's own checkout. Live the moment they were written, never reviewed, and an installed extension's next update replaces them.","maxItems":10,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What it wrote, as it named it: relative to the workspace for the sandbox's shared state, absolute for the owner's own checkout."},"extension":{"description":"The extension, by its manifest id, when what it changed is the installed copy of one. Absent for anything else.","type":"string"}},"required":["path"],"additionalProperties":false}},"liveMore":{"description":"How many more such files there were beyond the ones listed. Absent when all are listed.","type":"number"},"stranded":{"description":"Repositories it cloned inside its own copy and left work in. A clone of its own never rides its land, so what is in one stays there.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"The clone, as a folder relative to its own copy of the workspace."},"uncommitted":{"type":"number","description":"Files changed in it and never committed."},"unpushed":{"type":"number","description":"Commits in it its upstream does not have, or that no remote has when it follows none."}},"required":["dir","uncommitted","unpushed"],"additionalProperties":false}},"strandedMore":{"description":"How many more such clones there were beyond the ones listed. Absent when all are listed.","type":"number"},"published":{"description":"Pushes it made with git that went through: work it sent out of the sandbox itself, never through a land.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"description":"The folder it pushed from, as the command named it. Absent for its own working folder.","type":"string"},"remote":{"description":"The remote it pushed to, as the command named it. Absent when the command named none.","type":"string"},"branch":{"description":"The branch it pushed to, as the command named it. Absent when the command named none.","type":"string"},"command":{"type":"string","description":"The push itself, trimmed to one line."}},"required":["command"],"additionalProperties":false}},"publishedMore":{"description":"How many more such pushes there were beyond the ones listed. Absent when all are listed.","type":"number"}},"required":["at"],"additionalProperties":false},"turns":{"description":"Turns it has finished.","type":"number"},"toolUses":{"description":"Tools it has used, over its whole life.","type":"number"},"subagents":{"description":"Subagents this one started, in-process and spawned alike. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost.","type":"object","properties":{"running":{"type":"number","description":"Subagents working right now."},"total":{"type":"number","description":"Subagents it has started over its whole life."}},"required":["running","total"],"additionalProperties":false},"diff":{"description":"Everything it has written, measured from where it started. Independent of how much has been merged.","type":"object","properties":{"files":{"type":"number","description":"Files touched."},"insertions":{"type":"number","description":"Lines added."},"deletions":{"type":"number","description":"Lines removed."}},"required":["files","insertions","deletions"],"additionalProperties":false},"landedPresence":{"description":"Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it.","type":"object","properties":{"landed":{"type":"number","description":"Paths this conversation merged in."},"present":{"type":"number","description":"How many of them are still there, either pending or committed."},"removedBy":{"description":"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"id":{"type":"string","description":"The conversation that took them out."},"title":{"description":"What that conversation is called.","type":"string"}},"required":["kind","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"description":"Who it was, as the sandbox verified them. Absent when the request carried no identity.","type":"string"},"name":{"description":"What to call them, when their sign-in carried a name.","type":"string"}},"required":["kind"],"additionalProperties":false}]}},"required":["landed","present"],"additionalProperties":false},"loop":{"description":"The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them.","type":"object","properties":{"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How the loop is going."},"iteration":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which round it is on."},"maxIterations":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many rounds it will attempt before giving up."},"goal":{"type":"string","description":"What it is looping towards."}},"required":["state","iteration","maxIterations","goal"],"additionalProperties":false},"workflow":{"description":"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.","type":"object","properties":{"runId":{"type":"string","description":"The run this belongs to, which is how a board groups its steps together."},"name":{"type":"string","description":"The workflow's name."},"step":{"type":"string","description":"Which step this conversation is on now. It moves when steps are chained."},"index":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"This step's place in the workflow, counting from one."},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many steps the workflow has."}},"required":["runId","name","step","index","total"],"additionalProperties":false},"awaitingWake":{"description":"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.","type":"boolean"},"watches":{"description":"Outside conditions this conversation is parked on, each of which will wake it. Absent means none, which is nearly every conversation: an armed watch is why a finished-looking agent starts working by itself, and why a hosted machine will not go idle.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this watch, the same one the agent was given when it armed it."},"note":{"type":"string","description":"The agent's own line on what it is waiting for."},"intervalSeconds":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How often the check runs."},"deadlineAt":{"type":"number","description":"When it gives up and wakes the conversation anyway, in milliseconds. Every watch has one."}},"required":["id","note","intervalSeconds","deadlineAt"],"additionalProperties":false}},"needs":{"description":"What it is waiting on people for and has not got yet, oldest first. Absent means nothing: an open need is why an idle-looking agent still needs you.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["capability","secret","grant","release","environment"],"description":"What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image."},"title":{"type":"string"},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."}},"required":["id","kind","title","status"],"additionalProperties":false}},"jobs":{"description":"Commands this conversation left running in the background, and how the most recent ones ended. Absent means none since the daemon started. A job is running exactly while it has no end.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this job, the one its transcript row names."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"session":{"type":"string","description":"The terminal session its pane runs in, which is what opening it focuses."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When the command exited, in milliseconds. Absent while it runs.","type":"number"},"exitCode":{"description":"The code it exited with. Absent while it runs, or when its exit left none.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"watch":{"description":"The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.","type":"string"},"handed":{"description":"Left running for the person: the agent kept it for them with the `keep` tool (or it holds a port this conversation already left them), so it outlives the turn, wakes nothing and is theirs to stop.","type":"boolean"},"ports":{"description":"The ports it was listening on when its turn ended.","type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"stoppedBy":{"description":"Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.","type":"string","enum":["turn","person","agent"]},"inputWait":{"description":"Present while it sits blocked reading its terminal with nothing moving: a prompt nobody in the sandbox will answer, which only stopping it or a person typing into its terminal ends. Absent while it runs on, and once it ends.","type":"object","properties":{"since":{"type":"number","description":"When it went still, in milliseconds."},"program":{"type":"string","description":"The process blocked reading the terminal, as its command line."}},"required":["since","program"],"additionalProperties":false}},"required":["id","label","session","startedAt"],"additionalProperties":false}},"archivedAt":{"description":"When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board.","type":"number"}},"required":["id","status","provider","harness","updatedAt","attention"],"additionalProperties":false}}}}}}},"/agents/{id}/assign":{"post":{"operationId":"agents.assign","summary":"Make a member answerable for this conversation","description":"Hands a conversation to a member: its owner is who answers its questions and who a reviewer asks about its work. Its owner may hand it to anyone; a maintainer may reassign any conversation; one nobody owns may be claimed by anyone allowed to drive agents. Refused for an address that is not a member's. Nothing about the conversation's own work changes.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","format":"email","pattern":"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$","description":"Who should answer for it from now on, by the address they sign in with. Must be the sandbox owner or a member."}},"required":["to"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The conversation id, which is how every other call addresses it."},"sessionId":{"description":"The provider session behind the last turn. It is retired whenever the model or account changes.","type":"string"},"title":{"description":"What to call it: the first prompt cut to one line, unless somebody renamed it.","type":"string"},"titleAction":{"description":"One word for the kind of work the naming pass read in it (fix, audit, redesign). Never part of the displayed name: a board tints and glyphs a card by it. Absent for a title nothing named an action for.","type":"string"},"status":{"type":"string","enum":["idle","running","awaiting","stopping","dismissing","stopped","resuming","landing","ready","landed","conflict","error","interrupted"],"description":"What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; dismissing is the same window for a question waved away, which ends the turn too but owes the user nothing; resuming means the sandbox is already putting right whatever killed the turn; landing means its work is being carried into the workspace right now, and nothing may act on its branch until that settles."},"failure":{"description":"Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.","type":"string"},"failureCode":{"description":"Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.","type":"string"},"limitResetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant.","type":"number"},"limitHeld":{"description":"Whether the refused turn is held whole, so sending again re-runs it instead of appending to it.","type":"boolean"},"limitScheduled":{"description":"Whether the held turn is already booked to go again at the reset, so nobody has to press anything.","type":"boolean"},"limitMoving":{"description":"The account the held turn is being moved to by the owner's policy, while that move is booked.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it runs on."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop it runs on."},"runner":{"description":"The runner this conversation runs on. Absent means this sandbox.","type":"string"},"startIn":{"description":"Which folder it opened in, relative to the workspace root. Absent means the root.","type":"string"},"actsAs":{"description":"Which persona its first turn acted as. Absent for an ordinary chat.","type":"string"},"lastActsAs":{"description":"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.","type":"string"},"model":{"description":"What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked.","type":"string"},"effort":{"description":"How hard that turn was told to think.","type":"string"},"thinking":{"description":"Whether that turn showed its reasoning.","type":"boolean"},"fast":{"description":"Whether that turn asked for higher speed. What was asked for, not what was served.","type":"boolean"},"account":{"description":"Which connected account paid for it.","type":"string"},"branch":{"description":"The branch its private copy works on. Absent for a conversation that works directly in the shared tree.","type":"string"},"autoLand":{"description":"This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case.","type":"boolean"},"limitPolicy":{"type":"string","enum":["wait","resend","move"]},"outagePolicy":{"type":"string","enum":["wait","retry"]},"stopPolicy":{"type":"string","enum":["wait","retry"]},"landRequested":{"description":"A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting.","type":"object","properties":{"email":{"type":"string","description":"Who asked."},"name":{"description":"Their display name.","type":"string"},"at":{"type":"number","description":"When they asked, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"reactions":{"description":"What people have marked this conversation with, one entry per emoji, in the order the emoji were first used. Absent means nobody has marked it, which is most conversations.","type":"array","items":{"type":"object","properties":{"emoji":{"type":"string","description":"The mark itself, one emoji, carried as the character rather than as a name that would need a table on both sides."},"by":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Who it was, as the sandbox verified them."},"name":{"description":"What to call them, when their sign-in carried a name. Absent leaves the address to stand for them.","type":"string"},"at":{"type":"number","description":"When they marked it, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"description":"Everyone wearing this mark, oldest first, each by name. Carried whole rather than as a count, because a chip reading 3 that cannot say whose is a number nobody can answer. Never empty: the last person taking theirs back takes the whole chip with it."}},"required":["emoji","by"],"additionalProperties":false}},"origin":{"description":"Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"startedBy":{"description":"Who asked for the first turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a child another conversation spawned. Absent when nothing was verified (a wake, a loopback caller).","type":"string"},"areas":{"description":"Which named areas of the workspace this conversation was started within, latched from whoever asked for the first turn. Absent means its starter held the whole workspace, which is why a fenced member is not shown it.","type":"array","items":{"type":"string"}},"owner":{"description":"The member answerable for this conversation: set from whoever started it, inherited from the parent by a spawned child, moved by handing it over. Absent means nobody has claimed it yet.","type":"object","properties":{"email":{"type":"string","description":"Who answers for this conversation, as the sandbox verified them."},"name":{"description":"What to call them, when the sign-in that made them its owner carried a name. Absent leaves the address to stand for them.","type":"string"},"since":{"type":"number","description":"When they became its owner, in milliseconds."}},"required":["email","since"],"additionalProperties":false},"forkedFrom":{"description":"The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"type":"string","enum":["then","now"]}},"required":["conversationId","index","files"],"additionalProperties":false},"base":{"description":"The commit its private copy started from, shortened.","type":"string"},"costUsd":{"description":"What it has cost so far, in dollars. A subagent's spend is its own and is not folded in here.","type":"number"},"inputTokens":{"description":"Uncached input tokens, excluding cache reads and cache writes.","type":"number"},"outputTokens":{"description":"Tokens received.","type":"number"},"contextTokens":{"description":"How much of the window the conversation currently fills.","type":"number"},"contextWindow":{"description":"How large that window is.","type":"number"},"promptCache":{"description":"When this conversation's prompt cache was last kept alive and how long it lasts, which together say when picking the conversation up stops being cheap. Absent when the provider publishes nothing to ground it on.","type":"object","properties":{"at":{"type":"number","description":"When its last request touched the provider's prompt cache, in milliseconds."},"ttlMs":{"type":"number","description":"How long that entry lives from `at`, in milliseconds."},"rollsAt":{"description":"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.","type":"number"},"keepableUntil":{"description":"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.","type":"number"}},"required":["at","ttlMs"],"additionalProperties":false},"keepWarm":{"description":"Whether the sandbox is keeping this conversation's prompt cache warm while it sits idle, how that is going, or why it stopped. Absent when nobody asked for it.","type":"object","properties":{"since":{"type":"number","description":"When keeping it warm started, in milliseconds."},"until":{"type":"number","description":"When it stops by itself, in milliseconds: the time asked for, shortened to what the sandbox can honestly keep, which is never past the point where refreshing costs more than re-reading, nor past the date change that rewrites the prompt."},"refreshes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Refreshes sent so far."},"readTokens":{"description":"How much the last refresh read back from the provider's cache, in tokens: the proof the cache was still there.","type":"number"},"ended":{"description":"Why keeping it warm stopped before anyone picked the conversation up. Absent while it is still being kept.","type":"object","properties":{"at":{"type":"number","description":"When it stopped, in milliseconds."},"reason":{"type":"string","enum":["elapsed","allowance","changed","cold","failed"],"description":"Why: `elapsed` the time asked for ran out; `allowance` the account reached the reserve kept for real work, or the provider refused a refresh for its limit; `changed` what the next turn would send no longer matches the cache (a part of the prompt, the date in it, or the conversation's session or account); `cold` the cache was gone, expired before a refresh could run or found missing by one; `failed` a refresh failed."},"detail":{"description":"The specifics, when there are any: which parts of the prompt changed, how full the account was, or the failure's own words.","type":"string"}},"required":["at","reason"],"additionalProperties":false}},"required":["since","until","refreshes"],"additionalProperties":false},"activity":{"description":"What it is doing at this moment.","type":"object","properties":{"tool":{"description":"The last tool it reached for.","type":"string"},"target":{"description":"What it reached for that tool with: a file, a command, a URL.","type":"string"},"todo":{"description":"The item on its own list that it is working through.","type":"string"}},"additionalProperties":false},"checklist":{"description":"How far it is through its own checklist. Absent for a conversation that kept no list, which is most short ones.","type":"object","properties":{"done":{"type":"number","description":"Items it has completed."},"total":{"type":"number","description":"Items on the list. Never zero: a conversation that kept no list carries no clause at all."}},"required":["done","total"],"additionalProperties":false},"landedMessageDraft":{"description":"The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes.","type":"object","properties":{"startedAt":{"type":"number","description":"When the drafting began, in milliseconds."},"steps":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider was asked."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"status":{"type":"string","enum":["asking","answered","refused","skipped"],"description":"How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."},"at":{"description":"When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time.","type":"number"},"ms":{"description":"How long it took. Absent while it is still being asked.","type":"number"},"reason":{"description":"Why it refused, in its own words.","type":"string"}},"required":["provider","model","status"],"additionalProperties":false},"description":"Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."},"outcome":{"description":"How it ended. Absent means it is still going.","type":"string","enum":["written","failed"]},"reason":{"description":"The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words.","type":"string"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"}},"required":["startedAt","steps"],"additionalProperties":false},"landedMessage":{"description":"What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false},"startedAt":{"description":"When the running turn started, in milliseconds. Absent when none is running.","type":"number"},"run":{"description":"The run under way right now: what a stop names, so it cannot cancel a turn that started after it was pressed. Absent when none is running, and for a turn with no run to attach to.","type":"string"},"queue":{"description":"Messages waiting for its next turn, and whether they are held. Absent for a conversation nothing has ever waited for.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false},"updatedAt":{"type":"number","description":"When it last did something, in milliseconds. Reading it does not count."},"seenAt":{"description":"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.","type":"number"},"unsentAt":{"description":"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.","type":"number"},"attention":{"type":"object","properties":{"plan":{"type":"boolean","description":"It has proposed a plan and is waiting for a yes."},"question":{"type":"boolean","description":"It has asked you something."},"permission":{"type":"boolean","description":"It wants to use a tool it needs permission for."},"capability":{"type":"boolean","description":"It needs something connected that is not connected yet."},"credential":{"type":"boolean","description":"It is waiting for a named person to release a credential. The one pause that may not be yours to clear, whatever your role."},"conflict":{"type":"boolean","description":"Its work cannot be merged without somebody resolving a clash."},"need":{"description":"It asked a person for something it still needs: a connection, a secret, wider reach, a tool. Absent from a daemon older than needs.","type":"boolean"}},"required":["plan","question","permission","capability","credential","conflict"],"additionalProperties":false,"description":"Which kinds of waiting-for-you it is doing."},"permissionAsk":{"description":"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.","type":"object","properties":{"requestId":{"type":"string","description":"Which request this is: the id an answer to it names."},"ask":{"type":"string","description":"What it asks to do, on one line: the runtime's own sentence, else the tool's short name."}},"required":["requestId","ask"],"additionalProperties":false},"landFailure":{"description":"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.","type":"object","properties":{"reason":{"type":"string","description":"Why it failed, in the words it failed with."},"code":{"description":"Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise.","type":"string"},"at":{"type":"number","description":"When it failed, in milliseconds."}},"required":["reason","at"],"additionalProperties":false},"delivery":{"description":"What became of its last land in the folder on the owner's computer, for a project folder attached to this computer's sandbox. Absent for any other land, and from a sandbox older than it.","type":"object","properties":{"state":{"type":"string","enum":["delivered","partial","waiting","failed","too-large"]},"at":{"type":"number","description":"When this state was reached, in milliseconds."},"project":{"type":"string"},"folder":{"description":"The folder on the owner's computer, as its machine reported it.","type":"string"},"applied":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files written, merged ones included."},"conflicts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"reason":{"type":"string","enum":["edited","link","outside","missing-git"]}},"required":["path","reason"],"additionalProperties":false}},"point":{"description":"The machine's restore point taken before writing.","type":"string"},"reason":{"description":"Why it failed or waits, in a sentence.","type":"string"}},"required":["state","at","project","applied","conflicts"],"additionalProperties":false},"conflictCauses":{"description":"Why its work will not merge, and so who can clear it: your own uncommitted edits, which only you can commit or stash, against a moved main line or an unmergeable binary, which the conversation can redo on its own copy. Absent unless it is refusing to merge.","type":"array","items":{"type":"string","enum":["workspace","diverged","binary"]}},"unfinished":{"description":"What its last turn left open: steps it never completed. Absent for a turn that finished what it started.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"steps":{"description":"The agent's own checklist where that turn left it. Absent for a conversation that kept no list.","type":"object","properties":{"open":{"type":"number","description":"Items on it that were never completed."},"total":{"type":"number","description":"Items on the whole list."},"next":{"description":"The one it would have done next: what it was working through, or the first still waiting.","type":"string"}},"required":["open","total"],"additionalProperties":false},"check":{"description":"The end-of-turn check that was still failing when the turn ended, by name. No longer written: what a check finds as a turn ends is said back to the model and never leaves its work unfinished.","type":"string"}},"required":["at"],"additionalProperties":false},"proof":{"description":"What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"verification":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to."},"check":{"description":"The command that spoke, when one did, so a targeted test is never read as the whole suite.","type":"string"},"unviewed":{"description":"How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none.","type":"number"}},"required":["at","verification"],"additionalProperties":false},"reach":{"description":"Where its last turn's work went when it went somewhere besides its own branch: files changed live outside it, clones of its own holding work that will not land, pushes it made itself. Absent when everything it did is on its branch.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"live":{"description":"Files it changed in place where every conversation reads them, outside its own branch: an installed extension's copy, the sandbox's shared state, the owner's own checkout. Live the moment they were written, never reviewed, and an installed extension's next update replaces them.","maxItems":10,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What it wrote, as it named it: relative to the workspace for the sandbox's shared state, absolute for the owner's own checkout."},"extension":{"description":"The extension, by its manifest id, when what it changed is the installed copy of one. Absent for anything else.","type":"string"}},"required":["path"],"additionalProperties":false}},"liveMore":{"description":"How many more such files there were beyond the ones listed. Absent when all are listed.","type":"number"},"stranded":{"description":"Repositories it cloned inside its own copy and left work in. A clone of its own never rides its land, so what is in one stays there.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"The clone, as a folder relative to its own copy of the workspace."},"uncommitted":{"type":"number","description":"Files changed in it and never committed."},"unpushed":{"type":"number","description":"Commits in it its upstream does not have, or that no remote has when it follows none."}},"required":["dir","uncommitted","unpushed"],"additionalProperties":false}},"strandedMore":{"description":"How many more such clones there were beyond the ones listed. Absent when all are listed.","type":"number"},"published":{"description":"Pushes it made with git that went through: work it sent out of the sandbox itself, never through a land.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"description":"The folder it pushed from, as the command named it. Absent for its own working folder.","type":"string"},"remote":{"description":"The remote it pushed to, as the command named it. Absent when the command named none.","type":"string"},"branch":{"description":"The branch it pushed to, as the command named it. Absent when the command named none.","type":"string"},"command":{"type":"string","description":"The push itself, trimmed to one line."}},"required":["command"],"additionalProperties":false}},"publishedMore":{"description":"How many more such pushes there were beyond the ones listed. Absent when all are listed.","type":"number"}},"required":["at"],"additionalProperties":false},"turns":{"description":"Turns it has finished.","type":"number"},"toolUses":{"description":"Tools it has used, over its whole life.","type":"number"},"subagents":{"description":"Subagents this one started, in-process and spawned alike. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost.","type":"object","properties":{"running":{"type":"number","description":"Subagents working right now."},"total":{"type":"number","description":"Subagents it has started over its whole life."}},"required":["running","total"],"additionalProperties":false},"diff":{"description":"Everything it has written, measured from where it started. Independent of how much has been merged.","type":"object","properties":{"files":{"type":"number","description":"Files touched."},"insertions":{"type":"number","description":"Lines added."},"deletions":{"type":"number","description":"Lines removed."}},"required":["files","insertions","deletions"],"additionalProperties":false},"landedPresence":{"description":"Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it.","type":"object","properties":{"landed":{"type":"number","description":"Paths this conversation merged in."},"present":{"type":"number","description":"How many of them are still there, either pending or committed."},"removedBy":{"description":"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"id":{"type":"string","description":"The conversation that took them out."},"title":{"description":"What that conversation is called.","type":"string"}},"required":["kind","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"description":"Who it was, as the sandbox verified them. Absent when the request carried no identity.","type":"string"},"name":{"description":"What to call them, when their sign-in carried a name.","type":"string"}},"required":["kind"],"additionalProperties":false}]}},"required":["landed","present"],"additionalProperties":false},"loop":{"description":"The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them.","type":"object","properties":{"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How the loop is going."},"iteration":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which round it is on."},"maxIterations":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many rounds it will attempt before giving up."},"goal":{"type":"string","description":"What it is looping towards."}},"required":["state","iteration","maxIterations","goal"],"additionalProperties":false},"workflow":{"description":"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.","type":"object","properties":{"runId":{"type":"string","description":"The run this belongs to, which is how a board groups its steps together."},"name":{"type":"string","description":"The workflow's name."},"step":{"type":"string","description":"Which step this conversation is on now. It moves when steps are chained."},"index":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"This step's place in the workflow, counting from one."},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many steps the workflow has."}},"required":["runId","name","step","index","total"],"additionalProperties":false},"awaitingWake":{"description":"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.","type":"boolean"},"watches":{"description":"Outside conditions this conversation is parked on, each of which will wake it. Absent means none, which is nearly every conversation: an armed watch is why a finished-looking agent starts working by itself, and why a hosted machine will not go idle.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this watch, the same one the agent was given when it armed it."},"note":{"type":"string","description":"The agent's own line on what it is waiting for."},"intervalSeconds":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How often the check runs."},"deadlineAt":{"type":"number","description":"When it gives up and wakes the conversation anyway, in milliseconds. Every watch has one."}},"required":["id","note","intervalSeconds","deadlineAt"],"additionalProperties":false}},"needs":{"description":"What it is waiting on people for and has not got yet, oldest first. Absent means nothing: an open need is why an idle-looking agent still needs you.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["capability","secret","grant","release","environment"],"description":"What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image."},"title":{"type":"string"},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."}},"required":["id","kind","title","status"],"additionalProperties":false}},"jobs":{"description":"Commands this conversation left running in the background, and how the most recent ones ended. Absent means none since the daemon started. A job is running exactly while it has no end.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this job, the one its transcript row names."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"session":{"type":"string","description":"The terminal session its pane runs in, which is what opening it focuses."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When the command exited, in milliseconds. Absent while it runs.","type":"number"},"exitCode":{"description":"The code it exited with. Absent while it runs, or when its exit left none.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"watch":{"description":"The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.","type":"string"},"handed":{"description":"Left running for the person: the agent kept it for them with the `keep` tool (or it holds a port this conversation already left them), so it outlives the turn, wakes nothing and is theirs to stop.","type":"boolean"},"ports":{"description":"The ports it was listening on when its turn ended.","type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"stoppedBy":{"description":"Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.","type":"string","enum":["turn","person","agent"]},"inputWait":{"description":"Present while it sits blocked reading its terminal with nothing moving: a prompt nobody in the sandbox will answer, which only stopping it or a person typing into its terminal ends. Absent while it runs on, and once it ends.","type":"object","properties":{"since":{"type":"number","description":"When it went still, in milliseconds."},"program":{"type":"string","description":"The process blocked reading the terminal, as its command line."}},"required":["since","program"],"additionalProperties":false}},"required":["id","label","session","startedAt"],"additionalProperties":false}},"archivedAt":{"description":"When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board.","type":"number"}},"required":["id","status","provider","harness","updatedAt","attention"],"additionalProperties":false}}}}}}},"/agents/{id}/react":{"post":{"operationId":"agents.react","summary":"Mark a conversation with an emoji","description":"Puts your mark on a conversation, or takes it back. Everyone sharing the sandbox sees it, with who left it, which is what makes it worth more than a private bookmark. One mark per person per emoji; nothing about the conversation's own work changes.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"emoji":{"type":"string","maxLength":64,"description":"The mark to leave, as the emoji character itself. Exactly one: a chip has room for one mark, and a press is one press."},"on":{"type":"boolean","description":"Whether to add your mark or take it back. Saying what you want rather than flipping what is there, so pressing twice lands where pressing once did."}},"required":["emoji","on"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The conversation id, which is how every other call addresses it."},"sessionId":{"description":"The provider session behind the last turn. It is retired whenever the model or account changes.","type":"string"},"title":{"description":"What to call it: the first prompt cut to one line, unless somebody renamed it.","type":"string"},"titleAction":{"description":"One word for the kind of work the naming pass read in it (fix, audit, redesign). Never part of the displayed name: a board tints and glyphs a card by it. Absent for a title nothing named an action for.","type":"string"},"status":{"type":"string","enum":["idle","running","awaiting","stopping","dismissing","stopped","resuming","landing","ready","landed","conflict","error","interrupted"],"description":"What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; dismissing is the same window for a question waved away, which ends the turn too but owes the user nothing; resuming means the sandbox is already putting right whatever killed the turn; landing means its work is being carried into the workspace right now, and nothing may act on its branch until that settles."},"failure":{"description":"Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.","type":"string"},"failureCode":{"description":"Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.","type":"string"},"limitResetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant.","type":"number"},"limitHeld":{"description":"Whether the refused turn is held whole, so sending again re-runs it instead of appending to it.","type":"boolean"},"limitScheduled":{"description":"Whether the held turn is already booked to go again at the reset, so nobody has to press anything.","type":"boolean"},"limitMoving":{"description":"The account the held turn is being moved to by the owner's policy, while that move is booked.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it runs on."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop it runs on."},"runner":{"description":"The runner this conversation runs on. Absent means this sandbox.","type":"string"},"startIn":{"description":"Which folder it opened in, relative to the workspace root. Absent means the root.","type":"string"},"actsAs":{"description":"Which persona its first turn acted as. Absent for an ordinary chat.","type":"string"},"lastActsAs":{"description":"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.","type":"string"},"model":{"description":"What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked.","type":"string"},"effort":{"description":"How hard that turn was told to think.","type":"string"},"thinking":{"description":"Whether that turn showed its reasoning.","type":"boolean"},"fast":{"description":"Whether that turn asked for higher speed. What was asked for, not what was served.","type":"boolean"},"account":{"description":"Which connected account paid for it.","type":"string"},"branch":{"description":"The branch its private copy works on. Absent for a conversation that works directly in the shared tree.","type":"string"},"autoLand":{"description":"This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case.","type":"boolean"},"limitPolicy":{"type":"string","enum":["wait","resend","move"]},"outagePolicy":{"type":"string","enum":["wait","retry"]},"stopPolicy":{"type":"string","enum":["wait","retry"]},"landRequested":{"description":"A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting.","type":"object","properties":{"email":{"type":"string","description":"Who asked."},"name":{"description":"Their display name.","type":"string"},"at":{"type":"number","description":"When they asked, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"reactions":{"description":"What people have marked this conversation with, one entry per emoji, in the order the emoji were first used. Absent means nobody has marked it, which is most conversations.","type":"array","items":{"type":"object","properties":{"emoji":{"type":"string","description":"The mark itself, one emoji, carried as the character rather than as a name that would need a table on both sides."},"by":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Who it was, as the sandbox verified them."},"name":{"description":"What to call them, when their sign-in carried a name. Absent leaves the address to stand for them.","type":"string"},"at":{"type":"number","description":"When they marked it, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"description":"Everyone wearing this mark, oldest first, each by name. Carried whole rather than as a count, because a chip reading 3 that cannot say whose is a number nobody can answer. Never empty: the last person taking theirs back takes the whole chip with it."}},"required":["emoji","by"],"additionalProperties":false}},"origin":{"description":"Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"startedBy":{"description":"Who asked for the first turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a child another conversation spawned. Absent when nothing was verified (a wake, a loopback caller).","type":"string"},"areas":{"description":"Which named areas of the workspace this conversation was started within, latched from whoever asked for the first turn. Absent means its starter held the whole workspace, which is why a fenced member is not shown it.","type":"array","items":{"type":"string"}},"owner":{"description":"The member answerable for this conversation: set from whoever started it, inherited from the parent by a spawned child, moved by handing it over. Absent means nobody has claimed it yet.","type":"object","properties":{"email":{"type":"string","description":"Who answers for this conversation, as the sandbox verified them."},"name":{"description":"What to call them, when the sign-in that made them its owner carried a name. Absent leaves the address to stand for them.","type":"string"},"since":{"type":"number","description":"When they became its owner, in milliseconds."}},"required":["email","since"],"additionalProperties":false},"forkedFrom":{"description":"The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"type":"string","enum":["then","now"]}},"required":["conversationId","index","files"],"additionalProperties":false},"base":{"description":"The commit its private copy started from, shortened.","type":"string"},"costUsd":{"description":"What it has cost so far, in dollars. A subagent's spend is its own and is not folded in here.","type":"number"},"inputTokens":{"description":"Uncached input tokens, excluding cache reads and cache writes.","type":"number"},"outputTokens":{"description":"Tokens received.","type":"number"},"contextTokens":{"description":"How much of the window the conversation currently fills.","type":"number"},"contextWindow":{"description":"How large that window is.","type":"number"},"promptCache":{"description":"When this conversation's prompt cache was last kept alive and how long it lasts, which together say when picking the conversation up stops being cheap. Absent when the provider publishes nothing to ground it on.","type":"object","properties":{"at":{"type":"number","description":"When its last request touched the provider's prompt cache, in milliseconds."},"ttlMs":{"type":"number","description":"How long that entry lives from `at`, in milliseconds."},"rollsAt":{"description":"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.","type":"number"},"keepableUntil":{"description":"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.","type":"number"}},"required":["at","ttlMs"],"additionalProperties":false},"keepWarm":{"description":"Whether the sandbox is keeping this conversation's prompt cache warm while it sits idle, how that is going, or why it stopped. Absent when nobody asked for it.","type":"object","properties":{"since":{"type":"number","description":"When keeping it warm started, in milliseconds."},"until":{"type":"number","description":"When it stops by itself, in milliseconds: the time asked for, shortened to what the sandbox can honestly keep, which is never past the point where refreshing costs more than re-reading, nor past the date change that rewrites the prompt."},"refreshes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Refreshes sent so far."},"readTokens":{"description":"How much the last refresh read back from the provider's cache, in tokens: the proof the cache was still there.","type":"number"},"ended":{"description":"Why keeping it warm stopped before anyone picked the conversation up. Absent while it is still being kept.","type":"object","properties":{"at":{"type":"number","description":"When it stopped, in milliseconds."},"reason":{"type":"string","enum":["elapsed","allowance","changed","cold","failed"],"description":"Why: `elapsed` the time asked for ran out; `allowance` the account reached the reserve kept for real work, or the provider refused a refresh for its limit; `changed` what the next turn would send no longer matches the cache (a part of the prompt, the date in it, or the conversation's session or account); `cold` the cache was gone, expired before a refresh could run or found missing by one; `failed` a refresh failed."},"detail":{"description":"The specifics, when there are any: which parts of the prompt changed, how full the account was, or the failure's own words.","type":"string"}},"required":["at","reason"],"additionalProperties":false}},"required":["since","until","refreshes"],"additionalProperties":false},"activity":{"description":"What it is doing at this moment.","type":"object","properties":{"tool":{"description":"The last tool it reached for.","type":"string"},"target":{"description":"What it reached for that tool with: a file, a command, a URL.","type":"string"},"todo":{"description":"The item on its own list that it is working through.","type":"string"}},"additionalProperties":false},"checklist":{"description":"How far it is through its own checklist. Absent for a conversation that kept no list, which is most short ones.","type":"object","properties":{"done":{"type":"number","description":"Items it has completed."},"total":{"type":"number","description":"Items on the list. Never zero: a conversation that kept no list carries no clause at all."}},"required":["done","total"],"additionalProperties":false},"landedMessageDraft":{"description":"The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes.","type":"object","properties":{"startedAt":{"type":"number","description":"When the drafting began, in milliseconds."},"steps":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider was asked."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"status":{"type":"string","enum":["asking","answered","refused","skipped"],"description":"How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."},"at":{"description":"When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time.","type":"number"},"ms":{"description":"How long it took. Absent while it is still being asked.","type":"number"},"reason":{"description":"Why it refused, in its own words.","type":"string"}},"required":["provider","model","status"],"additionalProperties":false},"description":"Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."},"outcome":{"description":"How it ended. Absent means it is still going.","type":"string","enum":["written","failed"]},"reason":{"description":"The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words.","type":"string"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"}},"required":["startedAt","steps"],"additionalProperties":false},"landedMessage":{"description":"What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false},"startedAt":{"description":"When the running turn started, in milliseconds. Absent when none is running.","type":"number"},"run":{"description":"The run under way right now: what a stop names, so it cannot cancel a turn that started after it was pressed. Absent when none is running, and for a turn with no run to attach to.","type":"string"},"queue":{"description":"Messages waiting for its next turn, and whether they are held. Absent for a conversation nothing has ever waited for.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false},"updatedAt":{"type":"number","description":"When it last did something, in milliseconds. Reading it does not count."},"seenAt":{"description":"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.","type":"number"},"unsentAt":{"description":"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.","type":"number"},"attention":{"type":"object","properties":{"plan":{"type":"boolean","description":"It has proposed a plan and is waiting for a yes."},"question":{"type":"boolean","description":"It has asked you something."},"permission":{"type":"boolean","description":"It wants to use a tool it needs permission for."},"capability":{"type":"boolean","description":"It needs something connected that is not connected yet."},"credential":{"type":"boolean","description":"It is waiting for a named person to release a credential. The one pause that may not be yours to clear, whatever your role."},"conflict":{"type":"boolean","description":"Its work cannot be merged without somebody resolving a clash."},"need":{"description":"It asked a person for something it still needs: a connection, a secret, wider reach, a tool. Absent from a daemon older than needs.","type":"boolean"}},"required":["plan","question","permission","capability","credential","conflict"],"additionalProperties":false,"description":"Which kinds of waiting-for-you it is doing."},"permissionAsk":{"description":"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.","type":"object","properties":{"requestId":{"type":"string","description":"Which request this is: the id an answer to it names."},"ask":{"type":"string","description":"What it asks to do, on one line: the runtime's own sentence, else the tool's short name."}},"required":["requestId","ask"],"additionalProperties":false},"landFailure":{"description":"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.","type":"object","properties":{"reason":{"type":"string","description":"Why it failed, in the words it failed with."},"code":{"description":"Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise.","type":"string"},"at":{"type":"number","description":"When it failed, in milliseconds."}},"required":["reason","at"],"additionalProperties":false},"delivery":{"description":"What became of its last land in the folder on the owner's computer, for a project folder attached to this computer's sandbox. Absent for any other land, and from a sandbox older than it.","type":"object","properties":{"state":{"type":"string","enum":["delivered","partial","waiting","failed","too-large"]},"at":{"type":"number","description":"When this state was reached, in milliseconds."},"project":{"type":"string"},"folder":{"description":"The folder on the owner's computer, as its machine reported it.","type":"string"},"applied":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files written, merged ones included."},"conflicts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"reason":{"type":"string","enum":["edited","link","outside","missing-git"]}},"required":["path","reason"],"additionalProperties":false}},"point":{"description":"The machine's restore point taken before writing.","type":"string"},"reason":{"description":"Why it failed or waits, in a sentence.","type":"string"}},"required":["state","at","project","applied","conflicts"],"additionalProperties":false},"conflictCauses":{"description":"Why its work will not merge, and so who can clear it: your own uncommitted edits, which only you can commit or stash, against a moved main line or an unmergeable binary, which the conversation can redo on its own copy. Absent unless it is refusing to merge.","type":"array","items":{"type":"string","enum":["workspace","diverged","binary"]}},"unfinished":{"description":"What its last turn left open: steps it never completed. Absent for a turn that finished what it started.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"steps":{"description":"The agent's own checklist where that turn left it. Absent for a conversation that kept no list.","type":"object","properties":{"open":{"type":"number","description":"Items on it that were never completed."},"total":{"type":"number","description":"Items on the whole list."},"next":{"description":"The one it would have done next: what it was working through, or the first still waiting.","type":"string"}},"required":["open","total"],"additionalProperties":false},"check":{"description":"The end-of-turn check that was still failing when the turn ended, by name. No longer written: what a check finds as a turn ends is said back to the model and never leaves its work unfinished.","type":"string"}},"required":["at"],"additionalProperties":false},"proof":{"description":"What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"verification":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to."},"check":{"description":"The command that spoke, when one did, so a targeted test is never read as the whole suite.","type":"string"},"unviewed":{"description":"How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none.","type":"number"}},"required":["at","verification"],"additionalProperties":false},"reach":{"description":"Where its last turn's work went when it went somewhere besides its own branch: files changed live outside it, clones of its own holding work that will not land, pushes it made itself. Absent when everything it did is on its branch.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"live":{"description":"Files it changed in place where every conversation reads them, outside its own branch: an installed extension's copy, the sandbox's shared state, the owner's own checkout. Live the moment they were written, never reviewed, and an installed extension's next update replaces them.","maxItems":10,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What it wrote, as it named it: relative to the workspace for the sandbox's shared state, absolute for the owner's own checkout."},"extension":{"description":"The extension, by its manifest id, when what it changed is the installed copy of one. Absent for anything else.","type":"string"}},"required":["path"],"additionalProperties":false}},"liveMore":{"description":"How many more such files there were beyond the ones listed. Absent when all are listed.","type":"number"},"stranded":{"description":"Repositories it cloned inside its own copy and left work in. A clone of its own never rides its land, so what is in one stays there.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"The clone, as a folder relative to its own copy of the workspace."},"uncommitted":{"type":"number","description":"Files changed in it and never committed."},"unpushed":{"type":"number","description":"Commits in it its upstream does not have, or that no remote has when it follows none."}},"required":["dir","uncommitted","unpushed"],"additionalProperties":false}},"strandedMore":{"description":"How many more such clones there were beyond the ones listed. Absent when all are listed.","type":"number"},"published":{"description":"Pushes it made with git that went through: work it sent out of the sandbox itself, never through a land.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"description":"The folder it pushed from, as the command named it. Absent for its own working folder.","type":"string"},"remote":{"description":"The remote it pushed to, as the command named it. Absent when the command named none.","type":"string"},"branch":{"description":"The branch it pushed to, as the command named it. Absent when the command named none.","type":"string"},"command":{"type":"string","description":"The push itself, trimmed to one line."}},"required":["command"],"additionalProperties":false}},"publishedMore":{"description":"How many more such pushes there were beyond the ones listed. Absent when all are listed.","type":"number"}},"required":["at"],"additionalProperties":false},"turns":{"description":"Turns it has finished.","type":"number"},"toolUses":{"description":"Tools it has used, over its whole life.","type":"number"},"subagents":{"description":"Subagents this one started, in-process and spawned alike. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost.","type":"object","properties":{"running":{"type":"number","description":"Subagents working right now."},"total":{"type":"number","description":"Subagents it has started over its whole life."}},"required":["running","total"],"additionalProperties":false},"diff":{"description":"Everything it has written, measured from where it started. Independent of how much has been merged.","type":"object","properties":{"files":{"type":"number","description":"Files touched."},"insertions":{"type":"number","description":"Lines added."},"deletions":{"type":"number","description":"Lines removed."}},"required":["files","insertions","deletions"],"additionalProperties":false},"landedPresence":{"description":"Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it.","type":"object","properties":{"landed":{"type":"number","description":"Paths this conversation merged in."},"present":{"type":"number","description":"How many of them are still there, either pending or committed."},"removedBy":{"description":"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"id":{"type":"string","description":"The conversation that took them out."},"title":{"description":"What that conversation is called.","type":"string"}},"required":["kind","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"description":"Who it was, as the sandbox verified them. Absent when the request carried no identity.","type":"string"},"name":{"description":"What to call them, when their sign-in carried a name.","type":"string"}},"required":["kind"],"additionalProperties":false}]}},"required":["landed","present"],"additionalProperties":false},"loop":{"description":"The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them.","type":"object","properties":{"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How the loop is going."},"iteration":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which round it is on."},"maxIterations":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many rounds it will attempt before giving up."},"goal":{"type":"string","description":"What it is looping towards."}},"required":["state","iteration","maxIterations","goal"],"additionalProperties":false},"workflow":{"description":"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.","type":"object","properties":{"runId":{"type":"string","description":"The run this belongs to, which is how a board groups its steps together."},"name":{"type":"string","description":"The workflow's name."},"step":{"type":"string","description":"Which step this conversation is on now. It moves when steps are chained."},"index":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"This step's place in the workflow, counting from one."},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many steps the workflow has."}},"required":["runId","name","step","index","total"],"additionalProperties":false},"awaitingWake":{"description":"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.","type":"boolean"},"watches":{"description":"Outside conditions this conversation is parked on, each of which will wake it. Absent means none, which is nearly every conversation: an armed watch is why a finished-looking agent starts working by itself, and why a hosted machine will not go idle.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this watch, the same one the agent was given when it armed it."},"note":{"type":"string","description":"The agent's own line on what it is waiting for."},"intervalSeconds":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How often the check runs."},"deadlineAt":{"type":"number","description":"When it gives up and wakes the conversation anyway, in milliseconds. Every watch has one."}},"required":["id","note","intervalSeconds","deadlineAt"],"additionalProperties":false}},"needs":{"description":"What it is waiting on people for and has not got yet, oldest first. Absent means nothing: an open need is why an idle-looking agent still needs you.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["capability","secret","grant","release","environment"],"description":"What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image."},"title":{"type":"string"},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."}},"required":["id","kind","title","status"],"additionalProperties":false}},"jobs":{"description":"Commands this conversation left running in the background, and how the most recent ones ended. Absent means none since the daemon started. A job is running exactly while it has no end.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this job, the one its transcript row names."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"session":{"type":"string","description":"The terminal session its pane runs in, which is what opening it focuses."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When the command exited, in milliseconds. Absent while it runs.","type":"number"},"exitCode":{"description":"The code it exited with. Absent while it runs, or when its exit left none.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"watch":{"description":"The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.","type":"string"},"handed":{"description":"Left running for the person: the agent kept it for them with the `keep` tool (or it holds a port this conversation already left them), so it outlives the turn, wakes nothing and is theirs to stop.","type":"boolean"},"ports":{"description":"The ports it was listening on when its turn ended.","type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"stoppedBy":{"description":"Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.","type":"string","enum":["turn","person","agent"]},"inputWait":{"description":"Present while it sits blocked reading its terminal with nothing moving: a prompt nobody in the sandbox will answer, which only stopping it or a person typing into its terminal ends. Absent while it runs on, and once it ends.","type":"object","properties":{"since":{"type":"number","description":"When it went still, in milliseconds."},"program":{"type":"string","description":"The process blocked reading the terminal, as its command line."}},"required":["since","program"],"additionalProperties":false}},"required":["id","label","session","startedAt"],"additionalProperties":false}},"archivedAt":{"description":"When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board.","type":"number"}},"required":["id","status","provider","harness","updatedAt","attention"],"additionalProperties":false}}}}}}},"/agents/{id}/discard":{"post":{"operationId":"agents.discard","summary":"Throw a conversation's work away","description":"Deletes the conversation's working copies, its branches and its entry. Nothing is kept. Refused while a turn is running, and refused for a conversation working in the shared tree.","tags":["The fleet"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which conversation."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/agents/archive":{"post":{"operationId":"agents.archive","summary":"Put conversations away","description":"The gentle counterpart to discarding. Commits whatever the conversation still has in progress onto its own branch, releases its working copy, and keeps the entry and the record. Its scratch is not committed and goes with the copy. It leaves the live fleet and joins the archive. Refused for a conversation that is running.","tags":["The fleet"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"minItems":1,"maxItems":500,"type":"array","items":{"type":"string","minLength":1},"description":"Which conversations."}},"required":["ids"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"moved":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The conversation id, which is how every other call addresses it."},"sessionId":{"description":"The provider session behind the last turn. It is retired whenever the model or account changes.","type":"string"},"title":{"description":"What to call it: the first prompt cut to one line, unless somebody renamed it.","type":"string"},"titleAction":{"description":"One word for the kind of work the naming pass read in it (fix, audit, redesign). Never part of the displayed name: a board tints and glyphs a card by it. Absent for a title nothing named an action for.","type":"string"},"status":{"type":"string","enum":["idle","running","awaiting","stopping","dismissing","stopped","resuming","landing","ready","landed","conflict","error","interrupted"],"description":"What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; dismissing is the same window for a question waved away, which ends the turn too but owes the user nothing; resuming means the sandbox is already putting right whatever killed the turn; landing means its work is being carried into the workspace right now, and nothing may act on its branch until that settles."},"failure":{"description":"Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.","type":"string"},"failureCode":{"description":"Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.","type":"string"},"limitResetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant.","type":"number"},"limitHeld":{"description":"Whether the refused turn is held whole, so sending again re-runs it instead of appending to it.","type":"boolean"},"limitScheduled":{"description":"Whether the held turn is already booked to go again at the reset, so nobody has to press anything.","type":"boolean"},"limitMoving":{"description":"The account the held turn is being moved to by the owner's policy, while that move is booked.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it runs on."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop it runs on."},"runner":{"description":"The runner this conversation runs on. Absent means this sandbox.","type":"string"},"startIn":{"description":"Which folder it opened in, relative to the workspace root. Absent means the root.","type":"string"},"actsAs":{"description":"Which persona its first turn acted as. Absent for an ordinary chat.","type":"string"},"lastActsAs":{"description":"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.","type":"string"},"model":{"description":"What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked.","type":"string"},"effort":{"description":"How hard that turn was told to think.","type":"string"},"thinking":{"description":"Whether that turn showed its reasoning.","type":"boolean"},"fast":{"description":"Whether that turn asked for higher speed. What was asked for, not what was served.","type":"boolean"},"account":{"description":"Which connected account paid for it.","type":"string"},"branch":{"description":"The branch its private copy works on. Absent for a conversation that works directly in the shared tree.","type":"string"},"autoLand":{"description":"This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case.","type":"boolean"},"limitPolicy":{"type":"string","enum":["wait","resend","move"]},"outagePolicy":{"type":"string","enum":["wait","retry"]},"stopPolicy":{"type":"string","enum":["wait","retry"]},"landRequested":{"description":"A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting.","type":"object","properties":{"email":{"type":"string","description":"Who asked."},"name":{"description":"Their display name.","type":"string"},"at":{"type":"number","description":"When they asked, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"reactions":{"description":"What people have marked this conversation with, one entry per emoji, in the order the emoji were first used. Absent means nobody has marked it, which is most conversations.","type":"array","items":{"type":"object","properties":{"emoji":{"type":"string","description":"The mark itself, one emoji, carried as the character rather than as a name that would need a table on both sides."},"by":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Who it was, as the sandbox verified them."},"name":{"description":"What to call them, when their sign-in carried a name. Absent leaves the address to stand for them.","type":"string"},"at":{"type":"number","description":"When they marked it, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"description":"Everyone wearing this mark, oldest first, each by name. Carried whole rather than as a count, because a chip reading 3 that cannot say whose is a number nobody can answer. Never empty: the last person taking theirs back takes the whole chip with it."}},"required":["emoji","by"],"additionalProperties":false}},"origin":{"description":"Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"startedBy":{"description":"Who asked for the first turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a child another conversation spawned. Absent when nothing was verified (a wake, a loopback caller).","type":"string"},"areas":{"description":"Which named areas of the workspace this conversation was started within, latched from whoever asked for the first turn. Absent means its starter held the whole workspace, which is why a fenced member is not shown it.","type":"array","items":{"type":"string"}},"owner":{"description":"The member answerable for this conversation: set from whoever started it, inherited from the parent by a spawned child, moved by handing it over. Absent means nobody has claimed it yet.","type":"object","properties":{"email":{"type":"string","description":"Who answers for this conversation, as the sandbox verified them."},"name":{"description":"What to call them, when the sign-in that made them its owner carried a name. Absent leaves the address to stand for them.","type":"string"},"since":{"type":"number","description":"When they became its owner, in milliseconds."}},"required":["email","since"],"additionalProperties":false},"forkedFrom":{"description":"The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"type":"string","enum":["then","now"]}},"required":["conversationId","index","files"],"additionalProperties":false},"base":{"description":"The commit its private copy started from, shortened.","type":"string"},"costUsd":{"description":"What it has cost so far, in dollars. A subagent's spend is its own and is not folded in here.","type":"number"},"inputTokens":{"description":"Uncached input tokens, excluding cache reads and cache writes.","type":"number"},"outputTokens":{"description":"Tokens received.","type":"number"},"contextTokens":{"description":"How much of the window the conversation currently fills.","type":"number"},"contextWindow":{"description":"How large that window is.","type":"number"},"promptCache":{"description":"When this conversation's prompt cache was last kept alive and how long it lasts, which together say when picking the conversation up stops being cheap. Absent when the provider publishes nothing to ground it on.","type":"object","properties":{"at":{"type":"number","description":"When its last request touched the provider's prompt cache, in milliseconds."},"ttlMs":{"type":"number","description":"How long that entry lives from `at`, in milliseconds."},"rollsAt":{"description":"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.","type":"number"},"keepableUntil":{"description":"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.","type":"number"}},"required":["at","ttlMs"],"additionalProperties":false},"keepWarm":{"description":"Whether the sandbox is keeping this conversation's prompt cache warm while it sits idle, how that is going, or why it stopped. Absent when nobody asked for it.","type":"object","properties":{"since":{"type":"number","description":"When keeping it warm started, in milliseconds."},"until":{"type":"number","description":"When it stops by itself, in milliseconds: the time asked for, shortened to what the sandbox can honestly keep, which is never past the point where refreshing costs more than re-reading, nor past the date change that rewrites the prompt."},"refreshes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Refreshes sent so far."},"readTokens":{"description":"How much the last refresh read back from the provider's cache, in tokens: the proof the cache was still there.","type":"number"},"ended":{"description":"Why keeping it warm stopped before anyone picked the conversation up. Absent while it is still being kept.","type":"object","properties":{"at":{"type":"number","description":"When it stopped, in milliseconds."},"reason":{"type":"string","enum":["elapsed","allowance","changed","cold","failed"],"description":"Why: `elapsed` the time asked for ran out; `allowance` the account reached the reserve kept for real work, or the provider refused a refresh for its limit; `changed` what the next turn would send no longer matches the cache (a part of the prompt, the date in it, or the conversation's session or account); `cold` the cache was gone, expired before a refresh could run or found missing by one; `failed` a refresh failed."},"detail":{"description":"The specifics, when there are any: which parts of the prompt changed, how full the account was, or the failure's own words.","type":"string"}},"required":["at","reason"],"additionalProperties":false}},"required":["since","until","refreshes"],"additionalProperties":false},"activity":{"description":"What it is doing at this moment.","type":"object","properties":{"tool":{"description":"The last tool it reached for.","type":"string"},"target":{"description":"What it reached for that tool with: a file, a command, a URL.","type":"string"},"todo":{"description":"The item on its own list that it is working through.","type":"string"}},"additionalProperties":false},"checklist":{"description":"How far it is through its own checklist. Absent for a conversation that kept no list, which is most short ones.","type":"object","properties":{"done":{"type":"number","description":"Items it has completed."},"total":{"type":"number","description":"Items on the list. Never zero: a conversation that kept no list carries no clause at all."}},"required":["done","total"],"additionalProperties":false},"landedMessageDraft":{"description":"The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes.","type":"object","properties":{"startedAt":{"type":"number","description":"When the drafting began, in milliseconds."},"steps":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider was asked."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"status":{"type":"string","enum":["asking","answered","refused","skipped"],"description":"How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."},"at":{"description":"When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time.","type":"number"},"ms":{"description":"How long it took. Absent while it is still being asked.","type":"number"},"reason":{"description":"Why it refused, in its own words.","type":"string"}},"required":["provider","model","status"],"additionalProperties":false},"description":"Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."},"outcome":{"description":"How it ended. Absent means it is still going.","type":"string","enum":["written","failed"]},"reason":{"description":"The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words.","type":"string"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"}},"required":["startedAt","steps"],"additionalProperties":false},"landedMessage":{"description":"What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false},"startedAt":{"description":"When the running turn started, in milliseconds. Absent when none is running.","type":"number"},"run":{"description":"The run under way right now: what a stop names, so it cannot cancel a turn that started after it was pressed. Absent when none is running, and for a turn with no run to attach to.","type":"string"},"queue":{"description":"Messages waiting for its next turn, and whether they are held. Absent for a conversation nothing has ever waited for.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false},"updatedAt":{"type":"number","description":"When it last did something, in milliseconds. Reading it does not count."},"seenAt":{"description":"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.","type":"number"},"unsentAt":{"description":"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.","type":"number"},"attention":{"type":"object","properties":{"plan":{"type":"boolean","description":"It has proposed a plan and is waiting for a yes."},"question":{"type":"boolean","description":"It has asked you something."},"permission":{"type":"boolean","description":"It wants to use a tool it needs permission for."},"capability":{"type":"boolean","description":"It needs something connected that is not connected yet."},"credential":{"type":"boolean","description":"It is waiting for a named person to release a credential. The one pause that may not be yours to clear, whatever your role."},"conflict":{"type":"boolean","description":"Its work cannot be merged without somebody resolving a clash."},"need":{"description":"It asked a person for something it still needs: a connection, a secret, wider reach, a tool. Absent from a daemon older than needs.","type":"boolean"}},"required":["plan","question","permission","capability","credential","conflict"],"additionalProperties":false,"description":"Which kinds of waiting-for-you it is doing."},"permissionAsk":{"description":"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.","type":"object","properties":{"requestId":{"type":"string","description":"Which request this is: the id an answer to it names."},"ask":{"type":"string","description":"What it asks to do, on one line: the runtime's own sentence, else the tool's short name."}},"required":["requestId","ask"],"additionalProperties":false},"landFailure":{"description":"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.","type":"object","properties":{"reason":{"type":"string","description":"Why it failed, in the words it failed with."},"code":{"description":"Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise.","type":"string"},"at":{"type":"number","description":"When it failed, in milliseconds."}},"required":["reason","at"],"additionalProperties":false},"delivery":{"description":"What became of its last land in the folder on the owner's computer, for a project folder attached to this computer's sandbox. Absent for any other land, and from a sandbox older than it.","type":"object","properties":{"state":{"type":"string","enum":["delivered","partial","waiting","failed","too-large"]},"at":{"type":"number","description":"When this state was reached, in milliseconds."},"project":{"type":"string"},"folder":{"description":"The folder on the owner's computer, as its machine reported it.","type":"string"},"applied":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files written, merged ones included."},"conflicts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"reason":{"type":"string","enum":["edited","link","outside","missing-git"]}},"required":["path","reason"],"additionalProperties":false}},"point":{"description":"The machine's restore point taken before writing.","type":"string"},"reason":{"description":"Why it failed or waits, in a sentence.","type":"string"}},"required":["state","at","project","applied","conflicts"],"additionalProperties":false},"conflictCauses":{"description":"Why its work will not merge, and so who can clear it: your own uncommitted edits, which only you can commit or stash, against a moved main line or an unmergeable binary, which the conversation can redo on its own copy. Absent unless it is refusing to merge.","type":"array","items":{"type":"string","enum":["workspace","diverged","binary"]}},"unfinished":{"description":"What its last turn left open: steps it never completed. Absent for a turn that finished what it started.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"steps":{"description":"The agent's own checklist where that turn left it. Absent for a conversation that kept no list.","type":"object","properties":{"open":{"type":"number","description":"Items on it that were never completed."},"total":{"type":"number","description":"Items on the whole list."},"next":{"description":"The one it would have done next: what it was working through, or the first still waiting.","type":"string"}},"required":["open","total"],"additionalProperties":false},"check":{"description":"The end-of-turn check that was still failing when the turn ended, by name. No longer written: what a check finds as a turn ends is said back to the model and never leaves its work unfinished.","type":"string"}},"required":["at"],"additionalProperties":false},"proof":{"description":"What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"verification":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to."},"check":{"description":"The command that spoke, when one did, so a targeted test is never read as the whole suite.","type":"string"},"unviewed":{"description":"How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none.","type":"number"}},"required":["at","verification"],"additionalProperties":false},"reach":{"description":"Where its last turn's work went when it went somewhere besides its own branch: files changed live outside it, clones of its own holding work that will not land, pushes it made itself. Absent when everything it did is on its branch.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"live":{"description":"Files it changed in place where every conversation reads them, outside its own branch: an installed extension's copy, the sandbox's shared state, the owner's own checkout. Live the moment they were written, never reviewed, and an installed extension's next update replaces them.","maxItems":10,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What it wrote, as it named it: relative to the workspace for the sandbox's shared state, absolute for the owner's own checkout."},"extension":{"description":"The extension, by its manifest id, when what it changed is the installed copy of one. Absent for anything else.","type":"string"}},"required":["path"],"additionalProperties":false}},"liveMore":{"description":"How many more such files there were beyond the ones listed. Absent when all are listed.","type":"number"},"stranded":{"description":"Repositories it cloned inside its own copy and left work in. A clone of its own never rides its land, so what is in one stays there.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"The clone, as a folder relative to its own copy of the workspace."},"uncommitted":{"type":"number","description":"Files changed in it and never committed."},"unpushed":{"type":"number","description":"Commits in it its upstream does not have, or that no remote has when it follows none."}},"required":["dir","uncommitted","unpushed"],"additionalProperties":false}},"strandedMore":{"description":"How many more such clones there were beyond the ones listed. Absent when all are listed.","type":"number"},"published":{"description":"Pushes it made with git that went through: work it sent out of the sandbox itself, never through a land.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"description":"The folder it pushed from, as the command named it. Absent for its own working folder.","type":"string"},"remote":{"description":"The remote it pushed to, as the command named it. Absent when the command named none.","type":"string"},"branch":{"description":"The branch it pushed to, as the command named it. Absent when the command named none.","type":"string"},"command":{"type":"string","description":"The push itself, trimmed to one line."}},"required":["command"],"additionalProperties":false}},"publishedMore":{"description":"How many more such pushes there were beyond the ones listed. Absent when all are listed.","type":"number"}},"required":["at"],"additionalProperties":false},"turns":{"description":"Turns it has finished.","type":"number"},"toolUses":{"description":"Tools it has used, over its whole life.","type":"number"},"subagents":{"description":"Subagents this one started, in-process and spawned alike. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost.","type":"object","properties":{"running":{"type":"number","description":"Subagents working right now."},"total":{"type":"number","description":"Subagents it has started over its whole life."}},"required":["running","total"],"additionalProperties":false},"diff":{"description":"Everything it has written, measured from where it started. Independent of how much has been merged.","type":"object","properties":{"files":{"type":"number","description":"Files touched."},"insertions":{"type":"number","description":"Lines added."},"deletions":{"type":"number","description":"Lines removed."}},"required":["files","insertions","deletions"],"additionalProperties":false},"landedPresence":{"description":"Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it.","type":"object","properties":{"landed":{"type":"number","description":"Paths this conversation merged in."},"present":{"type":"number","description":"How many of them are still there, either pending or committed."},"removedBy":{"description":"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"id":{"type":"string","description":"The conversation that took them out."},"title":{"description":"What that conversation is called.","type":"string"}},"required":["kind","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"description":"Who it was, as the sandbox verified them. Absent when the request carried no identity.","type":"string"},"name":{"description":"What to call them, when their sign-in carried a name.","type":"string"}},"required":["kind"],"additionalProperties":false}]}},"required":["landed","present"],"additionalProperties":false},"loop":{"description":"The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them.","type":"object","properties":{"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How the loop is going."},"iteration":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which round it is on."},"maxIterations":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many rounds it will attempt before giving up."},"goal":{"type":"string","description":"What it is looping towards."}},"required":["state","iteration","maxIterations","goal"],"additionalProperties":false},"workflow":{"description":"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.","type":"object","properties":{"runId":{"type":"string","description":"The run this belongs to, which is how a board groups its steps together."},"name":{"type":"string","description":"The workflow's name."},"step":{"type":"string","description":"Which step this conversation is on now. It moves when steps are chained."},"index":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"This step's place in the workflow, counting from one."},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many steps the workflow has."}},"required":["runId","name","step","index","total"],"additionalProperties":false},"awaitingWake":{"description":"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.","type":"boolean"},"watches":{"description":"Outside conditions this conversation is parked on, each of which will wake it. Absent means none, which is nearly every conversation: an armed watch is why a finished-looking agent starts working by itself, and why a hosted machine will not go idle.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this watch, the same one the agent was given when it armed it."},"note":{"type":"string","description":"The agent's own line on what it is waiting for."},"intervalSeconds":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How often the check runs."},"deadlineAt":{"type":"number","description":"When it gives up and wakes the conversation anyway, in milliseconds. Every watch has one."}},"required":["id","note","intervalSeconds","deadlineAt"],"additionalProperties":false}},"needs":{"description":"What it is waiting on people for and has not got yet, oldest first. Absent means nothing: an open need is why an idle-looking agent still needs you.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["capability","secret","grant","release","environment"],"description":"What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image."},"title":{"type":"string"},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."}},"required":["id","kind","title","status"],"additionalProperties":false}},"jobs":{"description":"Commands this conversation left running in the background, and how the most recent ones ended. Absent means none since the daemon started. A job is running exactly while it has no end.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this job, the one its transcript row names."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"session":{"type":"string","description":"The terminal session its pane runs in, which is what opening it focuses."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When the command exited, in milliseconds. Absent while it runs.","type":"number"},"exitCode":{"description":"The code it exited with. Absent while it runs, or when its exit left none.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"watch":{"description":"The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.","type":"string"},"handed":{"description":"Left running for the person: the agent kept it for them with the `keep` tool (or it holds a port this conversation already left them), so it outlives the turn, wakes nothing and is theirs to stop.","type":"boolean"},"ports":{"description":"The ports it was listening on when its turn ended.","type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"stoppedBy":{"description":"Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.","type":"string","enum":["turn","person","agent"]},"inputWait":{"description":"Present while it sits blocked reading its terminal with nothing moving: a prompt nobody in the sandbox will answer, which only stopping it or a person typing into its terminal ends. Absent while it runs on, and once it ends.","type":"object","properties":{"since":{"type":"number","description":"When it went still, in milliseconds."},"program":{"type":"string","description":"The process blocked reading the terminal, as its command line."}},"required":["since","program"],"additionalProperties":false}},"required":["id","label","session","startedAt"],"additionalProperties":false}},"archivedAt":{"description":"When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board.","type":"number"}},"required":["id","status","provider","harness","updatedAt","attention"],"additionalProperties":false},"description":"What actually moved, whole, rather than the fleet afterwards. Two archives finishing at once would each carry a snapshot from a different instant, and swapping one in wholesale would let the slower answer resurrect what the faster one just filed away."},"rev":{"type":"number","description":"The version of the fleet that includes this move, so a caller can hold its own optimistic change until it sees a list at least that new."},"failed":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Which conversation stayed on the board."},"reason":{"type":"string","description":"Why its working copy could not be released, in the words the failure came with."}},"required":["id","reason"],"additionalProperties":false},"description":"The conversations this press could not put away, each with the reason, so the board can say it instead of reporting silence."}},"required":["moved","rev","failed"],"additionalProperties":false}}}}}}},"/agents/unarchive":{"post":{"operationId":"agents.unarchive","summary":"Bring conversations back","description":"Returns archived conversations to the live fleet. The next turn picks up a fresh working copy from the branch that was kept.","tags":["The fleet"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"minItems":1,"maxItems":500,"type":"array","items":{"type":"string","minLength":1},"description":"Which conversations."}},"required":["ids"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"moved":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The conversation id, which is how every other call addresses it."},"sessionId":{"description":"The provider session behind the last turn. It is retired whenever the model or account changes.","type":"string"},"title":{"description":"What to call it: the first prompt cut to one line, unless somebody renamed it.","type":"string"},"titleAction":{"description":"One word for the kind of work the naming pass read in it (fix, audit, redesign). Never part of the displayed name: a board tints and glyphs a card by it. Absent for a title nothing named an action for.","type":"string"},"status":{"type":"string","enum":["idle","running","awaiting","stopping","dismissing","stopped","resuming","landing","ready","landed","conflict","error","interrupted"],"description":"What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; dismissing is the same window for a question waved away, which ends the turn too but owes the user nothing; resuming means the sandbox is already putting right whatever killed the turn; landing means its work is being carried into the workspace right now, and nothing may act on its branch until that settles."},"failure":{"description":"Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.","type":"string"},"failureCode":{"description":"Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.","type":"string"},"limitResetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant.","type":"number"},"limitHeld":{"description":"Whether the refused turn is held whole, so sending again re-runs it instead of appending to it.","type":"boolean"},"limitScheduled":{"description":"Whether the held turn is already booked to go again at the reset, so nobody has to press anything.","type":"boolean"},"limitMoving":{"description":"The account the held turn is being moved to by the owner's policy, while that move is booked.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it runs on."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop it runs on."},"runner":{"description":"The runner this conversation runs on. Absent means this sandbox.","type":"string"},"startIn":{"description":"Which folder it opened in, relative to the workspace root. Absent means the root.","type":"string"},"actsAs":{"description":"Which persona its first turn acted as. Absent for an ordinary chat.","type":"string"},"lastActsAs":{"description":"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.","type":"string"},"model":{"description":"What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked.","type":"string"},"effort":{"description":"How hard that turn was told to think.","type":"string"},"thinking":{"description":"Whether that turn showed its reasoning.","type":"boolean"},"fast":{"description":"Whether that turn asked for higher speed. What was asked for, not what was served.","type":"boolean"},"account":{"description":"Which connected account paid for it.","type":"string"},"branch":{"description":"The branch its private copy works on. Absent for a conversation that works directly in the shared tree.","type":"string"},"autoLand":{"description":"This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case.","type":"boolean"},"limitPolicy":{"type":"string","enum":["wait","resend","move"]},"outagePolicy":{"type":"string","enum":["wait","retry"]},"stopPolicy":{"type":"string","enum":["wait","retry"]},"landRequested":{"description":"A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting.","type":"object","properties":{"email":{"type":"string","description":"Who asked."},"name":{"description":"Their display name.","type":"string"},"at":{"type":"number","description":"When they asked, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"reactions":{"description":"What people have marked this conversation with, one entry per emoji, in the order the emoji were first used. Absent means nobody has marked it, which is most conversations.","type":"array","items":{"type":"object","properties":{"emoji":{"type":"string","description":"The mark itself, one emoji, carried as the character rather than as a name that would need a table on both sides."},"by":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Who it was, as the sandbox verified them."},"name":{"description":"What to call them, when their sign-in carried a name. Absent leaves the address to stand for them.","type":"string"},"at":{"type":"number","description":"When they marked it, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"description":"Everyone wearing this mark, oldest first, each by name. Carried whole rather than as a count, because a chip reading 3 that cannot say whose is a number nobody can answer. Never empty: the last person taking theirs back takes the whole chip with it."}},"required":["emoji","by"],"additionalProperties":false}},"origin":{"description":"Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"startedBy":{"description":"Who asked for the first turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a child another conversation spawned. Absent when nothing was verified (a wake, a loopback caller).","type":"string"},"areas":{"description":"Which named areas of the workspace this conversation was started within, latched from whoever asked for the first turn. Absent means its starter held the whole workspace, which is why a fenced member is not shown it.","type":"array","items":{"type":"string"}},"owner":{"description":"The member answerable for this conversation: set from whoever started it, inherited from the parent by a spawned child, moved by handing it over. Absent means nobody has claimed it yet.","type":"object","properties":{"email":{"type":"string","description":"Who answers for this conversation, as the sandbox verified them."},"name":{"description":"What to call them, when the sign-in that made them its owner carried a name. Absent leaves the address to stand for them.","type":"string"},"since":{"type":"number","description":"When they became its owner, in milliseconds."}},"required":["email","since"],"additionalProperties":false},"forkedFrom":{"description":"The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"type":"string","enum":["then","now"]}},"required":["conversationId","index","files"],"additionalProperties":false},"base":{"description":"The commit its private copy started from, shortened.","type":"string"},"costUsd":{"description":"What it has cost so far, in dollars. A subagent's spend is its own and is not folded in here.","type":"number"},"inputTokens":{"description":"Uncached input tokens, excluding cache reads and cache writes.","type":"number"},"outputTokens":{"description":"Tokens received.","type":"number"},"contextTokens":{"description":"How much of the window the conversation currently fills.","type":"number"},"contextWindow":{"description":"How large that window is.","type":"number"},"promptCache":{"description":"When this conversation's prompt cache was last kept alive and how long it lasts, which together say when picking the conversation up stops being cheap. Absent when the provider publishes nothing to ground it on.","type":"object","properties":{"at":{"type":"number","description":"When its last request touched the provider's prompt cache, in milliseconds."},"ttlMs":{"type":"number","description":"How long that entry lives from `at`, in milliseconds."},"rollsAt":{"description":"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.","type":"number"},"keepableUntil":{"description":"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.","type":"number"}},"required":["at","ttlMs"],"additionalProperties":false},"keepWarm":{"description":"Whether the sandbox is keeping this conversation's prompt cache warm while it sits idle, how that is going, or why it stopped. Absent when nobody asked for it.","type":"object","properties":{"since":{"type":"number","description":"When keeping it warm started, in milliseconds."},"until":{"type":"number","description":"When it stops by itself, in milliseconds: the time asked for, shortened to what the sandbox can honestly keep, which is never past the point where refreshing costs more than re-reading, nor past the date change that rewrites the prompt."},"refreshes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Refreshes sent so far."},"readTokens":{"description":"How much the last refresh read back from the provider's cache, in tokens: the proof the cache was still there.","type":"number"},"ended":{"description":"Why keeping it warm stopped before anyone picked the conversation up. Absent while it is still being kept.","type":"object","properties":{"at":{"type":"number","description":"When it stopped, in milliseconds."},"reason":{"type":"string","enum":["elapsed","allowance","changed","cold","failed"],"description":"Why: `elapsed` the time asked for ran out; `allowance` the account reached the reserve kept for real work, or the provider refused a refresh for its limit; `changed` what the next turn would send no longer matches the cache (a part of the prompt, the date in it, or the conversation's session or account); `cold` the cache was gone, expired before a refresh could run or found missing by one; `failed` a refresh failed."},"detail":{"description":"The specifics, when there are any: which parts of the prompt changed, how full the account was, or the failure's own words.","type":"string"}},"required":["at","reason"],"additionalProperties":false}},"required":["since","until","refreshes"],"additionalProperties":false},"activity":{"description":"What it is doing at this moment.","type":"object","properties":{"tool":{"description":"The last tool it reached for.","type":"string"},"target":{"description":"What it reached for that tool with: a file, a command, a URL.","type":"string"},"todo":{"description":"The item on its own list that it is working through.","type":"string"}},"additionalProperties":false},"checklist":{"description":"How far it is through its own checklist. Absent for a conversation that kept no list, which is most short ones.","type":"object","properties":{"done":{"type":"number","description":"Items it has completed."},"total":{"type":"number","description":"Items on the list. Never zero: a conversation that kept no list carries no clause at all."}},"required":["done","total"],"additionalProperties":false},"landedMessageDraft":{"description":"The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes.","type":"object","properties":{"startedAt":{"type":"number","description":"When the drafting began, in milliseconds."},"steps":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider was asked."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"status":{"type":"string","enum":["asking","answered","refused","skipped"],"description":"How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."},"at":{"description":"When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time.","type":"number"},"ms":{"description":"How long it took. Absent while it is still being asked.","type":"number"},"reason":{"description":"Why it refused, in its own words.","type":"string"}},"required":["provider","model","status"],"additionalProperties":false},"description":"Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."},"outcome":{"description":"How it ended. Absent means it is still going.","type":"string","enum":["written","failed"]},"reason":{"description":"The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words.","type":"string"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"}},"required":["startedAt","steps"],"additionalProperties":false},"landedMessage":{"description":"What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false},"startedAt":{"description":"When the running turn started, in milliseconds. Absent when none is running.","type":"number"},"run":{"description":"The run under way right now: what a stop names, so it cannot cancel a turn that started after it was pressed. Absent when none is running, and for a turn with no run to attach to.","type":"string"},"queue":{"description":"Messages waiting for its next turn, and whether they are held. Absent for a conversation nothing has ever waited for.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false},"updatedAt":{"type":"number","description":"When it last did something, in milliseconds. Reading it does not count."},"seenAt":{"description":"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.","type":"number"},"unsentAt":{"description":"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.","type":"number"},"attention":{"type":"object","properties":{"plan":{"type":"boolean","description":"It has proposed a plan and is waiting for a yes."},"question":{"type":"boolean","description":"It has asked you something."},"permission":{"type":"boolean","description":"It wants to use a tool it needs permission for."},"capability":{"type":"boolean","description":"It needs something connected that is not connected yet."},"credential":{"type":"boolean","description":"It is waiting for a named person to release a credential. The one pause that may not be yours to clear, whatever your role."},"conflict":{"type":"boolean","description":"Its work cannot be merged without somebody resolving a clash."},"need":{"description":"It asked a person for something it still needs: a connection, a secret, wider reach, a tool. Absent from a daemon older than needs.","type":"boolean"}},"required":["plan","question","permission","capability","credential","conflict"],"additionalProperties":false,"description":"Which kinds of waiting-for-you it is doing."},"permissionAsk":{"description":"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.","type":"object","properties":{"requestId":{"type":"string","description":"Which request this is: the id an answer to it names."},"ask":{"type":"string","description":"What it asks to do, on one line: the runtime's own sentence, else the tool's short name."}},"required":["requestId","ask"],"additionalProperties":false},"landFailure":{"description":"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.","type":"object","properties":{"reason":{"type":"string","description":"Why it failed, in the words it failed with."},"code":{"description":"Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise.","type":"string"},"at":{"type":"number","description":"When it failed, in milliseconds."}},"required":["reason","at"],"additionalProperties":false},"delivery":{"description":"What became of its last land in the folder on the owner's computer, for a project folder attached to this computer's sandbox. Absent for any other land, and from a sandbox older than it.","type":"object","properties":{"state":{"type":"string","enum":["delivered","partial","waiting","failed","too-large"]},"at":{"type":"number","description":"When this state was reached, in milliseconds."},"project":{"type":"string"},"folder":{"description":"The folder on the owner's computer, as its machine reported it.","type":"string"},"applied":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files written, merged ones included."},"conflicts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"reason":{"type":"string","enum":["edited","link","outside","missing-git"]}},"required":["path","reason"],"additionalProperties":false}},"point":{"description":"The machine's restore point taken before writing.","type":"string"},"reason":{"description":"Why it failed or waits, in a sentence.","type":"string"}},"required":["state","at","project","applied","conflicts"],"additionalProperties":false},"conflictCauses":{"description":"Why its work will not merge, and so who can clear it: your own uncommitted edits, which only you can commit or stash, against a moved main line or an unmergeable binary, which the conversation can redo on its own copy. Absent unless it is refusing to merge.","type":"array","items":{"type":"string","enum":["workspace","diverged","binary"]}},"unfinished":{"description":"What its last turn left open: steps it never completed. Absent for a turn that finished what it started.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"steps":{"description":"The agent's own checklist where that turn left it. Absent for a conversation that kept no list.","type":"object","properties":{"open":{"type":"number","description":"Items on it that were never completed."},"total":{"type":"number","description":"Items on the whole list."},"next":{"description":"The one it would have done next: what it was working through, or the first still waiting.","type":"string"}},"required":["open","total"],"additionalProperties":false},"check":{"description":"The end-of-turn check that was still failing when the turn ended, by name. No longer written: what a check finds as a turn ends is said back to the model and never leaves its work unfinished.","type":"string"}},"required":["at"],"additionalProperties":false},"proof":{"description":"What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"verification":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to."},"check":{"description":"The command that spoke, when one did, so a targeted test is never read as the whole suite.","type":"string"},"unviewed":{"description":"How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none.","type":"number"}},"required":["at","verification"],"additionalProperties":false},"reach":{"description":"Where its last turn's work went when it went somewhere besides its own branch: files changed live outside it, clones of its own holding work that will not land, pushes it made itself. Absent when everything it did is on its branch.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"live":{"description":"Files it changed in place where every conversation reads them, outside its own branch: an installed extension's copy, the sandbox's shared state, the owner's own checkout. Live the moment they were written, never reviewed, and an installed extension's next update replaces them.","maxItems":10,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What it wrote, as it named it: relative to the workspace for the sandbox's shared state, absolute for the owner's own checkout."},"extension":{"description":"The extension, by its manifest id, when what it changed is the installed copy of one. Absent for anything else.","type":"string"}},"required":["path"],"additionalProperties":false}},"liveMore":{"description":"How many more such files there were beyond the ones listed. Absent when all are listed.","type":"number"},"stranded":{"description":"Repositories it cloned inside its own copy and left work in. A clone of its own never rides its land, so what is in one stays there.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"The clone, as a folder relative to its own copy of the workspace."},"uncommitted":{"type":"number","description":"Files changed in it and never committed."},"unpushed":{"type":"number","description":"Commits in it its upstream does not have, or that no remote has when it follows none."}},"required":["dir","uncommitted","unpushed"],"additionalProperties":false}},"strandedMore":{"description":"How many more such clones there were beyond the ones listed. Absent when all are listed.","type":"number"},"published":{"description":"Pushes it made with git that went through: work it sent out of the sandbox itself, never through a land.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"description":"The folder it pushed from, as the command named it. Absent for its own working folder.","type":"string"},"remote":{"description":"The remote it pushed to, as the command named it. Absent when the command named none.","type":"string"},"branch":{"description":"The branch it pushed to, as the command named it. Absent when the command named none.","type":"string"},"command":{"type":"string","description":"The push itself, trimmed to one line."}},"required":["command"],"additionalProperties":false}},"publishedMore":{"description":"How many more such pushes there were beyond the ones listed. Absent when all are listed.","type":"number"}},"required":["at"],"additionalProperties":false},"turns":{"description":"Turns it has finished.","type":"number"},"toolUses":{"description":"Tools it has used, over its whole life.","type":"number"},"subagents":{"description":"Subagents this one started, in-process and spawned alike. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost.","type":"object","properties":{"running":{"type":"number","description":"Subagents working right now."},"total":{"type":"number","description":"Subagents it has started over its whole life."}},"required":["running","total"],"additionalProperties":false},"diff":{"description":"Everything it has written, measured from where it started. Independent of how much has been merged.","type":"object","properties":{"files":{"type":"number","description":"Files touched."},"insertions":{"type":"number","description":"Lines added."},"deletions":{"type":"number","description":"Lines removed."}},"required":["files","insertions","deletions"],"additionalProperties":false},"landedPresence":{"description":"Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it.","type":"object","properties":{"landed":{"type":"number","description":"Paths this conversation merged in."},"present":{"type":"number","description":"How many of them are still there, either pending or committed."},"removedBy":{"description":"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"id":{"type":"string","description":"The conversation that took them out."},"title":{"description":"What that conversation is called.","type":"string"}},"required":["kind","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"description":"Who it was, as the sandbox verified them. Absent when the request carried no identity.","type":"string"},"name":{"description":"What to call them, when their sign-in carried a name.","type":"string"}},"required":["kind"],"additionalProperties":false}]}},"required":["landed","present"],"additionalProperties":false},"loop":{"description":"The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them.","type":"object","properties":{"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How the loop is going."},"iteration":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which round it is on."},"maxIterations":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many rounds it will attempt before giving up."},"goal":{"type":"string","description":"What it is looping towards."}},"required":["state","iteration","maxIterations","goal"],"additionalProperties":false},"workflow":{"description":"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.","type":"object","properties":{"runId":{"type":"string","description":"The run this belongs to, which is how a board groups its steps together."},"name":{"type":"string","description":"The workflow's name."},"step":{"type":"string","description":"Which step this conversation is on now. It moves when steps are chained."},"index":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"This step's place in the workflow, counting from one."},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many steps the workflow has."}},"required":["runId","name","step","index","total"],"additionalProperties":false},"awaitingWake":{"description":"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.","type":"boolean"},"watches":{"description":"Outside conditions this conversation is parked on, each of which will wake it. Absent means none, which is nearly every conversation: an armed watch is why a finished-looking agent starts working by itself, and why a hosted machine will not go idle.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this watch, the same one the agent was given when it armed it."},"note":{"type":"string","description":"The agent's own line on what it is waiting for."},"intervalSeconds":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How often the check runs."},"deadlineAt":{"type":"number","description":"When it gives up and wakes the conversation anyway, in milliseconds. Every watch has one."}},"required":["id","note","intervalSeconds","deadlineAt"],"additionalProperties":false}},"needs":{"description":"What it is waiting on people for and has not got yet, oldest first. Absent means nothing: an open need is why an idle-looking agent still needs you.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["capability","secret","grant","release","environment"],"description":"What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image."},"title":{"type":"string"},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."}},"required":["id","kind","title","status"],"additionalProperties":false}},"jobs":{"description":"Commands this conversation left running in the background, and how the most recent ones ended. Absent means none since the daemon started. A job is running exactly while it has no end.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this job, the one its transcript row names."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"session":{"type":"string","description":"The terminal session its pane runs in, which is what opening it focuses."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When the command exited, in milliseconds. Absent while it runs.","type":"number"},"exitCode":{"description":"The code it exited with. Absent while it runs, or when its exit left none.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"watch":{"description":"The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.","type":"string"},"handed":{"description":"Left running for the person: the agent kept it for them with the `keep` tool (or it holds a port this conversation already left them), so it outlives the turn, wakes nothing and is theirs to stop.","type":"boolean"},"ports":{"description":"The ports it was listening on when its turn ended.","type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"stoppedBy":{"description":"Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.","type":"string","enum":["turn","person","agent"]},"inputWait":{"description":"Present while it sits blocked reading its terminal with nothing moving: a prompt nobody in the sandbox will answer, which only stopping it or a person typing into its terminal ends. Absent while it runs on, and once it ends.","type":"object","properties":{"since":{"type":"number","description":"When it went still, in milliseconds."},"program":{"type":"string","description":"The process blocked reading the terminal, as its command line."}},"required":["since","program"],"additionalProperties":false}},"required":["id","label","session","startedAt"],"additionalProperties":false}},"archivedAt":{"description":"When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board.","type":"number"}},"required":["id","status","provider","harness","updatedAt","attention"],"additionalProperties":false},"description":"What actually moved, whole, rather than the fleet afterwards. Two archives finishing at once would each carry a snapshot from a different instant, and swapping one in wholesale would let the slower answer resurrect what the faster one just filed away."},"rev":{"type":"number","description":"The version of the fleet that includes this move, so a caller can hold its own optimistic change until it sees a list at least that new."}},"required":["moved","rev"],"additionalProperties":false}}}}}}},"/agents/purge":{"post":{"operationId":"agents.purge","summary":"Empty the archive for good","description":"Discards every conversation already in the archive: working copies, branches and entries. The whole archive rather than a chosen few, because the archive is the pile somebody has already decided is over. A teardown that fails on one conversation leaves that one behind instead of taking the rest down with it.","tags":["The fleet"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"removed":{"type":"array","items":{"type":"string"},"description":"Which conversations were deleted, as ids. Ids rather than whole cards, because these no longer exist anywhere: there is nothing left to show and nothing to put back."}},"required":["removed"],"additionalProperties":false}}}}}}},"/sessions":{"get":{"operationId":"sessions.list","summary":"Past conversations in this workspace","description":"Summaries for a history menu, filtered when you pass a search. Covers conversations that worked in their own private copies too, so nothing is hidden just because it happened on a branch.","tags":["Past sessions"],"parameters":[{"name":"query","in":"query","schema":{"type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"caseSensitive","in":"query","schema":{"type":"string"},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"sessions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Its id."},"title":{"type":"string","description":"What it is called."},"updatedAt":{"type":"number","description":"When it last moved, in milliseconds."},"snippet":{"description":"Why a search matched: the line it hit, with a little around it, and who said it. Absent on an unfiltered list, and on a match the title already shows, where repeating it would be noise rather than evidence.","type":"object","properties":{"text":{"type":"string","description":"The matching line, with a little either side of it."},"speaker":{"type":"string","enum":["user","agent"],"description":"Who said it. Carried with the words rather than beside them, because a line of the agent's prose under a card reads as something you typed until the row says otherwise."}},"required":["text","speaker"],"additionalProperties":false}},"required":["id","title","updatedAt"],"additionalProperties":false},"description":"Past conversations, newest first."}},"required":["sessions"],"additionalProperties":false}}}}}}},"/sessions/{id}":{"get":{"operationId":"sessions.get","summary":"Read one past conversation","description":"The full record of a single conversation, restored for display.","tags":["Past sessions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which past conversation."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"type":"object","properties":{"role":{"type":"string","enum":["user","assistant","notice"],"description":"Who said it. A notice is neither side: it is something that happened to the turn, recorded so a reopened conversation can say it. Without those, a turn a provider refused ends on the user's message and reads as broken."},"text":{"type":"string","description":"The words."},"run":{"description":"The run that produced this row. Present on everything a turn produced, absent on rows written outside one. A client draws a run's rows over whatever it already holds for that run, which is what this identifies; content cannot, because the last row of a live run keeps growing.","type":"string"},"sentAt":{"description":"When it was sent, in milliseconds. On the user's rows only, because that is the only moment actually known: a turn's own frames arrive with no clock, so stamping the agent's rows could only ever mean the whole turn's start or end.","type":"number"},"messageId":{"description":"The message's own id, on the rows of messages sent to the agent: what its sender named it, or what the sandbox did. A rewind names the message by it, and the same id sent again is recognised rather than delivered twice.","type":"string"},"attachments":{"description":"Files attached to this message, as workspace paths.","type":"array","items":{"type":"string"}},"checkpointId":{"description":"The saved point this message can be rewound to. Looked up on each read rather than stored, so what is offered is exactly what is still there to go back to.","type":"string"},"rewindIndex":{"description":"This message's position in the conversation's record, which is how a rewind names it. Present only beside a checkpoint.","type":"integer","minimum":0,"maximum":9007199254740991},"thinking":{"description":"What the agent was reasoning about.","type":"string"},"tools":{"description":"The tool calls this part of the turn made.","type":"array","items":{"$ref":"#/$defs/__schema0"}},"todos":{"description":"The agent's task checklist, as of this bubble.","type":"array","items":{"type":"object","properties":{"content":{"type":"string","description":"The item, as the agent wrote it."},"status":{"type":"string","enum":["pending","in_progress","completed"],"description":"Where it is."},"activeForm":{"description":"How to phrase it while it is happening, so a screen can say what the agent is doing rather than what it plans to do.","type":"string"}},"required":["content","status"],"additionalProperties":false}},"usage":{"description":"What the turn cost, on the bubble its answer ended in.","type":"object","properties":{"costUsd":{"type":"number"},"inputTokens":{"type":"number"},"outputTokens":{"type":"number"},"durationMs":{"type":"number"},"numTurns":{"type":"number"}},"additionalProperties":false},"notes":{"description":"What the sandbox added to this message before the model saw it. Carried on the message rather than as rows of their own, because they genuinely were part of what was sent.","type":"array","items":{"type":"object","properties":{"title":{"type":"string","description":"The one line a reader sees, on a row that opens to the text below."},"text":{"type":"string","description":"The note itself, which is also exactly what the model was told."}},"required":["title","text"],"additionalProperties":false}},"speaker":{"description":"Who sent this message, as the sandbox verified it. Absent where it does not say.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"type":"string","description":"The signed-in member, as the sandbox verified them."},"name":{"description":"Their display name, where the sign-in carries one.","type":"string"}},"required":["kind","email"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"program"},"token":{"type":"string","description":"The label of the control token a person minted and handed to this program."}},"required":["kind","token"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"conversationId":{"type":"string","description":"The conversation whose agent is speaking: a child reporting back, a peer's message."}},"required":["kind","conversationId"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"sandbox"},"source":{"description":"What in the sandbox spoke: an automation, a watch that fired, a job that ended, a repair. Absent when it does not say.","type":"string"}},"required":["kind"],"additionalProperties":false}]},"errand":{"description":"What this message is for, when the sandbox or the app composed it rather than a person typing it. Absent on a person's own words.","type":"string","enum":["land-conflict","verify-nudge","land-breakage","land-fix","land-fix-nudge","land-held","push-fix","push-fix-nudge","ci-fix","ci-fix-nudge"]},"placed":{"description":"A person wrote this in the agent's voice, with no turn behind it. Marked for the human re-reading the conversation months later, so their own words do not pass as the agent's. The agent itself never sees the mark.","type":"boolean"},"noticeAction":{"description":"A one-press follow-up this notice offers, by name. The chat decides what it does and whether it still applies.","type":"string","enum":["landHold","depsInstall","watchStop","sandboxMemory","sendAnyway","sendAgain"]},"sandboxHeld":{"description":"The sandbox kept this refused turn whole, its message still above, so the notice's press runs that turn again instead of letting the conversation's queue go, which never held these words.","type":"boolean"},"noticeWait":{"description":"The wait this notice describes, by name, so a reader can say whether it is still on.","type":"string","enum":["credentialRenewal","chatRoute","watch"]},"noticeWaitId":{"description":"Which instance of the wait this notice names, for a kind that can have several running at once.","type":"string"},"noticeCode":{"description":"Which of the sandbox's own notices this row is, and the facts it was worded from, so a reader can say it in the reader's own language. The text stays the English sentence.","type":"object","properties":{"code":{"type":"string","description":"Which of the sandbox's notices this row is, by name. A reader that does not know the name shows the row's text."},"params":{"description":"The facts the notice was worded from, by name: counts, names, and the provider's own sentence where the notice quotes one.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number","boolean"]}}},"required":["code"],"additionalProperties":false},"agentNotice":{"description":"This notice was said by the agent's runtime or one of its extensions rather than by the sandbox: how loud, and who said it.","type":"object","properties":{"level":{"type":"string","enum":["info","warning","error"],"description":"How loud it was said: a muted line, a warning or an error."},"source":{"description":"Who said it, when the runtime named one: the extension or plugin, as the runtime spells it. Absent when the runtime's loop said it itself.","type":"string"}},"required":["level"],"additionalProperties":false},"plan":{"description":"The plan this row asked approval for, and the answer.","type":"object","properties":{"requestId":{"type":"string","description":"What to send back when you answer."},"text":{"type":"string","description":"The plan itself."},"document":{"description":"The write-up this plan refers to, when the plan itself is a pointer to one.","type":"object","properties":{"path":{"type":"string","description":"Where it lives, as a workspace path."},"title":{"type":"string","description":"What it is called: its opening heading, or its file name."},"markdown":{"type":"string","description":"The document itself."},"truncated":{"description":"It was clipped at the wire cap; the file on disk has more.","type":"boolean"},"plan":{"description":"It is one of the CLI's plan files, written to be approved rather than merely read.","type":"boolean"}},"required":["path","title","markdown"],"additionalProperties":false},"status":{"type":"string","enum":["pending","approved","rejected","cancelled"],"description":"Where the decision stands."}},"required":["requestId","text","status"],"additionalProperties":false},"question":{"description":"The questions this row asked, and the picks that answered them.","type":"object","properties":{"requestId":{"type":"string","description":"What to send back when you answer."},"questions":{"type":"array","items":{"type":"object","properties":{"question":{"type":"string","description":"What the agent is asking."},"header":{"type":"string","description":"A short label for the question."},"multiSelect":{"type":"boolean","description":"Whether more than one answer can be picked."},"options":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","description":"The choice, in a few words."},"description":{"type":"string","description":"What picking it means."},"preview":{"description":"Something to look at while deciding: a mock-up, a snippet, a layout.","type":"string"}},"required":["label","description"],"additionalProperties":false},"description":"The choices offered. A free-text answer is always possible as well."}},"required":["question","header","multiSelect","options"],"additionalProperties":false},"description":"What it wants to know."},"document":{"description":"The document this turn wrote and is asking about, so the choice can be read beside it.","type":"object","properties":{"path":{"type":"string","description":"Where it lives, as a workspace path."},"title":{"type":"string","description":"What it is called: its opening heading, or its file name."},"markdown":{"type":"string","description":"The document itself."},"truncated":{"description":"It was clipped at the wire cap; the file on disk has more.","type":"boolean"},"plan":{"description":"It is one of the CLI's plan files, written to be approved rather than merely read.","type":"boolean"}},"required":["path","title","markdown"],"additionalProperties":false},"status":{"type":"string","enum":["pending","answered","cancelled"],"description":"Where the answer stands."},"answers":{"description":"What was chosen, keyed by the question, with the chosen labels or the user's own words.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"string"}}},"attachments":{"description":"Files the user attached to their own-words answer, keyed by the question, as workspace-relative paths.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"string"}}}},"required":["requestId","questions","status"],"additionalProperties":false},"permission":{"description":"The tool this row asked permission for, and the decision.","type":"object","properties":{"toolName":{"type":"string","description":"Which tool it wants to use."},"title":{"description":"The whole question, as a sentence, exactly as the runtime words it.","type":"string"},"displayName":{"description":"A short phrase for the button, such as read file.","type":"string"},"description":{"description":"More about what it is asking for.","type":"string"},"reason":{"description":"Why it is asking at all: a rule, the current mode, something that looked risky.","type":"string"},"path":{"description":"Which file it concerns, when it concerns one.","type":"string"},"alwaysLabel":{"description":"The wording for an always-allow answer. Present only when there is something an always could actually remember; without it the only answers are once and no.","type":"string"},"alwaysAsks":{"description":"This request asks every time: an owner's hard rule, a sandbox restart other conversations would feel, or a change only the owner may make. Allow everything in this conversation is not offered on it and never answers it.","type":"boolean","const":true},"program":{"description":"The program this request is holding, when the request is about one. Present on a command gate's request and absent on every other permission ask.","type":"object","properties":{"text":{"type":"string","description":"What would run."},"language":{"type":"string","enum":["bash","javascript"],"description":"Which of the two backends it is written for, named as the grammar that colours it."},"truncated":{"type":"boolean","description":"Whether this is an excerpt of a longer program, so the request can say so instead of ending mid-word. An excerpt always carries the flagged fragment: the beginning, then a window around the fragment, with any skipped middle written into the text as a bracketed count."},"spans":{"type":"array","items":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"maximum":9007199254740991},"end":{"type":"integer","minimum":0,"maximum":9007199254740991}},"required":["start","end"],"additionalProperties":false},"description":"Which fragments of the text the pattern match fired on: every matched class's, or, under the hard rule, only the class the title names. Offsets into text, in order, never overlapping."}},"required":["text","language","truncated","spans"],"additionalProperties":false},"child":{"description":"The subagent this request would start or reach, and what it runs on. Present on the request to start or reach a subagent and absent on every other permission ask.","type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves it."},"model":{"type":"string","minLength":1,"description":"Which of that provider's models."},"harness":{"description":"Which agentic loop runs it. Absent is the provider's own.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which of that provider's connected accounts pays for it. Absent is whichever has the most room when it starts.","type":"string"},"effort":{"description":"How hard it thinks, where the model offers a choice. Absent is the model's own default.","type":"string"},"thinking":{"description":"Whether it reasons before it answers, where the model offers the choice.","type":"boolean"},"fast":{"description":"Whether it asks for the faster rate, at the higher price.","type":"boolean"},"move":{"type":"string","enum":["spawn","send","answer"],"description":"What the parent asks to do: start a new child, say something to one it started, or answer one's question."},"child":{"description":"The child's id, for one that already exists.","type":"string"},"task":{"description":"What the child is for, in a line.","type":"string"},"message":{"description":"What the parent would say to it: the message it sends, or the answers it gives. Clipped for the request.","type":"string"},"on":{"description":"Which machine it runs on, when one is named: a runner, or \"here\" for this sandbox.","type":"string"},"proposed":{"description":"What the agent asked to start it on, when the owner started it on something else instead. Present only once the request has settled that way.","type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves it."},"model":{"type":"string","minLength":1,"description":"Which of that provider's models."},"harness":{"description":"Which agentic loop runs it. Absent is the provider's own.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which of that provider's connected accounts pays for it. Absent is whichever has the most room when it starts.","type":"string"},"effort":{"description":"How hard it thinks, where the model offers a choice. Absent is the model's own default.","type":"string"},"thinking":{"description":"Whether it reasons before it answers, where the model offers the choice.","type":"boolean"},"fast":{"description":"Whether it asks for the faster rate, at the higher price.","type":"boolean"}},"required":["provider","model"],"additionalProperties":false}},"required":["provider","model","move"],"additionalProperties":false},"explain":{"description":"One plain sentence saying what the program does and why it is being asked about, where the title says something else. Written by the judge that read your safety policy, never by the agent being gated.","type":"string"},"requestId":{"type":"string","description":"What to send back when you answer."},"status":{"type":"string","enum":["pending","allowed","always","everything","denied","cancelled"],"description":"Where the decision stands."}},"required":["toolName","requestId","status"],"additionalProperties":false},"browserHelp":{"description":"The browser hand-over this row asked for, and how it ended.","type":"object","properties":{"requestId":{"type":"string"},"session":{"type":"string"},"account":{"type":"string"},"message":{"type":"string"},"status":{"type":"string","enum":["pending","helped","declined","cancelled"],"description":"How the hand-over ended."}},"required":["requestId","session","account","message","status"],"additionalProperties":false},"terminalHelp":{"description":"The terminal hand-over this row asked for, and how it ended.","type":"object","properties":{"requestId":{"type":"string"},"session":{"type":"string"},"message":{"type":"string"},"status":{"type":"string","enum":["pending","helped","declined","cancelled"],"description":"How the hand-over ended."}},"required":["requestId","session","message","status"],"additionalProperties":false},"capabilityOffer":{"description":"The capability setup this row asked for, the decision, and the outcome.","type":"object","properties":{"requestId":{"type":"string"},"offer":{"type":"object","properties":{"entry":{"type":"string","description":"Which catalog entry is being asked for."},"name":{"type":"string","description":"What it is called, as the catalogue titles it rather than as the agent named it."},"why":{"description":"The agent's case for connecting it, and the only words on this request that are the agent's.","type":"string"}},"required":["entry","name"],"additionalProperties":false},"status":{"type":"string","enum":["pending","connecting","skipped","cancelled"],"description":"Where the decision stands."},"outcome":{"description":"How an accepted ask's setup ended (the capability_outcome frame).","type":"object","properties":{"outcome":{"type":"string","enum":["connected","unfinished"]},"id":{"type":"string"}},"required":["outcome"],"additionalProperties":false}},"required":["requestId","offer","status"],"additionalProperties":false},"paymentOffer":{"description":"The payment this row asked for, the decision, and the receipt.","type":"object","properties":{"requestId":{"type":"string"},"offer":{"type":"object","properties":{"url":{"type":"string","description":"What is being paid for."},"description":{"description":"What the endpoint says it is.","type":"string"},"payTo":{"type":"string","description":"Where the money goes, taken verbatim from the endpoint's own demand."},"network":{"type":"string","description":"On which network."},"asset":{"type":"string","description":"In which token."},"assetName":{"type":"string","description":"That token's name. It is pegged to the dollar, which is what lets every amount here read as dollars."},"amountUsd":{"type":"string","description":"The exact price. Not a ceiling: this scheme has no ranges, so this is the whole spend."},"spentTodayUsd":{"type":"string","description":"What has already gone out today."},"dailyCapUsd":{"type":"string","description":"What may go out in a day."},"why":{"description":"The agent's case for paying, and the only words on this request that are the agent's.","type":"string"}},"required":["url","payTo","network","asset","assetName","amountUsd","spentTodayUsd","dailyCapUsd"],"additionalProperties":false},"status":{"type":"string","enum":["pending","approved","skipped","cancelled"],"description":"Where the decision stands."},"receipt":{"description":"How the approved payment ended (the payment_receipt frame).","type":"object","properties":{"outcome":{"type":"string","enum":["paid","failed"]},"amountUsd":{"type":"string"},"transaction":{"type":"string"},"network":{"type":"string"}},"required":["outcome","amountUsd"],"additionalProperties":false}},"required":["requestId","offer","status"],"additionalProperties":false},"watchWake":{"description":"The condition watch that woke this conversation, and the prompt it was woken with.","type":"object","properties":{"outcome":{"type":"string","enum":["met","timeout","restart-expired","broken"],"description":"How the watch ended: the condition held, the deadline passed, or a restart cut it short."},"note":{"type":"string","description":"The agent's own line on what it was waiting for."},"elapsed":{"type":"string","description":"How long the watch stood, already worded ('43m'): carried rather than recomputed, since the arming instant is not on the row."},"sent":{"type":"string","description":"The whole prompt the model was woken with, disclosed under the row."}},"required":["outcome","note","elapsed","sent"],"additionalProperties":false},"need":{"description":"Something the agent asked a person for, as it was when raised. Its live state (answered, met) is read by its id, since it outlives the turn.","type":"object","properties":{"id":{"type":"string","description":"The need's handle, the one `needs cancel` takes."},"conversationId":{"type":"string","description":"The conversation that asked, and the one its answer wakes."},"subject":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"capability"},"entry":{"type":"string","minLength":1,"description":"The catalog entry, as the catalog names it."},"name":{"type":"string","description":"What the catalog calls it, never the agent's spelling."},"mode":{"type":"string","enum":["connect","reconnect","change"],"description":"Connect something new, give a connected one a credential that works again, or change a setting on a connected one."},"instance":{"description":"The connection a reconnect or a change is about.","type":"string"},"target":{"description":"The site, host or address the connection is for, when the entry can hold several.","type":"string"},"prefill":{"description":"Settings the agent could fill in for a new connection, never a credential: the daemon keeps only the entry's own non-secret fields.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"changes":{"description":"For a change: each setting and the value it would take. Never a credential.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"reason":{"description":"The daemon's own sentence on why this is the ask, such as the refusal a connected credential keeps getting.","type":"string"},"reported":{"description":"The agent reported the credential refused while the connection still probes as working, so only a person's word that it is fixed meets it: the probe could not see the refusal in the first place.","type":"boolean"}},"required":["kind","entry","name","mode"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret"},"name":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,127}$","description":"The name it is stored under, and what `{{secret:NAME}}` will resolve."},"where":{"description":"How it will be used: the header, the command, the site it goes to.","type":"string","maxLength":200},"link":{"description":"Where a person gets one, shown as a link on the card.","type":"string","format":"uri"},"hint":{"description":"What a valid one looks like, so a wrong paste is caught by eye.","type":"string","maxLength":120},"replace":{"description":"One is stored under this name and is being refused: the ask is for a new value in its place.","type":"boolean"}},"required":["kind","name"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"grant"},"subject":{"type":"string","enum":["capability","folder","shelf","site"],"description":"A connected capability the persona leaves out, a folder outside the conversation's reach, a whole shelf of tools, or a site in the person's own browser, which only their browser extension can allow."},"what":{"type":"string","minLength":1,"description":"The capability's id, the folder, or the shelf."},"label":{"type":"string","description":"What it is, in the daemon's words: the account and its kind, the folder, the shelf's name."},"persona":{"description":"The persona that withholds it, when one does.","type":"string"},"scope":{"description":"How far the yes went, once there was one.","type":"string","enum":["conversation","persona"]}},"required":["kind","subject","what","label"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"release"},"subject":{"type":"string","minLength":1,"description":"The gated account or connector."},"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it. Anyone else's answer is refused and leaves it waiting."}},"required":["kind","subject","approvers"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"environment"},"tool":{"type":"string","minLength":1,"description":"What the steps install, the name the proposal is filed under."},"steps":{"type":"string","minLength":1,"description":"The Dockerfile steps proposed for the image's custom section: RUN and ENV lines only."},"approvedHash":{"description":"The overlay these steps were approved into; met once the running container was built from it.","type":"string"}},"required":["kind","tool","steps"],"additionalProperties":false}],"description":"What exactly is asked for."},"title":{"type":"string","description":"The one line it leads with, in the daemon's words."},"why":{"description":"The agent's case for it, and the only words on a need that are the agent's.","type":"string","maxLength":280},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."},"createdAt":{"type":"number","description":"When it was raised, in milliseconds."},"updatedAt":{"type":"number","description":"When it last moved, in milliseconds."},"answeredBy":{"description":"Who answered it, as the sandbox verified them.","type":"string"},"outcome":{"description":"How it ended, in the daemon's words, once it has.","type":"string"},"told":{"description":"How the agent heard the outcome, once it has.","type":"string","enum":["call","turn","queued"]},"unattended":{"description":"Raised by a turn nobody was watching, so the card waited for whoever came next.","type":"boolean"}},"required":["id","conversationId","subject","title","status","createdAt","updatedAt"],"additionalProperties":false},"needWake":{"description":"The answered need that reached this conversation, and the prompt it came as.","type":"object","properties":{"outcome":{"type":"string","enum":["met","declined"],"description":"How the need ended: a person gave it, or said no."},"title":{"type":"string","description":"The need, as its card leads with it."},"id":{"type":"string","description":"The need's handle, which its live card is keyed by."},"sent":{"type":"string","description":"The whole prompt the model received, disclosed under the row."}},"required":["outcome","title","id","sent"],"additionalProperties":false},"agentWords":{"description":"Another agent's words that reached this conversation, whose they are, and the prompt they came as.","type":"object","properties":{"kind":{"type":"string","enum":["peer","child"],"description":"Who sent it: another conversation in the workspace, or a subagent this one started."},"from":{"type":"string","description":"The sending conversation's id."},"title":{"description":"The sender's title, when it had one.","type":"string"},"failed":{"description":"A child's report on a turn that failed rather than finished.","type":"boolean"},"sent":{"type":"string","description":"The whole prompt the model received, disclosed under the row."}},"required":["kind","from","sent"],"additionalProperties":false},"backgroundJob":{"description":"The background job this row marks the start of.","type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for the job, which its live state on the conversation's card is keyed by."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"command":{"type":"string","description":"The command as the agent wrote it, folded to one line."},"startedAt":{"type":"number","description":"When it started, in milliseconds."}},"required":["id","label","command","startedAt"],"additionalProperties":false},"credentialOffer":{"description":"The gated credential this row asked to use, who may release it, and who did.","type":"object","properties":{"requestId":{"type":"string"},"offer":{"type":"object","properties":{"subject":{"type":"string","description":"Which credential is being asked for."},"kind":{"type":"string","enum":["secret","capability"],"description":"Whether this gate covers one stored secret, by the name a reference carries, or one whole connected capability, by its id."},"lane":{"type":"string","enum":["shell","code","browser","session","otp"],"description":"What the credential was about to be used for: a shell command, a script, typing into a page, mounting a connected account, or one one-time code."},"detail":{"description":"Where it would go: the start of the command, the site, or what is being mounted. Never a value: the command still reads as a reference at this point.","type":"string"},"why":{"description":"The agent's case for using it, and the only words on this request that are the agent's.","type":"string"},"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it. A click from anyone else is refused and leaves the request standing."},"scope":{"type":"string","enum":["use","conversation"],"description":"How far one release goes: `use` asks again every single time (one click releases exactly one use), `conversation` covers the rest of this conversation and is forgotten when the daemon restarts."}},"required":["subject","kind","lane","approvers","scope"],"additionalProperties":false},"status":{"type":"string","enum":["pending","approved","skipped","cancelled"],"description":"Where the decision stands."},"receipt":{"description":"Who released it, or that somebody refused (the credential_receipt frame).","type":"object","properties":{"outcome":{"type":"string","enum":["released","refused"]},"approvedBy":{"type":"string"}},"required":["outcome"],"additionalProperties":false}},"required":["requestId","offer","status"],"additionalProperties":false}},"required":["role","text"],"additionalProperties":false},"description":"The conversation, in order. Each block of the agent's prose is its own message with the tools that block introduced, which is what reproduces the way it actually unfolded."}},"required":["messages"],"additionalProperties":false,"$defs":{"__schema0":{"type":"object","properties":{"id":{"type":"string","description":"The call's id."},"name":{"type":"string","description":"Which tool."},"category":{"type":"string","enum":["read","edit","delete","move","search","execute","think","fetch","other"],"description":"What kind of thing it does: read, edit, delete, move, search, run, think, fetch. Named the same way whatever the backend called the tool."},"status":{"type":"string","enum":["pending","in_progress","completed","failed"],"description":"How it went."},"target":{"description":"What it acted on, in one line: a file, a command, an address.","type":"string"},"locations":{"description":"The files it touched.","type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The file, as a workspace path, whatever directory the tool was run from."},"line":{"description":"Which line, counting from one.","type":"number"}},"required":["path"],"additionalProperties":false}},"content":{"description":"What it produced: text, a change to a file, or a picture.","type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"text","description":"Plain output."},"text":{"type":"string","description":"What the tool said."}},"required":["type","text"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"diff","description":"A change to a file."},"path":{"type":"string","description":"Which file, as a workspace path."},"oldText":{"description":"What was there. Absent for a new file, or where the previous contents are not known.","type":"string"},"newText":{"type":"string","description":"What is there now."},"truncated":{"description":"One of the two sides was too large to send whole.","type":"boolean"}},"required":["type","path","newText"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"image","description":"A picture the tool produced."},"path":{"type":"string","description":"Where it is, as a workspace path. A path rather than the bytes, because the workspace already serves it, sending it inline would bloat every stored record, and this way the picture stays openable afterwards."}},"required":["type","path"],"additionalProperties":false}]}},"children":{"description":"Calls a delegated subagent made, nested under the call that started it, so a reopened conversation redraws the delegation rather than collapsing it into one result.","type":"array","items":{"$ref":"#/$defs/__schema0"}},"nested":{"description":"How many calls sit under this one, present in place of `children` when they were left behind. A transcript page does that, since a settled delegation draws collapsed; ask for the call's own children to fill it in.","type":"integer","minimum":0,"maximum":9007199254740991},"thinking":{"description":"What the agent was reasoning about around this call.","type":"string"},"subagent":{"description":"The helper this call started, as the daemon's registry sees it: what it is, how it is going, what it has spent. What a card can say about a backgrounded child whose result is minutes away.","type":"object","properties":{"id":{"description":"Its own id, where that is not the card's: a spawned subagent is named by its own conversation, which is what the roster, `wait` and its own chat call it. Absent, the card's id is its id, as it is for one the runtime started in-process.","type":"string"},"kind":{"type":"string","enum":["subagent","spawned"]},"agentType":{"type":"string"},"description":{"type":"string"},"model":{"type":"string"},"provider":{"type":"string"},"background":{"type":"boolean"},"status":{"type":"string","enum":["pending","running","blocked","completed","failed","killed","paused"]},"tokens":{"type":"number"},"toolUses":{"type":"number"},"lastTool":{"type":"string"},"summary":{"type":"string"},"error":{"type":"string"},"verification":{"type":"object","properties":{"state":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Whether anything proved its work: a check passed after its last edit, it changed code and nothing checked it, a check ran and failed, or it changed no code at all."},"paths":{"description":"The code files it changed, most recent last. The first few; the record holds the rest.","type":"array","items":{"type":"string"}},"check":{"description":"The command that spoke: the one that cleared it, or the one that failed. Named rather than summarised, so a targeted test is not read as the whole suite.","type":"string"}},"required":["state"],"additionalProperties":false}},"required":["kind","status"],"additionalProperties":false}},"required":["id","name","category","status"],"additionalProperties":false}}}}}}}}},"/workflows":{"get":{"operationId":"workflows.list","summary":"Saved workflows and their runs","description":"Every workflow somebody has designed, each with its own run history, newest first. One answer rather than two, because a workflow that has never been run is the interesting case rather than a mistake.","tags":["Workflows"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"workflows":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The workflow's id."},"name":{"type":"string","minLength":1,"maxLength":80,"description":"What to call it."},"description":{"description":"What it is for.","type":"string","maxLength":400},"steps":{"minItems":1,"maxItems":24,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$","description":"This step's own name, which other steps use to say they wait on it."},"title":{"type":"string","minLength":1,"maxLength":60,"description":"What to call it on screen. Short: the instruction below is where the detail goes."},"goal":{"description":"What done means for this step, in your words. It is what the step is judged against, and a different sentence from what it is told to do.","type":"string","minLength":1},"prompt":{"description":"What the step is told to do. The goal is the suite passes; this is run the tests, take the top failure, fix it. Leaving it out hands over the run's own request untouched, which is right for a step whose whole job is do what was asked.","type":"string","minLength":1},"needs":{"type":"array","items":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$"},"description":"Which steps must finish first. Empty means it starts when the run does. Naming a step that does not exist, or a loop between steps, is refused when the workflow is saved."},"handoff":{"type":"string","enum":["fresh","continue"],"description":"How it meets what came before: a fresh conversation handed the previous step's result, or the same conversation carried on."},"output":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"none","description":"It produces nothing but its work. The classic make the suite pass: what it leaves behind is a passing suite, and asking it to also file a report is asking it to spend a round on paperwork."}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"claim","description":"Each round says whether it is done and why. Structured prose: done is a value read rather than a sentence interpreted. Self-assessment, so advisory by construction; it exists because plenty of goals have no command that could check them."}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"json","description":"Each round writes a real answer in a shape you declared. This is the one that makes a step's output usable as the next step's input: a paragraph mentioning three files cannot be fed to anything, a list of three files can."},"fields":{"minItems":1,"maxItems":16,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-zA-Z_][a-zA-Z0-9_]{0,39}$"},"type":{"type":"string","enum":["string","number","boolean","string[]"]},"description":{"type":"string","minLength":1},"required":{"type":"boolean"}},"required":["name","type","description","required"],"additionalProperties":false},"description":"The shape that answer has to match."}},"required":["kind","fields"],"additionalProperties":false}],"description":"What it has to produce for the step to count."},"checks":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"command","description":"Run something and see if it passes. Deterministic, free, and the only signal here whose answer does not come from a model. A passing test suite beats any amount of self-report."},"command":{"type":"string","minLength":1,"description":"The command to run in the conversation's own tree. Exiting cleanly means satisfied."}},"required":["kind","command"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"judge","description":"Put the question to a separate model with no tools, which reads the round's own report and rules on it, having done none of the work and nothing invested in its being finished."},"rubric":{"type":"string","minLength":1,"description":"What that judge is asked."},"model":{"description":"Which model judges. Leave it out for the cheap one the other small jobs use.","type":"string"}},"required":["kind","rubric"],"additionalProperties":false}]},"description":"What has to pass before it counts as done."},"context":{"type":"string","enum":["fresh","continue"],"description":"How the step's own repeats meet each other. A long-running step wants to start clean each round; a short polish-this step wants to carry on."},"maxSpendUsd":{"description":"A ceiling on what this step may spend. The one resource that cannot be recovered after an unattended fan-out, which is why it is here and iteration limits are not. Absent is uncapped.","type":"number","exclusiveMinimum":0},"agent":{"description":"Which provider runs it.","type":"string","minLength":1},"harness":{"description":"Which agentic loop runs it.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which account pays for it.","type":"string"},"model":{"description":"Which model runs it.","type":"string"},"actsAs":{"description":"Which persona it acts as. Unpinned, a step gets the strict unwatched default: every tool, and no signed-in accounts at all. Pinning one is how a release check gets a voice, a folder to work in, or the single account it may post from.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"}},"required":["id","title","needs","handoff","output","checks","context"],"additionalProperties":false},"description":"The steps, each with what it waits on. Every one runs in its own private copy of the repos, always, because parallel steps sharing a tree collide."},"gate":{"description":"Present means a machine can run this design and get a ship-it answer back. Absent means an ordinary workflow, started by a person, with no outside door onto it at all.","type":"object","properties":{"step":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$","description":"Which step's answer carries the decision. Usually a last step that weighs up the ones before it, though nothing requires that."},"field":{"type":"string","minLength":1,"description":"Which of that step's declared answers to read. A declared field is the one part of a step's answer that was checked rather than fished out of prose, which is the whole rule here. Checked when the workflow is saved."},"pass":{"minItems":1,"type":"array","items":{"type":"string","minLength":1},"description":"Which values mean ship it. Everything else fails. A list of what passes rather than what fails, because a step answering mostly-pass or pass-with-notes must not ship, and this gets that right without anybody having had to enumerate the ways a model can hedge."},"dailyMax":{"description":"How many runs a day, across every caller. A gate is a paid door with nobody in the loop: one wired into a push-triggered pipeline is a fan-out of conversations per commit. Absent is a small default rather than unlimited.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["step","field","pass"],"additionalProperties":false},"maxParallel":{"type":"integer","minimum":1,"maximum":8,"description":"How many steps may run at once. Bounded, because a fan-out of twelve is twelve model sessions, twelve working copies and twelve times the burn rate, on one machine."},"runs":{"type":"array","items":{"type":"object","properties":{"runId":{"type":"string","minLength":1,"description":"This run's id."},"workflow":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The workflow's id."},"name":{"type":"string","minLength":1,"maxLength":80,"description":"What to call it."},"description":{"description":"What it is for.","type":"string","maxLength":400},"steps":{"minItems":1,"maxItems":24,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$","description":"This step's own name, which other steps use to say they wait on it."},"title":{"type":"string","minLength":1,"maxLength":60,"description":"What to call it on screen. Short: the instruction below is where the detail goes."},"goal":{"description":"What done means for this step, in your words. It is what the step is judged against, and a different sentence from what it is told to do.","type":"string","minLength":1},"prompt":{"description":"What the step is told to do. The goal is the suite passes; this is run the tests, take the top failure, fix it. Leaving it out hands over the run's own request untouched, which is right for a step whose whole job is do what was asked.","type":"string","minLength":1},"needs":{"type":"array","items":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$"},"description":"Which steps must finish first. Empty means it starts when the run does. Naming a step that does not exist, or a loop between steps, is refused when the workflow is saved."},"handoff":{"type":"string","enum":["fresh","continue"],"description":"How it meets what came before: a fresh conversation handed the previous step's result, or the same conversation carried on."},"output":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"none","description":"It produces nothing but its work. The classic make the suite pass: what it leaves behind is a passing suite, and asking it to also file a report is asking it to spend a round on paperwork."}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"claim","description":"Each round says whether it is done and why. Structured prose: done is a value read rather than a sentence interpreted. Self-assessment, so advisory by construction; it exists because plenty of goals have no command that could check them."}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"json","description":"Each round writes a real answer in a shape you declared. This is the one that makes a step's output usable as the next step's input: a paragraph mentioning three files cannot be fed to anything, a list of three files can."},"fields":{"minItems":1,"maxItems":16,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-zA-Z_][a-zA-Z0-9_]{0,39}$"},"type":{"type":"string","enum":["string","number","boolean","string[]"]},"description":{"type":"string","minLength":1},"required":{"type":"boolean"}},"required":["name","type","description","required"],"additionalProperties":false},"description":"The shape that answer has to match."}},"required":["kind","fields"],"additionalProperties":false}],"description":"What it has to produce for the step to count."},"checks":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"command","description":"Run something and see if it passes. Deterministic, free, and the only signal here whose answer does not come from a model. A passing test suite beats any amount of self-report."},"command":{"type":"string","minLength":1,"description":"The command to run in the conversation's own tree. Exiting cleanly means satisfied."}},"required":["kind","command"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"judge","description":"Put the question to a separate model with no tools, which reads the round's own report and rules on it, having done none of the work and nothing invested in its being finished."},"rubric":{"type":"string","minLength":1,"description":"What that judge is asked."},"model":{"description":"Which model judges. Leave it out for the cheap one the other small jobs use.","type":"string"}},"required":["kind","rubric"],"additionalProperties":false}]},"description":"What has to pass before it counts as done."},"context":{"type":"string","enum":["fresh","continue"],"description":"How the step's own repeats meet each other. A long-running step wants to start clean each round; a short polish-this step wants to carry on."},"maxSpendUsd":{"description":"A ceiling on what this step may spend. The one resource that cannot be recovered after an unattended fan-out, which is why it is here and iteration limits are not. Absent is uncapped.","type":"number","exclusiveMinimum":0},"agent":{"description":"Which provider runs it.","type":"string","minLength":1},"harness":{"description":"Which agentic loop runs it.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which account pays for it.","type":"string"},"model":{"description":"Which model runs it.","type":"string"},"actsAs":{"description":"Which persona it acts as. Unpinned, a step gets the strict unwatched default: every tool, and no signed-in accounts at all. Pinning one is how a release check gets a voice, a folder to work in, or the single account it may post from.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"}},"required":["id","title","needs","handoff","output","checks","context"],"additionalProperties":false},"description":"The steps, each with what it waits on. Every one runs in its own private copy of the repos, always, because parallel steps sharing a tree collide."},"gate":{"description":"Present means a machine can run this design and get a ship-it answer back. Absent means an ordinary workflow, started by a person, with no outside door onto it at all.","type":"object","properties":{"step":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$","description":"Which step's answer carries the decision. Usually a last step that weighs up the ones before it, though nothing requires that."},"field":{"type":"string","minLength":1,"description":"Which of that step's declared answers to read. A declared field is the one part of a step's answer that was checked rather than fished out of prose, which is the whole rule here. Checked when the workflow is saved."},"pass":{"minItems":1,"type":"array","items":{"type":"string","minLength":1},"description":"Which values mean ship it. Everything else fails. A list of what passes rather than what fails, because a step answering mostly-pass or pass-with-notes must not ship, and this gets that right without anybody having had to enumerate the ways a model can hedge."},"dailyMax":{"description":"How many runs a day, across every caller. A gate is a paid door with nobody in the loop: one wired into a push-triggered pipeline is a fan-out of conversations per commit. Absent is a small default rather than unlimited.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["step","field","pass"],"additionalProperties":false},"maxParallel":{"type":"integer","minimum":1,"maximum":8,"description":"How many steps may run at once. Bounded, because a fan-out of twelve is twelve model sessions, twelve working copies and twelve times the burn rate, on one machine."}},"required":["id","name","steps","maxParallel"],"additionalProperties":false,"description":"The design as it stood when the run started, copied rather than looked up. The run has to keep showing the graph it actually ran, not the one edited twice since, and a run of a deleted workflow has to stay readable."},"repos":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"repo":{"type":"string"},"base":{"type":"string","minLength":1}},"required":["repo","base"],"additionalProperties":false},"description":"The workspace as this run began, one exact commit per repository. Every step branches from these, even if the shared tree moves while a wide fan-out is still opening its copies, so the steps can be compared with each other afterwards."},"request":{"description":"What this run was asked to do, handed to every step on top of its own instructions. It is what makes one saved design worth keeping: two models, one task is a shape, and the task is different every time. Absent for a run started with nowhere to type one.","type":"string"},"state":{"type":"string","enum":["running","done","failed","stopped","overspent","error"],"description":"How the run is going. Finished means every step that ran got there; a run with skipped steps counts as failed, because a graph that never reached its end did not do what it was asked whatever the survivors managed."},"startedAt":{"type":"number","description":"When it began, in milliseconds."},"endedAt":{"description":"When it ended, in milliseconds.","type":"number"},"resumed":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"How many times the sandbox restarted under it and picked it back up."},"detail":{"description":"What went wrong, when something did.","type":"string"},"steps":{"type":"array","items":{"type":"object","properties":{"stepId":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$","description":"Which step this is."},"state":{"type":"string","enum":["pending","running","done","failed","skipped","stopped"],"description":"How it went. Skipped carries what the others cannot: it never ran, because something it was waiting on did not finish. That is why a failed run shows one failed step and a trail of skipped ones."},"conversationId":{"type":"string","description":"The conversation it ran on, and the way from a node on the graph to a real record. Shared with the step before it when they were chained, which is what makes those two one card."},"startedAt":{"description":"When it began, in milliseconds.","type":"number"},"endedAt":{"description":"When it ended, in milliseconds.","type":"number"},"iterations":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"How many rounds it took."},"costUsd":{"description":"What it cost, in dollars.","type":"number"},"loopState":{"description":"How its repeating ended. Out of rounds and stuck both come out as a failed step, and the difference between them is the difference between give it more room and more room will not help.","type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"]},"detail":{"description":"What went wrong, when something did.","type":"string"},"document":{"description":"What it produced, once it has produced something that passes its own declared shape. This is what the steps after it are handed.","type":"object","properties":{"done":{"type":"boolean","description":"Whether the goal is met. Reading this is the whole point of the file."},"reason":{"type":"string","description":"Why, in one line. The most-read sentence in the feature: the next round reads it first and the history shows it."},"evidence":{"description":"What was checked to know that. Optional, so a round with nothing to point at says so by leaving it out rather than by inventing a sentence.","type":"string"},"data":{"description":"The declared answer, for a loop that asked for one, checked against the shape it declared.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["done","reason"],"additionalProperties":false},"report":{"description":"The start of its closing words. Bounded, so a long answer is not silently cut down to its last few thousand characters and the record stays a sensible size.","type":"string"},"reportPath":{"description":"Where the whole answer is, as a workspace path. Every step can read it, so a long handoff need not be copied into anybody's prompt.","type":"string"}},"required":["stepId","state","conversationId","iterations"],"additionalProperties":false},"description":"One entry per step, in the design's own order. Every one is written down as waiting when the run starts, so the picture is complete from the first frame and a missing step never has to mean two things."},"archivedAt":{"description":"When it was put away, in milliseconds. The record stays readable and every step's branch, transcript and counters are untouched. Its conversations are put away with it, and brought back with it. Absent means live on the board.","type":"number"}},"required":["runId","workflow","repos","state","startedAt","resumed","steps"],"additionalProperties":false},"description":"Its runs, newest first."},"gateToken":{"description":"What a pipeline presents at /workflows/{id}/gate, when the design declares a gate. Shown to a maintainer or the owner only.","type":"string"}},"required":["id","name","steps","maxParallel","runs"],"additionalProperties":false},"description":"Every saved design with its own run history."}},"required":["workflows"],"additionalProperties":false}}}}}},"post":{"operationId":"workflows.save","summary":"Create or replace a workflow","description":"Writes a workflow design. Say which of the two you mean, so an id that happens to collide cannot silently overwrite somebody's work. A design that could never run is refused, in the same words the editor shows while you type: a loop in the steps, a step waiting on one that is not there, a step with no way of knowing it is finished.","tags":["Workflows"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"workflow":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The workflow's id."},"name":{"type":"string","minLength":1,"maxLength":80,"description":"What to call it."},"description":{"description":"What it is for.","type":"string","maxLength":400},"steps":{"minItems":1,"maxItems":24,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$","description":"This step's own name, which other steps use to say they wait on it."},"title":{"type":"string","minLength":1,"maxLength":60,"description":"What to call it on screen. Short: the instruction below is where the detail goes."},"goal":{"description":"What done means for this step, in your words. It is what the step is judged against, and a different sentence from what it is told to do.","type":"string","minLength":1},"prompt":{"description":"What the step is told to do. The goal is the suite passes; this is run the tests, take the top failure, fix it. Leaving it out hands over the run's own request untouched, which is right for a step whose whole job is do what was asked.","type":"string","minLength":1},"needs":{"type":"array","items":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$"},"description":"Which steps must finish first. Empty means it starts when the run does. Naming a step that does not exist, or a loop between steps, is refused when the workflow is saved."},"handoff":{"type":"string","enum":["fresh","continue"],"description":"How it meets what came before: a fresh conversation handed the previous step's result, or the same conversation carried on."},"output":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"none","description":"It produces nothing but its work. The classic make the suite pass: what it leaves behind is a passing suite, and asking it to also file a report is asking it to spend a round on paperwork."}},"required":["kind"]},{"type":"object","properties":{"kind":{"type":"string","const":"claim","description":"Each round says whether it is done and why. Structured prose: done is a value read rather than a sentence interpreted. Self-assessment, so advisory by construction; it exists because plenty of goals have no command that could check them."}},"required":["kind"]},{"type":"object","properties":{"kind":{"type":"string","const":"json","description":"Each round writes a real answer in a shape you declared. This is the one that makes a step's output usable as the next step's input: a paragraph mentioning three files cannot be fed to anything, a list of three files can."},"fields":{"minItems":1,"maxItems":16,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-zA-Z_][a-zA-Z0-9_]{0,39}$"},"type":{"type":"string","enum":["string","number","boolean","string[]"]},"description":{"type":"string","minLength":1},"required":{"type":"boolean"}},"required":["name","type","description","required"]},"description":"The shape that answer has to match."}},"required":["kind","fields"]}],"description":"What it has to produce for the step to count."},"checks":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"command","description":"Run something and see if it passes. Deterministic, free, and the only signal here whose answer does not come from a model. A passing test suite beats any amount of self-report."},"command":{"type":"string","minLength":1,"description":"The command to run in the conversation's own tree. Exiting cleanly means satisfied."}},"required":["kind","command"]},{"type":"object","properties":{"kind":{"type":"string","const":"judge","description":"Put the question to a separate model with no tools, which reads the round's own report and rules on it, having done none of the work and nothing invested in its being finished."},"rubric":{"type":"string","minLength":1,"description":"What that judge is asked."},"model":{"description":"Which model judges. Leave it out for the cheap one the other small jobs use.","type":"string"}},"required":["kind","rubric"]}]},"description":"What has to pass before it counts as done."},"context":{"type":"string","enum":["fresh","continue"],"description":"How the step's own repeats meet each other. A long-running step wants to start clean each round; a short polish-this step wants to carry on."},"maxSpendUsd":{"description":"A ceiling on what this step may spend. The one resource that cannot be recovered after an unattended fan-out, which is why it is here and iteration limits are not. Absent is uncapped.","type":"number","exclusiveMinimum":0},"agent":{"description":"Which provider runs it.","type":"string","minLength":1},"harness":{"description":"Which agentic loop runs it.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which account pays for it.","type":"string"},"model":{"description":"Which model runs it.","type":"string"},"actsAs":{"description":"Which persona it acts as. Unpinned, a step gets the strict unwatched default: every tool, and no signed-in accounts at all. Pinning one is how a release check gets a voice, a folder to work in, or the single account it may post from.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"}},"required":["id","title","needs","handoff","output","checks","context"]},"description":"The steps, each with what it waits on. Every one runs in its own private copy of the repos, always, because parallel steps sharing a tree collide."},"gate":{"description":"Present means a machine can run this design and get a ship-it answer back. Absent means an ordinary workflow, started by a person, with no outside door onto it at all.","type":"object","properties":{"step":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$","description":"Which step's answer carries the decision. Usually a last step that weighs up the ones before it, though nothing requires that."},"field":{"type":"string","minLength":1,"description":"Which of that step's declared answers to read. A declared field is the one part of a step's answer that was checked rather than fished out of prose, which is the whole rule here. Checked when the workflow is saved."},"pass":{"minItems":1,"type":"array","items":{"type":"string","minLength":1},"description":"Which values mean ship it. Everything else fails. A list of what passes rather than what fails, because a step answering mostly-pass or pass-with-notes must not ship, and this gets that right without anybody having had to enumerate the ways a model can hedge."},"dailyMax":{"description":"How many runs a day, across every caller. A gate is a paid door with nobody in the loop: one wired into a push-triggered pipeline is a fan-out of conversations per commit. Absent is a small default rather than unlimited.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["step","field","pass"]},"maxParallel":{"type":"integer","minimum":1,"maximum":8,"description":"How many steps may run at once. Bounded, because a fan-out of twelve is twelve model sessions, twelve working copies and twelve times the burn rate, on one machine."}},"required":["id","name","steps","maxParallel"],"description":"The design to write."},"create":{"type":"boolean","description":"Whether you mean to make a new one or replace an existing one. Said outright rather than inferred, so an id that happens to collide is a refusal instead of one saved design quietly overwriting another."}},"required":["workflow","create"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The workflow's id."},"name":{"type":"string","minLength":1,"maxLength":80,"description":"What to call it."},"description":{"description":"What it is for.","type":"string","maxLength":400},"steps":{"minItems":1,"maxItems":24,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$","description":"This step's own name, which other steps use to say they wait on it."},"title":{"type":"string","minLength":1,"maxLength":60,"description":"What to call it on screen. Short: the instruction below is where the detail goes."},"goal":{"description":"What done means for this step, in your words. It is what the step is judged against, and a different sentence from what it is told to do.","type":"string","minLength":1},"prompt":{"description":"What the step is told to do. The goal is the suite passes; this is run the tests, take the top failure, fix it. Leaving it out hands over the run's own request untouched, which is right for a step whose whole job is do what was asked.","type":"string","minLength":1},"needs":{"type":"array","items":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$"},"description":"Which steps must finish first. Empty means it starts when the run does. Naming a step that does not exist, or a loop between steps, is refused when the workflow is saved."},"handoff":{"type":"string","enum":["fresh","continue"],"description":"How it meets what came before: a fresh conversation handed the previous step's result, or the same conversation carried on."},"output":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"none","description":"It produces nothing but its work. The classic make the suite pass: what it leaves behind is a passing suite, and asking it to also file a report is asking it to spend a round on paperwork."}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"claim","description":"Each round says whether it is done and why. Structured prose: done is a value read rather than a sentence interpreted. Self-assessment, so advisory by construction; it exists because plenty of goals have no command that could check them."}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"json","description":"Each round writes a real answer in a shape you declared. This is the one that makes a step's output usable as the next step's input: a paragraph mentioning three files cannot be fed to anything, a list of three files can."},"fields":{"minItems":1,"maxItems":16,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-zA-Z_][a-zA-Z0-9_]{0,39}$"},"type":{"type":"string","enum":["string","number","boolean","string[]"]},"description":{"type":"string","minLength":1},"required":{"type":"boolean"}},"required":["name","type","description","required"],"additionalProperties":false},"description":"The shape that answer has to match."}},"required":["kind","fields"],"additionalProperties":false}],"description":"What it has to produce for the step to count."},"checks":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"command","description":"Run something and see if it passes. Deterministic, free, and the only signal here whose answer does not come from a model. A passing test suite beats any amount of self-report."},"command":{"type":"string","minLength":1,"description":"The command to run in the conversation's own tree. Exiting cleanly means satisfied."}},"required":["kind","command"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"judge","description":"Put the question to a separate model with no tools, which reads the round's own report and rules on it, having done none of the work and nothing invested in its being finished."},"rubric":{"type":"string","minLength":1,"description":"What that judge is asked."},"model":{"description":"Which model judges. Leave it out for the cheap one the other small jobs use.","type":"string"}},"required":["kind","rubric"],"additionalProperties":false}]},"description":"What has to pass before it counts as done."},"context":{"type":"string","enum":["fresh","continue"],"description":"How the step's own repeats meet each other. A long-running step wants to start clean each round; a short polish-this step wants to carry on."},"maxSpendUsd":{"description":"A ceiling on what this step may spend. The one resource that cannot be recovered after an unattended fan-out, which is why it is here and iteration limits are not. Absent is uncapped.","type":"number","exclusiveMinimum":0},"agent":{"description":"Which provider runs it.","type":"string","minLength":1},"harness":{"description":"Which agentic loop runs it.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which account pays for it.","type":"string"},"model":{"description":"Which model runs it.","type":"string"},"actsAs":{"description":"Which persona it acts as. Unpinned, a step gets the strict unwatched default: every tool, and no signed-in accounts at all. Pinning one is how a release check gets a voice, a folder to work in, or the single account it may post from.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"}},"required":["id","title","needs","handoff","output","checks","context"],"additionalProperties":false},"description":"The steps, each with what it waits on. Every one runs in its own private copy of the repos, always, because parallel steps sharing a tree collide."},"gate":{"description":"Present means a machine can run this design and get a ship-it answer back. Absent means an ordinary workflow, started by a person, with no outside door onto it at all.","type":"object","properties":{"step":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$","description":"Which step's answer carries the decision. Usually a last step that weighs up the ones before it, though nothing requires that."},"field":{"type":"string","minLength":1,"description":"Which of that step's declared answers to read. A declared field is the one part of a step's answer that was checked rather than fished out of prose, which is the whole rule here. Checked when the workflow is saved."},"pass":{"minItems":1,"type":"array","items":{"type":"string","minLength":1},"description":"Which values mean ship it. Everything else fails. A list of what passes rather than what fails, because a step answering mostly-pass or pass-with-notes must not ship, and this gets that right without anybody having had to enumerate the ways a model can hedge."},"dailyMax":{"description":"How many runs a day, across every caller. A gate is a paid door with nobody in the loop: one wired into a push-triggered pipeline is a fan-out of conversations per commit. Absent is a small default rather than unlimited.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["step","field","pass"],"additionalProperties":false},"maxParallel":{"type":"integer","minimum":1,"maximum":8,"description":"How many steps may run at once. Bounded, because a fan-out of twelve is twelve model sessions, twelve working copies and twelve times the burn rate, on one machine."},"gateToken":{"description":"What a pipeline presents at /workflows/{id}/gate, when the design declares a gate. Shown to a maintainer or the owner only.","type":"string"}},"required":["id","name","steps","maxParallel"],"additionalProperties":false}}}}}}},"/workflows/{id}/gate/rotate":{"post":{"operationId":"workflows.rotateGateToken","summary":"Rotate a release gate's token","description":"Mints a new credential for the workflow's release gate and retires the old one at once. Every pipeline wired to the gate has to be handed the new URL. Refused for a workflow that declares no gate.","tags":["Workflows"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which workflow."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":1,"description":"The freshly minted credential. The previous one stopped working the moment this answered."}},"required":["token"],"additionalProperties":false}}}}}}},"/workflows/{id}":{"delete":{"operationId":"workflows.remove","summary":"Delete a workflow","description":"Removes the design. A run of it that is already going keeps going and stays readable and stoppable, because a run takes its own copy of the design when it starts.","tags":["Workflows"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which workflow."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/workflows/{id}/run":{"post":{"operationId":"workflows.run","summary":"Start a workflow","description":"Kicks a workflow off and answers immediately with the run as recorded; the work carries on without you. Point it at a question and every step gets that on top of its own instructions. Every step is written down as waiting up front, so the picture is complete from the first frame. Several runs of one design can be in flight at once without colliding.","tags":["Workflows"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which workflow."}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"request":{"description":"What to point it at. Optional, because a design whose steps already say what they want is complete on its own; only one written as a shape needs today's sentence.","type":"string","minLength":1,"maxLength":20000}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"runId":{"type":"string","minLength":1,"description":"This run's id."},"workflow":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The workflow's id."},"name":{"type":"string","minLength":1,"maxLength":80,"description":"What to call it."},"description":{"description":"What it is for.","type":"string","maxLength":400},"steps":{"minItems":1,"maxItems":24,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$","description":"This step's own name, which other steps use to say they wait on it."},"title":{"type":"string","minLength":1,"maxLength":60,"description":"What to call it on screen. Short: the instruction below is where the detail goes."},"goal":{"description":"What done means for this step, in your words. It is what the step is judged against, and a different sentence from what it is told to do.","type":"string","minLength":1},"prompt":{"description":"What the step is told to do. The goal is the suite passes; this is run the tests, take the top failure, fix it. Leaving it out hands over the run's own request untouched, which is right for a step whose whole job is do what was asked.","type":"string","minLength":1},"needs":{"type":"array","items":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$"},"description":"Which steps must finish first. Empty means it starts when the run does. Naming a step that does not exist, or a loop between steps, is refused when the workflow is saved."},"handoff":{"type":"string","enum":["fresh","continue"],"description":"How it meets what came before: a fresh conversation handed the previous step's result, or the same conversation carried on."},"output":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"none","description":"It produces nothing but its work. The classic make the suite pass: what it leaves behind is a passing suite, and asking it to also file a report is asking it to spend a round on paperwork."}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"claim","description":"Each round says whether it is done and why. Structured prose: done is a value read rather than a sentence interpreted. Self-assessment, so advisory by construction; it exists because plenty of goals have no command that could check them."}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"json","description":"Each round writes a real answer in a shape you declared. This is the one that makes a step's output usable as the next step's input: a paragraph mentioning three files cannot be fed to anything, a list of three files can."},"fields":{"minItems":1,"maxItems":16,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-zA-Z_][a-zA-Z0-9_]{0,39}$"},"type":{"type":"string","enum":["string","number","boolean","string[]"]},"description":{"type":"string","minLength":1},"required":{"type":"boolean"}},"required":["name","type","description","required"],"additionalProperties":false},"description":"The shape that answer has to match."}},"required":["kind","fields"],"additionalProperties":false}],"description":"What it has to produce for the step to count."},"checks":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"command","description":"Run something and see if it passes. Deterministic, free, and the only signal here whose answer does not come from a model. A passing test suite beats any amount of self-report."},"command":{"type":"string","minLength":1,"description":"The command to run in the conversation's own tree. Exiting cleanly means satisfied."}},"required":["kind","command"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"judge","description":"Put the question to a separate model with no tools, which reads the round's own report and rules on it, having done none of the work and nothing invested in its being finished."},"rubric":{"type":"string","minLength":1,"description":"What that judge is asked."},"model":{"description":"Which model judges. Leave it out for the cheap one the other small jobs use.","type":"string"}},"required":["kind","rubric"],"additionalProperties":false}]},"description":"What has to pass before it counts as done."},"context":{"type":"string","enum":["fresh","continue"],"description":"How the step's own repeats meet each other. A long-running step wants to start clean each round; a short polish-this step wants to carry on."},"maxSpendUsd":{"description":"A ceiling on what this step may spend. The one resource that cannot be recovered after an unattended fan-out, which is why it is here and iteration limits are not. Absent is uncapped.","type":"number","exclusiveMinimum":0},"agent":{"description":"Which provider runs it.","type":"string","minLength":1},"harness":{"description":"Which agentic loop runs it.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which account pays for it.","type":"string"},"model":{"description":"Which model runs it.","type":"string"},"actsAs":{"description":"Which persona it acts as. Unpinned, a step gets the strict unwatched default: every tool, and no signed-in accounts at all. Pinning one is how a release check gets a voice, a folder to work in, or the single account it may post from.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"}},"required":["id","title","needs","handoff","output","checks","context"],"additionalProperties":false},"description":"The steps, each with what it waits on. Every one runs in its own private copy of the repos, always, because parallel steps sharing a tree collide."},"gate":{"description":"Present means a machine can run this design and get a ship-it answer back. Absent means an ordinary workflow, started by a person, with no outside door onto it at all.","type":"object","properties":{"step":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$","description":"Which step's answer carries the decision. Usually a last step that weighs up the ones before it, though nothing requires that."},"field":{"type":"string","minLength":1,"description":"Which of that step's declared answers to read. A declared field is the one part of a step's answer that was checked rather than fished out of prose, which is the whole rule here. Checked when the workflow is saved."},"pass":{"minItems":1,"type":"array","items":{"type":"string","minLength":1},"description":"Which values mean ship it. Everything else fails. A list of what passes rather than what fails, because a step answering mostly-pass or pass-with-notes must not ship, and this gets that right without anybody having had to enumerate the ways a model can hedge."},"dailyMax":{"description":"How many runs a day, across every caller. A gate is a paid door with nobody in the loop: one wired into a push-triggered pipeline is a fan-out of conversations per commit. Absent is a small default rather than unlimited.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["step","field","pass"],"additionalProperties":false},"maxParallel":{"type":"integer","minimum":1,"maximum":8,"description":"How many steps may run at once. Bounded, because a fan-out of twelve is twelve model sessions, twelve working copies and twelve times the burn rate, on one machine."}},"required":["id","name","steps","maxParallel"],"additionalProperties":false,"description":"The design as it stood when the run started, copied rather than looked up. The run has to keep showing the graph it actually ran, not the one edited twice since, and a run of a deleted workflow has to stay readable."},"repos":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"repo":{"type":"string"},"base":{"type":"string","minLength":1}},"required":["repo","base"],"additionalProperties":false},"description":"The workspace as this run began, one exact commit per repository. Every step branches from these, even if the shared tree moves while a wide fan-out is still opening its copies, so the steps can be compared with each other afterwards."},"request":{"description":"What this run was asked to do, handed to every step on top of its own instructions. It is what makes one saved design worth keeping: two models, one task is a shape, and the task is different every time. Absent for a run started with nowhere to type one.","type":"string"},"state":{"type":"string","enum":["running","done","failed","stopped","overspent","error"],"description":"How the run is going. Finished means every step that ran got there; a run with skipped steps counts as failed, because a graph that never reached its end did not do what it was asked whatever the survivors managed."},"startedAt":{"type":"number","description":"When it began, in milliseconds."},"endedAt":{"description":"When it ended, in milliseconds.","type":"number"},"resumed":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"How many times the sandbox restarted under it and picked it back up."},"detail":{"description":"What went wrong, when something did.","type":"string"},"steps":{"type":"array","items":{"type":"object","properties":{"stepId":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$","description":"Which step this is."},"state":{"type":"string","enum":["pending","running","done","failed","skipped","stopped"],"description":"How it went. Skipped carries what the others cannot: it never ran, because something it was waiting on did not finish. That is why a failed run shows one failed step and a trail of skipped ones."},"conversationId":{"type":"string","description":"The conversation it ran on, and the way from a node on the graph to a real record. Shared with the step before it when they were chained, which is what makes those two one card."},"startedAt":{"description":"When it began, in milliseconds.","type":"number"},"endedAt":{"description":"When it ended, in milliseconds.","type":"number"},"iterations":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"How many rounds it took."},"costUsd":{"description":"What it cost, in dollars.","type":"number"},"loopState":{"description":"How its repeating ended. Out of rounds and stuck both come out as a failed step, and the difference between them is the difference between give it more room and more room will not help.","type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"]},"detail":{"description":"What went wrong, when something did.","type":"string"},"document":{"description":"What it produced, once it has produced something that passes its own declared shape. This is what the steps after it are handed.","type":"object","properties":{"done":{"type":"boolean","description":"Whether the goal is met. Reading this is the whole point of the file."},"reason":{"type":"string","description":"Why, in one line. The most-read sentence in the feature: the next round reads it first and the history shows it."},"evidence":{"description":"What was checked to know that. Optional, so a round with nothing to point at says so by leaving it out rather than by inventing a sentence.","type":"string"},"data":{"description":"The declared answer, for a loop that asked for one, checked against the shape it declared.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["done","reason"],"additionalProperties":false},"report":{"description":"The start of its closing words. Bounded, so a long answer is not silently cut down to its last few thousand characters and the record stays a sensible size.","type":"string"},"reportPath":{"description":"Where the whole answer is, as a workspace path. Every step can read it, so a long handoff need not be copied into anybody's prompt.","type":"string"}},"required":["stepId","state","conversationId","iterations"],"additionalProperties":false},"description":"One entry per step, in the design's own order. Every one is written down as waiting when the run starts, so the picture is complete from the first frame and a missing step never has to mean two things."},"archivedAt":{"description":"When it was put away, in milliseconds. The record stays readable and every step's branch, transcript and counters are untouched. Its conversations are put away with it, and brought back with it. Absent means live on the board.","type":"number"}},"required":["runId","workflow","repos","state","startedAt","resumed","steps"],"additionalProperties":false}}}}}}},"/workflows/runs":{"get":{"operationId":"workflows.runs","summary":"Every workflow run","description":"All runs across all workflows, newest first. This is also the only place the runs of a deleted workflow are still reachable.","tags":["Workflows"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"runs":{"type":"array","items":{"type":"object","properties":{"runId":{"type":"string","minLength":1,"description":"This run's id."},"workflow":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The workflow's id."},"name":{"type":"string","minLength":1,"maxLength":80,"description":"What to call it."},"description":{"description":"What it is for.","type":"string","maxLength":400},"steps":{"minItems":1,"maxItems":24,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$","description":"This step's own name, which other steps use to say they wait on it."},"title":{"type":"string","minLength":1,"maxLength":60,"description":"What to call it on screen. Short: the instruction below is where the detail goes."},"goal":{"description":"What done means for this step, in your words. It is what the step is judged against, and a different sentence from what it is told to do.","type":"string","minLength":1},"prompt":{"description":"What the step is told to do. The goal is the suite passes; this is run the tests, take the top failure, fix it. Leaving it out hands over the run's own request untouched, which is right for a step whose whole job is do what was asked.","type":"string","minLength":1},"needs":{"type":"array","items":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$"},"description":"Which steps must finish first. Empty means it starts when the run does. Naming a step that does not exist, or a loop between steps, is refused when the workflow is saved."},"handoff":{"type":"string","enum":["fresh","continue"],"description":"How it meets what came before: a fresh conversation handed the previous step's result, or the same conversation carried on."},"output":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"none","description":"It produces nothing but its work. The classic make the suite pass: what it leaves behind is a passing suite, and asking it to also file a report is asking it to spend a round on paperwork."}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"claim","description":"Each round says whether it is done and why. Structured prose: done is a value read rather than a sentence interpreted. Self-assessment, so advisory by construction; it exists because plenty of goals have no command that could check them."}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"json","description":"Each round writes a real answer in a shape you declared. This is the one that makes a step's output usable as the next step's input: a paragraph mentioning three files cannot be fed to anything, a list of three files can."},"fields":{"minItems":1,"maxItems":16,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-zA-Z_][a-zA-Z0-9_]{0,39}$"},"type":{"type":"string","enum":["string","number","boolean","string[]"]},"description":{"type":"string","minLength":1},"required":{"type":"boolean"}},"required":["name","type","description","required"],"additionalProperties":false},"description":"The shape that answer has to match."}},"required":["kind","fields"],"additionalProperties":false}],"description":"What it has to produce for the step to count."},"checks":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"command","description":"Run something and see if it passes. Deterministic, free, and the only signal here whose answer does not come from a model. A passing test suite beats any amount of self-report."},"command":{"type":"string","minLength":1,"description":"The command to run in the conversation's own tree. Exiting cleanly means satisfied."}},"required":["kind","command"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"judge","description":"Put the question to a separate model with no tools, which reads the round's own report and rules on it, having done none of the work and nothing invested in its being finished."},"rubric":{"type":"string","minLength":1,"description":"What that judge is asked."},"model":{"description":"Which model judges. Leave it out for the cheap one the other small jobs use.","type":"string"}},"required":["kind","rubric"],"additionalProperties":false}]},"description":"What has to pass before it counts as done."},"context":{"type":"string","enum":["fresh","continue"],"description":"How the step's own repeats meet each other. A long-running step wants to start clean each round; a short polish-this step wants to carry on."},"maxSpendUsd":{"description":"A ceiling on what this step may spend. The one resource that cannot be recovered after an unattended fan-out, which is why it is here and iteration limits are not. Absent is uncapped.","type":"number","exclusiveMinimum":0},"agent":{"description":"Which provider runs it.","type":"string","minLength":1},"harness":{"description":"Which agentic loop runs it.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which account pays for it.","type":"string"},"model":{"description":"Which model runs it.","type":"string"},"actsAs":{"description":"Which persona it acts as. Unpinned, a step gets the strict unwatched default: every tool, and no signed-in accounts at all. Pinning one is how a release check gets a voice, a folder to work in, or the single account it may post from.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"}},"required":["id","title","needs","handoff","output","checks","context"],"additionalProperties":false},"description":"The steps, each with what it waits on. Every one runs in its own private copy of the repos, always, because parallel steps sharing a tree collide."},"gate":{"description":"Present means a machine can run this design and get a ship-it answer back. Absent means an ordinary workflow, started by a person, with no outside door onto it at all.","type":"object","properties":{"step":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$","description":"Which step's answer carries the decision. Usually a last step that weighs up the ones before it, though nothing requires that."},"field":{"type":"string","minLength":1,"description":"Which of that step's declared answers to read. A declared field is the one part of a step's answer that was checked rather than fished out of prose, which is the whole rule here. Checked when the workflow is saved."},"pass":{"minItems":1,"type":"array","items":{"type":"string","minLength":1},"description":"Which values mean ship it. Everything else fails. A list of what passes rather than what fails, because a step answering mostly-pass or pass-with-notes must not ship, and this gets that right without anybody having had to enumerate the ways a model can hedge."},"dailyMax":{"description":"How many runs a day, across every caller. A gate is a paid door with nobody in the loop: one wired into a push-triggered pipeline is a fan-out of conversations per commit. Absent is a small default rather than unlimited.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["step","field","pass"],"additionalProperties":false},"maxParallel":{"type":"integer","minimum":1,"maximum":8,"description":"How many steps may run at once. Bounded, because a fan-out of twelve is twelve model sessions, twelve working copies and twelve times the burn rate, on one machine."}},"required":["id","name","steps","maxParallel"],"additionalProperties":false,"description":"The design as it stood when the run started, copied rather than looked up. The run has to keep showing the graph it actually ran, not the one edited twice since, and a run of a deleted workflow has to stay readable."},"repos":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"repo":{"type":"string"},"base":{"type":"string","minLength":1}},"required":["repo","base"],"additionalProperties":false},"description":"The workspace as this run began, one exact commit per repository. Every step branches from these, even if the shared tree moves while a wide fan-out is still opening its copies, so the steps can be compared with each other afterwards."},"request":{"description":"What this run was asked to do, handed to every step on top of its own instructions. It is what makes one saved design worth keeping: two models, one task is a shape, and the task is different every time. Absent for a run started with nowhere to type one.","type":"string"},"state":{"type":"string","enum":["running","done","failed","stopped","overspent","error"],"description":"How the run is going. Finished means every step that ran got there; a run with skipped steps counts as failed, because a graph that never reached its end did not do what it was asked whatever the survivors managed."},"startedAt":{"type":"number","description":"When it began, in milliseconds."},"endedAt":{"description":"When it ended, in milliseconds.","type":"number"},"resumed":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"How many times the sandbox restarted under it and picked it back up."},"detail":{"description":"What went wrong, when something did.","type":"string"},"steps":{"type":"array","items":{"type":"object","properties":{"stepId":{"type":"string","minLength":1,"maxLength":24,"pattern":"^[a-z0-9][a-z0-9-]*$","description":"Which step this is."},"state":{"type":"string","enum":["pending","running","done","failed","skipped","stopped"],"description":"How it went. Skipped carries what the others cannot: it never ran, because something it was waiting on did not finish. That is why a failed run shows one failed step and a trail of skipped ones."},"conversationId":{"type":"string","description":"The conversation it ran on, and the way from a node on the graph to a real record. Shared with the step before it when they were chained, which is what makes those two one card."},"startedAt":{"description":"When it began, in milliseconds.","type":"number"},"endedAt":{"description":"When it ended, in milliseconds.","type":"number"},"iterations":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"How many rounds it took."},"costUsd":{"description":"What it cost, in dollars.","type":"number"},"loopState":{"description":"How its repeating ended. Out of rounds and stuck both come out as a failed step, and the difference between them is the difference between give it more room and more room will not help.","type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"]},"detail":{"description":"What went wrong, when something did.","type":"string"},"document":{"description":"What it produced, once it has produced something that passes its own declared shape. This is what the steps after it are handed.","type":"object","properties":{"done":{"type":"boolean","description":"Whether the goal is met. Reading this is the whole point of the file."},"reason":{"type":"string","description":"Why, in one line. The most-read sentence in the feature: the next round reads it first and the history shows it."},"evidence":{"description":"What was checked to know that. Optional, so a round with nothing to point at says so by leaving it out rather than by inventing a sentence.","type":"string"},"data":{"description":"The declared answer, for a loop that asked for one, checked against the shape it declared.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["done","reason"],"additionalProperties":false},"report":{"description":"The start of its closing words. Bounded, so a long answer is not silently cut down to its last few thousand characters and the record stays a sensible size.","type":"string"},"reportPath":{"description":"Where the whole answer is, as a workspace path. Every step can read it, so a long handoff need not be copied into anybody's prompt.","type":"string"}},"required":["stepId","state","conversationId","iterations"],"additionalProperties":false},"description":"One entry per step, in the design's own order. Every one is written down as waiting when the run starts, so the picture is complete from the first frame and a missing step never has to mean two things."},"archivedAt":{"description":"When it was put away, in milliseconds. The record stays readable and every step's branch, transcript and counters are untouched. Its conversations are put away with it, and brought back with it. Absent means live on the board.","type":"number"}},"required":["runId","workflow","repos","state","startedAt","resumed","steps"],"additionalProperties":false},"description":"Every run across every workflow, newest first, including runs of workflows since deleted."}},"required":["runs"],"additionalProperties":false}}}}}}},"/workflows/runs/{runId}/stop":{"post":{"operationId":"workflows.stopRun","summary":"Stop a run now","description":"Nothing further starts, and the steps already going are cut off where they stand. Whatever they had written stays on their branches. Deliberately abrupt rather than letting the current step finish: a step is a whole agent turn, and a stop that kept spending for minutes afterwards is indistinguishable from a button that does nothing. It always ends the run, including one left stranded by a daemon that was replaced mid-flight.","tags":["Workflows"],"parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string","description":"Which run."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/workflows/runs/{runId}/archive":{"post":{"operationId":"workflows.archiveRun","summary":"Take a finished run off the board","description":"Nothing is lost and the working copies are reclaimed. Every conversation the run started is put away with it, which is what makes this an archive rather than a dismissal: a step has no card of its own, so merely dropping the run would spill its conversations onto the board at the moment somebody said they were done. Refused while the run is still going.","tags":["Workflows"],"parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string","description":"Which run."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/workflows/runs/{runId}/unarchive":{"post":{"operationId":"workflows.unarchiveRun","summary":"Bring an archived run back","description":"Puts a run and every conversation it started back on the board.","tags":["Workflows"],"parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string","description":"Which run."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/loops":{"get":{"operationId":"loops.list","summary":"Every loop that has run","description":"The loops this workspace has run, newest first, kept after they end. Why it stopped on the fourth round is the question a loop gets read for, and the round-by-round history is the answer.","tags":["Loops"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"loops":{"type":"array","items":{"type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$","description":"The conversation to loop. It need not exist yet: naming a fresh one opens it, which is what lets run this until it passes be the first thing you ever say."},"goal":{"type":"string","minLength":1,"description":"What done means, in your words. It goes into every round's instructions and into the judge's question, so the model is told the bar rather than left to infer it."},"prompt":{"type":"string","minLength":1,"description":"What each round is asked to do. The suite passes is the goal; run the tests, take the top failure, fix it is the instruction."},"context":{"type":"string","enum":["fresh","continue"],"description":"How each round meets the last. Starting fresh makes the files the memory rather than the conversation, so the twentieth round reads the tree as clearly as the first, and costs a re-read each time. Carrying on is cheaper and keeps the reasoning, which suits a short polish-this loop and degrades on long ones: a session that has spent eleven rounds arguing for its own approach is the worst available judge of whether that approach is finished."},"output":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"none","description":"It produces nothing but its work. The classic make the suite pass: what it leaves behind is a passing suite, and asking it to also file a report is asking it to spend a round on paperwork."}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"claim","description":"Each round says whether it is done and why. Structured prose: done is a value read rather than a sentence interpreted. Self-assessment, so advisory by construction; it exists because plenty of goals have no command that could check them."}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"json","description":"Each round writes a real answer in a shape you declared. This is the one that makes a step's output usable as the next step's input: a paragraph mentioning three files cannot be fed to anything, a list of three files can."},"fields":{"minItems":1,"maxItems":16,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-zA-Z_][a-zA-Z0-9_]{0,39}$"},"type":{"type":"string","enum":["string","number","boolean","string[]"]},"description":{"type":"string","minLength":1},"required":{"type":"boolean"}},"required":["name","type","description","required"],"additionalProperties":false},"description":"The shape that answer has to match."}},"required":["kind","fields"],"additionalProperties":false}]},"checks":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"command","description":"Run something and see if it passes. Deterministic, free, and the only signal here whose answer does not come from a model. A passing test suite beats any amount of self-report."},"command":{"type":"string","minLength":1,"description":"The command to run in the conversation's own tree. Exiting cleanly means satisfied."}},"required":["kind","command"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"judge","description":"Put the question to a separate model with no tools, which reads the round's own report and rules on it, having done none of the work and nothing invested in its being finished."},"rubric":{"type":"string","minLength":1,"description":"What that judge is asked."},"model":{"description":"Which model judges. Leave it out for the cheap one the other small jobs use.","type":"string"}},"required":["kind","rubric"],"additionalProperties":false}]},"description":"What else has to be true, all of them together. A list because the suite passes and the report is written is a real bar, and running it as two loops would do the work twice."},"maxIterations":{"type":"integer","minimum":1,"maximum":50,"description":"How many rounds before it gives up. A loop that has not got there in fifty is not one round short of it."},"maxSpendUsd":{"description":"A ceiling on what the whole loop may spend, in dollars. Optional for a short loop somebody is watching, and strongly wanted otherwise: this is the first thing here that can keep spending with nobody pressing anything between rounds.","type":"number","exclusiveMinimum":0},"stallLimit":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"Stop after this many rounds in a row that changed nothing on disk. The guard that matters most: a loop's failure is not runaway success, it is an agent re-reading the same three files, restating the same plan and declaring more work remains, eleven times. Every one of those rounds succeeds, so only the tree not moving catches it."},"isolated":{"type":"boolean","description":"Whether it works in the conversation's own private copy or in the shared tree. It also decides where a check runs: testing the shared tree would be testing code this loop has not merged yet."},"agent":{"description":"Which provider the rounds run on. Absent falls back to the conversation's own last choice.","type":"string","minLength":1},"harness":{"description":"Which agentic loop they run on.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which account pays.","type":"string"},"model":{"description":"Which model.","type":"string"},"actsAs":{"description":"Which persona the rounds act as. It matters here: every round is unwatched, and an unwatched turn naming no persona reaches no signed-in account at all, so pinning one is how a loop gets hands.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"worktreeBase":{"description":"Pin the private copy to these exact commits, so a restart cannot quietly change what the loop is working on.","minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"repo":{"type":"string"},"base":{"type":"string","minLength":1}},"required":["repo","base"],"additionalProperties":false}},"autoLand":{"description":"Whether the work merges as it goes.","type":"boolean"},"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How it ended, and each of these is a different thing to be told. Out of rounds says give it more room; stalled says it is not making progress and more room will not help. Overspent, stopped by a person, and the loop itself failing are all their own answers."},"startedAt":{"type":"number","description":"When it began, in milliseconds."},"endedAt":{"description":"When it ended, in milliseconds.","type":"number"},"resumed":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"How many times the sandbox restarted under it and picked it back up. Counted rather than flagged, so a loop whose round reliably kills the sandbox is not resurrected on every boot for ever."},"detail":{"description":"Why it ended, for the endings whose reason is not in their name.","type":"string"},"iterations":{"type":"array","items":{"type":"object","properties":{"n":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"Which round this was."},"at":{"type":"number","description":"When it ran, in milliseconds."},"outcome":{"type":"string","enum":["continue","done","error"],"description":"How the round ended, which is not the same question as how the loop did. A round that errored does not end the loop by itself: a failing turn is often exactly what the next round is meant to fix."},"detail":{"description":"What the check said, in its own words. What a run history is actually read for: why it kept going, and why it stopped.","type":"string"},"costUsd":{"description":"What the round cost, in dollars.","type":"number"},"changed":{"type":"boolean","description":"Whether anything on disk moved. Three unchanged rounds in a row is the shape of a loop that is not working."},"sessionId":{"description":"The session it ran on, and the way from a history row to a readable record.","type":"string"}},"required":["n","at","outcome","changed"],"additionalProperties":false},"description":"Every round, in order. Why it stopped at the fourth is the question a loop gets read for, and this is the answer."}},"required":["conversationId","goal","prompt","context","output","checks","maxIterations","stallLimit","isolated","state","startedAt","resumed","iterations"],"additionalProperties":false},"description":"Every loop this workspace has run, newest first, kept after they end."}},"required":["loops"],"additionalProperties":false}}}}}},"post":{"operationId":"loops.start","summary":"Run a conversation until it is done","description":"Starts repeating a conversation towards a goal and answers straight away with the loop as recorded; the work carries on without you. The conversation need not exist yet, so run this until it passes can be the first thing you ever say to a new agent. A conversation already looping is refused.","tags":["Loops"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$","description":"The conversation to loop. It need not exist yet: naming a fresh one opens it, which is what lets run this until it passes be the first thing you ever say."},"goal":{"type":"string","minLength":1,"description":"What done means, in your words. It goes into every round's instructions and into the judge's question, so the model is told the bar rather than left to infer it."},"prompt":{"type":"string","minLength":1,"description":"What each round is asked to do. The suite passes is the goal; run the tests, take the top failure, fix it is the instruction."},"context":{"type":"string","enum":["fresh","continue"],"description":"How each round meets the last. Starting fresh makes the files the memory rather than the conversation, so the twentieth round reads the tree as clearly as the first, and costs a re-read each time. Carrying on is cheaper and keeps the reasoning, which suits a short polish-this loop and degrades on long ones: a session that has spent eleven rounds arguing for its own approach is the worst available judge of whether that approach is finished."},"output":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"none","description":"It produces nothing but its work. The classic make the suite pass: what it leaves behind is a passing suite, and asking it to also file a report is asking it to spend a round on paperwork."}},"required":["kind"]},{"type":"object","properties":{"kind":{"type":"string","const":"claim","description":"Each round says whether it is done and why. Structured prose: done is a value read rather than a sentence interpreted. Self-assessment, so advisory by construction; it exists because plenty of goals have no command that could check them."}},"required":["kind"]},{"type":"object","properties":{"kind":{"type":"string","const":"json","description":"Each round writes a real answer in a shape you declared. This is the one that makes a step's output usable as the next step's input: a paragraph mentioning three files cannot be fed to anything, a list of three files can."},"fields":{"minItems":1,"maxItems":16,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-zA-Z_][a-zA-Z0-9_]{0,39}$"},"type":{"type":"string","enum":["string","number","boolean","string[]"]},"description":{"type":"string","minLength":1},"required":{"type":"boolean"}},"required":["name","type","description","required"]},"description":"The shape that answer has to match."}},"required":["kind","fields"]}]},"checks":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"command","description":"Run something and see if it passes. Deterministic, free, and the only signal here whose answer does not come from a model. A passing test suite beats any amount of self-report."},"command":{"type":"string","minLength":1,"description":"The command to run in the conversation's own tree. Exiting cleanly means satisfied."}},"required":["kind","command"]},{"type":"object","properties":{"kind":{"type":"string","const":"judge","description":"Put the question to a separate model with no tools, which reads the round's own report and rules on it, having done none of the work and nothing invested in its being finished."},"rubric":{"type":"string","minLength":1,"description":"What that judge is asked."},"model":{"description":"Which model judges. Leave it out for the cheap one the other small jobs use.","type":"string"}},"required":["kind","rubric"]}]},"description":"What else has to be true, all of them together. A list because the suite passes and the report is written is a real bar, and running it as two loops would do the work twice."},"maxIterations":{"type":"integer","minimum":1,"maximum":50,"description":"How many rounds before it gives up. A loop that has not got there in fifty is not one round short of it."},"maxSpendUsd":{"description":"A ceiling on what the whole loop may spend, in dollars. Optional for a short loop somebody is watching, and strongly wanted otherwise: this is the first thing here that can keep spending with nobody pressing anything between rounds.","type":"number","exclusiveMinimum":0},"stallLimit":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"Stop after this many rounds in a row that changed nothing on disk. The guard that matters most: a loop's failure is not runaway success, it is an agent re-reading the same three files, restating the same plan and declaring more work remains, eleven times. Every one of those rounds succeeds, so only the tree not moving catches it."},"isolated":{"type":"boolean","description":"Whether it works in the conversation's own private copy or in the shared tree. It also decides where a check runs: testing the shared tree would be testing code this loop has not merged yet."},"agent":{"description":"Which provider the rounds run on. Absent falls back to the conversation's own last choice.","type":"string","minLength":1},"harness":{"description":"Which agentic loop they run on.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which account pays.","type":"string"},"model":{"description":"Which model.","type":"string"},"actsAs":{"description":"Which persona the rounds act as. It matters here: every round is unwatched, and an unwatched turn naming no persona reaches no signed-in account at all, so pinning one is how a loop gets hands.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"worktreeBase":{"description":"Pin the private copy to these exact commits, so a restart cannot quietly change what the loop is working on.","minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"repo":{"type":"string"},"base":{"type":"string","minLength":1}},"required":["repo","base"]}},"autoLand":{"description":"Whether the work merges as it goes.","type":"boolean"}},"required":["conversationId","goal","prompt","context","output","checks","maxIterations","stallLimit","isolated"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$","description":"The conversation to loop. It need not exist yet: naming a fresh one opens it, which is what lets run this until it passes be the first thing you ever say."},"goal":{"type":"string","minLength":1,"description":"What done means, in your words. It goes into every round's instructions and into the judge's question, so the model is told the bar rather than left to infer it."},"prompt":{"type":"string","minLength":1,"description":"What each round is asked to do. The suite passes is the goal; run the tests, take the top failure, fix it is the instruction."},"context":{"type":"string","enum":["fresh","continue"],"description":"How each round meets the last. Starting fresh makes the files the memory rather than the conversation, so the twentieth round reads the tree as clearly as the first, and costs a re-read each time. Carrying on is cheaper and keeps the reasoning, which suits a short polish-this loop and degrades on long ones: a session that has spent eleven rounds arguing for its own approach is the worst available judge of whether that approach is finished."},"output":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"none","description":"It produces nothing but its work. The classic make the suite pass: what it leaves behind is a passing suite, and asking it to also file a report is asking it to spend a round on paperwork."}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"claim","description":"Each round says whether it is done and why. Structured prose: done is a value read rather than a sentence interpreted. Self-assessment, so advisory by construction; it exists because plenty of goals have no command that could check them."}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"json","description":"Each round writes a real answer in a shape you declared. This is the one that makes a step's output usable as the next step's input: a paragraph mentioning three files cannot be fed to anything, a list of three files can."},"fields":{"minItems":1,"maxItems":16,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-zA-Z_][a-zA-Z0-9_]{0,39}$"},"type":{"type":"string","enum":["string","number","boolean","string[]"]},"description":{"type":"string","minLength":1},"required":{"type":"boolean"}},"required":["name","type","description","required"],"additionalProperties":false},"description":"The shape that answer has to match."}},"required":["kind","fields"],"additionalProperties":false}]},"checks":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"command","description":"Run something and see if it passes. Deterministic, free, and the only signal here whose answer does not come from a model. A passing test suite beats any amount of self-report."},"command":{"type":"string","minLength":1,"description":"The command to run in the conversation's own tree. Exiting cleanly means satisfied."}},"required":["kind","command"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"judge","description":"Put the question to a separate model with no tools, which reads the round's own report and rules on it, having done none of the work and nothing invested in its being finished."},"rubric":{"type":"string","minLength":1,"description":"What that judge is asked."},"model":{"description":"Which model judges. Leave it out for the cheap one the other small jobs use.","type":"string"}},"required":["kind","rubric"],"additionalProperties":false}]},"description":"What else has to be true, all of them together. A list because the suite passes and the report is written is a real bar, and running it as two loops would do the work twice."},"maxIterations":{"type":"integer","minimum":1,"maximum":50,"description":"How many rounds before it gives up. A loop that has not got there in fifty is not one round short of it."},"maxSpendUsd":{"description":"A ceiling on what the whole loop may spend, in dollars. Optional for a short loop somebody is watching, and strongly wanted otherwise: this is the first thing here that can keep spending with nobody pressing anything between rounds.","type":"number","exclusiveMinimum":0},"stallLimit":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"Stop after this many rounds in a row that changed nothing on disk. The guard that matters most: a loop's failure is not runaway success, it is an agent re-reading the same three files, restating the same plan and declaring more work remains, eleven times. Every one of those rounds succeeds, so only the tree not moving catches it."},"isolated":{"type":"boolean","description":"Whether it works in the conversation's own private copy or in the shared tree. It also decides where a check runs: testing the shared tree would be testing code this loop has not merged yet."},"agent":{"description":"Which provider the rounds run on. Absent falls back to the conversation's own last choice.","type":"string","minLength":1},"harness":{"description":"Which agentic loop they run on.","type":"string","enum":["native","claude-code"]},"account":{"description":"Which account pays.","type":"string"},"model":{"description":"Which model.","type":"string"},"actsAs":{"description":"Which persona the rounds act as. It matters here: every round is unwatched, and an unwatched turn naming no persona reaches no signed-in account at all, so pinning one is how a loop gets hands.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"worktreeBase":{"description":"Pin the private copy to these exact commits, so a restart cannot quietly change what the loop is working on.","minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"repo":{"type":"string"},"base":{"type":"string","minLength":1}},"required":["repo","base"],"additionalProperties":false}},"autoLand":{"description":"Whether the work merges as it goes.","type":"boolean"},"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How it ended, and each of these is a different thing to be told. Out of rounds says give it more room; stalled says it is not making progress and more room will not help. Overspent, stopped by a person, and the loop itself failing are all their own answers."},"startedAt":{"type":"number","description":"When it began, in milliseconds."},"endedAt":{"description":"When it ended, in milliseconds.","type":"number"},"resumed":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"How many times the sandbox restarted under it and picked it back up. Counted rather than flagged, so a loop whose round reliably kills the sandbox is not resurrected on every boot for ever."},"detail":{"description":"Why it ended, for the endings whose reason is not in their name.","type":"string"},"iterations":{"type":"array","items":{"type":"object","properties":{"n":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"Which round this was."},"at":{"type":"number","description":"When it ran, in milliseconds."},"outcome":{"type":"string","enum":["continue","done","error"],"description":"How the round ended, which is not the same question as how the loop did. A round that errored does not end the loop by itself: a failing turn is often exactly what the next round is meant to fix."},"detail":{"description":"What the check said, in its own words. What a run history is actually read for: why it kept going, and why it stopped.","type":"string"},"costUsd":{"description":"What the round cost, in dollars.","type":"number"},"changed":{"type":"boolean","description":"Whether anything on disk moved. Three unchanged rounds in a row is the shape of a loop that is not working."},"sessionId":{"description":"The session it ran on, and the way from a history row to a readable record.","type":"string"}},"required":["n","at","outcome","changed"],"additionalProperties":false},"description":"Every round, in order. Why it stopped at the fourth is the question a loop gets read for, and this is the answer."}},"required":["conversationId","goal","prompt","context","output","checks","maxIterations","stallLimit","isolated","state","startedAt","resumed","iterations"],"additionalProperties":false}}}}}}},"/loops/{conversationId}/stop":{"post":{"operationId":"loops.stop","summary":"Make this round the last","description":"Means do not start another round, not stop what is running. Somebody watching the sixth round do good work can say this is the last one without throwing that work away. To cut the current round off as well, stop the conversation too.","tags":["Loops"],"parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$","description":"Which conversation's loop."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/loops/designs":{"get":{"operationId":"loops.designs","summary":"Saved loop designs","description":"Loops somebody authored once and can point at a different job each time. A saved loop is the same loop with its goal left blank until you type one, not a different feature.","tags":["Loops"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"designs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The design's id."},"name":{"type":"string","minLength":1,"maxLength":60,"description":"What to call it. Short, because it has to be readable on a small badge."},"description":{"description":"What it is for, in one line. Optional, because a well-named loop has already said it.","type":"string","maxLength":280},"prompt":{"description":"What each round is asked to do, when that is worth saying separately from the goal. Absent means each round works towards the goal however it sees fit.","type":"string"},"context":{"type":"string","enum":["fresh","continue"],"description":"How each round meets the last: starting clean, or carrying on."},"output":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"none","description":"It produces nothing but its work. The classic make the suite pass: what it leaves behind is a passing suite, and asking it to also file a report is asking it to spend a round on paperwork."}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"claim","description":"Each round says whether it is done and why. Structured prose: done is a value read rather than a sentence interpreted. Self-assessment, so advisory by construction; it exists because plenty of goals have no command that could check them."}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"json","description":"Each round writes a real answer in a shape you declared. This is the one that makes a step's output usable as the next step's input: a paragraph mentioning three files cannot be fed to anything, a list of three files can."},"fields":{"minItems":1,"maxItems":16,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-zA-Z_][a-zA-Z0-9_]{0,39}$"},"type":{"type":"string","enum":["string","number","boolean","string[]"]},"description":{"type":"string","minLength":1},"required":{"type":"boolean"}},"required":["name","type","description","required"],"additionalProperties":false},"description":"The shape that answer has to match."}},"required":["kind","fields"],"additionalProperties":false}],"description":"What it has to produce."},"checks":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"command","description":"Run something and see if it passes. Deterministic, free, and the only signal here whose answer does not come from a model. A passing test suite beats any amount of self-report."},"command":{"type":"string","minLength":1,"description":"The command to run in the conversation's own tree. Exiting cleanly means satisfied."}},"required":["kind","command"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"judge","description":"Put the question to a separate model with no tools, which reads the round's own report and rules on it, having done none of the work and nothing invested in its being finished."},"rubric":{"type":"string","minLength":1,"description":"What that judge is asked."},"model":{"description":"Which model judges. Leave it out for the cheap one the other small jobs use.","type":"string"}},"required":["kind","rubric"],"additionalProperties":false}]},"description":"What else has to be true."},"maxIterations":{"type":"integer","minimum":1,"maximum":50,"description":"How many rounds before it gives up."},"maxSpendUsd":{"description":"A ceiling on what it may spend, in dollars.","type":"number","exclusiveMinimum":0},"stallLimit":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"Stop after this many rounds in a row that changed nothing."}},"required":["id","name","context","output","checks","maxIterations","stallLimit"],"additionalProperties":false},"description":"Saved loops: the machinery with the goal left out, so one design can be pointed at a different job every time."}},"required":["designs"],"additionalProperties":false}}}}}},"post":{"operationId":"loops.saveDesign","summary":"Create or replace a saved loop","description":"Say which of the two you mean, so a name that happens to collide cannot silently overwrite somebody's work. A design that could never finish, with nothing to produce and nothing to check, is refused in the same words an ad-hoc loop would be: catching that at save time is the whole advantage of saving.","tags":["Loops"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"design":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The design's id."},"name":{"type":"string","minLength":1,"maxLength":60,"description":"What to call it. Short, because it has to be readable on a small badge."},"description":{"description":"What it is for, in one line. Optional, because a well-named loop has already said it.","type":"string","maxLength":280},"prompt":{"description":"What each round is asked to do, when that is worth saying separately from the goal. Absent means each round works towards the goal however it sees fit.","type":"string"},"context":{"type":"string","enum":["fresh","continue"],"description":"How each round meets the last: starting clean, or carrying on."},"output":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"none","description":"It produces nothing but its work. The classic make the suite pass: what it leaves behind is a passing suite, and asking it to also file a report is asking it to spend a round on paperwork."}},"required":["kind"]},{"type":"object","properties":{"kind":{"type":"string","const":"claim","description":"Each round says whether it is done and why. Structured prose: done is a value read rather than a sentence interpreted. Self-assessment, so advisory by construction; it exists because plenty of goals have no command that could check them."}},"required":["kind"]},{"type":"object","properties":{"kind":{"type":"string","const":"json","description":"Each round writes a real answer in a shape you declared. This is the one that makes a step's output usable as the next step's input: a paragraph mentioning three files cannot be fed to anything, a list of three files can."},"fields":{"minItems":1,"maxItems":16,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-zA-Z_][a-zA-Z0-9_]{0,39}$"},"type":{"type":"string","enum":["string","number","boolean","string[]"]},"description":{"type":"string","minLength":1},"required":{"type":"boolean"}},"required":["name","type","description","required"]},"description":"The shape that answer has to match."}},"required":["kind","fields"]}],"description":"What it has to produce."},"checks":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"command","description":"Run something and see if it passes. Deterministic, free, and the only signal here whose answer does not come from a model. A passing test suite beats any amount of self-report."},"command":{"type":"string","minLength":1,"description":"The command to run in the conversation's own tree. Exiting cleanly means satisfied."}},"required":["kind","command"]},{"type":"object","properties":{"kind":{"type":"string","const":"judge","description":"Put the question to a separate model with no tools, which reads the round's own report and rules on it, having done none of the work and nothing invested in its being finished."},"rubric":{"type":"string","minLength":1,"description":"What that judge is asked."},"model":{"description":"Which model judges. Leave it out for the cheap one the other small jobs use.","type":"string"}},"required":["kind","rubric"]}]},"description":"What else has to be true."},"maxIterations":{"type":"integer","minimum":1,"maximum":50,"description":"How many rounds before it gives up."},"maxSpendUsd":{"description":"A ceiling on what it may spend, in dollars.","type":"number","exclusiveMinimum":0},"stallLimit":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"Stop after this many rounds in a row that changed nothing."}},"required":["id","name","context","output","checks","maxIterations","stallLimit"],"description":"The design to write."},"create":{"type":"boolean","description":"Whether you mean to make a new one or replace an existing one, so an id that happens to collide cannot silently overwrite the one you had."}},"required":["design","create"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The design's id."},"name":{"type":"string","minLength":1,"maxLength":60,"description":"What to call it. Short, because it has to be readable on a small badge."},"description":{"description":"What it is for, in one line. Optional, because a well-named loop has already said it.","type":"string","maxLength":280},"prompt":{"description":"What each round is asked to do, when that is worth saying separately from the goal. Absent means each round works towards the goal however it sees fit.","type":"string"},"context":{"type":"string","enum":["fresh","continue"],"description":"How each round meets the last: starting clean, or carrying on."},"output":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"none","description":"It produces nothing but its work. The classic make the suite pass: what it leaves behind is a passing suite, and asking it to also file a report is asking it to spend a round on paperwork."}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"claim","description":"Each round says whether it is done and why. Structured prose: done is a value read rather than a sentence interpreted. Self-assessment, so advisory by construction; it exists because plenty of goals have no command that could check them."}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"json","description":"Each round writes a real answer in a shape you declared. This is the one that makes a step's output usable as the next step's input: a paragraph mentioning three files cannot be fed to anything, a list of three files can."},"fields":{"minItems":1,"maxItems":16,"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-zA-Z_][a-zA-Z0-9_]{0,39}$"},"type":{"type":"string","enum":["string","number","boolean","string[]"]},"description":{"type":"string","minLength":1},"required":{"type":"boolean"}},"required":["name","type","description","required"],"additionalProperties":false},"description":"The shape that answer has to match."}},"required":["kind","fields"],"additionalProperties":false}],"description":"What it has to produce."},"checks":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"command","description":"Run something and see if it passes. Deterministic, free, and the only signal here whose answer does not come from a model. A passing test suite beats any amount of self-report."},"command":{"type":"string","minLength":1,"description":"The command to run in the conversation's own tree. Exiting cleanly means satisfied."}},"required":["kind","command"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"judge","description":"Put the question to a separate model with no tools, which reads the round's own report and rules on it, having done none of the work and nothing invested in its being finished."},"rubric":{"type":"string","minLength":1,"description":"What that judge is asked."},"model":{"description":"Which model judges. Leave it out for the cheap one the other small jobs use.","type":"string"}},"required":["kind","rubric"],"additionalProperties":false}]},"description":"What else has to be true."},"maxIterations":{"type":"integer","minimum":1,"maximum":50,"description":"How many rounds before it gives up."},"maxSpendUsd":{"description":"A ceiling on what it may spend, in dollars.","type":"number","exclusiveMinimum":0},"stallLimit":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"Stop after this many rounds in a row that changed nothing."}},"required":["id","name","context","output","checks","maxIterations","stallLimit"],"additionalProperties":false}}}}}}},"/loops/designs/{id}":{"delete":{"operationId":"loops.removeDesign","summary":"Delete a saved loop","description":"Removes the design. A loop already running from it keeps going on its own terms, because it took a copy of what it needed when it started.","tags":["Loops"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"Which saved loop."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/automations":{"get":{"operationId":"automations.list","summary":"Things that wake an agent on their own","description":"Every automation with its recent runs and when it fires next.","tags":["Automations"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"automations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The automation's id."},"trigger":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"schedule","description":"On a clock."},"cron":{"type":"string","minLength":1,"description":"When, in cron notation."},"tz":{"description":"Which clock the times in the cron mean, as a zone name like Europe/Warsaw. Leave it out to use the sandbox's own setting, which is what you want unless this one chore belongs to a different place.","type":"string"},"afterSessions":{"description":"Fire only once at least this many new sessions have been run since the last wake. A due run short of that is skipped, and says how far off it is.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["kind","cron"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"once","description":"At one moment, and then never again."},"at":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991,"description":"The moment it fires, in milliseconds. An absolute instant, so it means the same thing wherever the sandbox runs."}},"required":["kind","at"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"event","description":"When something calls its webhook."},"dailyMax":{"description":"How many webhook calls a day may wake the agent, across every caller. Absent is a modest default rather than unlimited.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"listener","description":"When a message arrives from somewhere outside."},"provider":{"type":"string","minLength":1,"description":"Which service to listen to."},"channelId":{"description":"Narrow it to one channel or thread.","type":"string","minLength":1},"eventType":{"description":"Narrow it to one kind of event.","type":"string","minLength":1},"mentioned":{"description":"Only when the agent is actually addressed, rather than on everything said in earshot.","type":"boolean"},"branch":{"description":"Narrow it to one branch, for the sources that have branches. Absent means every branch of the repositories it matches.","type":"string","minLength":1},"allowedOrigins":{"description":"Which websites may reach the public endpoint, the chat widget's or the bug reporter's. Absent or empty admits nobody.","type":"array","items":{"type":"string"}}},"required":["kind","provider"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"workspace","description":"When something happens to the files or the repositories."},"event":{"type":"string","enum":["turn.settled","agent.landed","deps.broken","deps.fixed"],"description":"Which happening."},"repo":{"description":"Narrow it to one repository. Absent means any of them.","type":"string","minLength":1}},"required":["kind","event"],"additionalProperties":false}],"description":"What sets it off: a schedule, an event in the workspace, a message arriving from outside, or a webhook."},"guard":{"description":"A command run before the wake that decides whether there is anything to do. Skipped by the guard is often the most useful thing an automation can report.","type":"string","minLength":1},"prompt":{"type":"string","minLength":1,"description":"What the woken agent is told."},"webchat":{"description":"Settings for the public chat widget, for an automation that answers visitors.","type":"object","properties":{"access":{"description":"Who may write to it. Absent means anyone, which is the anonymous support box it looks like.","type":"string","enum":["public","google"]},"requireName":{"description":"Ask a visitor for a name first. Cosmetic: the name is typed, so it reaches the model as something a stranger said, never as identity.","type":"boolean"},"antiBot":{"description":"How to keep bots out: a third-party check that needs the site's own keys, or a puzzle the sandbox sets and the widget solves, so a site with no such account still has something. Absent leaves the site allowlist and the rate limit as the whole boundary.","type":"string","enum":["turnstile","pow"]},"turnstileSiteKey":{"description":"The public half of those keys, which ships to the visitor's browser.","type":"string"},"turnstileSecret":{"description":"The private half, which the sandbox keeps and the widget never sees.","type":"string"},"googleClientId":{"description":"The site's own sign-in client id. It cannot be ours: a sign-in is only issued to an approved origin, and no single client can list every customer's domain.","type":"string"},"title":{"type":"string","maxLength":80},"greeting":{"type":"string","maxLength":500},"accent":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"},"position":{"type":"string","enum":["top-right","top-left","bottom-right","bottom-left"]},"dailyMessageMax":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"conversationMessageMax":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"sessionTtlMinutes":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"additionalProperties":false},"issues":{"description":"Settings for the bug reporter, for an automation that takes crash reports from your own sites and apps.","type":"object","properties":{"keyFromBrowsers":{"description":"Let a browser report with the key alone, rather than only from a site you listed. Off unless you need it.","type":"boolean"},"dailyReportMax":{"description":"How many reports a day this intake accepts at all.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"escalateAfter":{"description":"How many more times a known crash must happen before it wakes an agent again.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"antiBot":{"description":"Make a person's browser solve a small puzzle before it accepts a written report.","type":"string","enum":["pow"]},"title":{"description":"The dialog's heading.","type":"string","maxLength":80},"prompt":{"description":"The line above the box they type in.","type":"string","maxLength":300},"thanks":{"description":"What it says once they have sent it.","type":"string","maxLength":300},"askEmail":{"description":"Ask for an address to reply to. Optional for them either way.","type":"boolean"},"accent":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"},"captureCrashes":{"description":"Catch uncaught errors automatically, as well as what people write in.","type":"boolean"}},"additionalProperties":false},"allowedTools":{"description":"Narrow the woken turn to these tools. For one driven by an outside message this list is the real boundary, because prompt wording is only advice and an empty toolbox is not.","type":"array","items":{"type":"string","minLength":1}},"models":{"minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves this work."},"model":{"type":"string","minLength":1,"description":"Which of its models. Both halves, because a model name only means anything to the provider that serves it."},"effort":{"description":"How hard this model should think, where it offers a choice. Leave it out to take the model's own default.","type":"string"},"thinking":{"description":"Whether this model reasons before it answers, where that is a choice it offers.","type":"boolean"},"fast":{"description":"Ask for this model's work at a higher rate for a higher price. A request rather than a promise.","type":"boolean"},"harness":{"description":"Which agentic loop runs it. Leave it out to use the provider's own.","type":"string","enum":["native","claude-code"]}},"required":["provider","model"],"additionalProperties":false},"description":"Which models this automation may run on, best first. Required, and nothing is chosen for you: work that fires while nobody is watching spends a real allowance, so it names the models it spends rather than inheriting one. Tried in order, so a spent account does not silently stop the job."},"account":{"description":"Which account pays for it.","type":"string"},"actsAs":{"description":"Which persona it speaks as. An unwatched turn naming none reaches no signed-in account at all.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"senders":{"description":"Who may talk to it, and as whom: rules by sender id or group, each naming the persona those people get, plus what everyone else gets. Absent admits everyone the trigger's filters do.","type":"object","properties":{"rules":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"label":{"description":"What to call these people on screen.","type":"string","maxLength":60},"ids":{"description":"Sender ids, as the service names them, never display names.","maxItems":200,"type":"array","items":{"type":"string","minLength":1,"maxLength":200}},"groups":{"description":"Group ids the service reports on a sender, a Discord role. Only for a source whose messages carry them.","maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":200}},"actsAs":{"description":"Which persona their wakes speak as. Absent is no persona: the full toolbox, reaching no account.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"requireApproval":{"description":"Hold their wakes for a person, even when the automation itself does not.","type":"boolean"}},"additionalProperties":false},"description":"Walked in order; the first rule naming the sender decides."},"others":{"type":"string","enum":["allow","hold","ignore"],"description":"What a sender no rule names gets: the automation as configured, a hold for a person, or nothing at all."}},"required":["rules","others"],"additionalProperties":false},"requireApproval":{"description":"Hold every fire for a person instead of running it. Only a person can release one of those.","type":"boolean"},"holdForSeconds":{"description":"Hold each fire this long before running it anyway, which is a delay rather than a decision.","type":"number"},"chore":{"description":"This automation is a maintenance job, which is what files it under chores rather than among ordinary automations.","type":"boolean"},"enabled":{"type":"boolean","description":"Whether it fires at all."},"runs":{"type":"array","items":{"type":"object","properties":{"at":{"type":"number"},"outcome":{"type":"string","enum":["completed","skipped","error","interrupted"]},"detail":{"type":"string"},"conversationId":{"type":"string"}},"required":["at","outcome"],"additionalProperties":false}},"nextRun":{"type":"number"},"webhookToken":{"description":"What a caller presents at /automations/{id}/fire, for an event automation. Shown to a maintainer or the owner only.","type":"string"},"ingestKey":{"description":"What a client with no website origin presents to a bug intake. Shown to a maintainer or the owner only.","type":"string"}},"required":["id","trigger","prompt","models","enabled","runs"],"additionalProperties":false}}},"required":["automations"],"additionalProperties":false}}}}}},"post":{"operationId":"automations.upsert","summary":"Create or edit an automation","description":"Writes an automation by id. Nothing needs provisioning: the scheduler picks it up on its next sweep.","tags":["Automations"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The automation's id."},"trigger":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"schedule","description":"On a clock."},"cron":{"type":"string","minLength":1,"description":"When, in cron notation."},"tz":{"description":"Which clock the times in the cron mean, as a zone name like Europe/Warsaw. Leave it out to use the sandbox's own setting, which is what you want unless this one chore belongs to a different place.","type":"string"},"afterSessions":{"description":"Fire only once at least this many new sessions have been run since the last wake. A due run short of that is skipped, and says how far off it is.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["kind","cron"]},{"type":"object","properties":{"kind":{"type":"string","const":"once","description":"At one moment, and then never again."},"at":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991,"description":"The moment it fires, in milliseconds. An absolute instant, so it means the same thing wherever the sandbox runs."}},"required":["kind","at"]},{"type":"object","properties":{"kind":{"type":"string","const":"event","description":"When something calls its webhook."},"dailyMax":{"description":"How many webhook calls a day may wake the agent, across every caller. Absent is a modest default rather than unlimited.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["kind"]},{"type":"object","properties":{"kind":{"type":"string","const":"listener","description":"When a message arrives from somewhere outside."},"provider":{"type":"string","minLength":1,"description":"Which service to listen to."},"channelId":{"description":"Narrow it to one channel or thread.","type":"string","minLength":1},"eventType":{"description":"Narrow it to one kind of event.","type":"string","minLength":1},"mentioned":{"description":"Only when the agent is actually addressed, rather than on everything said in earshot.","type":"boolean"},"branch":{"description":"Narrow it to one branch, for the sources that have branches. Absent means every branch of the repositories it matches.","type":"string","minLength":1},"allowedOrigins":{"description":"Which websites may reach the public endpoint, the chat widget's or the bug reporter's. Absent or empty admits nobody.","type":"array","items":{"type":"string"}}},"required":["kind","provider"]},{"type":"object","properties":{"kind":{"type":"string","const":"workspace","description":"When something happens to the files or the repositories."},"event":{"type":"string","enum":["turn.settled","agent.landed","deps.broken","deps.fixed"],"description":"Which happening."},"repo":{"description":"Narrow it to one repository. Absent means any of them.","type":"string","minLength":1}},"required":["kind","event"]}],"description":"What sets it off: a schedule, an event in the workspace, a message arriving from outside, or a webhook."},"guard":{"description":"A command run before the wake that decides whether there is anything to do. Skipped by the guard is often the most useful thing an automation can report.","type":"string","minLength":1},"prompt":{"type":"string","minLength":1,"description":"What the woken agent is told."},"webchat":{"description":"Settings for the public chat widget, for an automation that answers visitors.","type":"object","properties":{"access":{"description":"Who may write to it. Absent means anyone, which is the anonymous support box it looks like.","type":"string","enum":["public","google"]},"requireName":{"description":"Ask a visitor for a name first. Cosmetic: the name is typed, so it reaches the model as something a stranger said, never as identity.","type":"boolean"},"antiBot":{"description":"How to keep bots out: a third-party check that needs the site's own keys, or a puzzle the sandbox sets and the widget solves, so a site with no such account still has something. Absent leaves the site allowlist and the rate limit as the whole boundary.","type":"string","enum":["turnstile","pow"]},"turnstileSiteKey":{"description":"The public half of those keys, which ships to the visitor's browser.","type":"string"},"turnstileSecret":{"description":"The private half, which the sandbox keeps and the widget never sees.","type":"string"},"googleClientId":{"description":"The site's own sign-in client id. It cannot be ours: a sign-in is only issued to an approved origin, and no single client can list every customer's domain.","type":"string"},"title":{"type":"string","maxLength":80},"greeting":{"type":"string","maxLength":500},"accent":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"},"position":{"type":"string","enum":["top-right","top-left","bottom-right","bottom-left"]},"dailyMessageMax":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"conversationMessageMax":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"sessionTtlMinutes":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}}},"issues":{"description":"Settings for the bug reporter, for an automation that takes crash reports from your own sites and apps.","type":"object","properties":{"keyFromBrowsers":{"description":"Let a browser report with the key alone, rather than only from a site you listed. Off unless you need it.","type":"boolean"},"dailyReportMax":{"description":"How many reports a day this intake accepts at all.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"escalateAfter":{"description":"How many more times a known crash must happen before it wakes an agent again.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"antiBot":{"description":"Make a person's browser solve a small puzzle before it accepts a written report.","type":"string","enum":["pow"]},"title":{"description":"The dialog's heading.","type":"string","maxLength":80},"prompt":{"description":"The line above the box they type in.","type":"string","maxLength":300},"thanks":{"description":"What it says once they have sent it.","type":"string","maxLength":300},"askEmail":{"description":"Ask for an address to reply to. Optional for them either way.","type":"boolean"},"accent":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"},"captureCrashes":{"description":"Catch uncaught errors automatically, as well as what people write in.","type":"boolean"}}},"allowedTools":{"description":"Narrow the woken turn to these tools. For one driven by an outside message this list is the real boundary, because prompt wording is only advice and an empty toolbox is not.","type":"array","items":{"type":"string","minLength":1}},"models":{"minItems":1,"maxItems":10,"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves this work."},"model":{"type":"string","minLength":1,"description":"Which of its models. Both halves, because a model name only means anything to the provider that serves it."},"effort":{"description":"How hard this model should think, where it offers a choice. Leave it out to take the model's own default.","type":"string"},"thinking":{"description":"Whether this model reasons before it answers, where that is a choice it offers.","type":"boolean"},"fast":{"description":"Ask for this model's work at a higher rate for a higher price. A request rather than a promise.","type":"boolean"},"harness":{"description":"Which agentic loop runs it. Leave it out to use the provider's own.","type":"string","enum":["native","claude-code"]}},"required":["provider","model"]},"description":"Which models this automation may run on, best first. Required, and nothing is chosen for you: work that fires while nobody is watching spends a real allowance, so it names the models it spends rather than inheriting one. Tried in order, so a spent account does not silently stop the job."},"account":{"description":"Which account pays for it.","type":"string"},"actsAs":{"description":"Which persona it speaks as. An unwatched turn naming none reaches no signed-in account at all.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"senders":{"description":"Who may talk to it, and as whom: rules by sender id or group, each naming the persona those people get, plus what everyone else gets. Absent admits everyone the trigger's filters do.","type":"object","properties":{"rules":{"maxItems":50,"type":"array","items":{"type":"object","properties":{"label":{"description":"What to call these people on screen.","type":"string","maxLength":60},"ids":{"description":"Sender ids, as the service names them, never display names.","maxItems":200,"type":"array","items":{"type":"string","minLength":1,"maxLength":200}},"groups":{"description":"Group ids the service reports on a sender, a Discord role. Only for a source whose messages carry them.","maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":200}},"actsAs":{"description":"Which persona their wakes speak as. Absent is no persona: the full toolbox, reaching no account.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"requireApproval":{"description":"Hold their wakes for a person, even when the automation itself does not.","type":"boolean"}}},"description":"Walked in order; the first rule naming the sender decides."},"others":{"type":"string","enum":["allow","hold","ignore"],"description":"What a sender no rule names gets: the automation as configured, a hold for a person, or nothing at all."}},"required":["rules","others"]},"requireApproval":{"description":"Hold every fire for a person instead of running it. Only a person can release one of those.","type":"boolean"},"holdForSeconds":{"description":"Hold each fire this long before running it anyway, which is a delay rather than a decision.","type":"number"},"chore":{"description":"This automation is a maintenance job, which is what files it under chores rather than among ordinary automations.","type":"boolean"},"enabled":{"type":"boolean","description":"Whether it fires at all."}},"required":["id","trigger","prompt","models","enabled"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/automations/catalog":{"get":{"operationId":"automations.catalog","summary":"What can trigger an automation here","description":"Every trigger this sandbox understands and every template worth starting from, the daemon's own merged with each installed extension's. Writing an automation is checked against this same list, so a screen and the daemon can never disagree about what is allowed.","tags":["Automations"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"sources":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1},"label":{"type":"string","minLength":1},"logo":{"type":"string","minLength":1},"icon":{"type":"string","minLength":1},"events":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string","minLength":1},"label":{"type":"string","minLength":1}},"required":["value","label"],"additionalProperties":false}},"channel":{"type":"object","properties":{"label":{"type":"string","minLength":1},"placeholder":{"type":"string","minLength":1},"hint":{"type":"string","minLength":1}},"required":["label","placeholder"],"additionalProperties":false},"branchField":{"type":"object","properties":{"label":{"type":"string","minLength":1},"placeholder":{"type":"string","minLength":1},"hint":{"type":"string","minLength":1}},"required":["label","placeholder"],"additionalProperties":false},"sender":{"type":"object","properties":{"label":{"type":"string","minLength":1},"placeholder":{"type":"string","minLength":1},"hint":{"type":"string","minLength":1}},"required":["label","placeholder"],"additionalProperties":false},"senderGroup":{"type":"object","properties":{"label":{"type":"string","minLength":1},"placeholder":{"type":"string","minLength":1},"hint":{"type":"string","minLength":1}},"required":["label","placeholder"],"additionalProperties":false},"mentionLabel":{"type":"string","minLength":1},"starterPrompt":{"type":"string","minLength":1},"requires":{"default":[],"type":"array","items":{"type":"string","minLength":1}},"enabled":{"type":"boolean"}},"required":["provider","label","events","channel","requires","enabled"],"additionalProperties":false}},"templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"title":{"type":"string","minLength":1},"logo":{"type":"string","minLength":1},"icon":{"type":"string","minLength":1},"requires":{"default":[],"type":"array","items":{"type":"string","minLength":1}},"trigger":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"schedule","description":"On a clock."},"cron":{"type":"string","minLength":1,"description":"When, in cron notation."},"tz":{"description":"Which clock the times in the cron mean, as a zone name like Europe/Warsaw. Leave it out to use the sandbox's own setting, which is what you want unless this one chore belongs to a different place.","type":"string"},"afterSessions":{"description":"Fire only once at least this many new sessions have been run since the last wake. A due run short of that is skipped, and says how far off it is.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["kind","cron"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"once","description":"At one moment, and then never again."},"at":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991,"description":"The moment it fires, in milliseconds. An absolute instant, so it means the same thing wherever the sandbox runs."}},"required":["kind","at"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"event","description":"When something calls its webhook."},"dailyMax":{"description":"How many webhook calls a day may wake the agent, across every caller. Absent is a modest default rather than unlimited.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"listener","description":"When a message arrives from somewhere outside."},"provider":{"type":"string","minLength":1,"description":"Which service to listen to."},"channelId":{"description":"Narrow it to one channel or thread.","type":"string","minLength":1},"eventType":{"description":"Narrow it to one kind of event.","type":"string","minLength":1},"mentioned":{"description":"Only when the agent is actually addressed, rather than on everything said in earshot.","type":"boolean"},"branch":{"description":"Narrow it to one branch, for the sources that have branches. Absent means every branch of the repositories it matches.","type":"string","minLength":1},"allowedOrigins":{"description":"Which websites may reach the public endpoint, the chat widget's or the bug reporter's. Absent or empty admits nobody.","type":"array","items":{"type":"string"}}},"required":["kind","provider"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"workspace","description":"When something happens to the files or the repositories."},"event":{"type":"string","enum":["turn.settled","agent.landed","deps.broken","deps.fixed"],"description":"Which happening."},"repo":{"description":"Narrow it to one repository. Absent means any of them.","type":"string","minLength":1}},"required":["kind","event"],"additionalProperties":false}]},"guard":{"type":"string","minLength":1},"holdForSeconds":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"prompt":{"type":"string","minLength":1},"note":{"type":"string","minLength":1},"setup":{"type":"string","minLength":1},"description":{"type":"string","minLength":1},"offer":{"type":"string","enum":["create","configure"]},"chore":{"type":"boolean"}},"required":["id","title","requires","trigger","prompt"],"additionalProperties":false}}},"required":["sources","templates"],"additionalProperties":false}}}}}}},"/automations/{id}/enabled":{"post":{"operationId":"automations.setEnabled","summary":"Turn an automation on or off","description":"Flips only the switch, so a row in a list can be toggled without rebuilding the whole record.","tags":["Automations"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"}},"required":["enabled"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/automations/{id}":{"delete":{"operationId":"automations.remove","summary":"Delete an automation","description":"Removes it, so nothing fires from it again.","tags":["Automations"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/automations/{id}/rotate-token":{"post":{"operationId":"automations.rotateToken","summary":"Rotate an automation's webhook token or intake key","description":"Mints a new credential for the door this automation opens and retires the old one at once. Every caller has to be handed the new URL; that is the point. Refused for an automation with no door.","tags":["Automations"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":1,"description":"The freshly minted credential. The previous one stopped working the moment this answered."}},"required":["token"],"additionalProperties":false}}}}}}},"/automations/{id}/run":{"post":{"operationId":"automations.run","summary":"Fire an automation by hand","description":"The answer to writing something that runs at three in the morning and having no way to try it. It takes exactly the path the real trigger takes, including the check that decides whether there was anything to do, since skipped by the guard is the most useful thing this can tell you. A switched-off automation fires too, because trying it before switching it on is the main reason to press this. Not available for the trigger that listens for incoming messages, where a hand-fire would produce an agent asked to handle events and handed none; send the bot a message instead. Answers straight away and runs detached.","tags":["Automations"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/automations/senders/{provider}":{"get":{"operationId":"automations.senders","summary":"Who has written to a listener source","description":"Everyone whose message reached one of this source's automations, newest first, admitted or not. What the sender rules picker offers by name while storing the id the service vouches for.","tags":["Automations"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which listener source."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"senders":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The sender id the service vouches for, what a rule stores."},"name":{"type":"string","description":"What they were called on their last message, for display only."},"groups":{"description":"The group ids the service reported on their last message, a Discord role list.","type":"array","items":{"type":"string"}},"firstSeenAt":{"type":"number","description":"When they first reached an automation here, in milliseconds."},"lastSeenAt":{"type":"number","description":"When they last did, in milliseconds."},"messages":{"type":"number","description":"How many of their messages reached an automation's filters, admitted or not."}},"required":["id","name","firstSeenAt","lastSeenAt","messages"],"additionalProperties":false},"description":"Newest first."}},"required":["senders"],"additionalProperties":false}}}}}}},"/automations/pending":{"get":{"operationId":"automations.pendingList","summary":"Automations waiting for a yes","description":"The queue an automation set to ask first lands in each time it would have fired.","tags":["Automations"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"approvals":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"This waiting item's own id, which approving and rejecting take."},"automationId":{"type":"string","description":"Which automation it came from."},"payload":{"description":"What set it off, kept whole so an approved wake carries the same thing it would have had. Absent for one on a schedule, which carries nothing.","type":"string"},"origin":{"description":"Where the message came from, kept alongside the payload so an approved wake appears on the board exactly as an automatic one would have.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"title":{"description":"What the conversation would be called.","type":"string"},"conversationId":{"description":"The thread this belongs to, when it has one, so approving continues that conversation rather than opening a new one. Without it, one visitor's chat becomes a card per approved message and an agent that meets them again every turn.","type":"string"},"sessionId":{"description":"The provider session that thread last ran on.","type":"string"},"thread":{"description":"Which inbound thread this belongs to, so the approved run continues that thread's memory rather than a fresh one.","type":"string"},"actsAs":{"description":"Which persona the approved run speaks as, decided when it was held.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"createdAt":{"type":"number","description":"When it started waiting, in milliseconds."},"autoRunAt":{"description":"When it goes ahead on its own, in milliseconds, for a hold that is only a delay. Absent for one that genuinely waits on a person.","type":"number"}},"required":["id","automationId","createdAt"],"additionalProperties":false},"description":"Everything waiting for a yes."}},"required":["approvals"],"additionalProperties":false}}}}}}},"/automations/pending/{id}/approve":{"post":{"operationId":"automations.approve","summary":"Let a held automation run","description":"Releases one waiting automation and runs the wake it was holding. Answers straight away and runs detached.","tags":["Automations"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which waiting item."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/automations/pending/{id}/reject":{"post":{"operationId":"automations.reject","summary":"Drop a held automation","description":"Throws one waiting fire away. The automation stays on, and the next trigger queues as usual.","tags":["Automations"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which waiting item."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/workspace/tree":{"get":{"operationId":"workspace.tree","summary":"The workspace file tree","description":"Every folder and file under the workspace root, as one walk. Name a conversation to read its own private copy of the tree instead of the shared one. Folders the daemon skips, such as installed packages, come back without their contents; ask for those separately.","tags":["Workspace"],"parameters":[{"name":"agent","in":"query","schema":{"description":"Read a conversation's own private copy of the workspace rather than the shared tree. Leave it out for the shared tree. A conversation that is not working privately resolves back to the shared tree rather than failing, so a link need not know which mode it runs in.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"root":{"type":"string","description":"The path everything below is relative to."},"tree":{"type":"array","items":{"$ref":"#/$defs/__schema0"},"description":"The workspace, one entry per file and folder."},"hidden":{"type":"number","description":"How many entries at the top level were cut for size. Zero means the listing is complete."},"barren":{"type":"array","items":{"type":"string"},"description":"Folders whose whole contents are empty folders, and nothing else. Complete for the workspace, however much of the tree above was listed, and ordered like the tree, so a parent comes before the branch below it."},"generation":{"description":"Which state of the shared tree this is; the changes that follow count from it. Absent for a conversation's own checkout, which is listed when asked and followed by no changes.","type":"integer","minimum":0,"maximum":9007199254740991}},"required":["root","tree","hidden","barren"],"additionalProperties":false,"$defs":{"__schema0":{"type":"object","properties":{"name":{"type":"string","description":"Just this entry's own name."},"path":{"type":"string","description":"Its full path from the workspace root, which feeds straight back into the file routes."},"type":{"type":"string","enum":["file","dir"],"description":"What it is. For a link, what it points at, so a link to a folder opens like a folder."},"size":{"description":"Size in bytes, for a file.","type":"number"},"ignored":{"description":"Tooling ignores it: installed packages, git internals, anything the ignore rules exclude. Usually drawn greyed out.","type":"boolean"},"link":{"description":"Present when this entry is a link.","type":"object","properties":{"to":{"type":"string","description":"What the link says, verbatim, rather than where it ends up. That is what the person who made it wrote, and what they would edit."},"state":{"description":"Absent for an ordinary link. Broken means there is nothing at the other end, and it is listed anyway because a dangling link is worth seeing. Outside means it leads out of the workspace, so it is shown and refused.","type":"string","enum":["broken","outside"]}},"required":["to"],"additionalProperties":false},"children":{"description":"What is inside a folder. Absent means it was not opened, either because it is ignored or because the walk ran out of budget above it, so ask for it separately. An empty list means it really is empty.","type":"array","items":{"$ref":"#/$defs/__schema0"}}},"required":["name","path","type"],"additionalProperties":false}}}}}}}}},"/workspace/children":{"get":{"operationId":"workspace.children","summary":"A bounded folder listing","description":"The entries inside a folder as one flat list. Direct children are the default, which is how the explorer opens a folder the full tree walk left closed; callers that need a small subtree can ask for up to five levels without a request per directory.","tags":["Workspace"],"parameters":[{"name":"agent","in":"query","required":false,"schema":{"description":"Read a conversation's own private copy of the workspace rather than the shared tree. Leave it out for the shared tree. A conversation that is not working privately resolves back to the shared tree rather than failing, so a link need not know which mode it runs in.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"allowEmptyValue":true,"allowReserved":true},{"name":"path","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"The folder to open, as a workspace path."},"allowEmptyValue":true,"allowReserved":true},{"name":"depth","in":"query","required":false,"schema":{"description":"How many levels to include. Omitted means direct children only; at most five levels can be read in one request.","type":"integer","minimum":1,"maximum":5},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"$ref":"#/$defs/__schema0"},"description":"What is inside it, as a flat list. With the default depth these are direct children; a deeper request also includes descendants, whose full paths say where they belong. Folders carry no nested contents of their own."},"hidden":{"type":"number","description":"How many entries were cut for size. Zero means the listing is complete."}},"required":["entries","hidden"],"additionalProperties":false,"$defs":{"__schema0":{"type":"object","properties":{"name":{"type":"string","description":"Just this entry's own name."},"path":{"type":"string","description":"Its full path from the workspace root, which feeds straight back into the file routes."},"type":{"type":"string","enum":["file","dir"],"description":"What it is. For a link, what it points at, so a link to a folder opens like a folder."},"size":{"description":"Size in bytes, for a file.","type":"number"},"ignored":{"description":"Tooling ignores it: installed packages, git internals, anything the ignore rules exclude. Usually drawn greyed out.","type":"boolean"},"link":{"description":"Present when this entry is a link.","type":"object","properties":{"to":{"type":"string","description":"What the link says, verbatim, rather than where it ends up. That is what the person who made it wrote, and what they would edit."},"state":{"description":"Absent for an ordinary link. Broken means there is nothing at the other end, and it is listed anyway because a dangling link is worth seeing. Outside means it leads out of the workspace, so it is shown and refused.","type":"string","enum":["broken","outside"]}},"required":["to"],"additionalProperties":false},"children":{"description":"What is inside a folder. Absent means it was not opened, either because it is ignored or because the walk ran out of budget above it, so ask for it separately. An empty list means it really is empty.","type":"array","items":{"$ref":"#/$defs/__schema0"}}},"required":["name","path","type"],"additionalProperties":false}}}}}}}}},"/workspace/file":{"get":{"operationId":"workspace.file","summary":"Read part of a text file","description":"A window of one file's text, plus how large the whole file is. Never the entire file: an unbounded read is how a single enormous log stalls the daemon for everyone, so ask for the slice you mean to show and page through if you need more.","tags":["Workspace"],"parameters":[{"name":"agent","in":"query","required":false,"schema":{"description":"Read a conversation's own private copy of the workspace rather than the shared tree. Leave it out for the shared tree. A conversation that is not working privately resolves back to the shared tree rather than failing, so a link need not know which mode it runs in.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"allowEmptyValue":true,"allowReserved":true},{"name":"path","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"The file to read, as a workspace path."},"allowEmptyValue":true,"allowReserved":true},{"name":"offset","in":"query","required":false,"schema":{"description":"Which byte to start at. A negative number reads that many bytes from the end, which is how you follow a growing log without knowing its size first.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"allowEmptyValue":true,"allowReserved":true},{"name":"limit","in":"query","required":false,"schema":{"description":"How many bytes to read. Capped by the sandbox, so leaving it out or asking for too much gives you the cap rather than the whole file.","type":"integer","minimum":1,"maximum":9007199254740991},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"present":{"type":"boolean","const":true,"description":"There is something at that path."},"path":{"type":"string","description":"The path, as asked for."},"content":{"type":"string","description":"The bytes of the window you asked for, as text."},"size":{"type":"number","description":"How large the whole file is. Compare it with the window below to know whether there is more."},"offset":{"type":"number","description":"Which byte the window starts at."},"bytes":{"type":"number","description":"How many bytes the window holds."},"shared":{"type":"boolean","description":"Which tree answered. True when no conversation was named, and also when one was but its own copy has no such file, which is the case a reader has to be told about rather than left to assume."},"lossy":{"description":"The bytes are not valid UTF-8, so `content` holds replacement characters where they failed to decode. Saving that text back would change the file, so treat it as read-only.","type":"boolean","const":true}},"required":["present","path","content","size","offset","bytes","shared"],"additionalProperties":false},{"type":"object","properties":{"present":{"type":"boolean","const":false,"description":"Nothing there. An answer, not a failure: reading a file that may not exist yet is the ordinary case for half the reads in this product."},"path":{"type":"string","description":"The path, as asked for."}},"required":["present","path"],"additionalProperties":false}]}}}}}}},"/workspace/derived":{"get":{"operationId":"workspace.derived","summary":"Read a file's derived text","description":"What a document, picture, recording or archive says, as text, from the shadow the sandbox keeps beside it. This is the same rendering an agent reads instead of the bytes, so it is also the way to check what one is working from. Nothing is derived here: a file with no shadow yet answers that it has none, and whether it could have one.","tags":["Workspace"],"parameters":[{"name":"path","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"The file you want the text of, as a workspace path. The real file, not its shadow: where the text is kept is this route's business."},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"state":{"type":"string","enum":["deriving","idle","broken","undeliverable"],"description":"Where this file stands: being read right now, settled (what it has is what it gets until someone asks), or unreachable because the renderer is missing. `undeliverable` is a format nothing here reads."},"present":{"type":"boolean","const":true,"description":"There is derived text for that file."},"path":{"type":"string","description":"The file it was derived from, as asked for."},"content":{"type":"string","description":"The text itself, as markdown."},"deriver":{"type":"string","description":"Which reader wrote it, and at which version, such as `pdf+ocr v1`. A file re-derives when this changes."},"derivedAt":{"description":"When it was written, as an ISO timestamp. Absent only for a shadow whose front matter was edited by hand.","type":"string"},"title":{"description":"The title the format carried, where it carried one.","type":"string"},"notes":{"type":"array","items":{"type":"string"},"description":"Every cap and degradation the derivation hit: a sheet cut to 200 rows, a book cut at 2 MB, a scan recognised rather than read. Show these with the text, since text that was cut reading as complete is the one failure this whole feature cannot afford."},"tokens":{"type":"number","description":"Roughly what an agent spends reading it, by the same four-chars-a-token estimate every budget here uses."},"truncated":{"type":"boolean","description":"Whether this is only the start of the shadow, cut to keep the response sendable. The file on disk holds the rest."},"stale":{"type":"boolean","description":"Whether the file has changed since this text was derived, compared by content rather than by clock. True means you are reading a rendering of an older version of the file, and deriving it again catches it up."}},"required":["state","present","path","content","deriver","notes","tokens","truncated","stale"],"additionalProperties":false},{"type":"object","properties":{"state":{"type":"string","enum":["deriving","idle","broken","undeliverable"],"description":"Where this file stands: being read right now, settled (what it has is what it gets until someone asks), or unreachable because the renderer is missing. `undeliverable` is a format nothing here reads."},"present":{"type":"boolean","const":false,"description":"There is no derived text for that file. Read `state` before saying so to anyone: absent and being read are different answers."},"path":{"type":"string","description":"The file, as asked for."},"derivable":{"type":"boolean","description":"Whether this format can be turned into text at all. True means asking for it to be derived is worth offering; false means nothing here reads this format."},"reason":{"description":"Why there is none, when deriving was just attempted and produced nothing: the file is too large, corrupt, or of a format no reader claims.","type":"string"}},"required":["state","present","path","derivable"],"additionalProperties":false}]}}}}}}},"/workspace/derive":{"post":{"operationId":"workspace.derive","summary":"Derive a file's text now","description":"Renders one file to text and answers with the result, for when its shadow is missing or you want it rebuilt. The same work the background pass does when that setting is on, so this is how a reader gets the text without turning it on for the whole workspace. Costs a parse of exactly one file; a format nothing can read says so rather than failing.","tags":["Workspace"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","minLength":1,"description":"The file you want the text of, as a workspace path. The real file, not its shadow: where the text is kept is this route's business."}},"required":["path"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"state":{"type":"string","enum":["deriving","idle","broken","undeliverable"],"description":"Where this file stands: being read right now, settled (what it has is what it gets until someone asks), or unreachable because the renderer is missing. `undeliverable` is a format nothing here reads."},"present":{"type":"boolean","const":true,"description":"There is derived text for that file."},"path":{"type":"string","description":"The file it was derived from, as asked for."},"content":{"type":"string","description":"The text itself, as markdown."},"deriver":{"type":"string","description":"Which reader wrote it, and at which version, such as `pdf+ocr v1`. A file re-derives when this changes."},"derivedAt":{"description":"When it was written, as an ISO timestamp. Absent only for a shadow whose front matter was edited by hand.","type":"string"},"title":{"description":"The title the format carried, where it carried one.","type":"string"},"notes":{"type":"array","items":{"type":"string"},"description":"Every cap and degradation the derivation hit: a sheet cut to 200 rows, a book cut at 2 MB, a scan recognised rather than read. Show these with the text, since text that was cut reading as complete is the one failure this whole feature cannot afford."},"tokens":{"type":"number","description":"Roughly what an agent spends reading it, by the same four-chars-a-token estimate every budget here uses."},"truncated":{"type":"boolean","description":"Whether this is only the start of the shadow, cut to keep the response sendable. The file on disk holds the rest."},"stale":{"type":"boolean","description":"Whether the file has changed since this text was derived, compared by content rather than by clock. True means you are reading a rendering of an older version of the file, and deriving it again catches it up."}},"required":["state","present","path","content","deriver","notes","tokens","truncated","stale"],"additionalProperties":false},{"type":"object","properties":{"state":{"type":"string","enum":["deriving","idle","broken","undeliverable"],"description":"Where this file stands: being read right now, settled (what it has is what it gets until someone asks), or unreachable because the renderer is missing. `undeliverable` is a format nothing here reads."},"present":{"type":"boolean","const":false,"description":"There is no derived text for that file. Read `state` before saying so to anyone: absent and being read are different answers."},"path":{"type":"string","description":"The file, as asked for."},"derivable":{"type":"boolean","description":"Whether this format can be turned into text at all. True means asking for it to be derived is worth offering; false means nothing here reads this format."},"reason":{"description":"Why there is none, when deriving was just attempted and produced nothing: the file is too large, corrupt, or of a format no reader claims.","type":"string"}},"required":["state","present","path","derivable"],"additionalProperties":false}]}}}}}}},"/workspace/media-ticket":{"post":{"operationId":"workspace.mediaTicket","summary":"Get a pass for streaming a media file","description":"Mints the short-lived ticket a video or audio element hands to the streaming route, which serves byte ranges and so cannot carry an ordinary header. Minting it here means a caller can tell whether this sandbox streams media at all, rather than discovering it mid-playback.","tags":["Workspace"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"agent":{"description":"Read a conversation's own private copy of the workspace rather than the shared tree. Leave it out for the shared tree. A conversation that is not working privately resolves back to the shared tree rather than failing, so a link need not know which mode it runs in.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"path":{"type":"string","minLength":1,"description":"The media file the ticket should cover."}},"required":["path"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ticket":{"type":"string","description":"Hand this to the streaming route in the query string. It buys exactly the one file it was minted for."},"expiresAt":{"type":"number","description":"When it stops working, in milliseconds, so a player can tell a dead ticket from a dead file."}},"required":["ticket","expiresAt"],"additionalProperties":false}}}}}}},"/workspace/download-ticket":{"post":{"operationId":"workspace.downloadTicket","summary":"Get a pass for downloading files and folders together","description":"Mints the short-lived ticket the download route takes, bound to a selection of files and folders. The route answers one ZIP streamed straight from disk: folders whole, already-compressed formats stored as they are and everything else deflated. Minting it first means a missing or unreadable path is refused here, before the browser starts a download that cannot finish.","tags":["Workspace"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"agent":{"description":"Read a conversation's own private copy of the workspace rather than the shared tree. Leave it out for the shared tree. A conversation that is not working privately resolves back to the shared tree rather than failing, so a link need not know which mode it runs in.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"paths":{"minItems":1,"maxItems":10000,"type":"array","items":{"type":"string","minLength":1},"description":"The files and folders to download together, as workspace paths. A folder brings everything inside it."}},"required":["paths"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ticket":{"type":"string","description":"Hand this to the download route in the query string. It buys exactly the selection it was minted for, once resolved."},"expiresAt":{"type":"number","description":"When it stops working, in milliseconds. It is meant to be used at once."},"filename":{"type":"string","description":"What the archive is saved as, so a caller can say what is on its way."}},"required":["ticket","expiresAt","filename"],"additionalProperties":false}}}}}}},"/workspace/resolve":{"get":{"operationId":"workspace.resolve","summary":"Turn a written path into a real file","description":"Matches a path somebody wrote in prose against the real tree and says which file it means. A path mentioned in a message is often only the tail of the real one, so this is the lookup behind every clickable file reference rather than a plain existence check.","tags":["Workspace"],"parameters":[{"name":"agent","in":"query","required":false,"schema":{"description":"Read a conversation's own private copy of the workspace rather than the shared tree. Leave it out for the shared tree. A conversation that is not working privately resolves back to the shared tree rather than failing, so a link need not know which mode it runs in.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"allowEmptyValue":true,"allowReserved":true},{"name":"path","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":512,"description":"The reference as somebody wrote it. Often only the tail of the real path, which is why this is matched against the tree rather than read as-is."},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"path":{"description":"The real path it means. Absent when nothing in the workspace ends that way.","type":"string"}},"additionalProperties":false}}}}}}},"/workspace/search":{"get":{"operationId":"workspace.search","summary":"Search the code","description":"Ranked results across the whole workspace, grouped, each carrying why it matched and how fresh it is. Left alone it blends plain text, structure, meaning and history in one pass; narrow it to a single kind of search when you already know which you want. Long result sets resume from the cursor it hands back.","tags":["Workspace"],"parameters":[{"name":"query","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":512,"description":"What to look for. Plain words, a pattern, a symbol name, or a question."},"allowEmptyValue":true,"allowReserved":true},{"name":"mode","in":"query","required":false,"schema":{"description":"Narrow the search to one kind: plain text, filenames, definitions, references, symbols, or code structure. Leave it out to blend them, which also answers a question asked in words.","type":"string","enum":["q","find","files","def","refs","sym","ast"]},"allowEmptyValue":true,"allowReserved":true},{"name":"includeIgnored","in":"query","required":false,"schema":{"description":"Search inside installed packages and other ignored folders too.","type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"dir","in":"query","required":false,"schema":{"description":"Only look inside this folder, given as a path from the workspace root. Leave it out to search everything.","type":"string","maxLength":512},"allowEmptyValue":true,"allowReserved":true},{"name":"literal","in":"query","required":false,"schema":{"description":"Treat the query as fixed text rather than a pattern.","type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"word","in":"query","required":false,"schema":{"description":"Match whole words only.","type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"caseSensitive","in":"query","required":false,"schema":{"description":"Whether capitals matter. Off means they do not, rather than being guessed at from the query.","type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"include","in":"query","required":false,"schema":{"description":"Which files to ask, in the same grammar an editor's files-to-include box takes: comma-separated patterns, matched at any depth unless anchored, a leading exclamation mark excluding instead.","type":"string","maxLength":512},"allowEmptyValue":true,"allowReserved":true},{"name":"limit","in":"query","required":false,"schema":{"description":"How many results to return.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"allowEmptyValue":true,"allowReserved":true},{"name":"after","in":"query","required":false,"schema":{"description":"Resume from the cursor a previous answer handed back.","type":"string"},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"type":"string","description":"Which kind of search actually ran, which matters when you let it choose."},"total":{"type":"number","description":"Matching lines across the whole workspace, not just this page."},"files":{"type":"number","description":"Files the query matched in total."},"shown":{"type":"number","description":"How many of those lines are on this page."},"groups":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The file."},"score":{"type":"number","description":"How well it matched. Groups arrive best first, never in path order."},"hits":{"type":"array","items":{"type":"object","properties":{"line":{"type":"number","description":"Which line, counting from one."},"text":{"type":"string","description":"The line itself."},"spans":{"type":"array","items":{"type":"object","properties":{"start":{"type":"number","description":"First character of the match within the line."},"end":{"type":"number","description":"One past the last."}},"required":["start","end"],"additionalProperties":false},"description":"Where in the line the matches are, so you can highlight without searching again. Empty when the whole line is the match rather than part of it."},"tags":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["def","text","sem","bm25","rerank","path","import","call","type","write","fuzzy","heuristic"],"description":"Why this line matched: the literal text, its meaning, the path, a definition, a call, and so on. Several kinds can agree on one line."},"score":{"description":"How strongly that reason applied.","type":"number"}},"required":["kind"],"additionalProperties":false},"description":"Why it matched."},"context":{"description":"What it sits inside: the function, the class, the heading. Often enough that you need not open the file.","type":"string"}},"required":["line","text","spans","tags"],"additionalProperties":false},"description":"The matching lines in it."},"capped":{"description":"This file had more matches than are kept per file, so the count is a floor. Say fifty-plus rather than fifty.","type":"boolean"}},"required":["path","score","hits"],"additionalProperties":false},"description":"The results, grouped by file, best first."},"freshness":{"type":"object","properties":{"state":{"type":"string","enum":["fresh","building","stale"],"description":"Whether the index matches what is on disk, is still filling, or has fallen behind."},"ageMs":{"description":"How long since it last matched the disk, in milliseconds.","type":"number"},"progress":{"description":"How far through building it is, from zero to one.","type":"number"},"behind":{"description":"How many files it has not caught up with. Worth showing, because the word stale on its own reads as a warning about the answer, which it almost never is.","type":"number"}},"required":["state"],"additionalProperties":false,"description":"Whether the index behind the answer is up to date."},"truncated":{"type":"boolean","description":"This page is not all of it. Use the cursor."},"partial":{"description":"At least one file had more matches than are kept per file, so the total is a floor. Different from the page being truncated: a complete page can still count partially.","type":"boolean"},"cursor":{"description":"Pass this back as `after` to get the next page.","type":"string"},"hint":{"description":"A suggestion for getting a better answer out of this query.","type":"string"},"note":{"description":"What the engine did that you did not ask for: a pattern rerun as plain text because it was not valid, escapes rewritten, a language filter that matched nothing.","type":"string"},"related":{"description":"Places next door to the best results: where each is defined, and whatever calls it most.","type":"array","items":{"type":"string"}},"candidates":{"description":"Ranked places that scored but did not make the page, best first. The answer often sits at rank five to thirteen, so this saves paging through to find out.","type":"array","items":{"type":"string"}},"features":{"description":"Which stages of the search were switched off for this run. Absent means all of them ran.","type":"array","items":{"type":"string"}}},"required":["mode","total","files","shown","groups","freshness","truncated"],"additionalProperties":false}}}}}}},"/workspace/health":{"get":{"operationId":"workspace.health","summary":"A repo's shape in numbers","description":"Where one repo's risk sits: the files that change often and are complicated at once, what the index holds, and which modules the rest of the code leans on most. Scoped to a repo, because a codebase is a repo rather than the whole drop.","tags":["Workspace"],"parameters":[{"name":"repo","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"Which repository, using the same ids the git routes take."},"allowEmptyValue":true,"allowReserved":true},{"name":"since","in":"query","required":false,"schema":{"description":"How far back to count changes, written as a span such as 2d, 12h, 1w or 3m. Leave it out for all of history.","type":"string","maxLength":16},"allowEmptyValue":true,"allowReserved":true},{"name":"limit","in":"query","required":false,"schema":{"description":"How many files and modules to rank. A leaderboard rather than an inventory: past a screenful the ranking stops being the point.","type":"integer","exclusiveMinimum":0,"maximum":200},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository this describes."},"totals":{"type":"object","properties":{"files":{"type":"number","description":"Files counted."},"symbols":{"type":"number","description":"Named things they export."},"complexity":{"type":"number","description":"Branch points across all of them added up."},"hotspots":{"type":"number","description":"How many files qualify as hotspots at all. The list below is capped; this is not."}},"required":["files","symbols","complexity","hotspots"],"additionalProperties":false,"description":"Counts anybody could recount in the files themselves. Deliberately no single maintainability grade: those cannot be checked and are not comparable between projects."},"hotspots":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"commits":{"type":"number"},"adds":{"type":"number"},"dels":{"type":"number"},"complexity":{"type":"number"},"score":{"type":"number"},"latestMs":{"type":"number"}},"required":["path","commits","adds","dels","complexity","score","latestMs"],"additionalProperties":false},"description":"Files that change often and are complicated at the same time, worst first."},"modules":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"exports":{"type":"number"}},"required":["path","exports"],"additionalProperties":false},"description":"The parts of the codebase the rest of it leans on most."},"freshness":{"type":"object","properties":{"state":{"type":"string","enum":["fresh","building","stale"],"description":"Whether the index matches what is on disk, is still filling, or has fallen behind."},"ageMs":{"description":"How long since it last matched the disk, in milliseconds.","type":"number"},"progress":{"description":"How far through building it is, from zero to one.","type":"number"},"behind":{"description":"How many files it has not caught up with. Worth showing, because the word stale on its own reads as a warning about the answer, which it almost never is.","type":"number"}},"required":["state"],"additionalProperties":false,"description":"Whether the index these numbers were read from is up to date."}},"required":["repo","totals","hotspots","modules","freshness"],"additionalProperties":false}}}}}}},"/workspace/classify":{"get":{"operationId":"workspace.classify","summary":"Sort a messy drop into buckets","description":"Proposes which of the loose things in the workspace are code, documents, media or archives. A read-only suggestion by fixed rules, with no model involved: nothing moves until a caller applies the moves it likes through the move call.","tags":["Workspace"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"classifications":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What was looked at."},"bucket":{"type":"string","enum":["repositories","documents","media","archives","other"],"description":"Which bucket it was sorted into."},"reason":{"type":"string","description":"The signal that decided it, so the proposal can be argued with rather than trusted."}},"required":["path","bucket","reason"],"additionalProperties":false},"description":"One entry per repository folder and loose file at the top of the workspace. A read-only proposal: nothing moves until you apply it."}},"required":["classifications"],"additionalProperties":false}}}}}}},"/workspace/dir":{"post":{"operationId":"workspace.mkdir","summary":"Create a folder","description":"Makes a folder, and any missing folders above it.","tags":["Workspace"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","minLength":1,"description":"The folder to create. Missing folders above it are created too."}},"required":["path"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/workspace/entry":{"delete":{"operationId":"workspace.delete","summary":"Delete a file or folder","description":"Removes one entry and everything under it. It goes to the trash rather than being erased, and the answer carries the id that brings it back through the restore call for a day. The path travels in the body rather than the address, the same as every other write in this group.","tags":["Workspace"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","minLength":1,"description":"The file or folder, as a workspace path."}},"required":["path"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true: the entry is gone from where it was, or was never there."},"trashed":{"description":"The trash id that brings it back through the restore call, for a day. Absent when there was nothing at that path to delete.","type":"string"}},"required":["ok"],"additionalProperties":false}}}}}}},"/workspace/restore":{"post":{"operationId":"workspace.restore","summary":"Bring back something deleted","description":"Puts an entry a delete sent to the trash back where it was, recreating the folders above it. Nothing is written over: when something new holds the name, it comes back beside it and the answer says where. Trash older than a day is gone.","tags":["Workspace"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"trashed":{"type":"string","minLength":1,"description":"The trash id a delete answered with."}},"required":["trashed"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","description":"Where it came back, as a workspace path: where it was deleted from, or beside that under a `(restored)` name when something new has taken the name since."}},"required":["path"],"additionalProperties":false}}}}}}},"/workspace/move":{"post":{"operationId":"workspace.move","summary":"Move or rename something","description":"Moves one entry to a new path, which is also how you rename it.","tags":["Workspace"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"from":{"type":"string","minLength":1,"description":"What to move or copy, as a workspace path."},"to":{"type":"string","minLength":1,"description":"Where it should end up. Changing only the last part is how you rename something."}},"required":["from","to"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/workspace/copy":{"post":{"operationId":"workspace.copy","summary":"Copy a file or folder","description":"Duplicates one entry at a new path, recursively for a folder.","tags":["Workspace"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"from":{"type":"string","minLength":1,"description":"What to move or copy, as a workspace path."},"to":{"type":"string","minLength":1,"description":"Where it should end up. Changing only the last part is how you rename something."}},"required":["from","to"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/workspace/extract":{"post":{"operationId":"workspace.extract","summary":"Unpack an archive","description":"Unpacks a zip or tar already in the workspace into a new folder beside it, named after the archive. An archive that is one folder of its own name lands as that folder rather than as it twice, and a .gz, .bz2, .xz or .zst holding a single file lands as that file. Nothing is ever written over: the answer says where it landed. Formats with no tool here, such as .7z and .rar, are refused.","tags":["Workspace"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","minLength":1,"description":"The file or folder, as a workspace path."}},"required":["path"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","description":"Where the contents landed, as a workspace path: a new folder named after the archive, or the decompressed file itself when the archive held just one. Never an existing entry written over, so a name already taken lands beside it under a free one."}},"required":["path"],"additionalProperties":false}}}}}}},"/workspace/setup":{"get":{"operationId":"workspace.setup","summary":"Which projects have their dependencies installed","description":"Per project, whether its dependencies are actually present. A project that arrives by import comes without them, so files landing is not the same as the project working: until this says a project is ready, its type checks and tests can only mislead you.","tags":["Workspace"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"projects":{"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"Where the project is, relative to the workspace root. Empty means the root itself."},"ecosystem":{"type":"string","enum":["node","python"],"description":"Which language's tooling it uses."},"manager":{"type":"string","description":"The tool that would do the installing."},"command":{"type":"string","description":"The exact command that would run."},"evidence":{"type":"string","description":"The file that decided all of the above, so the answer can be checked rather than trusted."},"state":{"type":"string","enum":["ready","installing","needs-setup","unsupported","stale"],"description":"Ready means its dependencies are really there. Stale means it was installed once and has since outgrown that, which is what an agent leaves behind when it adds a dependency without installing it. Unsupported means this sandbox has no such tool."},"missing":{"description":"How many declared dependencies cannot be found on disk. What separates never-installed from outgrown.","type":"number"}},"required":["dir","ecosystem","manager","command","evidence","state"],"additionalProperties":false},"description":"Every project the sandbox found, and whether each is usable."}},"required":["projects"],"additionalProperties":false}}}}}}},"/workspace/setup/install":{"post":{"operationId":"workspace.install","summary":"Install a project's dependencies","description":"Starts the install for one or more projects in a terminal you can attach to, and answers immediately. The run survives a page reload and its output stays in the terminal history.","tags":["Workspace"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"dirs":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"string","maxLength":500},"description":"Which projects to install, by folder. Ones already ready, already installing, or with no tool to install them are skipped rather than refused."}},"required":["dirs"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"queued":{"type":"array","items":{"type":"string"},"description":"Which of them actually started, which is not necessarily what you asked for."}},"required":["queued"],"additionalProperties":false}}}}}}},"/workspace/repos":{"get":{"operationId":"workspace.repos","summary":"Repos in the workspace","description":"Every git repo the daemon found in the workspace, with where each one sits and what it is called.","tags":["Workspace"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"repos":{"type":"array","items":{"type":"string"},"description":"Every repository's id, sorted. An id is its folder relative to the workspace root, and \"root\" is the workspace itself."}},"required":["repos"],"additionalProperties":false}}}}}},"post":{"operationId":"workspace.addRepo","summary":"Clone a repo in","description":"Clones a repository into the workspace beside the others, using whatever forge credentials the sandbox already holds.","tags":["Workspace"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"What to call it in the workspace."},"cloneUrl":{"type":"string","minLength":1,"description":"Where to clone it from."},"branch":{"description":"Which branch to check out. Leave it out for the repository's default.","type":"string"}},"required":["name","cloneUrl"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"What it ended up called."},"path":{"type":"string","description":"Where it landed."}},"required":["name","path"],"additionalProperties":false}}}}}}},"/workspace/repos/new":{"post":{"operationId":"workspace.createRepo","summary":"Start a new repo","description":"Makes an empty repository in the workspace: a folder named after it, initialised, with a README that names it and one commit, so an agent can start on it at once. Nothing is cloned and nothing leaves the machine.","tags":["Workspace"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"What to call it, which is also its folder under the workspace root."}},"required":["name"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"What it ended up called."},"path":{"type":"string","description":"Where it landed."}},"required":["name","path"],"additionalProperties":false}}}}}}},"/workspace/sync":{"post":{"operationId":"workspace.sync","summary":"Pull every repo up to date","description":"Fetches every repo that has a remote and fast-forwards the ones that can move safely, reporting what happened to each. This runs by itself at the start of a turn; call it directly to refresh on demand, or to re-sync a repo that had drifted.","tags":["Workspace"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"repos":{"type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository."},"status":{"type":"string","enum":["updated","current","dirty","diverged","no-remote","skipped","error"],"description":"What happened to it. Dirty and diverged are why a repository was left alone: it had uncommitted work, or it had moved in a way that cannot be fast-forwarded."},"behind":{"description":"How many commits it was behind.","type":"number"},"ahead":{"description":"How many commits it was ahead.","type":"number"},"head":{"description":"The commit it ended up on.","type":"string"},"message":{"description":"What went wrong, when something did.","type":"string"}},"required":["repo","status"],"additionalProperties":false},"description":"One entry per repository, saying what happened to it."}},"required":["repos"],"additionalProperties":false}}}}}}},"/workspace/templates":{"get":{"operationId":"workspace.templates","summary":"App templates you can add","description":"The kinds of app the configured source repo knows how to scaffold, which is what an add-app picker lists.","tags":["Workspace"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"templates":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"The id to name when scaffolding one."},"label":{"type":"string","description":"What to call it on screen."},"description":{"type":"string","description":"What you get."}},"required":["key","label","description"],"additionalProperties":false},"description":"The kinds of app the configured source repository knows how to scaffold."}},"required":["templates"],"additionalProperties":false}}}}}}},"/workspace/repos/{repo}/apps":{"post":{"operationId":"workspace.addApps","summary":"Scaffold new apps into a repo","description":"Starts scaffolding one or more apps inside an existing multi-package repo and answers straight away. Watch the terminal it opens for progress and for anything that goes wrong.","tags":["Workspace"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository to scaffold into."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"apps":{"minItems":1,"type":"array","items":{"type":"object","properties":{"template":{"type":"string","minLength":1,"description":"Which kind of app to scaffold, by its key in the template list."},"name":{"type":"string","minLength":1,"pattern":"^[a-z][a-z0-9-]*$","description":"What to call this one."}},"required":["template","name"]},"description":"The apps to add."}},"required":["apps"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}},"get":{"operationId":"workspace.appsList","summary":"Apps inside a repo","description":"The apps in one multi-package repo, each with its preview address and whether its dev server is up.","tags":["Workspace"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"apps":{"type":"array","items":{"type":"object","properties":{"app":{"type":"string","description":"The app's name, which is also its folder."},"kind":{"description":"What sort of app it is: the template it came from, or the framework worked out from its dependencies. Absent when it was found purely by having a dev script.","type":"string"},"previewUrl":{"description":"Where to open it. Absent when this sandbox has no outside address.","type":"string"},"running":{"type":"boolean","description":"Whether its dev server is up."},"healthy":{"type":"boolean","description":"Whether it is actually answering."},"installed":{"type":"boolean","description":"Whether its dependencies are installed, which is what decides whether a start takes seconds or an install first."},"launch":{"description":"Where a start the sandbox is running has got to: its shell coming up, installing, its dev command running with nothing listening yet, or exited back to a prompt. Absent when nothing is starting and once it serves.","type":"string","enum":["launching","installing","starting","exited"]}},"required":["app","running","healthy","installed"],"additionalProperties":false},"description":"The apps in this repository."}},"required":["apps"],"additionalProperties":false}}}}}}},"/workspace/repos/{repo}/graph":{"get":{"operationId":"workspace.packageGraph","summary":"How a repo's packages depend on each other","description":"Every package in one multi-package repo and which of its siblings each one uses, which is what a dependency view draws.","tags":["Workspace"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"packages":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"The name the package declares."},"dir":{"type":"string","description":"Where it lives, relative to the repository."},"group":{"type":"string","description":"The top-level folder it sits under, which is what a diagram colours by."}},"required":["name","dir","group"],"additionalProperties":false},"description":"Every package in the repository."},"edges":{"type":"array","items":{"type":"object","properties":{"from":{"type":"string","description":"The package that depends."},"to":{"type":"string","description":"The package it depends on."},"type":{"type":"string","enum":["prod","dev","peer"],"description":"Which kind of dependency declared it."}},"required":["from","to","type"],"additionalProperties":false},"description":"Which of them use which. Pure data: how to lay it out is yours to decide."}},"required":["packages","edges"],"additionalProperties":false}}}}}}},"/workspace/modules":{"get":{"operationId":"workspace.modules","summary":"Every package across every repo","description":"The named packages in the whole workspace, which is what a review list groups changed files under when a reader wants packages rather than paths. Whole-workspace in one answer, because a review spans repos and asking per repo would be a fan-out on every open.","tags":["Workspace"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"repos":{"type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository."},"modules":{"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"Where the package lives, relative to its repository. Empty when the repository is itself one package."},"name":{"type":"string","description":"The name the package declares for itself."}},"required":["dir","name"],"additionalProperties":false},"description":"Its packages."}},"required":["repo","modules"],"additionalProperties":false},"description":"Every repository with the packages inside it."}},"required":["repos"],"additionalProperties":false}}}}}}},"/workspace/repos/{repo}/apps/{app}/start":{"post":{"operationId":"workspace.startApp","summary":"Start an app's dev server","description":"Brings up one app's preview server in an attachable terminal, so its address starts answering.","tags":["Workspace"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository."}},{"name":"app","in":"path","required":true,"schema":{"type":"string","minLength":1,"pattern":"^[a-z][a-z0-9-]*$","description":"Which app inside it."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/workspace/repos/{repo}/apps/{app}/stop":{"post":{"operationId":"workspace.stopApp","summary":"Stop an app's dev server","description":"Shuts one app's preview server down and frees its port.","tags":["Workspace"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository."}},{"name":"app","in":"path","required":true,"schema":{"type":"string","minLength":1,"pattern":"^[a-z][a-z0-9-]*$","description":"Which app inside it."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/workspace/repos/{repo}/tests":{"post":{"operationId":"workspace.runTests","summary":"Run a project's tests","description":"Starts the test run for the projects you name in an attachable terminal and answers straight away. The terminal is where the results appear.","tags":["Workspace"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"string","description":"What to call the terminal this runs in, so you can find it again."},"dirs":{"minItems":1,"type":"array","items":{"type":"string"},"description":"Which projects to test, as folders relative to the repository. Empty targets the repository root."}},"required":["session","dirs"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/changes":{"get":{"operationId":"git.changes","summary":"Uncommitted work across every repo","description":"The workspace's whole review set in one answer: every repo that has something uncommitted, and within it every changed file with its status and line counts. This is what the Changes panel draws, and it is the call to make when you want to know whether a workspace is clean without walking the repos yourself.","tags":["Git"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"repos":{"type":"array","items":{"type":"object","properties":{"repo":{"type":"string"},"branch":{"description":"The checked-out branch. Absent in a repository that has no commits yet.","type":"string"},"conflicted":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The path, relative to the repository root. For a rename this is the new one."},"status":{"type":"string","enum":["added","modified","deleted","renamed","type-changed","conflicted"],"description":"What happened to it. Conflicted is not a kind of edit: nothing can be committed anywhere in the repository while one exists."},"from":{"description":"Where a renamed file came from.","type":"string"},"additions":{"description":"Lines added. Absent for a binary file, and for an untracked one, which has nothing to compare against.","type":"number"},"deletions":{"description":"Lines removed. Absent for the same reasons additions is.","type":"number"},"code":{"description":"The same +/− with every comment stripped from both sides, which is what a review shows beside a diff that opens on code alone. Absent when the file cannot be read that way (binary, too large, or a language this build ships no grammar for): git's own counts above are then the reading.","type":"object","properties":{"additions":{"type":"number"},"deletions":{"type":"number"}},"required":["additions","deletions"],"additionalProperties":false}},"required":["path","status"],"additionalProperties":false},"description":"Paths a merge or rebase could not finish. First, because nothing anywhere in this repository can be committed until they are resolved. Held apart from the two lists below, because staged or not is not a question one of these has an answer to."},"operation":{"description":"What halted, when something did. This is the sentence that explains the conflicts above and names the way out of them.","type":"string","enum":["merge","rebase","cherry-pick","revert"]},"staged":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The path, relative to the repository root. For a rename this is the new one."},"status":{"type":"string","enum":["added","modified","deleted","renamed","type-changed","conflicted"],"description":"What happened to it. Conflicted is not a kind of edit: nothing can be committed anywhere in the repository while one exists."},"from":{"description":"Where a renamed file came from.","type":"string"},"additions":{"description":"Lines added. Absent for a binary file, and for an untracked one, which has nothing to compare against.","type":"number"},"deletions":{"description":"Lines removed. Absent for the same reasons additions is.","type":"number"},"code":{"description":"The same +/− with every comment stripped from both sides, which is what a review shows beside a diff that opens on code alone. Absent when the file cannot be read that way (binary, too large, or a language this build ships no grammar for): git's own counts above are then the reading.","type":"object","properties":{"additions":{"type":"number"},"deletions":{"type":"number"}},"required":["additions","deletions"],"additionalProperties":false}},"required":["path","status"],"additionalProperties":false},"description":"What a plain commit would record right now."},"unstaged":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The path, relative to the repository root. For a rename this is the new one."},"status":{"type":"string","enum":["added","modified","deleted","renamed","type-changed","conflicted"],"description":"What happened to it. Conflicted is not a kind of edit: nothing can be committed anywhere in the repository while one exists."},"from":{"description":"Where a renamed file came from.","type":"string"},"additions":{"description":"Lines added. Absent for a binary file, and for an untracked one, which has nothing to compare against.","type":"number"},"deletions":{"description":"Lines removed. Absent for the same reasons additions is.","type":"number"},"code":{"description":"The same +/− with every comment stripped from both sides, which is what a review shows beside a diff that opens on code alone. Absent when the file cannot be read that way (binary, too large, or a language this build ships no grammar for): git's own counts above are then the reading.","type":"object","properties":{"additions":{"type":"number"},"deletions":{"type":"number"}},"required":["additions","deletions"],"additionalProperties":false}},"required":["path","status"],"additionalProperties":false},"description":"Edits on disk that are not staged, plus untracked files. A path can be in both lists at once with different line counts, which is why they are separate."},"truncated":{"description":"How many changes were cut from each of the two lists above. A freshly cloned monorepo or a mass delete runs to six figures, which no screen can draw, so past a budget the lists arrive short and this says by how much on each side. Absent means they are complete.","type":"object","properties":{"staged":{"type":"number","description":"Staged changes not listed above."},"unstaged":{"type":"number","description":"Unstaged changes not listed above."}},"required":["staged","unstaged"],"additionalProperties":false},"scratch":{"description":"Untracked paths that look like scratch. Staging or committing everything leaves them out, while staging one by its own path takes it like any other file. Absent when there are none.","type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"Relative to its repository. A directory ends in a slash and stands for everything inside it."},"reason":{"type":"string","enum":["hidden","byproduct","checkout","oversized","root"],"description":"Why it looks like scratch. A new hidden directory that is not one a project keeps on purpose (like `.github`). A log, dump, backup or editor leftover. A git checkout of its own. A new file past the size source code reaches. Or a new dotfile at the top of a workspace whose projects are the repositories inside it."},"files":{"description":"How many files it holds. Absent for a checkout of its own, whose contents are not walked.","type":"number"},"bytes":{"description":"Their total size in bytes. Absent exactly when `files` is.","type":"number"}},"required":["path","reason"],"additionalProperties":false}},"remote":{"description":"Where this repository stands against its remote.","type":"object","properties":{"remote":{"description":"The remote this branch pushes to. Absent means none is configured. In a fork with two remotes, pushing to the wrong one succeeds and leaves the count stuck, which is why this says which.","type":"string"},"branch":{"description":"The checked-out branch. Absent when the repository is on a bare commit, or has no commits yet.","type":"string"},"upstream":{"description":"The branch on the remote this one follows. Absent means the next push will publish it.","type":"string"},"ahead":{"type":"number","description":"Commits you have that the remote does not."},"behind":{"type":"number","description":"Commits the remote has that you do not, as of the last fetch. Fetch before trusting it."}},"required":["ahead","behind"],"additionalProperties":false},"origins":{"description":"Which conversation put each path here, newest first, keyed by path. Only work that went through a merge can appear: edits made in the shared tree, in a terminal, or by a person are simply absent rather than guessed at.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"string"}}},"error":{"description":"Why the repository could not be read at all, in git's own words. A repository left broken by a failed import arrives with empty lists and this set, rather than vanishing from the answer with nothing to act on.","type":"string"}},"required":["repo","conflicted","staged","unstaged"],"additionalProperties":false},"description":"One entry per repository that has something pending, is out of step with its remote, or could not be read. A clean repository is simply absent."},"originAgents":{"description":"Who each conversation named above is, keyed by id, so a caller need not look them up. Absent when nothing in the review can be attributed.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"object","properties":{"title":{"description":"The conversation's title. Absent for one that never got as far as having a title.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it ran on."},"landedMessage":{"description":"What the merged work did, drafted by the conversation itself. Carried here as well as on its card, because merged lines outlive the card: archiving a finished conversation does not uncommit its work.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false}},"required":["provider"],"additionalProperties":false}},"committing":{"description":"Repositories with a commit running right now. The sandbox's answer rather than any one tab's, so a reload, a second window and another device all know. Absent means nothing is committing.","type":"array","items":{"type":"string"}}},"required":["repos"],"additionalProperties":false}}}}}}},"/git/repos":{"get":{"operationId":"git.repos","summary":"Every git repo in the workspace","description":"The repos the daemon found under the workspace root, each with the id every other call in this group expects as its `{repo}` segment. The workspace root itself is always present as `root`.","tags":["Git"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"repos":{"type":"array","items":{"type":"string"},"description":"Every repository's id. The workspace itself is always present as \"root\"."}},"required":["repos"],"additionalProperties":false}}}}}}},"/git/remote-repos":{"get":{"operationId":"git.remoteRepos","summary":"Repos matched to their remotes","description":"The same repo list, but with the forge host and `owner/name` each one's remote points at. Use it to recognise a workspace repo in a list of names that came from somewhere else, such as a set of pull requests. Costs a remote lookup per repo, which is why it is separate from the plain repo list.","tags":["Git"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"repos":{"type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"The workspace repository."},"host":{"type":"string","description":"Which forge its remote points at."},"project":{"type":"string","description":"Which project there, as owner and name."}},"required":["repo","host","project"],"additionalProperties":false},"description":"Each repository matched to the project its remote points at."}},"required":["repos"],"additionalProperties":false}}}}}}},"/git/{repo}/log":{"get":{"operationId":"git.log","summary":"Commit history for one repo","description":"A page of commits on the current branch, newest first, each with its author, subject, timestamp and the refs pointing at it. Paginate with the cursor the answer hands back rather than by offset, so a commit landing mid-scroll does not shift the page under you.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}},{"name":"limit","in":"query","schema":{"description":"How many commits to return.","type":"integer","exclusiveMinimum":0,"maximum":2000},"allowEmptyValue":true,"allowReserved":true},{"name":"skip","in":"query","schema":{"description":"How many newer commits to step over, which is how you page further back. Paged rather than read whole, because a large repository's history is tens of thousands of rows.","type":"integer","minimum":0,"maximum":1000000},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository."},"branch":{"description":"Which branch these are from.","type":"string"},"commits":{"type":"array","items":{"type":"object","properties":{"sha":{"type":"string","description":"The commit, in full."},"short":{"type":"string","description":"The abbreviated form, for showing."},"parents":{"type":"array","items":{"type":"string"},"description":"What it came from. None means the first commit, one is ordinary, two or more is a merge, which is what a graph draws its lanes from."},"subject":{"type":"string","description":"Its first line."},"body":{"type":"string","description":"Everything after that."},"author":{"type":"string","description":"Who wrote it."},"email":{"type":"string","description":"Their address."},"at":{"type":"number","description":"When they wrote it, in milliseconds."},"refs":{"type":"array","items":{"type":"string"},"description":"Branches and tags sitting on it."},"head":{"type":"boolean","description":"Whether this is where the repository currently stands."}},"required":["sha","short","parents","subject","body","author","email","at","refs","head"],"additionalProperties":false},"description":"The commits, newest first."},"hasMore":{"type":"boolean","description":"There are older ones behind this page. It is also what stops the last row being drawn as the beginning of history, which is how a truncated log used to claim it started where the page happened to stop."}},"required":["repo","commits","hasMore"],"additionalProperties":false}}}}}}},"/git/{repo}/commit-diff":{"get":{"operationId":"git.commitDiff","summary":"What one commit changed","description":"The list of files a single commit touched, with per-file status and line counts but not the content. Fetch the content of any one of them with the commit file diff call, so a commit with a thousand files stays one cheap answer.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}},{"name":"sha","in":"query","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{4,64}$","description":"Which commit."},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"files":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The path, relative to the repository root. For a rename this is the new one."},"status":{"type":"string","enum":["added","modified","deleted","renamed","type-changed","conflicted"],"description":"What happened to it. Conflicted is not a kind of edit: nothing can be committed anywhere in the repository while one exists."},"from":{"description":"Where a renamed file came from.","type":"string"},"additions":{"description":"Lines added. Absent for a binary file, and for an untracked one, which has nothing to compare against.","type":"number"},"deletions":{"description":"Lines removed. Absent for the same reasons additions is.","type":"number"},"code":{"description":"The same +/− with every comment stripped from both sides, which is what a review shows beside a diff that opens on code alone. Absent when the file cannot be read that way (binary, too large, or a language this build ships no grammar for): git's own counts above are then the reading.","type":"object","properties":{"additions":{"type":"number"},"deletions":{"type":"number"}},"required":["additions","deletions"],"additionalProperties":false}},"required":["path","status"],"additionalProperties":false},"description":"Which files it touched, with counts but not contents. Fetch any one file's contents separately, so a commit with a thousand files stays one cheap answer."}},"required":["files"],"additionalProperties":false}}}}}}},"/git/{repo}/commit-file-diff":{"get":{"operationId":"git.commitFileDiff","summary":"One file's before and after at a commit","description":"Both sides of a single file as of one commit: the content its parent had and the content that commit left. The daemon returns whole sides rather than a patch, so a caller can render the comparison however it likes.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}},{"name":"sha","in":"query","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{4,64}$","description":"Which commit."},"allowEmptyValue":true,"allowReserved":true},{"name":"path","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"Which file in it."},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"before":{"description":"The whole file as it was. Absent when it did not exist yet, or when `partial` is set.","type":"string"},"after":{"description":"The whole file as it is now. Absent when it was deleted, or when `partial` is set.","type":"string"},"binary":{"description":"The file is not text, so neither side is sent.","type":"boolean"},"partial":{"description":"Set when the file was too large to send whole: what is sent instead of the two sides.","type":"object","properties":{"beforeBytes":{"description":"How big the before side is, in bytes. Absent when the file did not exist yet.","type":"integer","minimum":0,"maximum":9007199254740991},"afterBytes":{"description":"How big the after side is, in bytes. Absent when the file was deleted.","type":"integer","minimum":0,"maximum":9007199254740991},"patch":{"description":"The changed regions as unified-diff hunks (`@@` sections only). Absent when the change was too large to render even as a patch.","type":"string"},"more":{"description":"There were more changed regions than fit; the patch stops at a region boundary.","type":"boolean"}},"additionalProperties":false}},"additionalProperties":false}}}}}}},"/git/{repo}/operation":{"get":{"operationId":"git.operation","summary":"Whether a merge or rebase is halted mid-flight","description":"Names the git operation the worktree is stuck inside, if any: a conflicted merge, an interrupted rebase, a half-applied cherry-pick. Check this first when another call refuses, because a halted worktree is the usual reason and the abort call is the way out.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"repo":{"type":"string","description":"The repository asked about."},"operation":{"description":"Which operation the working tree is stuck inside. Absent means it is not stuck at all, which is almost always. While one is present git refuses nearly everything else, and abandoning it is the only way out.","type":"string","enum":["merge","rebase","cherry-pick","revert"]}},"required":["repo"],"additionalProperties":false}}}}}}},"/git/{repo}/abort":{"post":{"operationId":"git.abort","summary":"Abandon a halted merge or rebase","description":"Runs git's own abort for whichever operation has the worktree halted, putting the repo back where it stood before the operation started. Nothing else clears that state.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"Whether it worked."},"reason":{"description":"Why not, in git's own words. A conflict, a missing remote and missing credentials are all reported here rather than raised, because they are things a screen has to render rather than breakages.","type":"string"}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/undo":{"get":{"operationId":"git.undoable","summary":"What undoing the last action would do","description":"Reads the branch's reflog to describe the move that undo would reverse, and hands back the commit it would land on. Pass that commit to the undo call as proof you looked, and an undo prepared against a view that has since moved is refused rather than landing somewhere unexamined.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository."},"action":{"description":"What undoing would reverse. Absent means there is nothing to go back from.","type":"object","properties":{"kind":{"type":"string","enum":["commit","amend","merge","rebase","cherry-pick","revert","reset","pull","other"],"description":"What the last action was."},"description":{"type":"string","description":"What undoing it would do, in words."},"branch":{"type":"string","description":"Which branch would move."},"sha":{"type":"string","description":"Where it stands now."},"previousSha":{"type":"string","description":"Where it would go back to. Send this with the undo as proof you looked, so one prepared against a view that has since moved is refused rather than landing somewhere unexamined."},"changesWorkingTree":{"type":"boolean","description":"Undoing would rewrite files as well as moving the branch, so anything offering it should warn about losing work."}},"required":["kind","description","branch","sha","previousSha","changesWorkingTree"],"additionalProperties":false}},"required":["repo"],"additionalProperties":false}}}}}},"post":{"operationId":"git.undo","summary":"Move the branch back one step","description":"Walks the current branch back to where it pointed before its last action. This moves the branch ref and leaves the working tree alone, which is the opposite of restoring a checkpoint. Requires the commit the matching read handed you.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"previousSha":{"type":"string","pattern":"^[0-9a-f]{4,64}$","description":"Where to go back to, from the matching read. It is also proof you looked: one prepared against a stale view is refused."},"discardChanges":{"description":"Also rewrite the files, rather than only moving the branch.","type":"boolean"}},"required":["previousSha"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"Whether it worked."},"reason":{"description":"Why not, in git's own words. A conflict, a missing remote and missing credentials are all reported here rather than raised, because they are things a screen has to render rather than breakages.","type":"string"}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/stashes":{"get":{"operationId":"git.stashes","summary":"Everything set aside in the stash","description":"The repo's stash entries, newest first, each with the message and the commit behind it. A stash entry is a commit, so it reads the same way a log entry does and its contents come back from the stash diff call.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository."},"stashes":{"type":"array","items":{"type":"object","properties":{"ref":{"type":"string","description":"How to address it, which applying and dropping take."},"sha":{"type":"string","description":"The commit behind it, because a stash entry is a commit."},"short":{"type":"string","description":"The abbreviated form, for showing."},"subject":{"type":"string","description":"What it was set aside as, with git's own scaffolding stripped off."},"branch":{"description":"Which branch it was set aside from.","type":"string"},"at":{"type":"number","description":"When, in milliseconds."},"parents":{"type":"array","items":{"type":"string"},"description":"What it sits on, so a graph can draw it like any other commit."}},"required":["ref","sha","short","subject","at","parents"],"additionalProperties":false},"description":"What is set aside, newest first."}},"required":["repo","stashes"],"additionalProperties":false}}}}}}},"/git/{repo}/stash-diff":{"get":{"operationId":"git.stashDiff","summary":"What one stash entry holds","description":"The files a single stash entry would bring back, with per-file status and line counts. The same shape a commit diff has, because a stash entry is a commit.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}},{"name":"ref","in":"query","required":true,"schema":{"type":"string","pattern":"^stash@\\{\\d{1,4}\\}$","description":"Which entry."},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"files":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The path, relative to the repository root. For a rename this is the new one."},"status":{"type":"string","enum":["added","modified","deleted","renamed","type-changed","conflicted"],"description":"What happened to it. Conflicted is not a kind of edit: nothing can be committed anywhere in the repository while one exists."},"from":{"description":"Where a renamed file came from.","type":"string"},"additions":{"description":"Lines added. Absent for a binary file, and for an untracked one, which has nothing to compare against.","type":"number"},"deletions":{"description":"Lines removed. Absent for the same reasons additions is.","type":"number"},"code":{"description":"The same +/− with every comment stripped from both sides, which is what a review shows beside a diff that opens on code alone. Absent when the file cannot be read that way (binary, too large, or a language this build ships no grammar for): git's own counts above are then the reading.","type":"object","properties":{"additions":{"type":"number"},"deletions":{"type":"number"}},"required":["additions","deletions"],"additionalProperties":false}},"required":["path","status"],"additionalProperties":false},"description":"Which files it touched, with counts but not contents. Fetch any one file's contents separately, so a commit with a thousand files stays one cheap answer."}},"required":["files"],"additionalProperties":false}}}}}}},"/git/{repo}/stash":{"post":{"operationId":"git.stashPush","summary":"Set the current changes aside","description":"Moves the working tree's changes onto the stash and leaves a clean tree behind. Nothing is lost: the entry is a commit you can inspect, apply or drop afterwards.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"description":"What to call it, so you know what it was later.","type":"string","maxLength":500},"includeUntracked":{"description":"Also set aside files git is not yet tracking, which are otherwise left where they are.","type":"boolean"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"Whether it worked."},"reason":{"description":"Why not, in git's own words. A conflict, a missing remote and missing credentials are all reported here rather than raised, because they are things a screen has to render rather than breakages.","type":"string"}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/stash/apply":{"post":{"operationId":"git.stashApply","summary":"Bring a stash entry back","description":"Replays one stash entry onto the working tree. A conflict is reported in the answer rather than raised as a failure, because a conflicting apply is an ordinary outcome a screen has to render.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ref":{"type":"string","pattern":"^stash@\\{\\d{1,4}\\}$","description":"Which entry."},"pop":{"description":"Remove it from the stash once it has been applied cleanly.","type":"boolean"}},"required":["ref"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"Whether it worked."},"reason":{"description":"Why not, in git's own words. A conflict, a missing remote and missing credentials are all reported here rather than raised, because they are things a screen has to render rather than breakages.","type":"string"}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/stash/drop":{"post":{"operationId":"git.stashDrop","summary":"Discard a stash entry","description":"Deletes one stash entry. This is the only unrecoverable call in the stash set, so the daemon takes a checkpoint of the workspace first.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ref":{"type":"string","pattern":"^stash@\\{\\d{1,4}\\}$","description":"Which entry."}},"required":["ref"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/branch":{"post":{"operationId":"git.createBranch","summary":"Start a branch at a commit","description":"Points a new branch name at any commit, without moving HEAD. Use the checkout call if you also want to switch to it.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sha":{"type":"string","pattern":"^[0-9a-f]{4,64}$","description":"Which commit to start it at."},"name":{"type":"string","maxLength":200,"pattern":"^[A-Za-z0-9][A-Za-z0-9._/-]*$","description":"The new branch's name."}},"required":["sha","name"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/tag":{"post":{"operationId":"git.createTag","summary":"Tag a commit","description":"Puts a tag on any commit. Local only: pushing it to the remote is a separate call.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sha":{"type":"string","pattern":"^[0-9a-f]{4,64}$","description":"Which commit to tag."},"name":{"type":"string","maxLength":200,"pattern":"^[A-Za-z0-9][A-Za-z0-9._/-]*$","description":"The tag's name."}},"required":["sha","name"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/tag/delete":{"post":{"operationId":"git.deleteTag","summary":"Remove a tag","description":"Deletes a tag locally. A tag already pushed stays on the remote until it is deleted there too.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":200,"pattern":"^[A-Za-z0-9][A-Za-z0-9._/-]*$","description":"Which tag."},"remote":{"description":"Also delete it there. Leave it out to remove it locally only.","type":"string","maxLength":200,"pattern":"^[A-Za-z0-9][A-Za-z0-9._/-]*$"}},"required":["name"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/tag/push":{"post":{"operationId":"git.pushTag","summary":"Send a tag to the remote","description":"Pushes one tag to the repo's remote. Reports the outcome rather than failing, since a missing remote or missing credentials are ordinary answers here.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":200,"pattern":"^[A-Za-z0-9][A-Za-z0-9._/-]*$","description":"Which tag."},"remote":{"type":"string","maxLength":200,"pattern":"^[A-Za-z0-9][A-Za-z0-9._/-]*$","description":"Which remote to send it to."}},"required":["name","remote"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"Whether it worked."},"reason":{"description":"Why not, in git's own words. A conflict, a missing remote and missing credentials are all reported here rather than raised, because they are things a screen has to render rather than breakages.","type":"string"}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/checkout":{"post":{"operationId":"git.checkout","summary":"Switch to a branch or commit","description":"Moves HEAD to a branch, tag or commit and reshapes the working tree to match. The daemon takes a checkpoint first, so an unexpected result is recoverable. Uncommitted work that would be overwritten is reported instead of being trampled.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ref":{"type":"string","maxLength":200,"pattern":"^[A-Za-z0-9][A-Za-z0-9._/-]*$","description":"Where to switch to: a branch, a tag, or a commit."}},"required":["ref"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"Whether it worked."},"reason":{"description":"Why not, in git's own words. A conflict, a missing remote and missing credentials are all reported here rather than raised, because they are things a screen has to render rather than breakages.","type":"string"}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/cherry-pick":{"post":{"operationId":"git.cherryPick","summary":"Replay one commit onto this branch","description":"Applies a single commit's changes on top of the current branch as a new commit. A conflict comes back in the answer, with the halted state readable from the operation call.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sha":{"type":"string","pattern":"^[0-9a-f]{4,64}$","description":"Which commit to act on."}},"required":["sha"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"Whether it worked."},"reason":{"description":"Why not, in git's own words. A conflict, a missing remote and missing credentials are all reported here rather than raised, because they are things a screen has to render rather than breakages.","type":"string"}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/revert":{"post":{"operationId":"git.revert","summary":"Undo a commit with a new commit","description":"Adds a commit that reverses an earlier one, leaving the history intact. This is the safe way to take something back on a branch other people have pulled.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sha":{"type":"string","pattern":"^[0-9a-f]{4,64}$","description":"Which commit to act on."}},"required":["sha"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"Whether it worked."},"reason":{"description":"Why not, in git's own words. A conflict, a missing remote and missing credentials are all reported here rather than raised, because they are things a screen has to render rather than breakages.","type":"string"}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/drop":{"post":{"operationId":"git.drop","summary":"Remove a commit from history","description":"Rewrites the branch so one commit is no longer in it. History changes, so this is for branches nobody else has pulled. A checkpoint is taken first.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sha":{"type":"string","pattern":"^[0-9a-f]{4,64}$","description":"Which commit to act on."}},"required":["sha"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"Whether it worked."},"reason":{"description":"Why not, in git's own words. A conflict, a missing remote and missing credentials are all reported here rather than raised, because they are things a screen has to render rather than breakages.","type":"string"}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/merge":{"post":{"operationId":"git.merge","summary":"Merge another branch in","description":"Merges a branch or commit into the current one. Conflicts are reported in the answer and leave the worktree halted, which the operation call explains and the abort call clears.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sha":{"type":"string","pattern":"^[0-9a-f]{4,64}$","description":"Which commit to act on."}},"required":["sha"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"Whether it worked."},"reason":{"description":"Why not, in git's own words. A conflict, a missing remote and missing credentials are all reported here rather than raised, because they are things a screen has to render rather than breakages.","type":"string"}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/rebase":{"post":{"operationId":"git.rebase","summary":"Replay this branch onto another","description":"Moves the current branch's commits on top of a different base. History changes. Conflicts halt the rebase and are reported rather than raised, so the operation and abort calls are the way through.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sha":{"type":"string","pattern":"^[0-9a-f]{4,64}$","description":"Which commit to act on."}},"required":["sha"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"Whether it worked."},"reason":{"description":"Why not, in git's own words. A conflict, a missing remote and missing credentials are all reported here rather than raised, because they are things a screen has to render rather than breakages.","type":"string"}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/reset":{"post":{"operationId":"git.reset","summary":"Move the branch to a commit","description":"Repoints the current branch at another commit, optionally reshaping the working tree to match. The destructive modes take a checkpoint first.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sha":{"type":"string","pattern":"^[0-9a-f]{4,64}$","description":"Which commit to move the branch to."},"mode":{"type":"string","enum":["soft","mixed","hard"],"description":"How much to take with it: move the branch alone, also unstage, or also throw away what is on disk. The last one takes a checkpoint first."}},"required":["sha","mode"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"Whether it worked."},"reason":{"description":"Why not, in git's own words. A conflict, a missing remote and missing credentials are all reported here rather than raised, because they are things a screen has to render rather than breakages.","type":"string"}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/file-diff":{"get":{"operationId":"git.fileDiff","summary":"One file's committed and working copies","description":"Both sides of a file as it stands right now: what the last commit holds and what is on disk. This is what a review pane shows for an uncommitted change.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}},{"name":"path","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"The file, relative to the repository root."},"allowEmptyValue":true,"allowReserved":true},{"name":"side","in":"query","required":true,"schema":{"type":"string","enum":["staged","unstaged","conflicted"],"description":"Which comparison you want. A file that is staged and then edited again has genuinely different answers for each, which is why this is required rather than assumed."},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"before":{"description":"The whole file as it was. Absent when it did not exist yet, or when `partial` is set.","type":"string"},"after":{"description":"The whole file as it is now. Absent when it was deleted, or when `partial` is set.","type":"string"},"binary":{"description":"The file is not text, so neither side is sent.","type":"boolean"},"partial":{"description":"Set when the file was too large to send whole: what is sent instead of the two sides.","type":"object","properties":{"beforeBytes":{"description":"How big the before side is, in bytes. Absent when the file did not exist yet.","type":"integer","minimum":0,"maximum":9007199254740991},"afterBytes":{"description":"How big the after side is, in bytes. Absent when the file was deleted.","type":"integer","minimum":0,"maximum":9007199254740991},"patch":{"description":"The changed regions as unified-diff hunks (`@@` sections only). Absent when the change was too large to render even as a patch.","type":"string"},"more":{"description":"There were more changed regions than fit; the patch stops at a region boundary.","type":"boolean"}},"additionalProperties":false}},"additionalProperties":false}}}}}}},"/git/{repo}/status":{"get":{"operationId":"git.status","summary":"One repo's branch and pending changes","description":"The current branch, its sync position against the remote, and every staged, unstaged and untracked path. The single-repo counterpart to the workspace-wide changes call.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"branch":{"type":"string","description":"The checked-out branch."},"dirty":{"type":"boolean","description":"Whether anything is uncommitted."},"files":{"type":"array","items":{"type":"string"},"description":"Every path with something pending, staged or not."}},"required":["branch","dirty","files"],"additionalProperties":false}}}}}}},"/git/{repo}/commit":{"post":{"operationId":"git.commit","summary":"Commit the pending changes","description":"Records a commit with your message. It commits whatever is staged; add `stage` to stage something first — an empty object for everything pending, or a scope such as one side or one conversation's landed files. The answer carries the commit it created.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","minLength":1,"description":"The commit message."},"stage":{"description":"What to stage before committing. Leave it out to record the index exactly as it stands; give it an empty object to stage everything first.","type":"object","properties":{"paths":{"maxItems":1000,"type":"array","items":{"type":"string","minLength":1},"description":"Exactly these repository-relative paths. For anything bigger than a hand-picked selection, describe a scope instead."},"scope":{"description":"What to act on, described rather than listed, so it covers every matching file in the repository and not just the ones a list could hold.","type":"object","properties":{"side":{"description":"Narrow to one of the three lists a repository's changes split into. Leave it out for all of them, which is the whole repository.","type":"string","enum":["staged","unstaged","conflicted"]},"origin":{"description":"Narrow to the files one conversation landed. Leave it out for everyone's, including your own edits.","type":"string","minLength":1}}}}}},"required":["message"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"committed":{"type":"boolean","description":"Whether a commit was actually recorded."},"changes":{"description":"What this repository looks like now, read in the same breath as the commit so a caller can redraw from here instead of asking for a fresh scan. Absent means there is nothing left to show.","type":"object","properties":{"repo":{"type":"string"},"branch":{"description":"The checked-out branch. Absent in a repository that has no commits yet.","type":"string"},"conflicted":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The path, relative to the repository root. For a rename this is the new one."},"status":{"type":"string","enum":["added","modified","deleted","renamed","type-changed","conflicted"],"description":"What happened to it. Conflicted is not a kind of edit: nothing can be committed anywhere in the repository while one exists."},"from":{"description":"Where a renamed file came from.","type":"string"},"additions":{"description":"Lines added. Absent for a binary file, and for an untracked one, which has nothing to compare against.","type":"number"},"deletions":{"description":"Lines removed. Absent for the same reasons additions is.","type":"number"},"code":{"description":"The same +/− with every comment stripped from both sides, which is what a review shows beside a diff that opens on code alone. Absent when the file cannot be read that way (binary, too large, or a language this build ships no grammar for): git's own counts above are then the reading.","type":"object","properties":{"additions":{"type":"number"},"deletions":{"type":"number"}},"required":["additions","deletions"],"additionalProperties":false}},"required":["path","status"],"additionalProperties":false},"description":"Paths a merge or rebase could not finish. First, because nothing anywhere in this repository can be committed until they are resolved. Held apart from the two lists below, because staged or not is not a question one of these has an answer to."},"operation":{"description":"What halted, when something did. This is the sentence that explains the conflicts above and names the way out of them.","type":"string","enum":["merge","rebase","cherry-pick","revert"]},"staged":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The path, relative to the repository root. For a rename this is the new one."},"status":{"type":"string","enum":["added","modified","deleted","renamed","type-changed","conflicted"],"description":"What happened to it. Conflicted is not a kind of edit: nothing can be committed anywhere in the repository while one exists."},"from":{"description":"Where a renamed file came from.","type":"string"},"additions":{"description":"Lines added. Absent for a binary file, and for an untracked one, which has nothing to compare against.","type":"number"},"deletions":{"description":"Lines removed. Absent for the same reasons additions is.","type":"number"},"code":{"description":"The same +/− with every comment stripped from both sides, which is what a review shows beside a diff that opens on code alone. Absent when the file cannot be read that way (binary, too large, or a language this build ships no grammar for): git's own counts above are then the reading.","type":"object","properties":{"additions":{"type":"number"},"deletions":{"type":"number"}},"required":["additions","deletions"],"additionalProperties":false}},"required":["path","status"],"additionalProperties":false},"description":"What a plain commit would record right now."},"unstaged":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The path, relative to the repository root. For a rename this is the new one."},"status":{"type":"string","enum":["added","modified","deleted","renamed","type-changed","conflicted"],"description":"What happened to it. Conflicted is not a kind of edit: nothing can be committed anywhere in the repository while one exists."},"from":{"description":"Where a renamed file came from.","type":"string"},"additions":{"description":"Lines added. Absent for a binary file, and for an untracked one, which has nothing to compare against.","type":"number"},"deletions":{"description":"Lines removed. Absent for the same reasons additions is.","type":"number"},"code":{"description":"The same +/− with every comment stripped from both sides, which is what a review shows beside a diff that opens on code alone. Absent when the file cannot be read that way (binary, too large, or a language this build ships no grammar for): git's own counts above are then the reading.","type":"object","properties":{"additions":{"type":"number"},"deletions":{"type":"number"}},"required":["additions","deletions"],"additionalProperties":false}},"required":["path","status"],"additionalProperties":false},"description":"Edits on disk that are not staged, plus untracked files. A path can be in both lists at once with different line counts, which is why they are separate."},"truncated":{"description":"How many changes were cut from each of the two lists above. A freshly cloned monorepo or a mass delete runs to six figures, which no screen can draw, so past a budget the lists arrive short and this says by how much on each side. Absent means they are complete.","type":"object","properties":{"staged":{"type":"number","description":"Staged changes not listed above."},"unstaged":{"type":"number","description":"Unstaged changes not listed above."}},"required":["staged","unstaged"],"additionalProperties":false},"scratch":{"description":"Untracked paths that look like scratch. Staging or committing everything leaves them out, while staging one by its own path takes it like any other file. Absent when there are none.","type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"Relative to its repository. A directory ends in a slash and stands for everything inside it."},"reason":{"type":"string","enum":["hidden","byproduct","checkout","oversized","root"],"description":"Why it looks like scratch. A new hidden directory that is not one a project keeps on purpose (like `.github`). A log, dump, backup or editor leftover. A git checkout of its own. A new file past the size source code reaches. Or a new dotfile at the top of a workspace whose projects are the repositories inside it."},"files":{"description":"How many files it holds. Absent for a checkout of its own, whose contents are not walked.","type":"number"},"bytes":{"description":"Their total size in bytes. Absent exactly when `files` is.","type":"number"}},"required":["path","reason"],"additionalProperties":false}},"remote":{"description":"Where this repository stands against its remote.","type":"object","properties":{"remote":{"description":"The remote this branch pushes to. Absent means none is configured. In a fork with two remotes, pushing to the wrong one succeeds and leaves the count stuck, which is why this says which.","type":"string"},"branch":{"description":"The checked-out branch. Absent when the repository is on a bare commit, or has no commits yet.","type":"string"},"upstream":{"description":"The branch on the remote this one follows. Absent means the next push will publish it.","type":"string"},"ahead":{"type":"number","description":"Commits you have that the remote does not."},"behind":{"type":"number","description":"Commits the remote has that you do not, as of the last fetch. Fetch before trusting it."}},"required":["ahead","behind"],"additionalProperties":false},"origins":{"description":"Which conversation put each path here, newest first, keyed by path. Only work that went through a merge can appear: edits made in the shared tree, in a terminal, or by a person are simply absent rather than guessed at.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"array","items":{"type":"string"}}},"error":{"description":"Why the repository could not be read at all, in git's own words. A repository left broken by a failed import arrives with empty lists and this set, rather than vanishing from the answer with nothing to act on.","type":"string"}},"required":["repo","conflicted","staged","unstaged"],"additionalProperties":false},"originAgents":{"description":"Who the conversations named in those changes are. Merge it over what you already hold rather than replacing: other repositories still name their own.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"object","properties":{"title":{"description":"The conversation's title. Absent for one that never got as far as having a title.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it ran on."},"landedMessage":{"description":"What the merged work did, drafted by the conversation itself. Carried here as well as on its card, because merged lines outlive the card: archiving a finished conversation does not uncommit its work.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false}},"required":["provider"],"additionalProperties":false}}},"required":["committed"],"additionalProperties":false}}}}}}},"/git/{repo}/discard":{"post":{"operationId":"git.discard","summary":"Throw away pending changes","description":"Restores files to their committed state and deletes untracked ones. Name paths or a scope to narrow it; with neither it throws away every uncommitted change in the repository. The daemon checkpoints the workspace first, so this is recoverable from the timeline.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"paths":{"maxItems":1000,"type":"array","items":{"type":"string","minLength":1},"description":"Exactly these repository-relative paths. For anything bigger than a hand-picked selection, describe a scope instead."},"scope":{"description":"What to act on, described rather than listed, so it covers every matching file in the repository and not just the ones a list could hold.","type":"object","properties":{"side":{"description":"Narrow to one of the three lists a repository's changes split into. Leave it out for all of them, which is the whole repository.","type":"string","enum":["staged","unstaged","conflicted"]},"origin":{"description":"Narrow to the files one conversation landed. Leave it out for everyone's, including your own edits.","type":"string","minLength":1}}}},"description":"What to throw away. Neither paths nor a scope discards every uncommitted change in the repository."}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/stage":{"post":{"operationId":"git.stage","summary":"Mark changes for the next commit","description":"Adds changes to the index: exactly the paths you name, everything a scope describes, or the whole repository when you name neither. Nothing on disk changes, so this is always safe and always reversible with the unstage call.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"paths":{"maxItems":1000,"type":"array","items":{"type":"string","minLength":1},"description":"Exactly these repository-relative paths. For anything bigger than a hand-picked selection, describe a scope instead."},"scope":{"description":"What to act on, described rather than listed, so it covers every matching file in the repository and not just the ones a list could hold.","type":"object","properties":{"side":{"description":"Narrow to one of the three lists a repository's changes split into. Leave it out for all of them, which is the whole repository.","type":"string","enum":["staged","unstaged","conflicted"]},"origin":{"description":"Narrow to the files one conversation landed. Leave it out for everyone's, including your own edits.","type":"string","minLength":1}}}},"description":"What to move across the index. Nothing on disk changes either way."}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/unstage":{"post":{"operationId":"git.unstage","summary":"Take changes back out of the next commit","description":"Removes changes from the index and leaves the files themselves untouched, on the same terms as staging. The exact reverse of it.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"paths":{"maxItems":1000,"type":"array","items":{"type":"string","minLength":1},"description":"Exactly these repository-relative paths. For anything bigger than a hand-picked selection, describe a scope instead."},"scope":{"description":"What to act on, described rather than listed, so it covers every matching file in the repository and not just the ones a list could hold.","type":"object","properties":{"side":{"description":"Narrow to one of the three lists a repository's changes split into. Leave it out for all of them, which is the whole repository.","type":"string","enum":["staged","unstaged","conflicted"]},"origin":{"description":"Narrow to the files one conversation landed. Leave it out for everyone's, including your own edits.","type":"string","minLength":1}}}},"description":"What to move across the index. Nothing on disk changes either way."}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/branches":{"get":{"operationId":"git.branches","summary":"Local branches and how far each has drifted","description":"Every local branch with how many commits it sits ahead of and behind its remote counterpart, so a branch switcher can show sync state without a call per branch.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"branches":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"The branch name."},"current":{"type":"boolean","description":"Whether this is the one checked out."},"upstream":{"description":"The branch on the remote it follows, if any.","type":"string"},"ahead":{"type":"number","description":"Commits this branch has that its remote counterpart does not."},"behind":{"type":"number","description":"Commits its remote counterpart has that it does not."},"gone":{"description":"The branch it followed no longer exists on the remote, usually because a merged pull request deleted it. The signal that this one is safe to delete.","type":"boolean"},"at":{"type":"number","description":"When its tip was committed, in milliseconds. Lists are newest first."}},"required":["name","current","ahead","behind","at"],"additionalProperties":false},"description":"Branches in this repository."},"remotes":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"The full name, such as origin/main."},"remote":{"type":"string","description":"Just the remote part, so a picker can group by it without re-parsing."},"branch":{"type":"string","description":"Just the branch part."},"at":{"type":"number","description":"When its tip was committed, in milliseconds, as this repository last saw it."}},"required":["name","remote","branch","at"],"additionalProperties":false},"description":"Branches on its remotes, as last seen. Sent together with the locals so a switcher never draws a half-filled list."}},"required":["branches","remotes"],"additionalProperties":false}}}}}},"post":{"operationId":"git.createBranchAt","summary":"Create a branch from a starting point","description":"Makes a branch at a named start point and optionally switches to it. The branch-switcher counterpart to creating a branch at a specific commit.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":200,"pattern":"^[A-Za-z0-9][A-Za-z0-9._/-]*$","description":"The new branch's name."},"start":{"description":"Where to start it: a commit or another branch. Leave it out to start from where you are.","type":"string","minLength":1},"checkout":{"description":"Switch to it as well as creating it.","type":"boolean"}},"required":["name"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/branches/delete":{"post":{"operationId":"git.deleteBranch","summary":"Delete a local branch","description":"Removes a branch from the repo. Unmerged work is refused unless you ask for it to be forced, and the remote branch is untouched either way.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":200,"pattern":"^[A-Za-z0-9][A-Za-z0-9._/-]*$","description":"The branch to delete."},"force":{"description":"Delete it even though it holds work that was never merged. The deliberate retry after the first attempt refuses.","type":"boolean"}},"required":["name"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/remote":{"get":{"operationId":"git.remote","summary":"Sync position against the remote","description":"How far the current branch sits ahead of and behind its remote, as of the last fetch, plus whether a remote and working credentials exist at all. This is a read of what the daemon already knows, not a network call, which is why fetching is a separate button.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"remote":{"description":"The remote this branch pushes to. Absent means none is configured. In a fork with two remotes, pushing to the wrong one succeeds and leaves the count stuck, which is why this says which.","type":"string"},"branch":{"description":"The checked-out branch. Absent when the repository is on a bare commit, or has no commits yet.","type":"string"},"upstream":{"description":"The branch on the remote this one follows. Absent means the next push will publish it.","type":"string"},"ahead":{"type":"number","description":"Commits you have that the remote does not."},"behind":{"type":"number","description":"Commits the remote has that you do not, as of the last fetch. Fetch before trusting it."}},"required":["ahead","behind"],"additionalProperties":false}}}}}}},"/git/{repo}/fetch":{"post":{"operationId":"git.fetch","summary":"Refresh what the remote holds","description":"Contacts the remote and updates the daemon's picture of it without touching your branch. Run this before trusting the sync position.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"Whether it worked."},"reason":{"description":"Why not, in git's own words. A conflict, a missing remote and missing credentials are all reported here rather than raised, because they are things a screen has to render rather than breakages.","type":"string"}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/pull":{"post":{"operationId":"git.pull","summary":"Bring remote commits down","description":"Fetches and integrates the remote's commits into the current branch. A pull that cannot fast-forward is reported in the answer rather than raised, because that is an ordinary thing to be told.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"Whether it worked."},"reason":{"description":"Why not, in git's own words. A conflict, a missing remote and missing credentials are all reported here rather than raised, because they are things a screen has to render rather than breakages.","type":"string"}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/push":{"post":{"operationId":"git.push","summary":"Start sending commits to the remote","description":"Starts pushing the current branch, setting its upstream on first push, and answers at once: the push runs in a real terminal (it runs this repository's pre-push hook, which can be a whole suite), so watch it there and poll pushState for the verdict. A second start while one is going joins it rather than pushing twice.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"branch":{"description":"Which branch to push. Leave it out for the checked-out one. A branch with no upstream yet gets one set on this push.","type":"string","minLength":1}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}},"get":{"operationId":"git.pushState","summary":"How the push is going","description":"The verdict, or the progress so far: where it is, the terminal it runs in, and for a push that did not go, git's last words and who refused it, the repository's own pre-push hook, the remote, or the transport. Idle when nothing has been started for this repository.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["idle","running","passed","failed","error","cancelled"],"description":"Where the run is. Failed and error are deliberately different: failed means the code is wrong, error means the command could not be run at all, and calling the second one a test failure would send an agent hunting a bug that is not there."},"command":{"type":"string","description":"What actually ran, echoed here rather than read back from the settings, so a result looked at after the setting changed still says what produced it."},"startedAt":{"description":"When it began, in milliseconds.","type":"number"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"},"exitCode":{"description":"How the command exited.","type":"number"},"timedOut":{"description":"It was killed for taking too long rather than finishing.","type":"boolean"},"session":{"description":"The terminal it runs in, which is where to watch it. Absent where the sandbox has no terminals, in which case there is nothing to attach to.","type":"string"},"output":{"type":"string","description":"The end of what it printed, as plain text with the colour codes and redrawn progress lines resolved away. The end rather than the beginning, because a suite's verdict is at the end. Empty while it runs, and for one that was killed."},"repo":{"type":"string","description":"The repository this run is about, the same id the routes take."},"reason":{"description":"Why not, in git's own words: the last verdict line, for a row that has room for one line. The whole tail is `output`.","type":"string"},"refusedBy":{"description":"Who refused a failed push: this repository's own pre-push hook (what it printed is about the code), the remote (pull first), or the transport (credentials, network: retry). Absent while it runs and for a push that went.","type":"string","enum":["hook","remote","transport"]}},"required":["status","command","output","repo"],"additionalProperties":false}}}}}}},"/git/{repo}/push/cancel":{"post":{"operationId":"git.pushCancel","summary":"Stop the push","description":"Kills the run. It settles as cancelled; nothing that git had not already sent reaches the remote.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/files":{"get":{"operationId":"git.files","summary":"Every tracked path in the repo","description":"The flat list of files git tracks, which is what a file picker or a search box wants. Ignored and untracked files are not in it.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"files":{"type":"array","items":{"type":"string"},"description":"Every path git tracks, relative to the repository root. Ignored and untracked files are not here."}},"required":["files"],"additionalProperties":false}}}}}}},"/git/{repo}/file":{"get":{"operationId":"git.readFile","summary":"Read a file from the repo","description":"The contents of one file as it stands on disk. A path that climbs out of the repo is refused.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}},{"name":"path","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"The file to read, relative to the repository root."},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","description":"The path, as asked for."},"content":{"type":"string","description":"The file's contents as they stand on disk."}},"required":["path","content"],"additionalProperties":false}}}}}},"put":{"operationId":"git.writeFile","summary":"Write a file into the repo","description":"Replaces one file's contents, creating it and its parent folders if they are missing. Nothing is committed: the change shows up as pending work.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","minLength":1,"description":"Where to write, relative to the repository root. Missing folders are created."},"content":{"type":"string","description":"The file's whole new contents."}},"required":["path","content"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/git/{repo}/publish-file":{"post":{"operationId":"git.publishFile","summary":"Write, commit and push one file","description":"The three steps as a single call with a single answer, committing only the path you named and leaving any other pending work alone. Being on a side branch, having no remote and having no credentials are all reported rather than raised.","tags":["Git"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository. \"root\" is the workspace itself; anything else is a repository's folder relative to the workspace root, URL-encoded."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","minLength":1,"description":"Which file, relative to the repository."},"content":{"type":"string","description":"Its whole new contents."},"message":{"type":"string","minLength":1,"description":"The commit message."}},"required":["path","content","message"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","description":"Whether the whole thing went through."},"wrote":{"type":"boolean","description":"The file was written."},"committed":{"type":"boolean","description":"The commit was recorded."},"pushed":{"type":"boolean","description":"It reached the remote."},"branch":{"description":"Which branch it happened on.","type":"string"},"defaultBranch":{"description":"Which branch the repository considers its main one, so a caller can see it was on a side branch.","type":"string"},"reason":{"description":"Why it stopped where it did. Being on a side branch, having no remote and having no credentials are all reported here rather than raised.","type":"string"}},"required":["ok","wrote","committed","pushed"],"additionalProperties":false}}}}}}},"/diff/derived":{"get":{"operationId":"diff.derived","summary":"Both sides of a document's diff, as text","description":"A document, spreadsheet, presentation or notebook that changed, both versions rendered to markdown the way an agent reads them, so the change can be shown as tracked changes instead of two downloads. The side on disk reuses the shadow the sandbox already keeps; a past version is rendered from its bytes and kept by content hash, so the same version is never rendered twice. A side nothing can read says why.","tags":["Diffs"],"parameters":[{"name":"source","in":"query","required":true,"schema":{"anyOf":[{"type":"string","const":"working"},{"type":"string","const":"agent"},{"type":"string","const":"commit"},{"type":"string","const":"checkpoint"}]},"allowEmptyValue":true,"allowReserved":true},{"name":"repo","in":"query","required":false,"schema":{"type":"string","minLength":1,"description":"Which repository: \"root\" for the workspace itself, otherwise a repo id."},"allowEmptyValue":true,"allowReserved":true},{"name":"side","in":"query","required":false,"schema":{"type":"string","enum":["staged","unstaged","conflicted"],"description":"Which git side the row came from; a half-staged file is two different diffs."},"allowEmptyValue":true,"allowReserved":true},{"name":"path","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"The file, relative to the repo or scope the diff belongs to."},"allowEmptyValue":true,"allowReserved":true},{"name":"agent","in":"query","required":false,"schema":{"type":"string","minLength":1,"description":"The conversation whose work is under review."},"allowEmptyValue":true,"allowReserved":true},{"name":"sha","in":"query","required":false,"schema":{"type":"string","pattern":"^[0-9a-f]{4,64}$","description":"The commit, compared against its first parent."},"allowEmptyValue":true,"allowReserved":true},{"name":"snapshot","in":"query","required":false,"schema":{"type":"string","minLength":1,"description":"Which saved point."},"allowEmptyValue":true,"allowReserved":true},{"name":"scope","in":"query","required":false,"schema":{"type":"string","minLength":1,"description":"Which part of the workspace the path belongs to."},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"before":{"description":"The file as it was, rendered to text. Absent when it did not exist yet.","oneOf":[{"type":"object","properties":{"present":{"type":"boolean","const":true},"content":{"type":"string","description":"The side as markdown."},"deriver":{"type":"string","description":"Which reader made this text, with its version."},"notes":{"type":"array","items":{"type":"string"},"description":"Every cap and degradation the conversion hit, one line each."},"truncated":{"type":"boolean","description":"The rendering was longer than this response carries; only its start is here."}},"required":["present","content","deriver","notes","truncated"],"additionalProperties":false},{"type":"object","properties":{"present":{"type":"boolean","const":false},"reason":{"type":"string","description":"Why this side has no text: a format nothing reads, a broken file, a sandbox with no reader."}},"required":["present","reason"],"additionalProperties":false}]},"after":{"description":"The file as it is now, rendered to text. Absent when it was deleted.","oneOf":[{"type":"object","properties":{"present":{"type":"boolean","const":true},"content":{"type":"string","description":"The side as markdown."},"deriver":{"type":"string","description":"Which reader made this text, with its version."},"notes":{"type":"array","items":{"type":"string"},"description":"Every cap and degradation the conversion hit, one line each."},"truncated":{"type":"boolean","description":"The rendering was longer than this response carries; only its start is here."}},"required":["present","content","deriver","notes","truncated"],"additionalProperties":false},{"type":"object","properties":{"present":{"type":"boolean","const":false},"reason":{"type":"string","description":"Why this side has no text: a format nothing reads, a broken file, a sandbox with no reader."}},"required":["present","reason"],"additionalProperties":false}]}},"additionalProperties":false}}}}}}},"/history/snapshots":{"get":{"operationId":"history.list","summary":"Points you can go back to","description":"The saved states of the whole workspace, taken automatically as work happens. This is the timeline behind undoing a change that was never committed.","tags":["History"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"snapshots":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The saved point's id, which is what restoring and diffing take."},"at":{"type":"number","description":"When it was taken, in milliseconds."},"trigger":{"type":"string","enum":["turn","interval","pre-restore","restore","user"],"description":"What caused it. The automatic between-turn captures are a safety net and are not listed; they dissolve into the next visible point's differences."},"label":{"description":"What to call it. For one taken before a turn, that turn's prompt.","type":"string"}},"required":["id","at","trigger"],"additionalProperties":false},"description":"Every point you can go back to, newest first."}},"required":["snapshots"],"additionalProperties":false}}}}}}},"/history/diff":{"get":{"operationId":"history.diff","summary":"What changed since a saved point","description":"The files that differ between one saved point and the one before it, taking in everything that happened in between.","tags":["History"],"parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"Which saved point."},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"changes":{"type":"array","items":{"type":"object","properties":{"scope":{"type":"string","description":"Which part of the workspace the path belongs to: the workspace root, or one of the repositories inside it."},"path":{"type":"string","description":"The path, relative to that scope."},"status":{"type":"string","enum":["added","modified","deleted","type-changed"],"description":"What happened to it."}},"required":["scope","path","status"],"additionalProperties":false},"description":"Everything that differs between this saved point and the one before it."}},"required":["changes"],"additionalProperties":false}}}}}}},"/history/file-diff":{"get":{"operationId":"history.fileDiff","summary":"One file's before and after across a saved point","description":"Both sides of a single file at one point in the timeline.","tags":["History"],"parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"Which saved point."},"allowEmptyValue":true,"allowReserved":true},{"name":"scope","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"Which part of the workspace the path belongs to."},"allowEmptyValue":true,"allowReserved":true},{"name":"path","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"The file, relative to that scope."},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"before":{"description":"The whole file as it was. Absent when it did not exist yet, or when `partial` is set.","type":"string"},"after":{"description":"The whole file as it is now. Absent when it was deleted, or when `partial` is set.","type":"string"},"binary":{"description":"The file is not text, so neither side is sent.","type":"boolean"},"partial":{"description":"Set when the file was too large to send whole: what is sent instead of the two sides.","type":"object","properties":{"beforeBytes":{"description":"How big the before side is, in bytes. Absent when the file did not exist yet.","type":"integer","minimum":0,"maximum":9007199254740991},"afterBytes":{"description":"How big the after side is, in bytes. Absent when the file was deleted.","type":"integer","minimum":0,"maximum":9007199254740991},"patch":{"description":"The changed regions as unified-diff hunks (`@@` sections only). Absent when the change was too large to render even as a patch.","type":"string"},"more":{"description":"There were more changed regions than fit; the patch stops at a region boundary.","type":"boolean"}},"additionalProperties":false}},"additionalProperties":false}}}}}}},"/history/restore":{"post":{"operationId":"history.restore","summary":"Put the workspace back","description":"Returns every file to how it stood at a saved point. This restores the files; moving a branch is a different thing and lives with the git calls.","tags":["History"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"description":"Which saved point."}},"required":["id"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/chores":{"get":{"operationId":"chores.list","summary":"What maintenance the repos are asking for","description":"Every repo's standing evidence in one read: what the last measurement found and how old it is, the cheap signals that are always current, and what has already been decided about each.","tags":["Chores"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"repos":{"type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository."},"probes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","enum":["outdated","audit","knip","jscpd","ui","bundle","mutation"],"description":"Which measurement this is."},"state":{"type":"string","enum":["ok","unavailable","failed"],"description":"Whether the tool ran and reported, is not part of this repository at all, or broke. The middle one is not evidence of health: the check simply cannot be made here."},"ranAt":{"type":"number","description":"When it last finished, in milliseconds, which is what its age is measured from."},"tookMs":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"How long it took. Worth knowing before asking for it again: some of these run for minutes."},"facts":{"description":"What it found, including finding nothing, which is a real answer and the one that keeps a chore quiet.","oneOf":[{"type":"object","properties":{"id":{"type":"string","const":"outdated"},"packages":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"The dependency."},"current":{"type":"string","description":"What you are on."},"latest":{"type":"string","description":"What is published."},"kind":{"type":"string","enum":["major","minor","patch"],"description":"How far apart those are. This is not one number because forty patch releases behind is a morning's work and one major version is a project."},"section":{"type":"string","description":"Which part of the manifest declares it. A major version behind on a build-time tool is a different risk from one that ships."}},"required":["name","current","latest","kind","section"],"additionalProperties":false}}},"required":["id","packages"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string","const":"audit"},"advisories":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"The dependency it concerns."},"severity":{"type":"string","enum":["critical","high","moderate","low","info"],"description":"How bad it is said to be."},"title":{"type":"string","description":"What it is, in one line. No scoring vector and no reference list: those are for reading on the advisory's own page, and carrying them would put a kilobyte of prose per finding on every poll."},"patched":{"description":"Which versions fix it. Absent means no fix has been published, which is exactly when nothing should offer to upgrade and something should say so instead.","type":"string"},"dev":{"type":"boolean","description":"Whether it only reaches build-time tooling, which is a different problem from one that reaches what you ship."}},"required":["name","severity","title","dev"],"additionalProperties":false}}},"required":["id","advisories"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string","const":"knip"},"deadCode":{"type":"object","properties":{"files":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files nothing reaches."},"exports":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Exported things nothing uses."},"types":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Types nothing uses."},"dependencies":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Declared dependencies nothing imports."},"devDependencies":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The same, for build-time ones."},"sample":{"type":"array","items":{"type":"string"},"description":"A handful of the files, so a reader need not take the count on faith. Counts and a sample rather than the whole list, because an agent re-measures against the live tree anyway."}},"required":["files","exports","types","dependencies","devDependencies","sample"],"additionalProperties":false}},"required":["id","deadCode"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string","const":"jscpd"},"duplication":{"type":"object","properties":{"percentage":{"type":"number","description":"How much of the scanned code is duplicated. A share rather than a count, because a count grows with the repository and would mean something different every quarter."},"clones":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"How many duplicated stretches were found."},"top":{"type":"array","items":{"type":"object","properties":{"lines":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"How long the duplicated stretch is."},"first":{"type":"string","description":"One of the two places."},"second":{"type":"string","description":"The other."}},"required":["lines","first","second"],"additionalProperties":false},"description":"The largest of them."}},"required":["percentage","clones","top"],"additionalProperties":false}},"required":["id","duplication"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string","const":"ui"},"scan":{"type":"object","properties":{"components":{"type":"array","items":{"type":"string"},"description":"The interface's own source files, with tests, stories and generated output left out."},"bypasses":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The file."},"count":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991,"description":"How many times, in that file."}},"required":["path","count"],"additionalProperties":false},"description":"Where the design system was routed around and a value hard-coded instead. Counted per file, because a reader deciding what to open is served by a file and a number, not by eleven snippets."},"idioms":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Which outdated idiom. Looked up rather than listed here, so a sandbox one version behind can still report one this list has never heard of."},"files":{"type":"array","items":{"type":"string"},"description":"The files still on it."}},"required":["id","files"],"additionalProperties":false},"description":"Files still written the way their framework has since replaced."}},"required":["components","bypasses","idioms"],"additionalProperties":false}},"required":["id","scan"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string","const":"bundle"},"bundle":{"type":"object","properties":{"dir":{"type":"string","description":"Which folder was measured. Read from build output already on disk rather than by building, so this is sometimes a commit behind and never leaves anything in your working tree."},"totalBytes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The whole thing, raw."},"totalGzip":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The whole thing, compressed. The ratio between the two is the difference between big and big-and-incompressible, which are different problems."},"assets":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The file."},"bytes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Its raw size."},"gzip":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Its compressed size."}},"required":["path","bytes","gzip"],"additionalProperties":false},"description":"What is in it, piece by piece."}},"required":["dir","totalBytes","totalGzip","assets"],"additionalProperties":false}},"required":["id","bundle"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string","const":"mutation"},"mutation":{"type":"object","properties":{"score":{"type":"number","description":"The share of injected faults the suite caught. Not a coverage figure: coverage says a line ran, this says an assertion depended on it."},"killed":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Faults the suite caught."},"survived":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Faults it did not: code that can be broken with every test still passing."},"inconclusive":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Faults it never got a verdict on, because they would not compile or were configured out. Left out of the score entirely, since neither answer is known."},"survivors":{"type":"array","items":{"type":"object","properties":{"file":{"type":"string","description":"Where it is."},"line":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which line."},"mutator":{"type":"string","description":"What was changed, in the mutation tool's own vocabulary."},"replacement":{"type":"string","description":"What it became, so a reader can judge whether it matters without opening the file."}},"required":["file","line","mutator","replacement"],"additionalProperties":false},"description":"The surviving faults themselves. A percentage is a mood; a named line with the change that went unnoticed is a morning's work."}},"required":["score","killed","survived","inconclusive","survivors"],"additionalProperties":false}},"required":["id","mutation"],"additionalProperties":false}]},"reason":{"description":"Why it broke, quoted from the tool rather than summarised, or, when it never ran, what is missing. Never a sentence built from the check's own name, which would have an unmeasured check claiming there is nothing to measure.","type":"string"}},"required":["id","state","ranAt","tookMs"],"additionalProperties":false},"description":"The expensive measurements, served from a cache with an age on each rather than run on demand."},"signals":{"type":"object","properties":{"packages":{"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"Where the package lives."},"name":{"type":"string","description":"What it declares itself as."},"engines":{"description":"Which runtime versions it says it needs, verbatim.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"dependencies":{"type":"array","items":{"type":"string"},"description":"What it depends on."},"devDependencies":{"type":"array","items":{"type":"string"},"description":"What it needs only to build."},"documented":{"type":"boolean","description":"Whether it has a README, which in this workspace is what a package's own documentation is."}},"required":["dir","name","dependencies","devDependencies","documented"],"additionalProperties":false},"description":"Each package in the repository, as its own manifest declares it."},"shape":{"type":"object","properties":{"docs":{"type":"array","items":{"type":"string"},"description":"The repository's own architecture documents, when it has any. Their existence is the question: a repository with none has never been through the documentation flow at all."},"dockerfiles":{"type":"array","items":{"type":"string"},"description":"Container definitions in it."},"ci":{"type":"array","items":{"type":"string"},"description":"Pipeline definitions in it."},"lockfile":{"type":"boolean","description":"Whether dependencies are pinned to exact versions, which is what makes a security audit mean anything."},"packageManifest":{"type":"boolean","description":"Whether it is a JavaScript project at all. A Rust or Go repository has no majors to be behind on, and offering it those checks would be this surface guessing at what it is looking at."},"deps":{"type":"array","items":{"type":"string"},"description":"Every dependency name declared anywhere in the repository. Names rather than a verdict about which framework this is, because that judgement belongs to whatever reads this, not to a sandbox baked months ago."}},"required":["docs","dockerfiles","ci","lockfile","packageManifest","deps"],"additionalProperties":false,"description":"What the repository is made of, which decides whether a given chore is even a sensible question to ask of it."},"hotspots":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"commits":{"type":"number"},"adds":{"type":"number"},"dels":{"type":"number"},"complexity":{"type":"number"},"score":{"type":"number"},"latestMs":{"type":"number"}},"required":["path","commits","adds","dels","complexity","score","latestMs"],"additionalProperties":false},"description":"Files that change often and are complicated at once, capped tight: a chore only asks whether something has entered the top of the ranking."},"keyModules":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"exports":{"type":"number"}},"required":["path","exports"],"additionalProperties":false},"description":"The parts the rest of the code leans on most, capped the same way."},"totals":{"type":"object","properties":{"files":{"type":"number","description":"Files counted."},"symbols":{"type":"number","description":"Named things they export."},"complexity":{"type":"number","description":"Branch points added up."},"hotspots":{"type":"number","description":"How many files qualify as hotspots at all."}},"required":["files","symbols","complexity","hotspots"],"additionalProperties":false,"description":"The repository in numbers."},"indexed":{"type":"boolean","description":"Whether the index these rankings came from is finished. Nothing should act on a half-built one."}},"required":["packages","shape","hotspots","keyModules","totals","indexed"],"additionalProperties":false,"description":"The cheap facts, worked out fresh every time."}},"required":["repo","probes","signals"],"additionalProperties":false},"description":"Every repository's standing evidence. One answer for all of them, because a badge polls this on a timer and one request per repository is the kind of poll that shows up in a battery graph."},"ledger":{"type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository."},"chore":{"type":"string","description":"Which chore."},"ranAt":{"type":"number","description":"When it ran, in milliseconds."},"runId":{"type":"string","description":"The conversation that ran it, so its whole record can be opened."},"outcome":{"type":"string","enum":["acted","reported","clean"],"description":"What it concluded: it did something, it wrote something down, or it looked and found the finding to be false. That last one matters most, or the same turn starts again for ever."},"digest":{"type":"string","description":"A fingerprint of the evidence standing at the time. A chore whose evidence has since changed is due again on its own merits; one whose evidence has not stays quiet."},"snoozedUntil":{"description":"Not until then, in milliseconds. The chore stays visible and stays out of the badge. Different from switching it off, which is a setting.","type":"number"}},"required":["repo","chore","ranAt","runId","outcome","digest"],"additionalProperties":false},"description":"What has already been done about all of it."},"running":{"type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository."},"id":{"type":"string","enum":["outdated","audit","knip","jscpd","ui","bundle","mutation"],"description":"Which measurement."},"askedAt":{"type":"number","description":"When it was asked for, in milliseconds, so one still waiting can say how long it has waited."},"startedAt":{"description":"When it actually began. Absent while it is queued behind another, which is a real and common state: there is one lane for the whole sandbox.","type":"number"}},"required":["repo","id","askedAt"],"additionalProperties":false},"description":"What is being measured right now and what is waiting behind it. Part of this read rather than a route of its own, because a screen that had to ask twice would show the two halves disagreeing."},"node":{"type":"string","description":"The runtime version this sandbox is actually running, read off the process rather than off a manifest, because what is installed is the fact that matters and a declared range is a wish."}},"required":["repos","ledger","running","node"],"additionalProperties":false}}}}}}},"/chores/probe":{"post":{"operationId":"chores.probe","summary":"Measure one repo again now","description":"Re-runs a single check without waiting for it to go stale. Answers immediately: the work happens in the background and the result turns up in the next read, because some of these sweeps outlive any sane request.","tags":["Chores"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"repo":{"type":"string","minLength":1,"description":"Which repository."},"id":{"type":"string","enum":["outdated","audit","knip","jscpd","ui","bundle","mutation"],"description":"Which measurement to retake, ahead of its usual schedule."}},"required":["repo","id"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/chores/ledger":{"post":{"operationId":"chores.record","summary":"Record a verdict, or snooze one","description":"Writes what somebody concluded about one repo's chore, replacing the previous verdict. A chore has one current answer, not a growing pile of times it was fine.","tags":["Chores"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository."},"chore":{"type":"string","description":"Which chore."},"ranAt":{"type":"number","description":"When it ran, in milliseconds."},"runId":{"type":"string","description":"The conversation that ran it, so its whole record can be opened."},"outcome":{"type":"string","enum":["acted","reported","clean"],"description":"What it concluded: it did something, it wrote something down, or it looked and found the finding to be false. That last one matters most, or the same turn starts again for ever."},"digest":{"type":"string","description":"A fingerprint of the evidence standing at the time. A chore whose evidence has since changed is due again on its own merits; one whose evidence has not stays quiet."},"snoozedUntil":{"description":"Not until then, in milliseconds. The chore stays visible and stays out of the badge. Different from switching it off, which is a setting.","type":"number"}},"required":["repo","chore","ranAt","runId","outcome","digest"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/panels":{"get":{"operationId":"panels.list","summary":"Repos you can run and preview","description":"Every repo with whether its dev server is up and what the sandbox worked out about its contents.","tags":["Panels"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"panels":{"type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository."},"hasPanel":{"type":"boolean","description":"Whether it has anything runnable at all."},"running":{"type":"boolean","description":"Whether the sandbox has it running."},"installed":{"type":"boolean","description":"Whether its dependencies are installed, which is what decides whether a start takes seconds or an install first."},"launch":{"description":"Where a start the sandbox is running has got to: its shell coming up, installing, its dev command running with nothing listening yet, or exited back to a prompt. Absent when nothing is starting and once it serves.","type":"string","enum":["launching","installing","starting","exited"]},"healthy":{"type":"boolean","description":"Whether anything it owns is actually answering. A different question: a server still installing is running and not yet healthy, and one somebody started by hand is healthy without the sandbox running it."},"port":{"description":"The port the sandbox told it to use. What it actually bound is below, and for a repository that pins its own ports those are different numbers.","type":"number"},"servers":{"type":"array","items":{"type":"object","properties":{"port":{"type":"number","description":"The port it is listening on, which is what forwarding it takes."},"url":{"type":"string","description":"Where it answers, with the right scheme: a server on its own certificate is served over https."},"dir":{"description":"Which part of the repository it belongs to, which for a repository whose dev command fans out is the only thing telling them apart.","type":"string"},"session":{"description":"The terminal it runs in: the sandbox's when it started it, yours when you did, and absent when nothing here owns it, which is the case worth designing for.","type":"string"}},"required":["port","url"],"additionalProperties":false},"description":"Every server this repository is really serving, found by looking at what is listening. Empty when nothing answers."},"previewUrl":{"description":"Where to open it from outside, present only while that address really serves it. Absent on a sandbox with no outside address.","type":"string"},"role":{"description":"Which of the workspace's three fixed roles this repository fills. Absent for one that was simply cloned in.","type":"string","enum":["intent","desired-state","app"]},"deployConfig":{"type":"boolean","description":"It declares infrastructure."},"desiredState":{"type":"boolean","description":"That declaration has been resolved at least once."},"directoryUi":{"type":"boolean","description":"It carries a small interface of its own."},"monorepo":{"type":"boolean","description":"It holds several packages."},"tests":{"type":"boolean","description":"It has tests that can be run."},"userStories":{"type":"boolean","description":"It carries stories an agent could test the running app against. The one fact here that says nothing about the language."},"docs":{"type":"boolean","description":"It carries generated architecture documentation."}},"required":["repo","hasPanel","running","installed","healthy","servers","deployConfig","desiredState","directoryUi","monorepo","tests","userStories","docs"],"additionalProperties":false},"description":"One entry per repository, worked out in a single pass so nothing has to walk the workspace file by file."}},"required":["panels"],"additionalProperties":false}}}}}}},"/panels/{repo}/start":{"post":{"operationId":"panels.start","summary":"Start a repo's dev server","description":"Brings the repo's own runnable app up in a terminal you can attach to, so its preview address starts answering.","tags":["Panels"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/panels/{repo}/stop":{"post":{"operationId":"panels.stop","summary":"Stop a repo's dev server","description":"Shuts it down and frees the port.","tags":["Panels"],"parameters":[{"name":"repo","in":"path","required":true,"schema":{"type":"string","description":"Which repository."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/ports":{"get":{"operationId":"ports.list","summary":"What is listening inside the sandbox","description":"Every port something is answering on, and whether each one is reachable from outside.","tags":["Ports"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ports":{"type":"array","items":{"type":"object","properties":{"port":{"type":"number","description":"The port number."},"host":{"type":"string","enum":["127.0.0.1","::1"],"description":"Which loopback address it actually answers on. Some tools bind only one of the two, and anything dialling it has to know which."},"forwardable":{"type":"boolean","description":"Whether it can be exposed at all. Some listeners answer only at their own address and nowhere else; those are listed for honesty and refused for forwarding."},"kind":{"type":"string","enum":["workspace","system"],"description":"Whether somebody's own work put it there, or the sandbox's own machinery did. Only the first kind is worth previewing."},"title":{"type":"string","description":"What a person would call it. Always present: a listener nothing can explain is still named, because the button beside it publishes the port to the internet."},"purpose":{"type":"string","description":"One sentence about what it is for, including when the honest answer is that nothing could work it out."},"origin":{"type":"string","enum":["terminal","agent","panel","extension","container","sandbox","unknown"],"description":"Who put it there, which is the question somebody is really asking: mine, my agent's, or the box's own."},"pid":{"description":"The process holding it. Absent when nothing could be matched to the socket.","type":"number"},"command":{"description":"The command behind it, as it was run. Absent only when nothing could be attributed at all.","type":"string"},"cwd":{"description":"Where it is running from, which is how a port gets attributed to a repository.","type":"string"},"session":{"description":"The terminal it came from, to watch it in or stop it from. Absent when nothing in its ancestry is one, which is the honest \"you cannot reach this from here\".","type":"string"},"job":{"description":"The background job an agent left running for you on this port, when that is what answers here.","type":"object","properties":{"conversationId":{"type":"string","description":"The conversation whose turn left it running."},"jobId":{"type":"string","description":"The job, as that conversation's card names it; stopping it stops this port."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any."}},"required":["conversationId","jobId","label"],"additionalProperties":false},"forwarded":{"type":"boolean","description":"Whether it is currently reachable from outside."},"previewUrl":{"description":"Where to open it. Present only while forwarded, and only on a sandbox that has an outside address.","type":"string"}},"required":["port","host","forwardable","kind","title","purpose","origin","forwarded"],"additionalProperties":false},"description":"Everything listening inside the sandbox right now, read fresh each time rather than from a register the sandbox keeps."}},"required":["ports"],"additionalProperties":false}}}}}}},"/ports/forward":{"post":{"operationId":"ports.forward","summary":"Make a port reachable","description":"Gives one port an address on the outside. Asking twice is harmless: the second call hands back the address the first one made.","tags":["Ports"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"port":{"type":"integer","minimum":1,"maximum":65535,"description":"Which port."}},"required":["port"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"previewUrl":{"description":"Where it can now be reached. Absent on a sandbox with no outside address, where the mapping exists but has no public name.","type":"string"}},"additionalProperties":false}}}}}}},"/ports/unforward":{"post":{"operationId":"ports.unforward","summary":"Stop exposing a port","description":"Frees the slot at once. The address keeps resolving; it simply stops leading anywhere.","tags":["Ports"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"port":{"type":"integer","minimum":1,"maximum":65535,"description":"Which port."}},"required":["port"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/areas":{"get":{"operationId":"areas.list","summary":"The named parts of the workspace","description":"Each area with the folders it admits. Access is granted in these rather than in folder lists per person, so widening what a team sees is one edit here instead of one edit per member.","tags":["Areas"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"areas":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The area's id, the name a member row points at."},"label":{"description":"What to call it on screen. Absent falls back to the id, which somebody chose anyway.","type":"string","maxLength":60},"brief":{"description":"What this part of the workspace is, in one line, so whoever grants it can tell what they are handing over.","type":"string","maxLength":200},"folders":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":200},"description":"The folders it admits, workspace-relative. At least one: an area naming nothing would be a grant with no reader, and the way to grant everything is to name no area at all."}},"required":["id","folders"],"additionalProperties":false},"description":"Every named part of the workspace this sandbox grants access in."}},"required":["areas"],"additionalProperties":false}}}}}},"post":{"operationId":"areas.save","summary":"Create or edit an area","description":"Writes the whole area; sending an id that exists edits it. Editing the folders of an area people already hold changes what those people see on their next request, which is why this is the sandbox owner's to do and why the file it writes is tracked and reviewable.","tags":["Areas"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The area's id, the name a member row points at."},"label":{"description":"What to call it on screen. Absent falls back to the id, which somebody chose anyway.","type":"string","maxLength":60},"brief":{"description":"What this part of the workspace is, in one line, so whoever grants it can tell what they are handing over.","type":"string","maxLength":200},"folders":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":200},"description":"The folders it admits, workspace-relative. At least one: an area naming nothing would be a grant with no reader, and the way to grant everything is to name no area at all."}},"required":["id","folders"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/areas/{id}":{"delete":{"operationId":"areas.remove","summary":"Delete an area","description":"Removes the name and the folders behind it. Refused while a member still points at it, since nobody chose what such a row should then mean; move them onto another area first, or off areas entirely.","tags":["Areas"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"Which area."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/personas":{"get":{"operationId":"personas.list","summary":"The characters an agent can wear","description":"Each persona with the connected accounts it speaks for, what a conversation wearing it is allowed to do, and where it works.","tags":["Personas"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"personas":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The persona's id."},"label":{"description":"What to call it on screen. Absent falls back to the id, which somebody chose anyway.","type":"string","maxLength":60},"capabilities":{"maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"description":"Which connected accounts are its hands. Named individually rather than by site, because two accounts on one site is the whole problem this solves. Naming one that is not connected yet is not an error: it is a card describing an account this sandbox has still to sign into."},"brief":{"description":"What this persona is for, in one line. A new chat is routed onto a persona by this sentence, and the Personas page shows it under the name.","type":"string","maxLength":200},"powers":{"description":"What a conversation wearing it may do. Absent means the full toolbox, so a card written before this existed behaves exactly as it did.","type":"object","properties":{"files":{"default":"write","description":"What it may do with files: nothing, look and search, or also create and change.","type":"string","enum":["none","read","write"]},"shell":{"default":true,"description":"Whether it may run commands, and with them the terminals, the test runs and every tool on the image. The switch the strength of the others depends on.","type":"boolean"},"code":{"default":true,"description":"Whether it may write and run a script rather than a command line. Its fence is real where the shell's is not: reads and writes follow the files answer, and it can start no other program unless commands are allowed too. The one stated gap is that the fence cannot cut the network.","type":"boolean"},"web":{"default":true,"description":"Whether it may fetch a page or run a search.","type":"boolean"},"browser":{"default":true,"type":"boolean"},"delegate":{"default":true,"type":"boolean"},"sandbox":{"default":true,"type":"boolean"},"connectors":{"maxItems":100,"type":"array","items":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"}},"devices":{"maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"}},"mcp":{"maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"}},"extensions":{"description":"Which extensions' own agent tools and agent plugin (skills, commands, subagents) it gets, by extension id. Absent means every enabled one; empty means none. The tools an extension serves for a connected card follow the connectors list instead.","maxItems":100,"type":"array","items":{"type":"string","minLength":1,"maxLength":121,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_.-]*$"}}},"required":["files","shell","code","web","browser","delegate","sandbox"],"additionalProperties":false},"workspace":{"description":"Where it works. Absent means the whole workspace.","type":"object","properties":{"startIn":{"description":"Which folder a conversation opens in.","type":"string","maxLength":200},"folders":{"description":"Which folders it may touch at all. Absent means the whole workspace.","maxItems":50,"type":"array","items":{"type":"string","minLength":1}}},"additionalProperties":false},"context":{"description":"Which part of the workspace a conversation wearing it carries: the repositories its checkout holds. Absent means every repository.","type":"object","properties":{"repos":{"maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":200},"description":"Which nested repositories a conversation on this persona carries, by workspace-relative path. The workspace itself is always carried; empty means the workspace alone."}},"required":["repos"],"additionalProperties":false},"briefing":{"description":"Which of the notes the sandbox prepends to every message this card's conversations do without. Absent means all of them, which is what a card written before this existed keeps.","type":"object","properties":{"omit":{"maxItems":20,"type":"array","items":{"type":"string","enum":["map","context","skills","search","delegation","checks","dependencies","repoSync","handoff"]},"description":"Which of the notes the sandbox prepends to each message a conversation on this persona does NOT get. Everything not named here is sent as usual; the notes that keep a turn inside its own branch or explain a missing account cannot be named at all."}},"required":["omit"],"additionalProperties":false},"models":{"description":"Which models a conversation wearing it runs on, tried in order. Absent means whatever the chat or the job would have run on anyway; a model chosen for the turn itself always wins.","maxItems":10,"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves this work."},"model":{"type":"string","minLength":1,"description":"Which of its models. Both halves, because a model name only means anything to the provider that serves it."},"effort":{"description":"How hard this model should think, where it offers a choice. Leave it out to take the model's own default.","type":"string"},"thinking":{"description":"Whether this model reasons before it answers, where that is a choice it offers.","type":"boolean"},"fast":{"description":"Ask for this model's work at a higher rate for a higher price. A request rather than a promise.","type":"boolean"},"harness":{"description":"Which agentic loop runs it. Leave it out to use the provider's own.","type":"string","enum":["native","claude-code"]}},"required":["provider","model"],"additionalProperties":false}},"systemPromptMode":{"type":"string","enum":["intentic","claude","custom"]}},"required":["id","capabilities"],"additionalProperties":false},"description":"The characters an agent can wear."},"connected":{"type":"array","items":{"type":"string"},"description":"Which accounts are actually connected right now, so a persona naming one that has since been disconnected can be shown as broken rather than as working."}},"required":["personas","connected"],"additionalProperties":false}}}}}},"post":{"operationId":"personas.save","summary":"Create or edit a persona","description":"Writes the whole card; sending an id that exists edits it. Nothing is connected, installed or spent by saving one, because a persona only records a decision about accounts that already exist. It is stored as a file you can equally well edit by hand, which is why this writes the card whole rather than patching a field: a round trip through a screen should leave a change a reviewer recognises.","tags":["Personas"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The persona's id."},"label":{"description":"What to call it on screen. Absent falls back to the id, which somebody chose anyway.","type":"string","maxLength":60},"capabilities":{"maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"description":"Which connected accounts are its hands. Named individually rather than by site, because two accounts on one site is the whole problem this solves. Naming one that is not connected yet is not an error: it is a card describing an account this sandbox has still to sign into."},"brief":{"description":"What this persona is for, in one line. A new chat is routed onto a persona by this sentence, and the Personas page shows it under the name.","type":"string","maxLength":200},"powers":{"description":"What a conversation wearing it may do. Absent means the full toolbox, so a card written before this existed behaves exactly as it did.","type":"object","properties":{"files":{"default":"write","description":"What it may do with files: nothing, look and search, or also create and change.","type":"string","enum":["none","read","write"]},"shell":{"default":true,"description":"Whether it may run commands, and with them the terminals, the test runs and every tool on the image. The switch the strength of the others depends on.","type":"boolean"},"code":{"default":true,"description":"Whether it may write and run a script rather than a command line. Its fence is real where the shell's is not: reads and writes follow the files answer, and it can start no other program unless commands are allowed too. The one stated gap is that the fence cannot cut the network.","type":"boolean"},"web":{"default":true,"description":"Whether it may fetch a page or run a search.","type":"boolean"},"browser":{"default":true,"type":"boolean"},"delegate":{"default":true,"type":"boolean"},"sandbox":{"default":true,"type":"boolean"},"connectors":{"maxItems":100,"type":"array","items":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"}},"devices":{"maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"}},"mcp":{"maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"}},"extensions":{"description":"Which extensions' own agent tools and agent plugin (skills, commands, subagents) it gets, by extension id. Absent means every enabled one; empty means none. The tools an extension serves for a connected card follow the connectors list instead.","maxItems":100,"type":"array","items":{"type":"string","minLength":1,"maxLength":121,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_.-]*$"}}}},"workspace":{"description":"Where it works. Absent means the whole workspace.","type":"object","properties":{"startIn":{"description":"Which folder a conversation opens in.","type":"string","maxLength":200},"folders":{"description":"Which folders it may touch at all. Absent means the whole workspace.","maxItems":50,"type":"array","items":{"type":"string","minLength":1}}}},"context":{"description":"Which part of the workspace a conversation wearing it carries: the repositories its checkout holds. Absent means every repository.","type":"object","properties":{"repos":{"maxItems":50,"type":"array","items":{"type":"string","minLength":1,"maxLength":200},"description":"Which nested repositories a conversation on this persona carries, by workspace-relative path. The workspace itself is always carried; empty means the workspace alone."}},"required":["repos"]},"briefing":{"description":"Which of the notes the sandbox prepends to every message this card's conversations do without. Absent means all of them, which is what a card written before this existed keeps.","type":"object","properties":{"omit":{"maxItems":20,"type":"array","items":{"type":"string","enum":["map","context","skills","search","delegation","checks","dependencies","repoSync","handoff"]},"description":"Which of the notes the sandbox prepends to each message a conversation on this persona does NOT get. Everything not named here is sent as usual; the notes that keep a turn inside its own branch or explain a missing account cannot be named at all."}},"required":["omit"]},"models":{"description":"Which models a conversation wearing it runs on, tried in order. Absent means whatever the chat or the job would have run on anyway; a model chosen for the turn itself always wins.","maxItems":10,"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves this work."},"model":{"type":"string","minLength":1,"description":"Which of its models. Both halves, because a model name only means anything to the provider that serves it."},"effort":{"description":"How hard this model should think, where it offers a choice. Leave it out to take the model's own default.","type":"string"},"thinking":{"description":"Whether this model reasons before it answers, where that is a choice it offers.","type":"boolean"},"fast":{"description":"Ask for this model's work at a higher rate for a higher price. A request rather than a promise.","type":"boolean"},"harness":{"description":"Which agentic loop runs it. Leave it out to use the provider's own.","type":"string","enum":["native","claude-code"]}},"required":["provider","model"]}},"systemPromptMode":{"type":"string","enum":["intentic","claude","custom"]}},"required":["id","capabilities"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/personas/{id}":{"delete":{"operationId":"personas.remove","summary":"Delete a persona","description":"Takes away the character, never the accounts: every login it named stays connected. Its own prompt and skills go with it, since a folder nothing can reach is worse than deleting what somebody just asked to delete. Anything still pointed at it goes quiet rather than falling back to speaking as everyone.","tags":["Personas"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"Which persona."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/personas/{id}/kit":{"get":{"operationId":"personas.kit","summary":"What one persona carries","description":"The instructions this persona is given and the skills only its conversations can reach. A different question from what the agent knows generally, with a different answer.","tags":["Personas"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"Which persona."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string","description":"What this persona is told, on top of everything else. Empty means it simply follows the sandbox's own instructions."},"skills":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"The skill's name."},"description":{"type":"string","description":"What it is for."}},"required":["name","description"],"additionalProperties":false},"description":"Skills only this persona's conversations can reach. A different question from what the agent knows generally, with a different answer."}},"required":["prompt","skills"],"additionalProperties":false}}}}}}},"/personas/{id}/prompt":{"post":{"operationId":"personas.savePrompt","summary":"Write a persona's instructions","description":"Sets what this persona is told. Saving an empty one removes it entirely rather than storing a blank, so the persona simply falls back to the sandbox's own instructions.","tags":["Personas"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"Which persona."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string","maxLength":20000,"description":"What to tell this persona. Sending an empty one removes it entirely rather than storing a blank, so the persona falls back to the sandbox's own instructions."}},"required":["prompt"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/personas/{id}/skills/read":{"get":{"operationId":"personas.readSkill","summary":"Read one of a persona's skills","description":"The full text of a single skill belonging to this persona.","tags":["Personas"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"Which persona."}},{"name":"name","in":"query","required":true,"schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$","description":"Which skill."},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"The skill's name."},"description":{"type":"string","description":"What it is for."},"body":{"type":"string","description":"The skill itself, in full."}},"required":["name","description","body"],"additionalProperties":false}}}}}}},"/personas/{id}/skills":{"post":{"operationId":"personas.saveSkill","summary":"Write one of a persona's skills","description":"Creates or replaces a skill by name. There is nothing to switch on: a persona's skill is available exactly when that persona is worn, which is what belonging to it has to mean.","tags":["Personas"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"Which persona."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$","description":"What to call it. Saving over an existing name rewrites it, which is also how one is renamed."},"description":{"type":"string","minLength":1,"maxLength":1024,"description":"What it is for, which is what the agent reads to decide whether to reach for it."},"body":{"type":"string","minLength":1,"description":"The skill itself."}},"required":["name","description","body"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/personas/{id}/skills/remove":{"post":{"operationId":"personas.removeSkill","summary":"Delete one of a persona's skills","description":"Removes a single skill from this persona and leaves the rest of its kit alone.","tags":["Personas"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"Which persona."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$","description":"Which skill."}},"required":["name"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/skills":{"get":{"operationId":"skills.list","summary":"What the agent knows how to do","description":"Every skill available here and whether it is switched on, joined from all the places they come from: the owner's own, the settings, plugins a connection installed, folders inside extensions, and persona kits.","tags":["Skills"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Its handle, which reading and deleting take. A skill of your own is simply its name; one belonging to something else is qualified, because two packages may each ship a review."},"name":{"type":"string","description":"Its name."},"description":{"type":"string","description":"What it is for, which is the line the agent reads to decide whether to reach for it. Empty when the skill declares none, which is worth showing as the blank it is: a skill with no description is rarely picked."},"origin":{"type":"string","enum":["builtin","own","capability","extension","plugin","persona","dropped"],"description":"Where it came from."},"owner":{"description":"Who ships it, as the row would name them.","type":"string"},"enabled":{"type":"boolean","description":"Whether the agent can reach it."},"switchable":{"type":"boolean","description":"Whether this surface can switch it. Everything else is on because its extension or its plugin is, and a switch here that silently did nothing would be worse than none, so the row names its owner instead."},"editable":{"type":"boolean","description":"Whether it can be rewritten here. Your own only: editing somebody else's in place would be undone the next time the thing that ships it catches up."},"removable":{"type":"boolean"}},"required":["id","name","description","origin","enabled","switchable","editable","removable"],"additionalProperties":false}}}}}}},"post":{"operationId":"skills.save","summary":"Write a skill","description":"Creates or rewrites a skill by name. A new one starts switched on, because you wrote it in order to use it; rewriting one you switched off leaves it off. Renaming is saving under the new name and deleting the old.","tags":["Skills"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$","description":"What to call it. Saving over an existing name rewrites it, which is also how one is renamed."},"description":{"type":"string","minLength":1,"maxLength":1024,"description":"What it is for, which is what the agent reads to decide whether to reach for it."},"body":{"type":"string","minLength":1,"description":"The skill itself."}},"required":["name","description","body"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/skills/read":{"get":{"operationId":"skills.read","summary":"Read one skill","description":"The full text of a single skill. The name travels in the query rather than the address, because a name can carry the owner it came from and that will not fit in a path.","tags":["Skills"],"parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"Which skill. It travels in the query rather than the address, because an id can name the owner it came from and that will not fit in a path."},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The skill's id, which can carry the owner it came from."},"name":{"type":"string","description":"Its name."},"body":{"type":"string","description":"The instructions themselves, as written."}},"required":["id","name","body"],"additionalProperties":false}}}}}}},"/skills/switch":{"post":{"operationId":"skills.switch","summary":"Switch one of your own skills on or off","description":"Off takes the agent's copy away and keeps your text; on writes the copy back from it. Built-in tools are switched in the agent settings instead, and nothing else has a switch.","tags":["Skills"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$","description":"Which skill of your own to switch."},"on":{"type":"boolean","description":"On writes the agent's copy from the stored text; off removes that copy and keeps the text."}},"required":["name","on"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/skills/remove":{"post":{"operationId":"skills.remove","summary":"Delete a skill","description":"Removes the text and the agent's copy in one step, so a screen never has to sequence two calls and never leaves one half done.","tags":["Skills"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$","description":"Which skill to delete. The stored text and the agent's copy go together, so nothing is left half done."}},"required":["name"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/extensions":{"get":{"operationId":"extensions.list","summary":"Installed extensions","description":"Every extension installed here, resolved to the manifest the owner approved, which is what the app boots its extension host from. The code itself is served separately, because raw script bytes are not a JSON answer.","tags":["Extensions"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"extensions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":121,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_.-]*$","description":"The extension's id."},"manifest":{"type":"object","properties":{"$schema":{"description":"The authoring schema, for editor completion and validation. Nothing at runtime reads it.","type":"string"},"publisher":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"name":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"version":{"type":"string","minLength":1,"description":"Your own semver, display and identity only. The installed code's identity is the pinned commit sha."},"category":{"description":"Which section of the Extensions tab this sits under: a grouping by what it is FOR, which cannot be derived from what it contributes. A section this app has never heard of lands in 'Other' rather than failing to install.","type":"string","minLength":1},"art":{"description":"This extension's own mark, as a complete SVG document inline: the tier an author controls fully. Give it a viewBox and let it fill its own square edge to edge; it is drawn as the tile, not as a glyph on a plate. Kept as readable SVG text (not base64) so a registry reviewer can see what they are publishing, drawn inert so it cannot script the page, and capped at 4 KB. Anything that does not parse as SVG falls back to `logo`, then `icon`, then initials.","type":"string","maxLength":4096},"logo":{"description":"A simple-icons slug, fetched from a CDN: right for standing in for somebody else's product. Add a \"/<hex>\" suffix to force a colour for a mark that vanishes against the surface it lands on. Unreachable in an offline sandbox, so it falls back to `icon`, then to initials.","type":"string"},"icon":{"description":"A name from the host's own icon set, drawn when no simple-icons slug fits. It ships in the image, follows the theme and costs no request: what actually carries a first-party extension. An unknown name falls back to initials rather than to a hole.","type":"string"},"engines":{"type":"object","properties":{"intentic":{"type":"string","minLength":1}},"required":["intentic"],"additionalProperties":false,"description":"A semver range over the host's extension API version, checked before your code is activated."},"entry":{"description":"Repo-relative path of your prebuilt single-file ESM bundle, built with `vue` and `@intentic/extension-api` as externals. Absent ⇒ an extension with no UI.","type":"string","minLength":1,"power":{"key":"entry","sentence":"runs a UI bundle in your browser"}},"server":{"description":"Repo-relative path of your prebuilt single-file node ESM server bundle, exporting `activateServer`. Served under your own route namespace, which the daemon proxies. Nothing is provided at runtime but node builtins, so bundle everything else in. Absent ⇒ no backend.","type":"string","minLength":1,"power":{"key":"server","sentence":"runs a backend bundle inside the daemon's extension host"}},"permissions":{"description":"How far this extension may reach into the daemon, as \"<METHOD> <path-glob>\" entries where `*` matches one path segment: e.g. \"GET /panels\", \"POST /panels/*/start\". The install dialog shows these, the host refuses anything undeclared, and the usage ledger records which were actually earned.","type":"object","properties":{"sandbox":{"description":"Daemon routes your UI half may call. Your own backend namespace needs no entry: its backend is your own code.","type":"array","items":{"type":"string","power":{"key":"sandbox:${value}","sentence":"its UI calls the sandbox route ${value}"}}},"daemon":{"description":"Daemon routes your SERVER half may call. Separate from `sandbox` because the two halves run as different principals: the UI as the owner's session, the backend as a minted per-extension token, so a grant to one must never quietly widen the other.","type":"array","items":{"type":"string","power":{"key":"daemon:${value}","sentence":"its backend calls the daemon route ${value}"}}}},"additionalProperties":false},"contributes":{"type":"object","properties":{"views":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"label":{"type":"string","minLength":1,"description":"The name shown on the tile or tab. The manifest's value wins over the one passed at registration."},"surface":{"type":"string","enum":["rail","directory","sandbox"],"description":"Where it appears. `rail` is a tile in the global left rail; `directory` is a panel opened from a repo in the Workspace tree; `sandbox` is a tab on the Sandbox hub, for a view whose subject is the box rather than the work."},"badge":{"description":"Allow this view to say something on its tile: a count, a glyph, or that work is running there. Declared because a badge interrupts from every other screen in the app; leave it out and any badge the extension registers is dropped.","power":{"key":"view-badge:${id}","sentence":"may badge the \"${label}\" tile from any screen"},"type":"boolean"}},"required":["id","label","surface"],"additionalProperties":false,"power":{"key":"view:${id}","sentence":"a ${surface} view \"${label}\""}},"description":"Sidebar elements this extension may register at runtime. Each entry reserves an id and a surface; the extension supplies the component with api.views.register, and the host refuses any registration this list does not cover."},"files":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","minLength":1,"description":"Workspace-root-relative, forward-slash, matched by prefix, so one entry covers an exact file (`.intentic/config/automations.json`), a directory (`.intentic/config/approvals/`, with the trailing slash so it cannot match a sibling file) or a name family (`.intentic/environment.`). Not a glob."},"invalidates":{"minItems":1,"type":"array","items":{"type":"string","minLength":1},"description":"The query keys this path makes stale, the first element of your own api.sandbox.key(...) keys. Keep both this and the path as narrow as the view actually needs: a broad prefix costs every connected browser a refetch on every matching write."}},"required":["path","invalidates"],"additionalProperties":false,"power":{"key":"files:${path}","sentence":"is told when ${path} changes"}},"description":"Which workspace files back your views, so the daemon's file watcher can tell the browser they went stale instead of you polling for it. The agent edits the workspace out of band from every HTTP route, and this push is the only thing that can notice."},"viewers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"extensions":{"minItems":1,"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+$"},"description":"Bare file extensions, no dot: e.g. [\"docx\", \"xlsx\"]."},"fetch":{"type":"string","enum":["text","blob","url","path"],"description":"How much of the file the host hands you. `text` for a format that is text (svg, a subtitle track). `blob` for one that must be parsed end to end before any of it shows (a .docx, a spreadsheet), bounded by the daemon's raw-read cap. `url` for anything range-read rather than parsed (audio, video): your component gets a streaming URL to point an element at, never the bytes. `path` for a viewer whose own backend reads and writes the file: you get the workspace path and the scope it is viewed in, plus `readOnly` where the window may not write the file and, in a desktop app's local window, a `text` slot holding the document's text for while your own view can't show it. Emit `dirty` (a boolean) whenever you start or stop holding edits the file doesn't have, so a window closing over them can ask first."},"edit":{"description":"Whether this viewer writes the file back. An editing viewer is chosen over a render-only viewer claiming the same extension, whatever order the two activated in.","type":"boolean"},"compare":{"description":"Whether this viewer also draws two versions of a file as one, with what changed marked in place: its registration then carries a `compare` component the host renders with `before` and `after` blobs. Only for `fetch: \"blob\"`.","type":"boolean"}},"required":["id","extensions","fetch"],"additionalProperties":false,"power":{"key":"viewer:${id}","sentence":"${edit?opens and edits:opens}${compare? and compares:} .${extensions|, .} files (${fetch})"}},"description":"File formats this extension can render. The host resolves an opened file to your viewer by its extension, fetches the content, and renders your component with it: you keep none of the fetch lifecycle and none of the daemon credentials."},"documents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"label":{"type":"string","minLength":1,"description":"The family's name, shown in the install dialog beside your other contributions. Per-row wording stays with the provider, which is the only thing that knows what it found."}},"required":["id","label"],"additionalProperties":false,"power":{"key":"document:${id}","sentence":"marks workspace directories (\"${label}\")"}},"description":"Per-directory documents this extension can offer. Your provider marks the rows in the Workspace tree it has something to say about, and the host opens your component as a tab."},"sideViews":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"label":{"type":"string","minLength":1,"description":"What one of these is called (\"CI run\"), shown in the install dialog and on a tab whose own title could not be read. Each tab's title is the extension's to say for the thing it shows."},"links":{"description":"Allow this side view to take links the chat renders: a link it recognises (its registration's `claim`) opens beside the chat instead of in a new browser tab. Declared because it changes what the reader's click does; leave it out and the host never asks.","power":{"key":"side-view-links:${id}","sentence":"opens links it recognises as \"${label}\" beside the chat"},"type":"boolean"}},"required":["id","label"],"additionalProperties":false,"power":{"key":"side-view:${id}","sentence":"shows \"${label}\" in the side panel"}},"description":"Things this extension can show in the editor's side panel, one input at a time, beside whatever the reader is doing. Each entry reserves an id; the extension supplies the component with api.sideViews.register and opens one with api.sideViews.open, and the host refuses any id this list does not cover."},"commands":{"type":"array","items":{"type":"object","properties":{"command":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*(\\.[a-z0-9][a-z0-9-]*)+$"},"title":{"type":"string","minLength":1,"description":"What the command palette shows. The manifest's value wins over the one passed at registration."},"category":{"description":"What the command acts on (\"Deployments\", \"Knowledge\"), drawn ahead of the title as \"Category: Title\" and searched with it. Use the extension's own name so its commands group together; omit it and the command stands alone.","type":"string","minLength":1},"icon":{"description":"A name from the host's icon set, drawn beside the title.","type":"string"},"keybinding":{"description":"A global keyboard shortcut, e.g. \"Mod+Shift+K\" — `Mod` is ⌘ on Apple and Ctrl elsewhere. Declared here because a global shortcut is consequential: the owner approves it at install, and the host binds only what was approved.","power":{"key":"keybinding:${command}","sentence":"the global shortcut ${keybinding} (\"${title}\")"},"type":"string","pattern":"^\\S+$"},"when":{"description":"When the shortcut applies, as a condition over the shell's context keys, `tabSurface == 'chat'`, `!editableTarget`. Without one the chord is claimed everywhere, including inside a terminal where a bare key belongs to the program running in it. The command palette ignores this: a command is always runnable by name.","type":"string"}},"required":["command","title"],"additionalProperties":false,"power":{"key":"command:${command}","sentence":"a palette command \"${title}\""}},"description":"Commands this extension may register handlers for, surfaced in the command palette. Title, icon and shortcut all come from here rather than from the registration call, because this is what the owner approved at install."},"settings":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-z0-9][a-zA-Z0-9-]*$"},"type":{"type":"string","enum":["boolean","string","number","enum"],"description":"Which control the Settings page draws. `enum` reads its choices from `enum`."},"title":{"type":"string","minLength":1},"description":{"description":"The line under the control.","type":"string"},"default":{"type":["string","number","boolean"]},"enum":{"description":"The choices, for type \"enum\". Meaningless otherwise.","type":"array","items":{"type":"string"}},"secret":{"description":"Mask the value in the UI and strip it from reads: a set secret round-trips as 'still set', never as its value.","type":"boolean"},"env":{"description":"Inject the stored value into the agent's shell environment under this name, every turn. How a credential you hold reaches the agent's command-line tools.","power":{"key":"setting-env:${key}","sentence":"puts the \"${key}\" setting into the agent's environment as ${env}"},"type":"string","pattern":"^[A-Z][A-Z0-9_]*$"}},"required":["key","type","title"],"additionalProperties":false},"description":"Typed settings the host renders into the Settings page for you and persists daemon-side. You never draw the form or store the value; you read it back with api.settings.get."},"processes":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"command":{"type":"string","minLength":1},"cwd":{"description":"Relative to the extension checkout. Absent ⇒ the checkout root.","type":"string"},"port":{"description":"Assign a free port and inject it as PORT.","type":"string","const":"auto"},"preview":{"description":"Expose the port on a tunnelled preview hostname.","type":"boolean"},"autoStart":{"description":"Launch it on install and on daemon boot, rather than waiting to be started.","type":"boolean"}},"required":["name","command"],"additionalProperties":false,"effect":"process","power":{"key":"process:${name}","sentence":"a background process \"${name}\"${autoStart? (starts on boot):}"}},"description":"Long-lived background processes the daemon runs for this extension: a gateway holding a connection the daemon must not, a dev server. Managed the same way panel dev servers are, and startable and stoppable from the Extensions tab."},"agent":{"type":"object","properties":{"path":{"description":"Relative to the extension checkout. Absent ⇒ the checkout root.","type":"string"}},"additionalProperties":false,"power":{"key":"agent","sentence":"contributes skills, agents and hooks to the agent's turns"},"description":"Declare that this checkout is also a Claude Code plugin, so Claude Code turns pick up its skills, agents, hooks and commands. Only Claude Code reads it: give the agent tools with `contributes.tools`, which every runtime gets, and put a skill every runtime should read in a capability card's `skill`. MCP servers in the plugin's `.mcp.json` are deprecated, reach Claude Code alone, and are warned about at load."},"environment":{"type":"object","properties":{"fragment":{"type":"string","minLength":1,"description":"Checkout-relative path to a file holding ONLY RUN and ENV instructions. FROM and privileged directives are rejected: those stay daemon-owned."}},"required":["fragment"],"additionalProperties":false,"effect":"image","power":{"key":"environment","sentence":"bakes an environment fragment into the sandbox image"},"description":"A Dockerfile fragment baked into the sandbox image so your tools are actually installed at runtime: a whisper binary, a psql client. The owner approves the composed overlay and rebuilds out of band, so this does not take effect immediately."},"capabilities":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"catalog":{"type":"object","properties":{"name":{"type":"string","minLength":1},"art":{"description":"This extension's own mark, as a complete SVG document inline: the tier an author controls fully. Give it a viewBox and let it fill its own square edge to edge; it is drawn as the tile, not as a glyph on a plate. Kept as readable SVG text (not base64) so a registry reviewer can see what they are publishing, drawn inert so it cannot script the page, and capped at 4 KB. Anything that does not parse as SVG falls back to `logo`, then `icon`, then initials.","type":"string","maxLength":4096},"logo":{"description":"A simple-icons slug, fetched from a CDN: right for standing in for somebody else's product. Add a \"/<hex>\" suffix to force a colour for a mark that vanishes against the surface it lands on. Unreachable in an offline sandbox, so it falls back to `icon`, then to initials.","type":"string"},"icon":{"description":"A name from the host's own icon set, drawn when no simple-icons slug fits. It ships in the image, follows the theme and costs no request: what actually carries a first-party extension. An unknown name falls back to initials rather than to a hole.","type":"string"},"description":{"type":"string","minLength":1,"description":"ONE LINE: aim for 60 characters or fewer. The grid clamps it at two lines in a narrow pane, so a paragraph here is a paragraph the reader gets truncated. Everything longer belongs in `hint`."},"category":{"type":"string","minLength":1},"hint":{"description":"The paragraph, shown under the add form and searched from the catalog, so the words that identify this card to someone hunting for it (\"webauthn\", \"socket mode\") belong here even when the tile cannot show them.","type":"string"},"guide":{"description":"The walkthrough the install dialog renders for getting the credential this card asks for.","type":"object","properties":{"url":{"type":"string"},"urlFromField":{"type":"string"},"path":{"type":"string"},"linkLabel":{"type":"string"},"scopes":{"type":"string"},"steps":{"type":"array","items":{"type":"string"}}},"additionalProperties":false}},"required":["name","description","category"],"additionalProperties":false},"fields":{"minItems":1,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-zA-Z][a-zA-Z0-9]*$"},"label":{"type":"string","minLength":1},"placeholder":{"type":"string"},"secret":{"description":"Mask it, and never echo it back.","type":"boolean"},"optional":{"type":"boolean"},"multiline":{"type":"boolean"},"advanced":{"description":"Fold this field behind the form's Advanced disclosure: for answers whose default is right for nearly everyone. The disclosure opens by itself while any advanced field holds a non-default value, so an edit never hides live settings.","type":"boolean"},"boolean":{"description":"Render it as a switch, carrying \"on\"/\"off\". For an opt-in EXTRA rather than a decision: a two-option picker says the same thing but presents a choice the user must make to proceed, sized like the required fields around it. A switch always holds a value, so a field like this never blocks a submit.","type":"boolean"},"hint":{"description":"A line under this control, for what the label alone cannot say: a host requirement, when a value takes effect. The card's own `hint` speaks for the whole card; this one is bound to the field it qualifies.","type":"string"},"rebuild":{"description":"This value only takes effect after the sandbox is rebuilt, because it rides the image overlay. Shown as a chip beside the label: two switches side by side, identical in every visible way, can otherwise cost five seconds or five minutes with no way to tell which.","type":"boolean"},"default":{"type":"string"},"options":{"description":"Turns the field into a select.","type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"],"additionalProperties":false}},"when":{"description":"Only show this field while a condition over the answers already given holds: `auth == 'key'`, `provider in ['ipsec', 'fortinet']`, `!advanced`. Supports `&&`, `||`, `!`, comparisons and `in`.","type":"string"},"value":{"description":"A fixed value baked into the config rather than asked for: how a card pins its discriminator (platform=\"reddit\", provider=\"stripe\"). Renders as nothing.","type":"string"},"totp":{"description":"This field holds a TOTP seed, the base32 key or otpauth:// URI a service shows when enrolling an authenticator app. Declare it with `secret: true`. Unlike an ordinary secret it never enters the agent's environment: the daemon mints the six-digit codes on demand and only those cross.","type":"boolean"}},"required":["key","label"],"additionalProperties":false}},"kind":{"type":"string","const":"cli"},"env":{"type":"object","propertyNames":{"type":"string","pattern":"^[A-Z][A-Z0-9_]*$"},"additionalProperties":{"type":"string"},"description":"The environment the agent's shell gets, as value templates over the fields: `${field}` substitutes, `${field:uri}` percent-encodes. Each name is suffixed per instance."},"skill":{"type":"string","minLength":1,"description":"Checkout-relative SKILL.md teaching the agent this tool. `${id}` in it is replaced with the instance name at apply time."},"fragment":{"description":"A Dockerfile fragment holding the client binary this tool needs (psql, mysql, whisper).","effect":"image","type":"string","minLength":1},"pack":{"description":"A sandbox feature pack name (whisper, llamacpp, browser, …) supplying this tool. Preferred over `fragment`: an image that already bakes the pack needs no rebuild, and there is no copy to drift.","effect":"image","type":"string","minLength":1},"probe":{"description":"One authenticated request that tests this card's settings before they are saved, so a wrong token or an unreachable host is answered on the form rather than by a card that says 'not connected' afterwards.","type":"object","properties":{"url":{"type":"string","minLength":1,"description":"The URL to call, as a template over the fields: `${field}` substitutes, `${field:uri}` percent-encodes. Same spelling as `env`."},"method":{"description":"Defaults to GET.","type":"string","enum":["GET","POST","HEAD"]},"headers":{"description":"The request headers, templated the same way: `{\"Authorization\": \"Bearer ${token}\"}`.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"identity":{"description":"A dotted path into the JSON answer naming who the caller is (\"login\", \"user.name\"), so success can say which account answered.","type":"string"},"insecure":{"description":"Accept a self-signed certificate, for a service whose local install ships one (Obsidian's Local REST API).","type":"boolean"}},"required":["url"],"additionalProperties":false},"hosts":{"description":"The hosts this card's credential is meant for, as templates over the fields like `env` (`api.github.com`, `*.githubusercontent.com`, `${url}`); a value that comes out as a URL counts as its host. The sandbox limits the credential's `{{secret:…}}` reference to them by default, so a use aimed anywhere else asks a person first. The owner can change or lift the list on the Secrets view.","type":"array","items":{"type":"string","minLength":1}},"mcp":{"description":"Deprecated: declare `contributes.tools` with `perCard` naming this card instead, and serve the tools with `api.tools.serve`. A path in this extension's backend (`server`) answering MCP over Streamable HTTP; every turn granted a card of this kind gets it as a server named by the card's id, each request arriving at `<path>/<card id>`.","effect":"mcp","mintsServer":true,"power":{"key":"capability-tools:${id}","sentence":"serves MCP tools to the agent for each \"${catalog.name}\" card"},"type":"string","pattern":"^[a-z0-9][a-z0-9-]*(?:\\/[a-z0-9][a-z0-9-]*)*$"}},"required":["id","catalog","fields","kind","env","skill"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"catalog":{"type":"object","properties":{"name":{"type":"string","minLength":1},"art":{"description":"This extension's own mark, as a complete SVG document inline: the tier an author controls fully. Give it a viewBox and let it fill its own square edge to edge; it is drawn as the tile, not as a glyph on a plate. Kept as readable SVG text (not base64) so a registry reviewer can see what they are publishing, drawn inert so it cannot script the page, and capped at 4 KB. Anything that does not parse as SVG falls back to `logo`, then `icon`, then initials.","type":"string","maxLength":4096},"logo":{"description":"A simple-icons slug, fetched from a CDN: right for standing in for somebody else's product. Add a \"/<hex>\" suffix to force a colour for a mark that vanishes against the surface it lands on. Unreachable in an offline sandbox, so it falls back to `icon`, then to initials.","type":"string"},"icon":{"description":"A name from the host's own icon set, drawn when no simple-icons slug fits. It ships in the image, follows the theme and costs no request: what actually carries a first-party extension. An unknown name falls back to initials rather than to a hole.","type":"string"},"description":{"type":"string","minLength":1,"description":"ONE LINE: aim for 60 characters or fewer. The grid clamps it at two lines in a narrow pane, so a paragraph here is a paragraph the reader gets truncated. Everything longer belongs in `hint`."},"category":{"type":"string","minLength":1},"hint":{"description":"The paragraph, shown under the add form and searched from the catalog, so the words that identify this card to someone hunting for it (\"webauthn\", \"socket mode\") belong here even when the tile cannot show them.","type":"string"},"guide":{"description":"The walkthrough the install dialog renders for getting the credential this card asks for.","type":"object","properties":{"url":{"type":"string"},"urlFromField":{"type":"string"},"path":{"type":"string"},"linkLabel":{"type":"string"},"scopes":{"type":"string"},"steps":{"type":"array","items":{"type":"string"}}},"additionalProperties":false}},"required":["name","description","category"],"additionalProperties":false},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-zA-Z][a-zA-Z0-9]*$"},"label":{"type":"string","minLength":1},"placeholder":{"type":"string"},"secret":{"description":"Mask it, and never echo it back.","type":"boolean"},"optional":{"type":"boolean"},"multiline":{"type":"boolean"},"advanced":{"description":"Fold this field behind the form's Advanced disclosure: for answers whose default is right for nearly everyone. The disclosure opens by itself while any advanced field holds a non-default value, so an edit never hides live settings.","type":"boolean"},"boolean":{"description":"Render it as a switch, carrying \"on\"/\"off\". For an opt-in EXTRA rather than a decision: a two-option picker says the same thing but presents a choice the user must make to proceed, sized like the required fields around it. A switch always holds a value, so a field like this never blocks a submit.","type":"boolean"},"hint":{"description":"A line under this control, for what the label alone cannot say: a host requirement, when a value takes effect. The card's own `hint` speaks for the whole card; this one is bound to the field it qualifies.","type":"string"},"rebuild":{"description":"This value only takes effect after the sandbox is rebuilt, because it rides the image overlay. Shown as a chip beside the label: two switches side by side, identical in every visible way, can otherwise cost five seconds or five minutes with no way to tell which.","type":"boolean"},"default":{"type":"string"},"options":{"description":"Turns the field into a select.","type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"],"additionalProperties":false}},"when":{"description":"Only show this field while a condition over the answers already given holds: `auth == 'key'`, `provider in ['ipsec', 'fortinet']`, `!advanced`. Supports `&&`, `||`, `!`, comparisons and `in`.","type":"string"},"value":{"description":"A fixed value baked into the config rather than asked for: how a card pins its discriminator (platform=\"reddit\", provider=\"stripe\"). Renders as nothing.","type":"string"},"totp":{"description":"This field holds a TOTP seed, the base32 key or otpauth:// URI a service shows when enrolling an authenticator app. Declare it with `secret: true`. Unlike an ordinary secret it never enters the agent's environment: the daemon mints the six-digit codes on demand and only those cross.","type":"boolean"}},"required":["key","label"],"additionalProperties":false}},"kind":{"type":"string","const":"browser"},"loginUrl":{"description":"What the sign-in window opens; the profile it persists IS the credential. Optional so one card can be the generic one that asks for the URL on its form instead, but a card must either pin this or declare a field that supplies it, or the window opens on nothing.","type":"string","format":"uri"},"homeUrl":{"description":"Where that same profile opens once it HAS a session: the owner's own hands on the connected browser. Separate from loginUrl because for some platforms the login lives on another site entirely (YouTube signs in at accounts.google.com).","type":"string","format":"uri"},"skill":{"type":"string","minLength":1,"description":"Checkout-relative SKILL.md teaching the agent this site's actions: rendered once per site, all its connected accounts on one roster (`${accounts}`), the core tool note at `${tools}`."}},"required":["id","catalog","fields","kind","skill"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"catalog":{"type":"object","properties":{"name":{"type":"string","minLength":1},"art":{"description":"This extension's own mark, as a complete SVG document inline: the tier an author controls fully. Give it a viewBox and let it fill its own square edge to edge; it is drawn as the tile, not as a glyph on a plate. Kept as readable SVG text (not base64) so a registry reviewer can see what they are publishing, drawn inert so it cannot script the page, and capped at 4 KB. Anything that does not parse as SVG falls back to `logo`, then `icon`, then initials.","type":"string","maxLength":4096},"logo":{"description":"A simple-icons slug, fetched from a CDN: right for standing in for somebody else's product. Add a \"/<hex>\" suffix to force a colour for a mark that vanishes against the surface it lands on. Unreachable in an offline sandbox, so it falls back to `icon`, then to initials.","type":"string"},"icon":{"description":"A name from the host's own icon set, drawn when no simple-icons slug fits. It ships in the image, follows the theme and costs no request: what actually carries a first-party extension. An unknown name falls back to initials rather than to a hole.","type":"string"},"description":{"type":"string","minLength":1,"description":"ONE LINE: aim for 60 characters or fewer. The grid clamps it at two lines in a narrow pane, so a paragraph here is a paragraph the reader gets truncated. Everything longer belongs in `hint`."},"category":{"type":"string","minLength":1},"hint":{"description":"The paragraph, shown under the add form and searched from the catalog, so the words that identify this card to someone hunting for it (\"webauthn\", \"socket mode\") belong here even when the tile cannot show them.","type":"string"},"guide":{"description":"The walkthrough the install dialog renders for getting the credential this card asks for.","type":"object","properties":{"url":{"type":"string"},"urlFromField":{"type":"string"},"path":{"type":"string"},"linkLabel":{"type":"string"},"scopes":{"type":"string"},"steps":{"type":"array","items":{"type":"string"}}},"additionalProperties":false}},"required":["name","description","category"],"additionalProperties":false},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-zA-Z][a-zA-Z0-9]*$"},"label":{"type":"string","minLength":1},"placeholder":{"type":"string"},"secret":{"description":"Mask it, and never echo it back.","type":"boolean"},"optional":{"type":"boolean"},"multiline":{"type":"boolean"},"advanced":{"description":"Fold this field behind the form's Advanced disclosure: for answers whose default is right for nearly everyone. The disclosure opens by itself while any advanced field holds a non-default value, so an edit never hides live settings.","type":"boolean"},"boolean":{"description":"Render it as a switch, carrying \"on\"/\"off\". For an opt-in EXTRA rather than a decision: a two-option picker says the same thing but presents a choice the user must make to proceed, sized like the required fields around it. A switch always holds a value, so a field like this never blocks a submit.","type":"boolean"},"hint":{"description":"A line under this control, for what the label alone cannot say: a host requirement, when a value takes effect. The card's own `hint` speaks for the whole card; this one is bound to the field it qualifies.","type":"string"},"rebuild":{"description":"This value only takes effect after the sandbox is rebuilt, because it rides the image overlay. Shown as a chip beside the label: two switches side by side, identical in every visible way, can otherwise cost five seconds or five minutes with no way to tell which.","type":"boolean"},"default":{"type":"string"},"options":{"description":"Turns the field into a select.","type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"],"additionalProperties":false}},"when":{"description":"Only show this field while a condition over the answers already given holds: `auth == 'key'`, `provider in ['ipsec', 'fortinet']`, `!advanced`. Supports `&&`, `||`, `!`, comparisons and `in`.","type":"string"},"value":{"description":"A fixed value baked into the config rather than asked for: how a card pins its discriminator (platform=\"reddit\", provider=\"stripe\"). Renders as nothing.","type":"string"},"totp":{"description":"This field holds a TOTP seed, the base32 key or otpauth:// URI a service shows when enrolling an authenticator app. Declare it with `secret: true`. Unlike an ordinary secret it never enters the agent's environment: the daemon mints the six-digit codes on demand and only those cross.","type":"boolean"}},"required":["key","label"],"additionalProperties":false}},"kind":{"type":"string","const":"device"},"skill":{"type":"string","minLength":1,"description":"Checkout-relative SKILL.md teaching the agent that machine's shell."}},"required":["id","catalog","fields","kind","skill"],"additionalProperties":false,"mintsServer":true},{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"catalog":{"type":"object","properties":{"name":{"type":"string","minLength":1},"art":{"description":"This extension's own mark, as a complete SVG document inline: the tier an author controls fully. Give it a viewBox and let it fill its own square edge to edge; it is drawn as the tile, not as a glyph on a plate. Kept as readable SVG text (not base64) so a registry reviewer can see what they are publishing, drawn inert so it cannot script the page, and capped at 4 KB. Anything that does not parse as SVG falls back to `logo`, then `icon`, then initials.","type":"string","maxLength":4096},"logo":{"description":"A simple-icons slug, fetched from a CDN: right for standing in for somebody else's product. Add a \"/<hex>\" suffix to force a colour for a mark that vanishes against the surface it lands on. Unreachable in an offline sandbox, so it falls back to `icon`, then to initials.","type":"string"},"icon":{"description":"A name from the host's own icon set, drawn when no simple-icons slug fits. It ships in the image, follows the theme and costs no request: what actually carries a first-party extension. An unknown name falls back to initials rather than to a hole.","type":"string"},"description":{"type":"string","minLength":1,"description":"ONE LINE: aim for 60 characters or fewer. The grid clamps it at two lines in a narrow pane, so a paragraph here is a paragraph the reader gets truncated. Everything longer belongs in `hint`."},"category":{"type":"string","minLength":1},"hint":{"description":"The paragraph, shown under the add form and searched from the catalog, so the words that identify this card to someone hunting for it (\"webauthn\", \"socket mode\") belong here even when the tile cannot show them.","type":"string"},"guide":{"description":"The walkthrough the install dialog renders for getting the credential this card asks for.","type":"object","properties":{"url":{"type":"string"},"urlFromField":{"type":"string"},"path":{"type":"string"},"linkLabel":{"type":"string"},"scopes":{"type":"string"},"steps":{"type":"array","items":{"type":"string"}}},"additionalProperties":false}},"required":["name","description","category"],"additionalProperties":false},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-zA-Z][a-zA-Z0-9]*$"},"label":{"type":"string","minLength":1},"placeholder":{"type":"string"},"secret":{"description":"Mask it, and never echo it back.","type":"boolean"},"optional":{"type":"boolean"},"multiline":{"type":"boolean"},"advanced":{"description":"Fold this field behind the form's Advanced disclosure: for answers whose default is right for nearly everyone. The disclosure opens by itself while any advanced field holds a non-default value, so an edit never hides live settings.","type":"boolean"},"boolean":{"description":"Render it as a switch, carrying \"on\"/\"off\". For an opt-in EXTRA rather than a decision: a two-option picker says the same thing but presents a choice the user must make to proceed, sized like the required fields around it. A switch always holds a value, so a field like this never blocks a submit.","type":"boolean"},"hint":{"description":"A line under this control, for what the label alone cannot say: a host requirement, when a value takes effect. The card's own `hint` speaks for the whole card; this one is bound to the field it qualifies.","type":"string"},"rebuild":{"description":"This value only takes effect after the sandbox is rebuilt, because it rides the image overlay. Shown as a chip beside the label: two switches side by side, identical in every visible way, can otherwise cost five seconds or five minutes with no way to tell which.","type":"boolean"},"default":{"type":"string"},"options":{"description":"Turns the field into a select.","type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"],"additionalProperties":false}},"when":{"description":"Only show this field while a condition over the answers already given holds: `auth == 'key'`, `provider in ['ipsec', 'fortinet']`, `!advanced`. Supports `&&`, `||`, `!`, comparisons and `in`.","type":"string"},"value":{"description":"A fixed value baked into the config rather than asked for: how a card pins its discriminator (platform=\"reddit\", provider=\"stripe\"). Renders as nothing.","type":"string"},"totp":{"description":"This field holds a TOTP seed, the base32 key or otpauth:// URI a service shows when enrolling an authenticator app. Declare it with `secret: true`. Unlike an ordinary secret it never enters the agent's environment: the daemon mints the six-digit codes on demand and only those cross.","type":"boolean"}},"required":["key","label"],"additionalProperties":false}},"kind":{"type":"string","const":"webext"},"install":{"description":"Where this browser's extension is installed from: its store listing, or a page offering the build.","type":"string","format":"uri"},"skill":{"type":"string","minLength":1,"description":"Checkout-relative SKILL.md teaching the agent to drive this browser."}},"required":["id","catalog","fields","kind","skill"],"additionalProperties":false,"mintsServer":true},{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"catalog":{"type":"object","properties":{"name":{"type":"string","minLength":1},"art":{"description":"This extension's own mark, as a complete SVG document inline: the tier an author controls fully. Give it a viewBox and let it fill its own square edge to edge; it is drawn as the tile, not as a glyph on a plate. Kept as readable SVG text (not base64) so a registry reviewer can see what they are publishing, drawn inert so it cannot script the page, and capped at 4 KB. Anything that does not parse as SVG falls back to `logo`, then `icon`, then initials.","type":"string","maxLength":4096},"logo":{"description":"A simple-icons slug, fetched from a CDN: right for standing in for somebody else's product. Add a \"/<hex>\" suffix to force a colour for a mark that vanishes against the surface it lands on. Unreachable in an offline sandbox, so it falls back to `icon`, then to initials.","type":"string"},"icon":{"description":"A name from the host's own icon set, drawn when no simple-icons slug fits. It ships in the image, follows the theme and costs no request: what actually carries a first-party extension. An unknown name falls back to initials rather than to a hole.","type":"string"},"description":{"type":"string","minLength":1,"description":"ONE LINE: aim for 60 characters or fewer. The grid clamps it at two lines in a narrow pane, so a paragraph here is a paragraph the reader gets truncated. Everything longer belongs in `hint`."},"category":{"type":"string","minLength":1},"hint":{"description":"The paragraph, shown under the add form and searched from the catalog, so the words that identify this card to someone hunting for it (\"webauthn\", \"socket mode\") belong here even when the tile cannot show them.","type":"string"},"guide":{"description":"The walkthrough the install dialog renders for getting the credential this card asks for.","type":"object","properties":{"url":{"type":"string"},"urlFromField":{"type":"string"},"path":{"type":"string"},"linkLabel":{"type":"string"},"scopes":{"type":"string"},"steps":{"type":"array","items":{"type":"string"}}},"additionalProperties":false}},"required":["name","description","category"],"additionalProperties":false},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-zA-Z][a-zA-Z0-9]*$"},"label":{"type":"string","minLength":1},"placeholder":{"type":"string"},"secret":{"description":"Mask it, and never echo it back.","type":"boolean"},"optional":{"type":"boolean"},"multiline":{"type":"boolean"},"advanced":{"description":"Fold this field behind the form's Advanced disclosure: for answers whose default is right for nearly everyone. The disclosure opens by itself while any advanced field holds a non-default value, so an edit never hides live settings.","type":"boolean"},"boolean":{"description":"Render it as a switch, carrying \"on\"/\"off\". For an opt-in EXTRA rather than a decision: a two-option picker says the same thing but presents a choice the user must make to proceed, sized like the required fields around it. A switch always holds a value, so a field like this never blocks a submit.","type":"boolean"},"hint":{"description":"A line under this control, for what the label alone cannot say: a host requirement, when a value takes effect. The card's own `hint` speaks for the whole card; this one is bound to the field it qualifies.","type":"string"},"rebuild":{"description":"This value only takes effect after the sandbox is rebuilt, because it rides the image overlay. Shown as a chip beside the label: two switches side by side, identical in every visible way, can otherwise cost five seconds or five minutes with no way to tell which.","type":"boolean"},"default":{"type":"string"},"options":{"description":"Turns the field into a select.","type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"],"additionalProperties":false}},"when":{"description":"Only show this field while a condition over the answers already given holds: `auth == 'key'`, `provider in ['ipsec', 'fortinet']`, `!advanced`. Supports `&&`, `||`, `!`, comparisons and `in`.","type":"string"},"value":{"description":"A fixed value baked into the config rather than asked for: how a card pins its discriminator (platform=\"reddit\", provider=\"stripe\"). Renders as nothing.","type":"string"},"totp":{"description":"This field holds a TOTP seed, the base32 key or otpauth:// URI a service shows when enrolling an authenticator app. Declare it with `secret: true`. Unlike an ordinary secret it never enters the agent's environment: the daemon mints the six-digit codes on demand and only those cross.","type":"boolean"}},"required":["key","label"],"additionalProperties":false}},"kind":{"type":"string","const":"phone"},"install":{"description":"Where this phone's app is installed from: its store listing, or a page offering the build.","type":"string","format":"uri"},"skill":{"type":"string","minLength":1,"description":"Checkout-relative SKILL.md teaching the agent to work on this phone."}},"required":["id","catalog","fields","kind","skill"],"additionalProperties":false,"mintsServer":true},{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"catalog":{"type":"object","properties":{"name":{"type":"string","minLength":1},"art":{"description":"This extension's own mark, as a complete SVG document inline: the tier an author controls fully. Give it a viewBox and let it fill its own square edge to edge; it is drawn as the tile, not as a glyph on a plate. Kept as readable SVG text (not base64) so a registry reviewer can see what they are publishing, drawn inert so it cannot script the page, and capped at 4 KB. Anything that does not parse as SVG falls back to `logo`, then `icon`, then initials.","type":"string","maxLength":4096},"logo":{"description":"A simple-icons slug, fetched from a CDN: right for standing in for somebody else's product. Add a \"/<hex>\" suffix to force a colour for a mark that vanishes against the surface it lands on. Unreachable in an offline sandbox, so it falls back to `icon`, then to initials.","type":"string"},"icon":{"description":"A name from the host's own icon set, drawn when no simple-icons slug fits. It ships in the image, follows the theme and costs no request: what actually carries a first-party extension. An unknown name falls back to initials rather than to a hole.","type":"string"},"description":{"type":"string","minLength":1,"description":"ONE LINE: aim for 60 characters or fewer. The grid clamps it at two lines in a narrow pane, so a paragraph here is a paragraph the reader gets truncated. Everything longer belongs in `hint`."},"category":{"type":"string","minLength":1},"hint":{"description":"The paragraph, shown under the add form and searched from the catalog, so the words that identify this card to someone hunting for it (\"webauthn\", \"socket mode\") belong here even when the tile cannot show them.","type":"string"},"guide":{"description":"The walkthrough the install dialog renders for getting the credential this card asks for.","type":"object","properties":{"url":{"type":"string"},"urlFromField":{"type":"string"},"path":{"type":"string"},"linkLabel":{"type":"string"},"scopes":{"type":"string"},"steps":{"type":"array","items":{"type":"string"}}},"additionalProperties":false}},"required":["name","description","category"],"additionalProperties":false},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-zA-Z][a-zA-Z0-9]*$"},"label":{"type":"string","minLength":1},"placeholder":{"type":"string"},"secret":{"description":"Mask it, and never echo it back.","type":"boolean"},"optional":{"type":"boolean"},"multiline":{"type":"boolean"},"advanced":{"description":"Fold this field behind the form's Advanced disclosure: for answers whose default is right for nearly everyone. The disclosure opens by itself while any advanced field holds a non-default value, so an edit never hides live settings.","type":"boolean"},"boolean":{"description":"Render it as a switch, carrying \"on\"/\"off\". For an opt-in EXTRA rather than a decision: a two-option picker says the same thing but presents a choice the user must make to proceed, sized like the required fields around it. A switch always holds a value, so a field like this never blocks a submit.","type":"boolean"},"hint":{"description":"A line under this control, for what the label alone cannot say: a host requirement, when a value takes effect. The card's own `hint` speaks for the whole card; this one is bound to the field it qualifies.","type":"string"},"rebuild":{"description":"This value only takes effect after the sandbox is rebuilt, because it rides the image overlay. Shown as a chip beside the label: two switches side by side, identical in every visible way, can otherwise cost five seconds or five minutes with no way to tell which.","type":"boolean"},"default":{"type":"string"},"options":{"description":"Turns the field into a select.","type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"],"additionalProperties":false}},"when":{"description":"Only show this field while a condition over the answers already given holds: `auth == 'key'`, `provider in ['ipsec', 'fortinet']`, `!advanced`. Supports `&&`, `||`, `!`, comparisons and `in`.","type":"string"},"value":{"description":"A fixed value baked into the config rather than asked for: how a card pins its discriminator (platform=\"reddit\", provider=\"stripe\"). Renders as nothing.","type":"string"},"totp":{"description":"This field holds a TOTP seed, the base32 key or otpauth:// URI a service shows when enrolling an authenticator app. Declare it with `secret: true`. Unlike an ordinary secret it never enters the agent's environment: the daemon mints the six-digit codes on demand and only those cross.","type":"boolean"}},"required":["key","label"],"additionalProperties":false}},"kind":{"type":"string","const":"agent"}},"required":["id","catalog","fields","kind"],"additionalProperties":false}],"power":{"key":"capability:${id}","sentence":"a ${kind} capability card \"${catalog.name}\""}},"description":"Capability cards this pack adds to the \"+\" grid: a connected CLI tool, a site the agent acts on as the owner through the shared browser, an operating system pack, a browser family the owner connects their own copy of, or a preset over a core kind. The card and its form are data here; the machinery that acts on them is core, which is why a card may only name one of these five kinds."},"listener":{"type":"object","properties":{"provider":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$","description":"The slug this source's automation triggers fire on."},"events":{"minItems":1,"type":"array","items":{"type":"object","properties":{"type":{"type":"string","pattern":"^[a-z0-9][a-z0-9_]*$"},"label":{"type":"string","minLength":1}},"required":["type","label"],"additionalProperties":false},"description":"The event types this source can fire, with the wording the automation editor offers them under. The daemon accepts no others."},"automation":{"type":"object","properties":{"label":{"type":"string","minLength":1},"mentionLabel":{"description":"Only for a source whose message events distinguish being addressed. Absent ⇒ the editor offers no mention-only filter, rather than inventing semantics you did not promise.","type":"string","minLength":1},"channel":{"type":"object","properties":{"label":{"type":"string","minLength":1},"placeholder":{"type":"string","minLength":1},"hint":{"description":"The sentence under the input, for a filter whose empty case is easy to get wrong.","type":"string","minLength":1}},"required":["label","placeholder"],"additionalProperties":false,"description":"The primary narrowing filter, a channel, a room, a repo."},"branchField":{"description":"A second narrowing axis, for a source whose events carry one: a pipeline's git ref, so a trigger can say \"the branch that ships\" rather than \"every agent's every failure\".","type":"object","properties":{"label":{"type":"string","minLength":1},"placeholder":{"type":"string","minLength":1},"hint":{"description":"The sentence under the input, for a filter whose empty case is easy to get wrong.","type":"string","minLength":1}},"required":["label","placeholder"],"additionalProperties":false},"sender":{"description":"How this source names a sender, and where a person finds that id. Declaring it promises that `author.id` is an identity the service vouches for, not a name the sender typed; absent ⇒ the editor offers no sender rules on this source.","type":"object","properties":{"label":{"type":"string","minLength":1},"placeholder":{"type":"string","minLength":1},"hint":{"description":"The sentence under the input, for a filter whose empty case is easy to get wrong.","type":"string","minLength":1}},"required":["label","placeholder"],"additionalProperties":false},"senderGroup":{"description":"How this source names a sender's group, for a source whose messages carry `author.groups` (a Discord role). Absent ⇒ rules match ids only.","type":"object","properties":{"label":{"type":"string","minLength":1},"placeholder":{"type":"string","minLength":1},"hint":{"description":"The sentence under the input, for a filter whose empty case is easy to get wrong.","type":"string","minLength":1}},"required":["label","placeholder"],"additionalProperties":false},"starterPrompt":{"type":"string","minLength":1,"description":"The first prompt a new automation on this source is prefilled with. You own the payload vocabulary, so you own the prompt that explains it."}},"required":["label","channel","starterPrompt"],"additionalProperties":false,"description":"How the generic automation editor presents this source: its name, its filters, and the prompt it starts people on."}},"required":["provider","events","automation"],"additionalProperties":false,"power":{"key":"listener:${provider}","sentence":"a realtime listener provider \"${provider}\""},"description":"A realtime event source this extension supplies, so automations can trigger on it. One declaration feeds both halves: the daemon accepts these event types and serves this provider's control surface, and the automation editor derives its source picker, filters and starter prompt from it, so a newly installed listener is configurable without a matching app release."},"automationTemplates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"Prefills the automation name, and is what \"does one of these exist already\" is asked by, so spell it as an id, not as prose."},"title":{"type":"string","minLength":1},"logo":{"description":"A simple-icons slug for the card.","type":"string","minLength":1},"icon":{"description":"A name from the host's icon set, drawn when no simple-icons slug fits.","type":"string","minLength":1},"requires":{"description":"Capability providers that make this template work: any one connected is enough (fixing CI rides github or gitlab). Omitted ⇒ nothing to connect, so it is always offered.","type":"array","items":{"type":"string","minLength":1}},"trigger":{"type":"object","properties":{"kind":{"type":"string","enum":["schedule","event","listener","workspace"]},"cron":{"type":"string","minLength":1},"provider":{"type":"string","minLength":1},"eventType":{"type":"string","minLength":1},"event":{"type":"string","minLength":1}},"required":["kind"],"additionalProperties":false,"description":"What wakes it. Checked against the real trigger schema when the daemon builds the catalogue, so a template can never offer one that would be refused."},"guard":{"description":"A condition that must hold before the turn runs: what makes a template safe to leave switched on.","type":"string","minLength":1},"holdForSeconds":{"description":"Wait this long and coalesce repeats, rather than firing on every event.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"prompt":{"type":"string","minLength":1,"description":"The turn this starts. You own the trigger's payload vocabulary, so you own the prompt that reads it."},"note":{"type":"string","minLength":1},"setup":{"description":"What the user must do themselves before this can work.","type":"string","minLength":1},"description":{"type":"string","minLength":1},"offer":{"description":"Absent ⇒ it waits in the gallery, where you go once you know what you want. `create` puts a card on the page that makes it, switched off, in one click. `configure` puts one there that opens the dialog prefilled, for a template that cannot work unconfigured. Both are for what a user would never think to go looking for: mark everything as offered and you have rebuilt the gallery with extra steps.","type":"string","enum":["create","configure"]},"chore":{"description":"Whether what this makes watches THIS codebase rather than the outside world. Declared rather than read off the trigger: a nightly dependency sweep and a nightly Stripe poll are both schedules.","type":"boolean"}},"required":["id","title","trigger","prompt"],"additionalProperties":false},"description":"Starting points this pack offers in the automation composer, a trigger, a prompt written for that trigger's payload, and whatever guard makes it safe to leave on. Declared by whoever knows the service rather than by the composer, so they appear when your pack is installed and disappear with it. Pure prefill: creating one makes an ordinary automation."},"bin":{"type":"string","minLength":1,"power":{"key":"bin","sentence":"puts its shipped tools on the agent's PATH"},"description":"A checkout-relative directory of executables the daemon puts on the agent's PATH every turn, how you ship the agent a command-line tool. The files are the approved code themselves: they ride the pinned checkout, and the daemon only adds the directory to PATH."},"tools":{"type":"object","properties":{"perCard":{"description":"The id of one of this extension's `cli` capability cards. Every turn granted a card of that kind gets one server named by the card's id, handed that card's settings (secrets included) with each call. Absent ⇒ one server for the extension, named by its `name`, in every turn while the extension is enabled.","type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"process":{"description":"A process from `contributes.processes`, declared with `port: \"auto\"`, that answers MCP over Streamable HTTP at `path` on its port. Absent ⇒ your `server` bundle serves the tools.","type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"path":{"description":"Where the MCP endpoint answers, without a leading or trailing slash: on the process's port, or in your backend's own namespace when your `server` bundle speaks MCP itself. Absent with no `process` ⇒ the host serves what `api.tools.serve` returns, which is what you want: the host owns the transport, the deadlines and the card lookup. With `perCard`, a request arrives at `<path>/<card id>`.","type":"string","pattern":"^[a-z0-9][a-z0-9-]*(?:\\/[a-z0-9][a-z0-9-]*)*$"}},"additionalProperties":false,"effect":"mcp","mintsServer":true,"power":{"key":"tools${perCard?-${perCard}:}","sentence":"gives the agent MCP tools${perCard?, one server for each \"${perCard}\" card:}"},"description":"Tools for the agent, as an MCP server the daemon mounts into every turn and every runtime (Claude Code, Codex, Cursor, ACP agents). Serve them from your `server` bundle with `api.tools.serve((card) => [...])`, or from a declared process's port. Replaces an agent plugin's `.mcp.json`, which only Claude Code read."}},"additionalProperties":false}},"required":["publisher","name","version","engines"],"additionalProperties":false,"description":"What it declares about itself: what it contributes, what it needs, and what it may reach."},"commit":{"type":"string","description":"Exactly which commit is installed."},"source":{"type":"string","enum":["builtin","installed","workspace"],"description":"Where the code comes from: baked into the sandbox image and not removable, installed from a repository at a pinned commit, or written in this workspace and edited in place."},"enabled":{"type":"boolean","description":"The owner's switch. A switched-off extension is still listed, which is what makes it switchable back on, but nothing it contributes is wired up."},"essential":{"description":"Its switch is fixed on, because it is the only way to see or stop an engine the sandbox runs regardless. Hiding that page would not stop the spending, only your ability to notice it. Declared by the core about its own surfaces, never by an extension about itself, which would be a pack making itself un-removable.","type":"boolean"},"usage":{"description":"How much of the reach it asked for it has actually used, keyed by what it declared. Absent means never observed doing anything, which is a different claim from uses none of them, and the two have to stay tellable apart: reading either as these permissions are unnecessary turns evidence into a guess with a number on it.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"object","properties":{"calls":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"How many times."},"last":{"type":"string","description":"When, most recently."}},"required":["calls","last"],"additionalProperties":false}},"backend":{"description":"Present only for an extension that ships a server half.","type":"object","properties":{"state":{"type":"string","enum":["running","error","absent","incompatible","starting","stopped"],"description":"How its server half is doing. Absent means the code is not in this image at all; incompatible means it needs a different sandbox version."},"detail":{"description":"What went wrong, so a backend that failed to start is a sentence rather than an address that answers nothing.","type":"string"}},"required":["state"],"additionalProperties":false},"problems":{"description":"Declarations in its manifest the sandbox refused at load, each a sentence saying what and why, such as a listener for a provider another extension already owns. The rest of it still loads. Absent when nothing was refused.","type":"array","items":{"type":"string"}},"update":{"description":"A newer version waiting. All five of these exist only for one installed from a repository: a built-in updates with the image and one written here is edited live.","type":"object","properties":{"ref":{"type":"string","description":"The commit being offered."},"version":{"description":"What it calls itself.","type":"string"},"url":{"type":"string","description":"Where it comes from."},"path":{"description":"Where inside that repository it lives.","type":"string"},"trust":{"type":"string","enum":["verified","listed"],"description":"Whether anybody vouched for it, or it is merely listed."},"securityFix":{"description":"This release fixes a security problem in earlier ones, so here the old version is the dangerous one.","type":"boolean"},"registry":{"type":"string","description":"Which registry said so."},"at":{"type":"string","description":"When it was published."},"needsReview":{"description":"Why this one was not taken automatically and is asking for a person instead: it wants more than it used to, or nobody has vouched for it.","type":"string"},"review":{"description":"An agent has already read the difference between what is installed and this, so the card can link to what it found rather than offer to start looking.","type":"object","properties":{"conversationId":{"type":"string","description":"Where to read what it found."},"at":{"type":"string","description":"When it looked."}},"required":["conversationId","at"],"additionalProperties":false}},"required":["ref","url","trust","registry","at"],"additionalProperties":false},"advisory":{"description":"A security warning about the installed version.","type":"object","properties":{"reason":{"type":"string","description":"Why the registry pulled the listing, in its own words. Delisting protects people browsing; this record is for the person already running it."},"registry":{"type":"string","description":"Which registry said so."},"at":{"type":"string","description":"When."},"autoDisabled":{"type":"boolean","description":"Whether the sandbox has already switched it off."}},"required":["reason","registry","at","autoDisabled"],"additionalProperties":false},"health":{"description":"How it has behaved since the last update, which is what decides whether that update sticks.","type":"object","properties":{"state":{"type":"string","enum":["watching","healthy","unhealthy"],"description":"How it has behaved since the last update. Checks catch broken, not wrong, so for a while after a swap it is simply watched."},"detail":{"description":"What is going wrong, when something is.","type":"string"},"fromRef":{"description":"Which version it was updated from, which is what going back would return to.","type":"string"},"at":{"type":"string","description":"When the watching started."},"autoReverted":{"description":"The update was already rolled back without anybody asking. The record stays rather than pretending the attempt never happened.","type":"boolean"}},"required":["state","at"],"additionalProperties":false},"previous":{"description":"The version kept one step back, which is what going back means.","type":"object","properties":{"ref":{"type":"string","description":"The commit that was running before."},"version":{"description":"What it called itself.","type":"string"}},"required":["ref"],"additionalProperties":false},"updatePolicy":{"description":"The owner's standing answer for this one: tell me, have an agent look, or just do it.","type":"object","properties":{"updates":{"type":"string","enum":["notify","agent","auto"]},"advisories":{"type":"string","enum":["auto-disable","notify"]}},"required":["updates","advisories"],"additionalProperties":false},"dev":{"description":"Set while an extension installed from a repository is pointed at a source checkout in this workspace, for working on the extension itself: its code is served from there instead of the pinned copy. `commit` keeps naming the pinned version, which is what updates and the registry compare against.","type":"object","properties":{"path":{"type":"string","description":"The source checkout it was pointed at, such as extensions/maintenance: relative to the workspace, or to the conversation's own copy of it when `conversation` is set."},"conversation":{"description":"The conversation whose isolated copy of the workspace holds that checkout. Absent means the workspace itself. That copy goes when the conversation does, and the pinned version runs again then.","type":"string"},"uncommitted":{"description":"How many files in the checkout differ from its last commit, a rebuilt bundle included. Absent when git could not say.","type":"integer","minimum":0,"maximum":9007199254740991},"revision":{"description":"A short fingerprint of the browser bundle served from the checkout. It changes with every rebuild, which is how a screen tells the copy it loaded is behind. Absent while held, and for an extension with no browser half.","type":"string"},"held":{"description":"Why the checkout is not what runs, as a sentence: it is gone, holds a different extension, is not built yet, or asks for powers the pinned version was never approved for. Present means the pinned version still runs.","type":"string"}},"required":["path"],"additionalProperties":false}},"required":["id","manifest","commit","source","enabled"],"additionalProperties":false},"description":"What is installed."},"invalid":{"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"Which folder."},"error":{"type":"string","description":"Why it could not be read."}},"required":["dir","error"],"additionalProperties":false},"description":"Extensions written here that could not be read at all. Listed rather than dropped, because there is no install moment at which to reject a broken one, so this is its only way of saying anything."},"pending":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":121,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_.-]*$","description":"The extension's id."},"dir":{"type":"string","description":"Which folder under .intentic/config/workspace-extensions/."},"manifest":{"type":"object","properties":{"$schema":{"description":"The authoring schema, for editor completion and validation. Nothing at runtime reads it.","type":"string"},"publisher":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"name":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"version":{"type":"string","minLength":1,"description":"Your own semver, display and identity only. The installed code's identity is the pinned commit sha."},"category":{"description":"Which section of the Extensions tab this sits under: a grouping by what it is FOR, which cannot be derived from what it contributes. A section this app has never heard of lands in 'Other' rather than failing to install.","type":"string","minLength":1},"art":{"description":"This extension's own mark, as a complete SVG document inline: the tier an author controls fully. Give it a viewBox and let it fill its own square edge to edge; it is drawn as the tile, not as a glyph on a plate. Kept as readable SVG text (not base64) so a registry reviewer can see what they are publishing, drawn inert so it cannot script the page, and capped at 4 KB. Anything that does not parse as SVG falls back to `logo`, then `icon`, then initials.","type":"string","maxLength":4096},"logo":{"description":"A simple-icons slug, fetched from a CDN: right for standing in for somebody else's product. Add a \"/<hex>\" suffix to force a colour for a mark that vanishes against the surface it lands on. Unreachable in an offline sandbox, so it falls back to `icon`, then to initials.","type":"string"},"icon":{"description":"A name from the host's own icon set, drawn when no simple-icons slug fits. It ships in the image, follows the theme and costs no request: what actually carries a first-party extension. An unknown name falls back to initials rather than to a hole.","type":"string"},"engines":{"type":"object","properties":{"intentic":{"type":"string","minLength":1}},"required":["intentic"],"additionalProperties":false,"description":"A semver range over the host's extension API version, checked before your code is activated."},"entry":{"description":"Repo-relative path of your prebuilt single-file ESM bundle, built with `vue` and `@intentic/extension-api` as externals. Absent ⇒ an extension with no UI.","type":"string","minLength":1,"power":{"key":"entry","sentence":"runs a UI bundle in your browser"}},"server":{"description":"Repo-relative path of your prebuilt single-file node ESM server bundle, exporting `activateServer`. Served under your own route namespace, which the daemon proxies. Nothing is provided at runtime but node builtins, so bundle everything else in. Absent ⇒ no backend.","type":"string","minLength":1,"power":{"key":"server","sentence":"runs a backend bundle inside the daemon's extension host"}},"permissions":{"description":"How far this extension may reach into the daemon, as \"<METHOD> <path-glob>\" entries where `*` matches one path segment: e.g. \"GET /panels\", \"POST /panels/*/start\". The install dialog shows these, the host refuses anything undeclared, and the usage ledger records which were actually earned.","type":"object","properties":{"sandbox":{"description":"Daemon routes your UI half may call. Your own backend namespace needs no entry: its backend is your own code.","type":"array","items":{"type":"string","power":{"key":"sandbox:${value}","sentence":"its UI calls the sandbox route ${value}"}}},"daemon":{"description":"Daemon routes your SERVER half may call. Separate from `sandbox` because the two halves run as different principals: the UI as the owner's session, the backend as a minted per-extension token, so a grant to one must never quietly widen the other.","type":"array","items":{"type":"string","power":{"key":"daemon:${value}","sentence":"its backend calls the daemon route ${value}"}}}},"additionalProperties":false},"contributes":{"type":"object","properties":{"views":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"label":{"type":"string","minLength":1,"description":"The name shown on the tile or tab. The manifest's value wins over the one passed at registration."},"surface":{"type":"string","enum":["rail","directory","sandbox"],"description":"Where it appears. `rail` is a tile in the global left rail; `directory` is a panel opened from a repo in the Workspace tree; `sandbox` is a tab on the Sandbox hub, for a view whose subject is the box rather than the work."},"badge":{"description":"Allow this view to say something on its tile: a count, a glyph, or that work is running there. Declared because a badge interrupts from every other screen in the app; leave it out and any badge the extension registers is dropped.","power":{"key":"view-badge:${id}","sentence":"may badge the \"${label}\" tile from any screen"},"type":"boolean"}},"required":["id","label","surface"],"additionalProperties":false,"power":{"key":"view:${id}","sentence":"a ${surface} view \"${label}\""}},"description":"Sidebar elements this extension may register at runtime. Each entry reserves an id and a surface; the extension supplies the component with api.views.register, and the host refuses any registration this list does not cover."},"files":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","minLength":1,"description":"Workspace-root-relative, forward-slash, matched by prefix, so one entry covers an exact file (`.intentic/config/automations.json`), a directory (`.intentic/config/approvals/`, with the trailing slash so it cannot match a sibling file) or a name family (`.intentic/environment.`). Not a glob."},"invalidates":{"minItems":1,"type":"array","items":{"type":"string","minLength":1},"description":"The query keys this path makes stale, the first element of your own api.sandbox.key(...) keys. Keep both this and the path as narrow as the view actually needs: a broad prefix costs every connected browser a refetch on every matching write."}},"required":["path","invalidates"],"additionalProperties":false,"power":{"key":"files:${path}","sentence":"is told when ${path} changes"}},"description":"Which workspace files back your views, so the daemon's file watcher can tell the browser they went stale instead of you polling for it. The agent edits the workspace out of band from every HTTP route, and this push is the only thing that can notice."},"viewers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"extensions":{"minItems":1,"type":"array","items":{"type":"string","pattern":"^[a-z0-9]+$"},"description":"Bare file extensions, no dot: e.g. [\"docx\", \"xlsx\"]."},"fetch":{"type":"string","enum":["text","blob","url","path"],"description":"How much of the file the host hands you. `text` for a format that is text (svg, a subtitle track). `blob` for one that must be parsed end to end before any of it shows (a .docx, a spreadsheet), bounded by the daemon's raw-read cap. `url` for anything range-read rather than parsed (audio, video): your component gets a streaming URL to point an element at, never the bytes. `path` for a viewer whose own backend reads and writes the file: you get the workspace path and the scope it is viewed in, plus `readOnly` where the window may not write the file and, in a desktop app's local window, a `text` slot holding the document's text for while your own view can't show it. Emit `dirty` (a boolean) whenever you start or stop holding edits the file doesn't have, so a window closing over them can ask first."},"edit":{"description":"Whether this viewer writes the file back. An editing viewer is chosen over a render-only viewer claiming the same extension, whatever order the two activated in.","type":"boolean"},"compare":{"description":"Whether this viewer also draws two versions of a file as one, with what changed marked in place: its registration then carries a `compare` component the host renders with `before` and `after` blobs. Only for `fetch: \"blob\"`.","type":"boolean"}},"required":["id","extensions","fetch"],"additionalProperties":false,"power":{"key":"viewer:${id}","sentence":"${edit?opens and edits:opens}${compare? and compares:} .${extensions|, .} files (${fetch})"}},"description":"File formats this extension can render. The host resolves an opened file to your viewer by its extension, fetches the content, and renders your component with it: you keep none of the fetch lifecycle and none of the daemon credentials."},"documents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"label":{"type":"string","minLength":1,"description":"The family's name, shown in the install dialog beside your other contributions. Per-row wording stays with the provider, which is the only thing that knows what it found."}},"required":["id","label"],"additionalProperties":false,"power":{"key":"document:${id}","sentence":"marks workspace directories (\"${label}\")"}},"description":"Per-directory documents this extension can offer. Your provider marks the rows in the Workspace tree it has something to say about, and the host opens your component as a tab."},"sideViews":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"label":{"type":"string","minLength":1,"description":"What one of these is called (\"CI run\"), shown in the install dialog and on a tab whose own title could not be read. Each tab's title is the extension's to say for the thing it shows."},"links":{"description":"Allow this side view to take links the chat renders: a link it recognises (its registration's `claim`) opens beside the chat instead of in a new browser tab. Declared because it changes what the reader's click does; leave it out and the host never asks.","power":{"key":"side-view-links:${id}","sentence":"opens links it recognises as \"${label}\" beside the chat"},"type":"boolean"}},"required":["id","label"],"additionalProperties":false,"power":{"key":"side-view:${id}","sentence":"shows \"${label}\" in the side panel"}},"description":"Things this extension can show in the editor's side panel, one input at a time, beside whatever the reader is doing. Each entry reserves an id; the extension supplies the component with api.sideViews.register and opens one with api.sideViews.open, and the host refuses any id this list does not cover."},"commands":{"type":"array","items":{"type":"object","properties":{"command":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*(\\.[a-z0-9][a-z0-9-]*)+$"},"title":{"type":"string","minLength":1,"description":"What the command palette shows. The manifest's value wins over the one passed at registration."},"category":{"description":"What the command acts on (\"Deployments\", \"Knowledge\"), drawn ahead of the title as \"Category: Title\" and searched with it. Use the extension's own name so its commands group together; omit it and the command stands alone.","type":"string","minLength":1},"icon":{"description":"A name from the host's icon set, drawn beside the title.","type":"string"},"keybinding":{"description":"A global keyboard shortcut, e.g. \"Mod+Shift+K\" — `Mod` is ⌘ on Apple and Ctrl elsewhere. Declared here because a global shortcut is consequential: the owner approves it at install, and the host binds only what was approved.","power":{"key":"keybinding:${command}","sentence":"the global shortcut ${keybinding} (\"${title}\")"},"type":"string","pattern":"^\\S+$"},"when":{"description":"When the shortcut applies, as a condition over the shell's context keys, `tabSurface == 'chat'`, `!editableTarget`. Without one the chord is claimed everywhere, including inside a terminal where a bare key belongs to the program running in it. The command palette ignores this: a command is always runnable by name.","type":"string"}},"required":["command","title"],"additionalProperties":false,"power":{"key":"command:${command}","sentence":"a palette command \"${title}\""}},"description":"Commands this extension may register handlers for, surfaced in the command palette. Title, icon and shortcut all come from here rather than from the registration call, because this is what the owner approved at install."},"settings":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-z0-9][a-zA-Z0-9-]*$"},"type":{"type":"string","enum":["boolean","string","number","enum"],"description":"Which control the Settings page draws. `enum` reads its choices from `enum`."},"title":{"type":"string","minLength":1},"description":{"description":"The line under the control.","type":"string"},"default":{"type":["string","number","boolean"]},"enum":{"description":"The choices, for type \"enum\". Meaningless otherwise.","type":"array","items":{"type":"string"}},"secret":{"description":"Mask the value in the UI and strip it from reads: a set secret round-trips as 'still set', never as its value.","type":"boolean"},"env":{"description":"Inject the stored value into the agent's shell environment under this name, every turn. How a credential you hold reaches the agent's command-line tools.","power":{"key":"setting-env:${key}","sentence":"puts the \"${key}\" setting into the agent's environment as ${env}"},"type":"string","pattern":"^[A-Z][A-Z0-9_]*$"}},"required":["key","type","title"],"additionalProperties":false},"description":"Typed settings the host renders into the Settings page for you and persists daemon-side. You never draw the form or store the value; you read it back with api.settings.get."},"processes":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"command":{"type":"string","minLength":1},"cwd":{"description":"Relative to the extension checkout. Absent ⇒ the checkout root.","type":"string"},"port":{"description":"Assign a free port and inject it as PORT.","type":"string","const":"auto"},"preview":{"description":"Expose the port on a tunnelled preview hostname.","type":"boolean"},"autoStart":{"description":"Launch it on install and on daemon boot, rather than waiting to be started.","type":"boolean"}},"required":["name","command"],"additionalProperties":false,"effect":"process","power":{"key":"process:${name}","sentence":"a background process \"${name}\"${autoStart? (starts on boot):}"}},"description":"Long-lived background processes the daemon runs for this extension: a gateway holding a connection the daemon must not, a dev server. Managed the same way panel dev servers are, and startable and stoppable from the Extensions tab."},"agent":{"type":"object","properties":{"path":{"description":"Relative to the extension checkout. Absent ⇒ the checkout root.","type":"string"}},"additionalProperties":false,"power":{"key":"agent","sentence":"contributes skills, agents and hooks to the agent's turns"},"description":"Declare that this checkout is also a Claude Code plugin, so Claude Code turns pick up its skills, agents, hooks and commands. Only Claude Code reads it: give the agent tools with `contributes.tools`, which every runtime gets, and put a skill every runtime should read in a capability card's `skill`. MCP servers in the plugin's `.mcp.json` are deprecated, reach Claude Code alone, and are warned about at load."},"environment":{"type":"object","properties":{"fragment":{"type":"string","minLength":1,"description":"Checkout-relative path to a file holding ONLY RUN and ENV instructions. FROM and privileged directives are rejected: those stay daemon-owned."}},"required":["fragment"],"additionalProperties":false,"effect":"image","power":{"key":"environment","sentence":"bakes an environment fragment into the sandbox image"},"description":"A Dockerfile fragment baked into the sandbox image so your tools are actually installed at runtime: a whisper binary, a psql client. The owner approves the composed overlay and rebuilds out of band, so this does not take effect immediately."},"capabilities":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"catalog":{"type":"object","properties":{"name":{"type":"string","minLength":1},"art":{"description":"This extension's own mark, as a complete SVG document inline: the tier an author controls fully. Give it a viewBox and let it fill its own square edge to edge; it is drawn as the tile, not as a glyph on a plate. Kept as readable SVG text (not base64) so a registry reviewer can see what they are publishing, drawn inert so it cannot script the page, and capped at 4 KB. Anything that does not parse as SVG falls back to `logo`, then `icon`, then initials.","type":"string","maxLength":4096},"logo":{"description":"A simple-icons slug, fetched from a CDN: right for standing in for somebody else's product. Add a \"/<hex>\" suffix to force a colour for a mark that vanishes against the surface it lands on. Unreachable in an offline sandbox, so it falls back to `icon`, then to initials.","type":"string"},"icon":{"description":"A name from the host's own icon set, drawn when no simple-icons slug fits. It ships in the image, follows the theme and costs no request: what actually carries a first-party extension. An unknown name falls back to initials rather than to a hole.","type":"string"},"description":{"type":"string","minLength":1,"description":"ONE LINE: aim for 60 characters or fewer. The grid clamps it at two lines in a narrow pane, so a paragraph here is a paragraph the reader gets truncated. Everything longer belongs in `hint`."},"category":{"type":"string","minLength":1},"hint":{"description":"The paragraph, shown under the add form and searched from the catalog, so the words that identify this card to someone hunting for it (\"webauthn\", \"socket mode\") belong here even when the tile cannot show them.","type":"string"},"guide":{"description":"The walkthrough the install dialog renders for getting the credential this card asks for.","type":"object","properties":{"url":{"type":"string"},"urlFromField":{"type":"string"},"path":{"type":"string"},"linkLabel":{"type":"string"},"scopes":{"type":"string"},"steps":{"type":"array","items":{"type":"string"}}},"additionalProperties":false}},"required":["name","description","category"],"additionalProperties":false},"fields":{"minItems":1,"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-zA-Z][a-zA-Z0-9]*$"},"label":{"type":"string","minLength":1},"placeholder":{"type":"string"},"secret":{"description":"Mask it, and never echo it back.","type":"boolean"},"optional":{"type":"boolean"},"multiline":{"type":"boolean"},"advanced":{"description":"Fold this field behind the form's Advanced disclosure: for answers whose default is right for nearly everyone. The disclosure opens by itself while any advanced field holds a non-default value, so an edit never hides live settings.","type":"boolean"},"boolean":{"description":"Render it as a switch, carrying \"on\"/\"off\". For an opt-in EXTRA rather than a decision: a two-option picker says the same thing but presents a choice the user must make to proceed, sized like the required fields around it. A switch always holds a value, so a field like this never blocks a submit.","type":"boolean"},"hint":{"description":"A line under this control, for what the label alone cannot say: a host requirement, when a value takes effect. The card's own `hint` speaks for the whole card; this one is bound to the field it qualifies.","type":"string"},"rebuild":{"description":"This value only takes effect after the sandbox is rebuilt, because it rides the image overlay. Shown as a chip beside the label: two switches side by side, identical in every visible way, can otherwise cost five seconds or five minutes with no way to tell which.","type":"boolean"},"default":{"type":"string"},"options":{"description":"Turns the field into a select.","type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"],"additionalProperties":false}},"when":{"description":"Only show this field while a condition over the answers already given holds: `auth == 'key'`, `provider in ['ipsec', 'fortinet']`, `!advanced`. Supports `&&`, `||`, `!`, comparisons and `in`.","type":"string"},"value":{"description":"A fixed value baked into the config rather than asked for: how a card pins its discriminator (platform=\"reddit\", provider=\"stripe\"). Renders as nothing.","type":"string"},"totp":{"description":"This field holds a TOTP seed, the base32 key or otpauth:// URI a service shows when enrolling an authenticator app. Declare it with `secret: true`. Unlike an ordinary secret it never enters the agent's environment: the daemon mints the six-digit codes on demand and only those cross.","type":"boolean"}},"required":["key","label"],"additionalProperties":false}},"kind":{"type":"string","const":"cli"},"env":{"type":"object","propertyNames":{"type":"string","pattern":"^[A-Z][A-Z0-9_]*$"},"additionalProperties":{"type":"string"},"description":"The environment the agent's shell gets, as value templates over the fields: `${field}` substitutes, `${field:uri}` percent-encodes. Each name is suffixed per instance."},"skill":{"type":"string","minLength":1,"description":"Checkout-relative SKILL.md teaching the agent this tool. `${id}` in it is replaced with the instance name at apply time."},"fragment":{"description":"A Dockerfile fragment holding the client binary this tool needs (psql, mysql, whisper).","effect":"image","type":"string","minLength":1},"pack":{"description":"A sandbox feature pack name (whisper, llamacpp, browser, …) supplying this tool. Preferred over `fragment`: an image that already bakes the pack needs no rebuild, and there is no copy to drift.","effect":"image","type":"string","minLength":1},"probe":{"description":"One authenticated request that tests this card's settings before they are saved, so a wrong token or an unreachable host is answered on the form rather than by a card that says 'not connected' afterwards.","type":"object","properties":{"url":{"type":"string","minLength":1,"description":"The URL to call, as a template over the fields: `${field}` substitutes, `${field:uri}` percent-encodes. Same spelling as `env`."},"method":{"description":"Defaults to GET.","type":"string","enum":["GET","POST","HEAD"]},"headers":{"description":"The request headers, templated the same way: `{\"Authorization\": \"Bearer ${token}\"}`.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"identity":{"description":"A dotted path into the JSON answer naming who the caller is (\"login\", \"user.name\"), so success can say which account answered.","type":"string"},"insecure":{"description":"Accept a self-signed certificate, for a service whose local install ships one (Obsidian's Local REST API).","type":"boolean"}},"required":["url"],"additionalProperties":false},"hosts":{"description":"The hosts this card's credential is meant for, as templates over the fields like `env` (`api.github.com`, `*.githubusercontent.com`, `${url}`); a value that comes out as a URL counts as its host. The sandbox limits the credential's `{{secret:…}}` reference to them by default, so a use aimed anywhere else asks a person first. The owner can change or lift the list on the Secrets view.","type":"array","items":{"type":"string","minLength":1}},"mcp":{"description":"Deprecated: declare `contributes.tools` with `perCard` naming this card instead, and serve the tools with `api.tools.serve`. A path in this extension's backend (`server`) answering MCP over Streamable HTTP; every turn granted a card of this kind gets it as a server named by the card's id, each request arriving at `<path>/<card id>`.","effect":"mcp","mintsServer":true,"power":{"key":"capability-tools:${id}","sentence":"serves MCP tools to the agent for each \"${catalog.name}\" card"},"type":"string","pattern":"^[a-z0-9][a-z0-9-]*(?:\\/[a-z0-9][a-z0-9-]*)*$"}},"required":["id","catalog","fields","kind","env","skill"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"catalog":{"type":"object","properties":{"name":{"type":"string","minLength":1},"art":{"description":"This extension's own mark, as a complete SVG document inline: the tier an author controls fully. Give it a viewBox and let it fill its own square edge to edge; it is drawn as the tile, not as a glyph on a plate. Kept as readable SVG text (not base64) so a registry reviewer can see what they are publishing, drawn inert so it cannot script the page, and capped at 4 KB. Anything that does not parse as SVG falls back to `logo`, then `icon`, then initials.","type":"string","maxLength":4096},"logo":{"description":"A simple-icons slug, fetched from a CDN: right for standing in for somebody else's product. Add a \"/<hex>\" suffix to force a colour for a mark that vanishes against the surface it lands on. Unreachable in an offline sandbox, so it falls back to `icon`, then to initials.","type":"string"},"icon":{"description":"A name from the host's own icon set, drawn when no simple-icons slug fits. It ships in the image, follows the theme and costs no request: what actually carries a first-party extension. An unknown name falls back to initials rather than to a hole.","type":"string"},"description":{"type":"string","minLength":1,"description":"ONE LINE: aim for 60 characters or fewer. The grid clamps it at two lines in a narrow pane, so a paragraph here is a paragraph the reader gets truncated. Everything longer belongs in `hint`."},"category":{"type":"string","minLength":1},"hint":{"description":"The paragraph, shown under the add form and searched from the catalog, so the words that identify this card to someone hunting for it (\"webauthn\", \"socket mode\") belong here even when the tile cannot show them.","type":"string"},"guide":{"description":"The walkthrough the install dialog renders for getting the credential this card asks for.","type":"object","properties":{"url":{"type":"string"},"urlFromField":{"type":"string"},"path":{"type":"string"},"linkLabel":{"type":"string"},"scopes":{"type":"string"},"steps":{"type":"array","items":{"type":"string"}}},"additionalProperties":false}},"required":["name","description","category"],"additionalProperties":false},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-zA-Z][a-zA-Z0-9]*$"},"label":{"type":"string","minLength":1},"placeholder":{"type":"string"},"secret":{"description":"Mask it, and never echo it back.","type":"boolean"},"optional":{"type":"boolean"},"multiline":{"type":"boolean"},"advanced":{"description":"Fold this field behind the form's Advanced disclosure: for answers whose default is right for nearly everyone. The disclosure opens by itself while any advanced field holds a non-default value, so an edit never hides live settings.","type":"boolean"},"boolean":{"description":"Render it as a switch, carrying \"on\"/\"off\". For an opt-in EXTRA rather than a decision: a two-option picker says the same thing but presents a choice the user must make to proceed, sized like the required fields around it. A switch always holds a value, so a field like this never blocks a submit.","type":"boolean"},"hint":{"description":"A line under this control, for what the label alone cannot say: a host requirement, when a value takes effect. The card's own `hint` speaks for the whole card; this one is bound to the field it qualifies.","type":"string"},"rebuild":{"description":"This value only takes effect after the sandbox is rebuilt, because it rides the image overlay. Shown as a chip beside the label: two switches side by side, identical in every visible way, can otherwise cost five seconds or five minutes with no way to tell which.","type":"boolean"},"default":{"type":"string"},"options":{"description":"Turns the field into a select.","type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"],"additionalProperties":false}},"when":{"description":"Only show this field while a condition over the answers already given holds: `auth == 'key'`, `provider in ['ipsec', 'fortinet']`, `!advanced`. Supports `&&`, `||`, `!`, comparisons and `in`.","type":"string"},"value":{"description":"A fixed value baked into the config rather than asked for: how a card pins its discriminator (platform=\"reddit\", provider=\"stripe\"). Renders as nothing.","type":"string"},"totp":{"description":"This field holds a TOTP seed, the base32 key or otpauth:// URI a service shows when enrolling an authenticator app. Declare it with `secret: true`. Unlike an ordinary secret it never enters the agent's environment: the daemon mints the six-digit codes on demand and only those cross.","type":"boolean"}},"required":["key","label"],"additionalProperties":false}},"kind":{"type":"string","const":"browser"},"loginUrl":{"description":"What the sign-in window opens; the profile it persists IS the credential. Optional so one card can be the generic one that asks for the URL on its form instead, but a card must either pin this or declare a field that supplies it, or the window opens on nothing.","type":"string","format":"uri"},"homeUrl":{"description":"Where that same profile opens once it HAS a session: the owner's own hands on the connected browser. Separate from loginUrl because for some platforms the login lives on another site entirely (YouTube signs in at accounts.google.com).","type":"string","format":"uri"},"skill":{"type":"string","minLength":1,"description":"Checkout-relative SKILL.md teaching the agent this site's actions: rendered once per site, all its connected accounts on one roster (`${accounts}`), the core tool note at `${tools}`."}},"required":["id","catalog","fields","kind","skill"],"additionalProperties":false},{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"catalog":{"type":"object","properties":{"name":{"type":"string","minLength":1},"art":{"description":"This extension's own mark, as a complete SVG document inline: the tier an author controls fully. Give it a viewBox and let it fill its own square edge to edge; it is drawn as the tile, not as a glyph on a plate. Kept as readable SVG text (not base64) so a registry reviewer can see what they are publishing, drawn inert so it cannot script the page, and capped at 4 KB. Anything that does not parse as SVG falls back to `logo`, then `icon`, then initials.","type":"string","maxLength":4096},"logo":{"description":"A simple-icons slug, fetched from a CDN: right for standing in for somebody else's product. Add a \"/<hex>\" suffix to force a colour for a mark that vanishes against the surface it lands on. Unreachable in an offline sandbox, so it falls back to `icon`, then to initials.","type":"string"},"icon":{"description":"A name from the host's own icon set, drawn when no simple-icons slug fits. It ships in the image, follows the theme and costs no request: what actually carries a first-party extension. An unknown name falls back to initials rather than to a hole.","type":"string"},"description":{"type":"string","minLength":1,"description":"ONE LINE: aim for 60 characters or fewer. The grid clamps it at two lines in a narrow pane, so a paragraph here is a paragraph the reader gets truncated. Everything longer belongs in `hint`."},"category":{"type":"string","minLength":1},"hint":{"description":"The paragraph, shown under the add form and searched from the catalog, so the words that identify this card to someone hunting for it (\"webauthn\", \"socket mode\") belong here even when the tile cannot show them.","type":"string"},"guide":{"description":"The walkthrough the install dialog renders for getting the credential this card asks for.","type":"object","properties":{"url":{"type":"string"},"urlFromField":{"type":"string"},"path":{"type":"string"},"linkLabel":{"type":"string"},"scopes":{"type":"string"},"steps":{"type":"array","items":{"type":"string"}}},"additionalProperties":false}},"required":["name","description","category"],"additionalProperties":false},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-zA-Z][a-zA-Z0-9]*$"},"label":{"type":"string","minLength":1},"placeholder":{"type":"string"},"secret":{"description":"Mask it, and never echo it back.","type":"boolean"},"optional":{"type":"boolean"},"multiline":{"type":"boolean"},"advanced":{"description":"Fold this field behind the form's Advanced disclosure: for answers whose default is right for nearly everyone. The disclosure opens by itself while any advanced field holds a non-default value, so an edit never hides live settings.","type":"boolean"},"boolean":{"description":"Render it as a switch, carrying \"on\"/\"off\". For an opt-in EXTRA rather than a decision: a two-option picker says the same thing but presents a choice the user must make to proceed, sized like the required fields around it. A switch always holds a value, so a field like this never blocks a submit.","type":"boolean"},"hint":{"description":"A line under this control, for what the label alone cannot say: a host requirement, when a value takes effect. The card's own `hint` speaks for the whole card; this one is bound to the field it qualifies.","type":"string"},"rebuild":{"description":"This value only takes effect after the sandbox is rebuilt, because it rides the image overlay. Shown as a chip beside the label: two switches side by side, identical in every visible way, can otherwise cost five seconds or five minutes with no way to tell which.","type":"boolean"},"default":{"type":"string"},"options":{"description":"Turns the field into a select.","type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"],"additionalProperties":false}},"when":{"description":"Only show this field while a condition over the answers already given holds: `auth == 'key'`, `provider in ['ipsec', 'fortinet']`, `!advanced`. Supports `&&`, `||`, `!`, comparisons and `in`.","type":"string"},"value":{"description":"A fixed value baked into the config rather than asked for: how a card pins its discriminator (platform=\"reddit\", provider=\"stripe\"). Renders as nothing.","type":"string"},"totp":{"description":"This field holds a TOTP seed, the base32 key or otpauth:// URI a service shows when enrolling an authenticator app. Declare it with `secret: true`. Unlike an ordinary secret it never enters the agent's environment: the daemon mints the six-digit codes on demand and only those cross.","type":"boolean"}},"required":["key","label"],"additionalProperties":false}},"kind":{"type":"string","const":"device"},"skill":{"type":"string","minLength":1,"description":"Checkout-relative SKILL.md teaching the agent that machine's shell."}},"required":["id","catalog","fields","kind","skill"],"additionalProperties":false,"mintsServer":true},{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"catalog":{"type":"object","properties":{"name":{"type":"string","minLength":1},"art":{"description":"This extension's own mark, as a complete SVG document inline: the tier an author controls fully. Give it a viewBox and let it fill its own square edge to edge; it is drawn as the tile, not as a glyph on a plate. Kept as readable SVG text (not base64) so a registry reviewer can see what they are publishing, drawn inert so it cannot script the page, and capped at 4 KB. Anything that does not parse as SVG falls back to `logo`, then `icon`, then initials.","type":"string","maxLength":4096},"logo":{"description":"A simple-icons slug, fetched from a CDN: right for standing in for somebody else's product. Add a \"/<hex>\" suffix to force a colour for a mark that vanishes against the surface it lands on. Unreachable in an offline sandbox, so it falls back to `icon`, then to initials.","type":"string"},"icon":{"description":"A name from the host's own icon set, drawn when no simple-icons slug fits. It ships in the image, follows the theme and costs no request: what actually carries a first-party extension. An unknown name falls back to initials rather than to a hole.","type":"string"},"description":{"type":"string","minLength":1,"description":"ONE LINE: aim for 60 characters or fewer. The grid clamps it at two lines in a narrow pane, so a paragraph here is a paragraph the reader gets truncated. Everything longer belongs in `hint`."},"category":{"type":"string","minLength":1},"hint":{"description":"The paragraph, shown under the add form and searched from the catalog, so the words that identify this card to someone hunting for it (\"webauthn\", \"socket mode\") belong here even when the tile cannot show them.","type":"string"},"guide":{"description":"The walkthrough the install dialog renders for getting the credential this card asks for.","type":"object","properties":{"url":{"type":"string"},"urlFromField":{"type":"string"},"path":{"type":"string"},"linkLabel":{"type":"string"},"scopes":{"type":"string"},"steps":{"type":"array","items":{"type":"string"}}},"additionalProperties":false}},"required":["name","description","category"],"additionalProperties":false},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-zA-Z][a-zA-Z0-9]*$"},"label":{"type":"string","minLength":1},"placeholder":{"type":"string"},"secret":{"description":"Mask it, and never echo it back.","type":"boolean"},"optional":{"type":"boolean"},"multiline":{"type":"boolean"},"advanced":{"description":"Fold this field behind the form's Advanced disclosure: for answers whose default is right for nearly everyone. The disclosure opens by itself while any advanced field holds a non-default value, so an edit never hides live settings.","type":"boolean"},"boolean":{"description":"Render it as a switch, carrying \"on\"/\"off\". For an opt-in EXTRA rather than a decision: a two-option picker says the same thing but presents a choice the user must make to proceed, sized like the required fields around it. A switch always holds a value, so a field like this never blocks a submit.","type":"boolean"},"hint":{"description":"A line under this control, for what the label alone cannot say: a host requirement, when a value takes effect. The card's own `hint` speaks for the whole card; this one is bound to the field it qualifies.","type":"string"},"rebuild":{"description":"This value only takes effect after the sandbox is rebuilt, because it rides the image overlay. Shown as a chip beside the label: two switches side by side, identical in every visible way, can otherwise cost five seconds or five minutes with no way to tell which.","type":"boolean"},"default":{"type":"string"},"options":{"description":"Turns the field into a select.","type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"],"additionalProperties":false}},"when":{"description":"Only show this field while a condition over the answers already given holds: `auth == 'key'`, `provider in ['ipsec', 'fortinet']`, `!advanced`. Supports `&&`, `||`, `!`, comparisons and `in`.","type":"string"},"value":{"description":"A fixed value baked into the config rather than asked for: how a card pins its discriminator (platform=\"reddit\", provider=\"stripe\"). Renders as nothing.","type":"string"},"totp":{"description":"This field holds a TOTP seed, the base32 key or otpauth:// URI a service shows when enrolling an authenticator app. Declare it with `secret: true`. Unlike an ordinary secret it never enters the agent's environment: the daemon mints the six-digit codes on demand and only those cross.","type":"boolean"}},"required":["key","label"],"additionalProperties":false}},"kind":{"type":"string","const":"webext"},"install":{"description":"Where this browser's extension is installed from: its store listing, or a page offering the build.","type":"string","format":"uri"},"skill":{"type":"string","minLength":1,"description":"Checkout-relative SKILL.md teaching the agent to drive this browser."}},"required":["id","catalog","fields","kind","skill"],"additionalProperties":false,"mintsServer":true},{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"catalog":{"type":"object","properties":{"name":{"type":"string","minLength":1},"art":{"description":"This extension's own mark, as a complete SVG document inline: the tier an author controls fully. Give it a viewBox and let it fill its own square edge to edge; it is drawn as the tile, not as a glyph on a plate. Kept as readable SVG text (not base64) so a registry reviewer can see what they are publishing, drawn inert so it cannot script the page, and capped at 4 KB. Anything that does not parse as SVG falls back to `logo`, then `icon`, then initials.","type":"string","maxLength":4096},"logo":{"description":"A simple-icons slug, fetched from a CDN: right for standing in for somebody else's product. Add a \"/<hex>\" suffix to force a colour for a mark that vanishes against the surface it lands on. Unreachable in an offline sandbox, so it falls back to `icon`, then to initials.","type":"string"},"icon":{"description":"A name from the host's own icon set, drawn when no simple-icons slug fits. It ships in the image, follows the theme and costs no request: what actually carries a first-party extension. An unknown name falls back to initials rather than to a hole.","type":"string"},"description":{"type":"string","minLength":1,"description":"ONE LINE: aim for 60 characters or fewer. The grid clamps it at two lines in a narrow pane, so a paragraph here is a paragraph the reader gets truncated. Everything longer belongs in `hint`."},"category":{"type":"string","minLength":1},"hint":{"description":"The paragraph, shown under the add form and searched from the catalog, so the words that identify this card to someone hunting for it (\"webauthn\", \"socket mode\") belong here even when the tile cannot show them.","type":"string"},"guide":{"description":"The walkthrough the install dialog renders for getting the credential this card asks for.","type":"object","properties":{"url":{"type":"string"},"urlFromField":{"type":"string"},"path":{"type":"string"},"linkLabel":{"type":"string"},"scopes":{"type":"string"},"steps":{"type":"array","items":{"type":"string"}}},"additionalProperties":false}},"required":["name","description","category"],"additionalProperties":false},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-zA-Z][a-zA-Z0-9]*$"},"label":{"type":"string","minLength":1},"placeholder":{"type":"string"},"secret":{"description":"Mask it, and never echo it back.","type":"boolean"},"optional":{"type":"boolean"},"multiline":{"type":"boolean"},"advanced":{"description":"Fold this field behind the form's Advanced disclosure: for answers whose default is right for nearly everyone. The disclosure opens by itself while any advanced field holds a non-default value, so an edit never hides live settings.","type":"boolean"},"boolean":{"description":"Render it as a switch, carrying \"on\"/\"off\". For an opt-in EXTRA rather than a decision: a two-option picker says the same thing but presents a choice the user must make to proceed, sized like the required fields around it. A switch always holds a value, so a field like this never blocks a submit.","type":"boolean"},"hint":{"description":"A line under this control, for what the label alone cannot say: a host requirement, when a value takes effect. The card's own `hint` speaks for the whole card; this one is bound to the field it qualifies.","type":"string"},"rebuild":{"description":"This value only takes effect after the sandbox is rebuilt, because it rides the image overlay. Shown as a chip beside the label: two switches side by side, identical in every visible way, can otherwise cost five seconds or five minutes with no way to tell which.","type":"boolean"},"default":{"type":"string"},"options":{"description":"Turns the field into a select.","type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"],"additionalProperties":false}},"when":{"description":"Only show this field while a condition over the answers already given holds: `auth == 'key'`, `provider in ['ipsec', 'fortinet']`, `!advanced`. Supports `&&`, `||`, `!`, comparisons and `in`.","type":"string"},"value":{"description":"A fixed value baked into the config rather than asked for: how a card pins its discriminator (platform=\"reddit\", provider=\"stripe\"). Renders as nothing.","type":"string"},"totp":{"description":"This field holds a TOTP seed, the base32 key or otpauth:// URI a service shows when enrolling an authenticator app. Declare it with `secret: true`. Unlike an ordinary secret it never enters the agent's environment: the daemon mints the six-digit codes on demand and only those cross.","type":"boolean"}},"required":["key","label"],"additionalProperties":false}},"kind":{"type":"string","const":"phone"},"install":{"description":"Where this phone's app is installed from: its store listing, or a page offering the build.","type":"string","format":"uri"},"skill":{"type":"string","minLength":1,"description":"Checkout-relative SKILL.md teaching the agent to work on this phone."}},"required":["id","catalog","fields","kind","skill"],"additionalProperties":false,"mintsServer":true},{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"catalog":{"type":"object","properties":{"name":{"type":"string","minLength":1},"art":{"description":"This extension's own mark, as a complete SVG document inline: the tier an author controls fully. Give it a viewBox and let it fill its own square edge to edge; it is drawn as the tile, not as a glyph on a plate. Kept as readable SVG text (not base64) so a registry reviewer can see what they are publishing, drawn inert so it cannot script the page, and capped at 4 KB. Anything that does not parse as SVG falls back to `logo`, then `icon`, then initials.","type":"string","maxLength":4096},"logo":{"description":"A simple-icons slug, fetched from a CDN: right for standing in for somebody else's product. Add a \"/<hex>\" suffix to force a colour for a mark that vanishes against the surface it lands on. Unreachable in an offline sandbox, so it falls back to `icon`, then to initials.","type":"string"},"icon":{"description":"A name from the host's own icon set, drawn when no simple-icons slug fits. It ships in the image, follows the theme and costs no request: what actually carries a first-party extension. An unknown name falls back to initials rather than to a hole.","type":"string"},"description":{"type":"string","minLength":1,"description":"ONE LINE: aim for 60 characters or fewer. The grid clamps it at two lines in a narrow pane, so a paragraph here is a paragraph the reader gets truncated. Everything longer belongs in `hint`."},"category":{"type":"string","minLength":1},"hint":{"description":"The paragraph, shown under the add form and searched from the catalog, so the words that identify this card to someone hunting for it (\"webauthn\", \"socket mode\") belong here even when the tile cannot show them.","type":"string"},"guide":{"description":"The walkthrough the install dialog renders for getting the credential this card asks for.","type":"object","properties":{"url":{"type":"string"},"urlFromField":{"type":"string"},"path":{"type":"string"},"linkLabel":{"type":"string"},"scopes":{"type":"string"},"steps":{"type":"array","items":{"type":"string"}}},"additionalProperties":false}},"required":["name","description","category"],"additionalProperties":false},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","pattern":"^[a-zA-Z][a-zA-Z0-9]*$"},"label":{"type":"string","minLength":1},"placeholder":{"type":"string"},"secret":{"description":"Mask it, and never echo it back.","type":"boolean"},"optional":{"type":"boolean"},"multiline":{"type":"boolean"},"advanced":{"description":"Fold this field behind the form's Advanced disclosure: for answers whose default is right for nearly everyone. The disclosure opens by itself while any advanced field holds a non-default value, so an edit never hides live settings.","type":"boolean"},"boolean":{"description":"Render it as a switch, carrying \"on\"/\"off\". For an opt-in EXTRA rather than a decision: a two-option picker says the same thing but presents a choice the user must make to proceed, sized like the required fields around it. A switch always holds a value, so a field like this never blocks a submit.","type":"boolean"},"hint":{"description":"A line under this control, for what the label alone cannot say: a host requirement, when a value takes effect. The card's own `hint` speaks for the whole card; this one is bound to the field it qualifies.","type":"string"},"rebuild":{"description":"This value only takes effect after the sandbox is rebuilt, because it rides the image overlay. Shown as a chip beside the label: two switches side by side, identical in every visible way, can otherwise cost five seconds or five minutes with no way to tell which.","type":"boolean"},"default":{"type":"string"},"options":{"description":"Turns the field into a select.","type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}},"required":["value","label"],"additionalProperties":false}},"when":{"description":"Only show this field while a condition over the answers already given holds: `auth == 'key'`, `provider in ['ipsec', 'fortinet']`, `!advanced`. Supports `&&`, `||`, `!`, comparisons and `in`.","type":"string"},"value":{"description":"A fixed value baked into the config rather than asked for: how a card pins its discriminator (platform=\"reddit\", provider=\"stripe\"). Renders as nothing.","type":"string"},"totp":{"description":"This field holds a TOTP seed, the base32 key or otpauth:// URI a service shows when enrolling an authenticator app. Declare it with `secret: true`. Unlike an ordinary secret it never enters the agent's environment: the daemon mints the six-digit codes on demand and only those cross.","type":"boolean"}},"required":["key","label"],"additionalProperties":false}},"kind":{"type":"string","const":"agent"}},"required":["id","catalog","fields","kind"],"additionalProperties":false}],"power":{"key":"capability:${id}","sentence":"a ${kind} capability card \"${catalog.name}\""}},"description":"Capability cards this pack adds to the \"+\" grid: a connected CLI tool, a site the agent acts on as the owner through the shared browser, an operating system pack, a browser family the owner connects their own copy of, or a preset over a core kind. The card and its form are data here; the machinery that acts on them is core, which is why a card may only name one of these five kinds."},"listener":{"type":"object","properties":{"provider":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$","description":"The slug this source's automation triggers fire on."},"events":{"minItems":1,"type":"array","items":{"type":"object","properties":{"type":{"type":"string","pattern":"^[a-z0-9][a-z0-9_]*$"},"label":{"type":"string","minLength":1}},"required":["type","label"],"additionalProperties":false},"description":"The event types this source can fire, with the wording the automation editor offers them under. The daemon accepts no others."},"automation":{"type":"object","properties":{"label":{"type":"string","minLength":1},"mentionLabel":{"description":"Only for a source whose message events distinguish being addressed. Absent ⇒ the editor offers no mention-only filter, rather than inventing semantics you did not promise.","type":"string","minLength":1},"channel":{"type":"object","properties":{"label":{"type":"string","minLength":1},"placeholder":{"type":"string","minLength":1},"hint":{"description":"The sentence under the input, for a filter whose empty case is easy to get wrong.","type":"string","minLength":1}},"required":["label","placeholder"],"additionalProperties":false,"description":"The primary narrowing filter, a channel, a room, a repo."},"branchField":{"description":"A second narrowing axis, for a source whose events carry one: a pipeline's git ref, so a trigger can say \"the branch that ships\" rather than \"every agent's every failure\".","type":"object","properties":{"label":{"type":"string","minLength":1},"placeholder":{"type":"string","minLength":1},"hint":{"description":"The sentence under the input, for a filter whose empty case is easy to get wrong.","type":"string","minLength":1}},"required":["label","placeholder"],"additionalProperties":false},"sender":{"description":"How this source names a sender, and where a person finds that id. Declaring it promises that `author.id` is an identity the service vouches for, not a name the sender typed; absent ⇒ the editor offers no sender rules on this source.","type":"object","properties":{"label":{"type":"string","minLength":1},"placeholder":{"type":"string","minLength":1},"hint":{"description":"The sentence under the input, for a filter whose empty case is easy to get wrong.","type":"string","minLength":1}},"required":["label","placeholder"],"additionalProperties":false},"senderGroup":{"description":"How this source names a sender's group, for a source whose messages carry `author.groups` (a Discord role). Absent ⇒ rules match ids only.","type":"object","properties":{"label":{"type":"string","minLength":1},"placeholder":{"type":"string","minLength":1},"hint":{"description":"The sentence under the input, for a filter whose empty case is easy to get wrong.","type":"string","minLength":1}},"required":["label","placeholder"],"additionalProperties":false},"starterPrompt":{"type":"string","minLength":1,"description":"The first prompt a new automation on this source is prefilled with. You own the payload vocabulary, so you own the prompt that explains it."}},"required":["label","channel","starterPrompt"],"additionalProperties":false,"description":"How the generic automation editor presents this source: its name, its filters, and the prompt it starts people on."}},"required":["provider","events","automation"],"additionalProperties":false,"power":{"key":"listener:${provider}","sentence":"a realtime listener provider \"${provider}\""},"description":"A realtime event source this extension supplies, so automations can trigger on it. One declaration feeds both halves: the daemon accepts these event types and serves this provider's control surface, and the automation editor derives its source picker, filters and starter prompt from it, so a newly installed listener is configurable without a matching app release."},"automationTemplates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"Prefills the automation name, and is what \"does one of these exist already\" is asked by, so spell it as an id, not as prose."},"title":{"type":"string","minLength":1},"logo":{"description":"A simple-icons slug for the card.","type":"string","minLength":1},"icon":{"description":"A name from the host's icon set, drawn when no simple-icons slug fits.","type":"string","minLength":1},"requires":{"description":"Capability providers that make this template work: any one connected is enough (fixing CI rides github or gitlab). Omitted ⇒ nothing to connect, so it is always offered.","type":"array","items":{"type":"string","minLength":1}},"trigger":{"type":"object","properties":{"kind":{"type":"string","enum":["schedule","event","listener","workspace"]},"cron":{"type":"string","minLength":1},"provider":{"type":"string","minLength":1},"eventType":{"type":"string","minLength":1},"event":{"type":"string","minLength":1}},"required":["kind"],"additionalProperties":false,"description":"What wakes it. Checked against the real trigger schema when the daemon builds the catalogue, so a template can never offer one that would be refused."},"guard":{"description":"A condition that must hold before the turn runs: what makes a template safe to leave switched on.","type":"string","minLength":1},"holdForSeconds":{"description":"Wait this long and coalesce repeats, rather than firing on every event.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"prompt":{"type":"string","minLength":1,"description":"The turn this starts. You own the trigger's payload vocabulary, so you own the prompt that reads it."},"note":{"type":"string","minLength":1},"setup":{"description":"What the user must do themselves before this can work.","type":"string","minLength":1},"description":{"type":"string","minLength":1},"offer":{"description":"Absent ⇒ it waits in the gallery, where you go once you know what you want. `create` puts a card on the page that makes it, switched off, in one click. `configure` puts one there that opens the dialog prefilled, for a template that cannot work unconfigured. Both are for what a user would never think to go looking for: mark everything as offered and you have rebuilt the gallery with extra steps.","type":"string","enum":["create","configure"]},"chore":{"description":"Whether what this makes watches THIS codebase rather than the outside world. Declared rather than read off the trigger: a nightly dependency sweep and a nightly Stripe poll are both schedules.","type":"boolean"}},"required":["id","title","trigger","prompt"],"additionalProperties":false},"description":"Starting points this pack offers in the automation composer, a trigger, a prompt written for that trigger's payload, and whatever guard makes it safe to leave on. Declared by whoever knows the service rather than by the composer, so they appear when your pack is installed and disappear with it. Pure prefill: creating one makes an ordinary automation."},"bin":{"type":"string","minLength":1,"power":{"key":"bin","sentence":"puts its shipped tools on the agent's PATH"},"description":"A checkout-relative directory of executables the daemon puts on the agent's PATH every turn, how you ship the agent a command-line tool. The files are the approved code themselves: they ride the pinned checkout, and the daemon only adds the directory to PATH."},"tools":{"type":"object","properties":{"perCard":{"description":"The id of one of this extension's `cli` capability cards. Every turn granted a card of that kind gets one server named by the card's id, handed that card's settings (secrets included) with each call. Absent ⇒ one server for the extension, named by its `name`, in every turn while the extension is enabled.","type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"process":{"description":"A process from `contributes.processes`, declared with `port: \"auto\"`, that answers MCP over Streamable HTTP at `path` on its port. Absent ⇒ your `server` bundle serves the tools.","type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"path":{"description":"Where the MCP endpoint answers, without a leading or trailing slash: on the process's port, or in your backend's own namespace when your `server` bundle speaks MCP itself. Absent with no `process` ⇒ the host serves what `api.tools.serve` returns, which is what you want: the host owns the transport, the deadlines and the card lookup. With `perCard`, a request arrives at `<path>/<card id>`.","type":"string","pattern":"^[a-z0-9][a-z0-9-]*(?:\\/[a-z0-9][a-z0-9-]*)*$"}},"additionalProperties":false,"effect":"mcp","mintsServer":true,"power":{"key":"tools${perCard?-${perCard}:}","sentence":"gives the agent MCP tools${perCard?, one server for each \"${perCard}\" card:}"},"description":"Tools for the agent, as an MCP server the daemon mounts into every turn and every runtime (Claude Code, Codex, Cursor, ACP agents). Serve them from your `server` bundle with `api.tools.serve((card) => [...])`, or from a declared process's port. Replaces an agent plugin's `.mcp.json`, which only Claude Code read."}},"additionalProperties":false}},"required":["publisher","name","version","engines"],"additionalProperties":false,"description":"What it declares about itself."},"powers":{"type":"object","properties":{"added":{"type":"array","items":{"type":"string"},"description":"What the new version asks for that the running one does not. The whole point of the comparison."},"removed":{"type":"array","items":{"type":"string"},"description":"What it no longer asks for."},"unchanged":{"type":"array","items":{"type":"string"},"description":"What stays the same."}},"required":["added","removed","unchanged"],"additionalProperties":false,"description":"What saying yes allows, as plain sentences. Against what was approved before when something was: `added` is what it asks for now that it did not then. Never approved before, everything it declares is `added`."},"approvedBefore":{"type":"boolean","description":"An earlier shape of it was approved, and the powers it declares have changed since, which is what put it back here."},"digest":{"type":"string","pattern":"^[0-9a-f]{64}$","description":"The fingerprint of the powers shown, sent back with the approval so a change made while you were reading is caught rather than approved."}},"required":["id","dir","manifest","powers","approvedBefore","digest"],"additionalProperties":false},"description":"Extensions written in this workspace that wait for the owner's approval before anything of theirs runs: never approved, or approved when they declared less than they do now."},"updatesCheckedAt":{"description":"When updates were last looked for. Absent until the first check has run. Sent so a screen can say checked an hour ago rather than presenting staleness as certainty.","type":"string"}},"required":["extensions","invalid","pending"],"additionalProperties":false}}}}}}},"/extensions/workspace":{"post":{"operationId":"extensions.create","summary":"Write a new extension in place","description":"Scaffolds a working extension into this workspace and installs it. The only call here that creates one, and it exists because that folder is otherwise reachable only through an agent's file tools, which is a fine way to change an extension and a poor way to meet the idea of one.","tags":["Extensions"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"publisher":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$","description":"Who it is by, which together with the name makes its id."},"name":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$","description":"What it is called."}},"required":["publisher","name"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The id it was given."},"dir":{"type":"string","description":"Where its files are, so you can open them."}},"required":["id","dir"],"additionalProperties":false}}}}}}},"/extensions/{id}/removal":{"get":{"operationId":"extensions.removalPlan","summary":"What removing an extension would take away","description":"Everything one removal destroys, before it happens: the files deleted, the connections configured from its cards, the settings and credentials forgotten, the background processes stopped, and the owner's own automations that quietly stop firing. Also answerable for an extension that cannot be removed, in which case it says why.","tags":["Extensions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which connection."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":121,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_.-]*$","description":"The extension's id, as the list addresses it."},"name":{"type":"string","description":"Its publisher.name identity, which is the key its settings and switch are stored under."},"version":{"type":"string","description":"The version being removed."},"source":{"type":"string","enum":["builtin","installed","workspace"],"description":"Where its code comes from, which decides what removal means."},"blocked":{"description":"Why this one cannot be removed, when it cannot. Present means every other field is what would go if it could.","type":"string"},"files":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"Workspace-relative."},"detail":{"type":"string","description":"What is in there."}},"required":["path","detail"],"additionalProperties":false},"description":"Directories deleted outright. For an extension written here this is the owner's own source, which nothing else keeps a copy of."},"connections":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The name the owner gave it, which is also the agent's handle for it."},"kind":{"type":"string","description":"Which core kind it is underneath: cli, browser, host or webext."},"entry":{"type":"string","description":"The catalog entry it was added from, named as the grid names it."},"secrets":{"type":"array","items":{"type":"string"},"description":"Credential fields stored for it, by name. The values are deleted with the entry and cannot be recovered from here."},"effect":{"type":"string","description":"What tearing it down actually takes away, in one sentence."}},"required":["id","kind","entry","secrets","effect"],"additionalProperties":false},"description":"Connections configured from its cards, which are removed with it."},"settings":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"Which setting."},"secret":{"type":"boolean","description":"Whether its value is a stored credential."}},"required":["key","secret"],"additionalProperties":false},"description":"Values the owner entered for this extension that are forgotten. Only keys actually holding a value are listed."},"processes":{"type":"array","items":{"type":"string"},"description":"Background processes it declared, stopped before its files go."},"automations":{"type":"array","items":{"type":"string"},"description":"Automations of the owner's own that wake on a listener this extension provides. They are NOT removed, and are listed because they stop firing, which is the sort of thing a removal is otherwise discovered by."},"rebuildNeeded":{"type":"boolean","description":"It bakes a layer into the sandbox image, so what it added to the image is only gone after the next environment rebuild."},"keeps":{"type":"array","items":{"type":"string"},"description":"What removal deliberately leaves alone, so the list of what goes can be read as complete."}},"required":["id","name","version","source","files","connections","settings","processes","automations","rebuildNeeded","keeps"],"additionalProperties":false}}}}}}},"/extensions/{id}/remove":{"post":{"operationId":"extensions.remove","summary":"Remove an extension","description":"Uninstalls it and everything that only existed because it was here: the connections added from its cards, with their stored credentials, its settings, its switch and its update record. What the owner made with it — automations, files in the workspace — is left alone. Owner only, for the same reason installing is. Built-in extensions cannot be removed; switch them off instead.","tags":["Extensions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which connection."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"It is gone."},"connections":{"type":"array","items":{"type":"string"},"description":"Which configured connections went with it, by name."},"rebuildNeeded":{"description":"Its image layer is still in the running sandbox until the next environment rebuild; nothing else is pending.","type":"boolean"}},"required":["ok","connections"],"additionalProperties":false}}}}}}},"/extensions/{id}/settings":{"get":{"operationId":"extensions.settings","summary":"An extension's settings","description":"The current values for the settings this extension declared it has.","tags":["Extensions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which connection."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number","boolean"]},"description":"The values, minus anything marked secret."},"secretsSet":{"type":"array","items":{"type":"string"},"description":"Which of its secret settings actually hold a value. Names only: the values themselves never come back."}},"required":["settings","secretsSet"],"additionalProperties":false}}}}}},"post":{"operationId":"extensions.setSettings","summary":"Change an extension's settings","description":"Writes new values. A key the extension never declared is refused rather than quietly stored, the same honesty rule that governs everything else an extension claims.","tags":["Extensions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which extension."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number","boolean"]},"description":"The values to write. A key the extension never declared is refused rather than quietly stored."}},"required":["settings"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/extensions/{id}/approve":{"post":{"operationId":"extensions.approve","summary":"Let a workspace extension run","description":"Approves an extension written in this workspace with the powers it declares now: its background processes start, its backend loads, and what it contributes is wired from the next turn. Editing its code keeps the approval; declaring a power it did not have puts it back in the pending list. Owner and maintainers only.","tags":["Extensions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":121,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_.-]*$","description":"Which extension."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"digest":{"type":"string","pattern":"^[0-9a-f]{64}$","description":"The fingerprint of the powers you read, from the pending list. A mismatch means they changed since, and nothing is approved."}},"required":["digest"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/extensions/{id}/enabled":{"post":{"operationId":"extensions.setEnabled","summary":"Turn an extension on or off","description":"The owner's switch. Turning one off stops its background processes at once. What it contributes to an agent's tools is rebuilt at the start of the next turn, and anything it adds to the sandbox image only at the next rebuild.","tags":["Extensions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which extension."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean","description":"On or off."}},"required":["enabled"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/extensions/usage":{"post":{"operationId":"extensions.recordUsage","summary":"Record what extensions just used","description":"One batch written by the app rather than measured by the daemon, because the permission gate runs in the browser: from the sandbox's side extension traffic is indistinguishable from anyone else's. This is how the record of which powers each extension actually exercises gets kept without one reporting request per extension.","tags":["Extensions"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reports":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991}},"description":"Each extension that called something, and the counts against the declared powers it exercised."}},"required":["reports"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/extensions/{id}/readiness":{"get":{"operationId":"extensions.readiness","summary":"Whether an extension is fit to share","description":"The checks that can be answered from an extension's own files, for an author about to publish. Read on demand rather than carried on the list, because it reads the code off disk each time.","tags":["Extensions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which connection."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"checks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Which check."},"label":{"type":"string","description":"What it is called."},"status":{"type":"string","enum":["pass","warn","fail"],"description":"How it went. A warning is a real third answer rather than a soft failure."},"detail":{"type":"string","description":"What it found."}},"required":["id","label","status","detail"],"additionalProperties":false},"description":"Everything that can be checked from the extension's own files, for an author about to publish."}},"required":["checks"],"additionalProperties":false}}}}}}},"/extensions/updates/check":{"post":{"operationId":"extensions.checkUpdates","summary":"Look for extension updates now","description":"Compares every installed extension against its source and reports what is newer, what carries an advisory and what looks unhealthy. This also happens on a schedule; call it to check on demand.","tags":["Extensions"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"The check ran."},"checkedAt":{"type":"string","description":"When, so a screen can date the answer."}},"required":["ok","checkedAt"],"additionalProperties":false}}}}}}},"/extensions/{id}/update/preview":{"post":{"operationId":"extensions.updatePreview","summary":"What an update would change","description":"The read before the click: which versions are involved and exactly which powers the new code asks for that the running one does not. Costs one throwaway copy of the source, the same as browsing a registry entry.","tags":["Extensions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":121,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_.-]*$","description":"Which extension."}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"ref":{"description":"Which commit, in full. Leave it out for whatever the last check found, which is what most callers mean.","type":"string","pattern":"^[0-9a-f]{40}$"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ref":{"type":"string","description":"The commit this would install."},"version":{"type":"string","description":"What that version calls itself."},"installedVersion":{"type":"string","description":"What is running now."},"engines":{"type":"string","description":"Which sandbox versions the new one says it needs."},"compatible":{"type":"boolean","description":"Whether this sandbox is one of them."},"powers":{"type":"object","properties":{"added":{"type":"array","items":{"type":"string"},"description":"What the new version asks for that the running one does not. The whole point of the comparison."},"removed":{"type":"array","items":{"type":"string"},"description":"What it no longer asks for."},"unchanged":{"type":"array","items":{"type":"string"},"description":"What stays the same."}},"required":["added","removed","unchanged"],"additionalProperties":false,"description":"Exactly what the new code asks for that the running one does not. This is what approving an update is approving."}},"required":["ref","version","installedVersion","engines","compatible","powers"],"additionalProperties":false}}}}}}},"/extensions/{id}/update":{"post":{"operationId":"extensions.applyUpdate","summary":"Update an extension","description":"The whole swap as one transaction: fetch, check, quiet the running one, replace it while keeping the outgoing copy one step back, restart and watch it come up. The existing configuration is kept, so a token for a private source survives what removing and re-adding would lose. Owner only, because it changes what code runs.","tags":["Extensions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":121,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_.-]*$","description":"Which extension."}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"ref":{"description":"Which commit, in full. Leave it out for whatever the last check found, which is what most callers mean.","type":"string","pattern":"^[0-9a-f]{40}$"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"It went through."},"ref":{"type":"string","description":"Which commit is now running."},"rebuildNeeded":{"description":"The new version changes what the sandbox image contains, so a one-time rebuild is still pending and the update is not wholly landed yet.","type":"boolean"}},"required":["ok","ref"],"additionalProperties":false}}}}}}},"/extensions/{id}/revert":{"post":{"operationId":"extensions.revert","summary":"Go back to the previous version","description":"Swaps the copy kept from before the last update back into place. Owner only, for the same reason updating is.","tags":["Extensions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which connection."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"It went through."},"ref":{"type":"string","description":"Which commit is now running."},"rebuildNeeded":{"description":"The new version changes what the sandbox image contains, so a one-time rebuild is still pending and the update is not wholly landed yet.","type":"boolean"}},"required":["ok","ref"],"additionalProperties":false}}}}}}},"/extensions/{id}/update-policy":{"post":{"operationId":"extensions.setUpdatePolicy","summary":"How an extension should handle its own updates","description":"The owner's standing answer for one extension: tell me, have an agent look at it, or just do it. Security advisories can be opted out of separately.","tags":["Extensions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":121,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_.-]*$","description":"Which extension."}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"updates":{"description":"What to do about a newer version: tell you, have an agent read the difference first, or just take it.","type":"string","enum":["notify","agent","auto"]},"advisories":{"description":"What to do about a security warning: switch it off at once, or tell you.","type":"string","enum":["auto-disable","notify"]}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/extensions/{id}/processes/{name}":{"get":{"operationId":"extensions.processStatus","summary":"Whether an extension's background process is up","description":"The state of one process an extension declared, with the port it was given and its preview address if it has one.","tags":["Extensions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which extension."}},{"name":"name","in":"path","required":true,"schema":{"type":"string","description":"Which of its declared processes."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Which process."},"running":{"type":"boolean","description":"Whether it is up. False with a port means it crashed and the supervisor is waiting to retry it."},"port":{"description":"The port it was given.","type":"number"},"restarts":{"description":"How many times it died and was brought back since it was started. A growing number is a service in trouble.","type":"number"},"lastExitCode":{"description":"How it last exited, when it has crashed at least once.","type":"number"},"previewUrl":{"description":"Where to open it, when it has an address.","type":"string"}},"required":["name","running"],"additionalProperties":false}}}}}}},"/extensions/{id}/processes/{name}/start":{"post":{"operationId":"extensions.processStart","summary":"Start an extension's background process","description":"Brings one of an extension's declared processes up in an attachable terminal.","tags":["Extensions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which extension."}},{"name":"name","in":"path","required":true,"schema":{"type":"string","description":"Which of its declared processes."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/extensions/{id}/processes/{name}/stop":{"post":{"operationId":"extensions.processStop","summary":"Stop an extension's background process","description":"Shuts one of an extension's declared processes down and frees its port.","tags":["Extensions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which extension."}},{"name":"name","in":"path","required":true,"schema":{"type":"string","description":"Which of its declared processes."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/extensions/dev":{"get":{"operationId":"extensions.devList","summary":"Installed extensions and their source checkouts","description":"Every extension installed from a repository, with its pinned commit, whether it runs from a source checkout, and the checkout of its repository this workspace holds, if any. The calling conversation's own copy is named first.","tags":["Extensions"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"extensions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":121,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_.-]*$","description":"Its connection id."},"name":{"type":"string","description":"Its manifest id, publisher.name."},"commit":{"type":"string","description":"The pinned commit, in full."},"dev":{"description":"Present while it is pointed at a source checkout.","type":"object","properties":{"path":{"type":"string","description":"The source checkout it was pointed at, such as extensions/maintenance: relative to the workspace, or to the conversation's own copy of it when `conversation` is set."},"conversation":{"description":"The conversation whose isolated copy of the workspace holds that checkout. Absent means the workspace itself. That copy goes when the conversation does, and the pinned version runs again then.","type":"string"},"uncommitted":{"description":"How many files in the checkout differ from its last commit, a rebuilt bundle included. Absent when git could not say.","type":"integer","minimum":0,"maximum":9007199254740991},"revision":{"description":"A short fingerprint of the browser bundle served from the checkout. It changes with every rebuild, which is how a screen tells the copy it loaded is behind. Absent while held, and for an extension with no browser half.","type":"string"},"held":{"description":"Why the checkout is not what runs, as a sentence: it is gone, holds a different extension, is not built yet, or asks for powers the pinned version was never approved for. Present means the pinned version still runs.","type":"string"}},"required":["path"],"additionalProperties":false},"checkout":{"description":"A checkout of its source repository in the workspace, which is where a change to it belongs. Absent when the workspace has none.","type":"object","properties":{"path":{"type":"string","description":"Relative to the workspace, or to the conversation's copy of it when `conversation` is set."},"conversation":{"description":"Set when the calling conversation's own copy is the one found.","type":"string"}},"required":["path"],"additionalProperties":false}},"required":["id","name","commit"],"additionalProperties":false},"description":"Every extension installed from a repository. Built-in and workspace extensions are not here: they already run from their source."}},"required":["extensions"],"additionalProperties":false}}}}}}},"/extensions/{id}/dev":{"post":{"operationId":"extensions.devSet","summary":"Run an installed extension from its source checkout","description":"Serves the extension's browser bundle, backend, processes, skills and contributions from a checkout of its source instead of its pinned copy, so a change to it shows on reload without being landed first. The checkout must hold the same extension and declare the same powers; one asking for more, or not built yet, is held and the pinned version keeps running, with the reason. Nothing is installed or updated: the pinned copy stays where it is.","tags":["Extensions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which installed extension: its connection id (intentic-maintenance), its manifest id (intentic.maintenance) or its short name (maintenance)."}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"path":{"description":"The checkout to run it from. From an agent in an isolated conversation, a relative or /work path reads against that conversation's own copy. Absent: the workspace repository cloned from the address it was installed from, the calling conversation's copy first.","type":"string","minLength":1}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":121,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_.-]*$","description":"Its connection id."},"name":{"type":"string","description":"Its manifest id, publisher.name."},"dev":{"type":"object","properties":{"path":{"type":"string","description":"The source checkout it was pointed at, such as extensions/maintenance: relative to the workspace, or to the conversation's own copy of it when `conversation` is set."},"conversation":{"description":"The conversation whose isolated copy of the workspace holds that checkout. Absent means the workspace itself. That copy goes when the conversation does, and the pinned version runs again then.","type":"string"},"uncommitted":{"description":"How many files in the checkout differ from its last commit, a rebuilt bundle included. Absent when git could not say.","type":"integer","minimum":0,"maximum":9007199254740991},"revision":{"description":"A short fingerprint of the browser bundle served from the checkout. It changes with every rebuild, which is how a screen tells the copy it loaded is behind. Absent while held, and for an extension with no browser half.","type":"string"},"held":{"description":"Why the checkout is not what runs, as a sentence: it is gone, holds a different extension, is not built yet, or asks for powers the pinned version was never approved for. Present means the pinned version still runs.","type":"string"}},"required":["path"],"additionalProperties":false,"description":"Where it runs from now, or why the pinned version still runs."}},"required":["id","name","dev"],"additionalProperties":false}}}}}}},"/extensions/{id}/dev/clear":{"post":{"operationId":"extensions.devClear","summary":"Go back to the pinned version","description":"Stops serving the extension from its source checkout. The checkout itself is left exactly as it is.","tags":["Extensions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which installed extension: its connection id (intentic-maintenance), its manifest id (intentic.maintenance) or its short name (maintenance)."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":121,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_.-]*$","description":"Its connection id."},"name":{"type":"string","description":"Its manifest id, publisher.name."},"cleared":{"type":"boolean","description":"Whether it was pointed at a checkout. False means it already ran its pinned version and nothing changed."}},"required":["id","name","cleared"],"additionalProperties":false}}}}}}},"/extensions/{id}/dev/reload":{"post":{"operationId":"extensions.devReload","summary":"Pick up a rebuilt checkout","description":"Call after rebuilding an extension that runs from its source checkout: its backend restarts on the new code, and an open app is told the list changed, so it offers to reload the new browser bundle.","tags":["Extensions"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which installed extension: its connection id (intentic-maintenance), its manifest id (intentic.maintenance) or its short name (maintenance)."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":121,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_.-]*$","description":"Its connection id."},"name":{"type":"string","description":"Its manifest id, publisher.name."},"dev":{"type":"object","properties":{"path":{"type":"string","description":"The source checkout it was pointed at, such as extensions/maintenance: relative to the workspace, or to the conversation's own copy of it when `conversation` is set."},"conversation":{"description":"The conversation whose isolated copy of the workspace holds that checkout. Absent means the workspace itself. That copy goes when the conversation does, and the pinned version runs again then.","type":"string"},"uncommitted":{"description":"How many files in the checkout differ from its last commit, a rebuilt bundle included. Absent when git could not say.","type":"integer","minimum":0,"maximum":9007199254740991},"revision":{"description":"A short fingerprint of the browser bundle served from the checkout. It changes with every rebuild, which is how a screen tells the copy it loaded is behind. Absent while held, and for an extension with no browser half.","type":"string"},"held":{"description":"Why the checkout is not what runs, as a sentence: it is gone, holds a different extension, is not built yet, or asks for powers the pinned version was never approved for. Present means the pinned version still runs.","type":"string"}},"required":["path"],"additionalProperties":false,"description":"Where it runs from now, or why the pinned version still runs."}},"required":["id","name","dev"],"additionalProperties":false}}}}}}},"/settings":{"get":{"operationId":"settings.get","summary":"How this sandbox is configured","description":"Every setting that governs how agents behave here, with the defaults filled in for anything nobody has chosen.","tags":["Settings"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"timezone":{"default":"","description":"Which clock this sandbox's schedules are set by, as a zone name like Europe/Warsaw. Automations that repeat on a clock fire by this, not by the machine's own time. Leave it empty and they fire by UTC, which is almost certainly not what you meant when you typed a time.","anyOf":[{"type":"string","const":""},{"type":"string"}]},"stableSystemPrompt":{"default":false,"description":"Keep the instructions identical between turns so the provider can cache them, moving anything that varies into the message instead. Cheaper, at the cost of some flexibility.","type":"boolean"},"skills":{"default":["lsp","fileq"],"description":"Which built-in tools are switched on. A skill of your own is not listed here: it is on while the agent's copy of it exists.","type":"array","items":{"type":"string"}},"personaRouting":{"default":true,"description":"Whether a new chat is matched to one of your personas from its first message. It is read once the message is sent, in the same single call that chooses what the chat runs on (the New chat routing job under Models), and the chat says in its own transcript which persona it landed on. Never applies to unwatched runs, which name their persona themselves.","type":"boolean"},"hashlineEdits":{"default":false,"description":"Have the agent edit files by line number rather than by quoting the text it wants replaced. Cheaper on large files, and less forgiving of a stale read.","type":"boolean"},"systemPromptMode":{"default":"intentic","description":"Which instructions the agent starts from: intentic's own, the ones the installed Claude Code carries, or your own. The first two both get this product's own guidance added on top; your own gets nothing added, which is the point of it.","type":"string","enum":["intentic","claude","custom"]},"systemPrompt":{"default":"","description":"Your own instructions, used only when the mode above says custom. Then it is the whole of them: both built-in bases go, and so does everything this product would otherwise add, including the guidance the chat's own cards are driven by. That is the price of total control.","type":"string","maxLength":20000},"leanGuidance":{"default":false,"description":"Send this product's own guidance in its short form: only what the agent cannot find out by looking, instead of a paragraph for every habit it was once caught in. Off by default, because the long form is the one the product was tuned on.","type":"boolean"},"leanGuidanceHoldout":{"default":0,"description":"What share of conversations to keep on the long form, so the two can be compared. Whole conversations rather than individual turns, because the guidance sits in the prompt for the whole session.","type":"number","minimum":0,"maximum":1},"toolResultClearing":{"default":false,"description":"Replace old tool results from a Claude conversation once it grows long, a large chunk at a time, keeping the most recent ones whole. The agent sees a short placeholder where each one was and can run the tool again. Smaller prompts cost less and answer sooner, at the risk of the agent re-reading what it already saw.","type":"boolean"},"toolResultClearingHoldout":{"default":0.5,"description":"What share of conversations keep every tool result, so the two can be compared. Whole conversations rather than individual turns, because what was dropped stays dropped for the rest of the session.","type":"number","minimum":0,"maximum":1},"iqSearch":{"default":false,"description":"Teach the agent how to use this workspace's own search tool, rather than leaving it to grep around.","type":"boolean"},"iqSearchHoldout":{"default":0,"description":"What share of conversations to run without that teaching, so the two can be compared. Whole conversations rather than individual turns, because once the teaching is in a session, withholding it from the next request does not make the model forget it.","type":"number","minimum":0,"maximum":1},"workspaceMap":{"default":false,"description":"Open every conversation with a map of the project it starts in: what is in it, what each part is for, and where the agent is standing. Worked out fresh each time rather than written down anywhere, because a written layout is wrong within a fortnight. Off by default, since it spends tokens on the first message of every conversation.","type":"boolean"},"workspaceMapHoldout":{"default":0,"description":"What share of conversations to open without the map, so the two can be compared. Whole conversations rather than individual turns, because the map is sent once and stays in the conversation's history afterwards.","type":"number","minimum":0,"maximum":1},"fieldNotes":{"default":false,"description":"Open every turn with a brief on how work actually goes in this sandbox: the traps that cost past sessions calls, the commands that really verify, what the machine can take. Written once a month by an automation that reads back the sessions run here, rather than worked out per turn, because it is drawn from history rather than from the tree. Off by default, since it rides every turn of every conversation.","type":"boolean"},"fieldNotesBudget":{"default":4000,"description":"How much of that brief to send. Its sections are ranked, most costly-to-not-know first, and they are taken whole in that order until this runs out — so raising it buys more of the tail, never a fuller version of the same thing.","type":"integer","minimum":500,"maximum":20000},"fieldNotesHoldout":{"default":0,"description":"What share of conversations to run without the brief, so the two can be compared. Whole conversations rather than individual turns, because the brief sits in the prompt for the whole session and withholding it from one turn would not take it back.","type":"number","minimum":0,"maximum":1},"outputCleaners":{"default":"","description":"Which command outputs to trim before the agent reads them, cutting the noise a build tool prints without cutting what it said.","type":"string"},"outputHoldout":{"default":0,"description":"What share of commands to leave untrimmed, so the saving can be measured against a real comparison rather than estimated.","type":"number","minimum":0,"maximum":1},"modelRoles":{"default":{},"description":"Which models do which job, one ordered list per job: commit messages, session titles, the safety judge, pipeline fixes, and every other place this sandbox picks a model for you. Tried in order, so one spent account does not take a job down. Nothing is chosen for you: a one-shot job with no list does not run, and a whole session with no list opens on whatever your own chat is set to.","type":"object","propertyNames":{"type":"string","enum":["commit-message","session-title","safety-judge","loop-verdict","model-router","pipeline-fix","deployment-fix","maintenance-chore","documentation-run","acceptance-run","approval-queue","extension-review","loop-iteration"]},"additionalProperties":{"maxItems":10,"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves this work."},"model":{"type":"string","minLength":1,"description":"Which of its models. Both halves, because a model name only means anything to the provider that serves it."},"effort":{"description":"How hard this model should think, where it offers a choice. Leave it out to take the model's own default.","type":"string"},"thinking":{"description":"Whether this model reasons before it answers, where that is a choice it offers.","type":"boolean"},"fast":{"description":"Ask for this model's work at a higher rate for a higher price. A request rather than a promise.","type":"boolean"},"harness":{"description":"Which agentic loop runs it. Leave it out to use the provider's own.","type":"string","enum":["native","claude-code"]}},"required":["provider","model"],"additionalProperties":false}}},"autoModelGuidance":{"default":"","description":"What you would tell somebody choosing the model for a new chat on your behalf: which model you want the cheap work on, which account to leave alone, when to reach for the strongest one. Read once per chat, alongside the models and allowances this sandbox can actually run, and it overrides the product's own advice where the two disagree. It cannot invent a model: the answer is still a choice from that list.","type":"string","maxLength":2000},"changelogRepos":{"default":[],"description":"Which repositories keep a changelog, and so get a user-facing note written alongside each merge. A list rather than a switch, and empty by default, because the commit writer's standing rule is to copy the house style rather than impose one, and a repository that has never written such a note gives it nothing to copy.","maxItems":50,"type":"array","items":{"type":"string"}},"agentRetentionDays":{"default":3,"description":"How many days a finished conversation stays on the board before being put away. Zero means never. The one setting here that defaults on, because each card left behind is a real working copy on disk, not just a row.","type":"number","minimum":0,"maximum":365},"limitPolicy":{"default":"wait","description":"What happens to a turn a spent usage limit refused. `wait` holds it for a press. `resend` sends it again by itself once the allowance reopens, which needs a provider that publishes a reset (Grok and Cursor publish none). `move` also tries another connected account of the same provider that still has room, as soon as the refusal lands, and keeps the reset as its fallback. The sandbox-wide default; any one conversation can say otherwise.","type":"string","enum":["wait","resend","move"]},"outagePolicy":{"default":"wait","description":"What happens to a turn the model provider's own failure killed. `wait` holds it for a press. `retry` re-runs it on the shared per-provider breaker, backing off between attempts. The sandbox-wide default; any one conversation can say otherwise. Worth `retry` for a sandbox whose work mostly happens with nobody in the room.","type":"string","enum":["wait","retry"]},"stopPolicy":{"default":"wait","description":"What happens to a turn that stopped short with nothing to repair — a hung runtime, a crashed harness. `wait` holds it for a press. `retry` re-runs the held turn on a short ladder, standing down after three tries that got nowhere rather than looping forever.","type":"string","enum":["wait","retry"]},"limitMoveCarryUnder":{"default":100000,"description":"When a spent usage limit moves a turn to another account, carry the provider session (the model keeps everything, and re-reads all of it once on the other account) while the conversation's context is under this many tokens; at or above it, start a fresh session with the sandbox's measured brief instead. Zero always starts fresh.","type":"integer","minimum":0,"maximum":9007199254740991},"keepWarm":{"description":"Keeping an idle conversation's prompt cache warm, so coming back to it hours later costs a cache read instead of re-sending everything. Any one conversation can be kept warm or let cool by hand whatever `auto` says.","type":"object","properties":{"auto":{"default":false,"description":"Keep a Claude conversation's prompt cache warm after each turn a person asked for. Each refresh re-reads the cached context at the cache price and adds nothing to the conversation.","type":"boolean"},"hours":{"default":4,"description":"How long an idle conversation is kept warm after its last turn, in hours, and what a press on one offers first. Shortened where refreshing would cost more than the cold resume it saves, and at midnight where the agent runs, when the date in its prompt changes.","type":"number","minimum":1,"maximum":8},"minTokens":{"default":100000,"description":"Only conversations at least this large, in tokens, are kept warm by `auto`: a small one is cheap to re-read anyway.","type":"integer","minimum":0,"maximum":9007199254740991},"reserve":{"default":15,"description":"How much of an account's usage limit, in percent, keeping conversations warm must leave untouched for real work. Refreshing stops once any limit that account's model spends is fuller than that.","type":"integer","minimum":0,"maximum":90}},"required":["auto","hours","minTokens","reserve"],"additionalProperties":false},"autoRepair":{"default":true,"description":"Whether main's CI failing is repaired without asking. The first job that fails on main starts one fix agent, without waiting for the rest of the run, and every later failure on main goes to that same agent until a run passes. It gets a few turns; when they are spent, or it finishes without changing anything (a failure that is not in the code), the failure waits for you. A failure on the CI fleet itself is re-run once instead. Off, all of it is only reported.","type":"boolean"},"followOrigin":{"default":true,"description":"Whether each workspace repo keeps up with the remote branch it tracks. Every couple of minutes it is fetched, and what arrived is brought into the main tree: a fast-forward when you have no commits of your own, else a merge commit. Only while it is quiet (no turn working in the main tree, no merge or rebase of yours open), and never half-way: a conflict, or an uncommitted file in the way, leaves the repo exactly as it was until the next try. Nothing is ever pushed. Off, nothing is fetched.","type":"boolean"},"offload":{"default":{"commands":{}},"description":"Which heavy work runs on a runner on one of your machines instead of this sandbox: agents' commands by the kind the heavy-command rules sort them into (tests, typechecks, verify…). The code travels as it stands, uncommitted work included; the output streams back, and any file the command changed comes back with it. A machine that is offline, outdated or busy hands the work back to this sandbox, and the output says so.","type":"object","properties":{"commands":{"default":{},"type":"object","propertyNames":{"type":"string","minLength":1},"additionalProperties":{"type":"string","minLength":1}}},"required":["commands"],"additionalProperties":false},"continueWhenNeedMet":{"default":true,"description":"Whether a conversation carries on by itself once something it asked a person for arrives: a connection made, a secret given, access allowed, a tool built into the image. Off leaves the answer on the conversation's card until someone sends a message.","type":"boolean"},"autoResumeOnRestart":{"default":false,"description":"Whether a turn killed by the sandbox restarting is re-run once it comes back. A switch rather than one of the policies above, because a restart is the one ending with nobody watching it, so there is no in-chat question to answer. Off to begin with: it would spend your allowance on work you are not watching and edit files while you are still waiting for the sandbox to return. Either way the interruption is recorded rather than silently lost.","type":"boolean"},"adoptedChecks":{"default":{},"description":"Which repositories may run the checks they declare for themselves, and exactly which version of those checks you agreed to. A repository's declaration does nothing until it appears here, the same rule git keeps for hooks, which are never cloned; and a declaration that changes afterwards is held until you look at it again.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"rules":{"default":[],"description":"Standing decisions about the sandbox's own work: land or hold finished work, save a version of what landed. Empty is the default and is exactly the behaviour of a fresh sandbox, because each of those defaults is what no rule matched means at its own moment. A command to run is a repository's own check, declared in its .intentic/checks.json.","maxItems":50,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"label":{"type":"string","minLength":1,"maxLength":80},"moment":{"type":"string","enum":["file.edited","turn.ending","agent.finished","agent.landed"]},"when":{"type":"object","properties":{"repo":{"type":"string","minLength":1},"paths":{"maxItems":20,"type":"array","items":{"type":"string","minLength":1}},"outcome":{"type":"array","items":{"type":"string","enum":["clean","error","conflict","checks-failed"]}},"sample":{"type":"number","exclusiveMinimum":0,"exclusiveMaximum":1}},"additionalProperties":false},"action":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"command"},"command":{"type":"string","maxLength":500},"timeoutMs":{"default":900000,"type":"number","minimum":60000,"maximum":3600000}},"required":["kind","command","timeoutMs"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"verdict"},"verdict":{"type":"string","enum":["allow","hold"]}},"required":["kind","verdict"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"builtin"},"name":{"type":"string","enum":["verify-ui-edits","version-landed"]}},"required":["kind","name"],"additionalProperties":false}]},"enabled":{"default":true,"type":"boolean"}},"required":["id","label","moment","action","enabled"],"additionalProperties":false}},"automationFailureLimit":{"default":0,"description":"How many failures in a row before an automation switches itself off. Zero means never, which is the default, because the failure is not always the automation's fault and a job disabled at three in the morning is one nobody re-enables. Only real errors count: a guard deciding there was nothing to do, or the sandbox dying mid-run, say nothing about the automation.","type":"number","minimum":0,"maximum":20},"admission":{"description":"Whether work started from outside may run, per kind of trigger: let it, hold it for approval, or refuse it. Composes with each automation's own setting, and the stricter of the two wins, so holding every visitor's message needs no edit to each automation.","type":"object","properties":{"schedule":{"default":"allow","type":"string","enum":["allow","hold","deny"]},"event":{"default":"allow","type":"string","enum":["allow","hold","deny"]},"listener":{"default":"allow","type":"string","enum":["allow","hold","deny"]},"webchat":{"default":"allow","type":"string","enum":["allow","hold","deny"]},"issues":{"default":"hold","type":"string","enum":["allow","hold","deny"]},"workspace":{"default":"allow","type":"string","enum":["allow","hold","deny"]},"workflow":{"default":"allow","type":"string","enum":["allow","deny"]}},"required":["schedule","event","listener","webchat","issues","workspace","workflow"],"additionalProperties":false},"actionRules":{"default":{},"description":"What an agent may do out in the world, per kind of action: go ahead, ask first, or never.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string","enum":["allow","hold","deny"]}},"commandJudge":{"default":"on","description":"Whether a model reads your safety policy before a flagged command runs. Off judges nothing and asks about nothing; Watch judges everything and records it without ever interrupting you, which is how you find out what your policy actually does before you let it stop anything; On lets the verdict decide. Wiping a disk or deleting under /history asks at every setting — that rule is typed rather than judged, and cannot be turned off.","type":"string","enum":["off","watch","on"]},"projectInstalls":{"default":"automatic","description":"What happens when an agent installs a project's packages itself (pnpm add, npm install, uv sync). Automatic lets it run and keep working; Ask first stops for your answer in the chat, once or for the whole conversation; Never refuses, and a dependency the agent added to a manifest is installed when you land its work. A conversation in its own worktree installs into its own copy, and the land review lists every dependency its work adds.","type":"string","enum":["automatic","ask","never"]},"subagentsAtOnce":{"default":20,"description":"How many subagents may work at the same time.","type":"number","minimum":1,"maximum":200},"subagentsPerTurn":{"default":200,"description":"How many a single turn may start in total.","type":"number","minimum":1,"maximum":2000},"subagentDepth":{"default":3,"description":"How many levels deep the delegation may go, since a subagent can start subagents of its own.","type":"number","minimum":1,"maximum":10}},"required":["timezone","stableSystemPrompt","skills","personaRouting","hashlineEdits","systemPromptMode","systemPrompt","leanGuidance","leanGuidanceHoldout","toolResultClearing","toolResultClearingHoldout","iqSearch","iqSearchHoldout","workspaceMap","workspaceMapHoldout","fieldNotes","fieldNotesBudget","fieldNotesHoldout","outputCleaners","outputHoldout","modelRoles","autoModelGuidance","changelogRepos","agentRetentionDays","limitPolicy","outagePolicy","stopPolicy","limitMoveCarryUnder","keepWarm","autoRepair","followOrigin","offload","continueWhenNeedMet","autoResumeOnRestart","adoptedChecks","rules","automationFailureLimit","admission","actionRules","commandJudge","projectInstalls","subagentsAtOnce","subagentsPerTurn","subagentDepth"],"additionalProperties":false}}}}}},"post":{"operationId":"settings.set","summary":"Change the sandbox settings","description":"Writes the settings whole, so send the complete object rather than the fields you changed.","tags":["Settings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"timezone":{"default":"","description":"Which clock this sandbox's schedules are set by, as a zone name like Europe/Warsaw. Automations that repeat on a clock fire by this, not by the machine's own time. Leave it empty and they fire by UTC, which is almost certainly not what you meant when you typed a time.","anyOf":[{"type":"string","const":""},{"type":"string"}]},"stableSystemPrompt":{"default":false,"description":"Keep the instructions identical between turns so the provider can cache them, moving anything that varies into the message instead. Cheaper, at the cost of some flexibility.","type":"boolean"},"skills":{"default":["lsp","fileq"],"description":"Which built-in tools are switched on. A skill of your own is not listed here: it is on while the agent's copy of it exists.","type":"array","items":{"type":"string"}},"personaRouting":{"default":true,"description":"Whether a new chat is matched to one of your personas from its first message. It is read once the message is sent, in the same single call that chooses what the chat runs on (the New chat routing job under Models), and the chat says in its own transcript which persona it landed on. Never applies to unwatched runs, which name their persona themselves.","type":"boolean"},"hashlineEdits":{"default":false,"description":"Have the agent edit files by line number rather than by quoting the text it wants replaced. Cheaper on large files, and less forgiving of a stale read.","type":"boolean"},"systemPromptMode":{"default":"intentic","description":"Which instructions the agent starts from: intentic's own, the ones the installed Claude Code carries, or your own. The first two both get this product's own guidance added on top; your own gets nothing added, which is the point of it.","type":"string","enum":["intentic","claude","custom"]},"systemPrompt":{"default":"","description":"Your own instructions, used only when the mode above says custom. Then it is the whole of them: both built-in bases go, and so does everything this product would otherwise add, including the guidance the chat's own cards are driven by. That is the price of total control.","type":"string","maxLength":20000},"leanGuidance":{"default":false,"description":"Send this product's own guidance in its short form: only what the agent cannot find out by looking, instead of a paragraph for every habit it was once caught in. Off by default, because the long form is the one the product was tuned on.","type":"boolean"},"leanGuidanceHoldout":{"default":0,"description":"What share of conversations to keep on the long form, so the two can be compared. Whole conversations rather than individual turns, because the guidance sits in the prompt for the whole session.","type":"number","minimum":0,"maximum":1},"toolResultClearing":{"default":false,"description":"Replace old tool results from a Claude conversation once it grows long, a large chunk at a time, keeping the most recent ones whole. The agent sees a short placeholder where each one was and can run the tool again. Smaller prompts cost less and answer sooner, at the risk of the agent re-reading what it already saw.","type":"boolean"},"toolResultClearingHoldout":{"default":0.5,"description":"What share of conversations keep every tool result, so the two can be compared. Whole conversations rather than individual turns, because what was dropped stays dropped for the rest of the session.","type":"number","minimum":0,"maximum":1},"iqSearch":{"default":false,"description":"Teach the agent how to use this workspace's own search tool, rather than leaving it to grep around.","type":"boolean"},"iqSearchHoldout":{"default":0,"description":"What share of conversations to run without that teaching, so the two can be compared. Whole conversations rather than individual turns, because once the teaching is in a session, withholding it from the next request does not make the model forget it.","type":"number","minimum":0,"maximum":1},"workspaceMap":{"default":false,"description":"Open every conversation with a map of the project it starts in: what is in it, what each part is for, and where the agent is standing. Worked out fresh each time rather than written down anywhere, because a written layout is wrong within a fortnight. Off by default, since it spends tokens on the first message of every conversation.","type":"boolean"},"workspaceMapHoldout":{"default":0,"description":"What share of conversations to open without the map, so the two can be compared. Whole conversations rather than individual turns, because the map is sent once and stays in the conversation's history afterwards.","type":"number","minimum":0,"maximum":1},"fieldNotes":{"default":false,"description":"Open every turn with a brief on how work actually goes in this sandbox: the traps that cost past sessions calls, the commands that really verify, what the machine can take. Written once a month by an automation that reads back the sessions run here, rather than worked out per turn, because it is drawn from history rather than from the tree. Off by default, since it rides every turn of every conversation.","type":"boolean"},"fieldNotesBudget":{"default":4000,"description":"How much of that brief to send. Its sections are ranked, most costly-to-not-know first, and they are taken whole in that order until this runs out — so raising it buys more of the tail, never a fuller version of the same thing.","type":"integer","minimum":500,"maximum":20000},"fieldNotesHoldout":{"default":0,"description":"What share of conversations to run without the brief, so the two can be compared. Whole conversations rather than individual turns, because the brief sits in the prompt for the whole session and withholding it from one turn would not take it back.","type":"number","minimum":0,"maximum":1},"outputCleaners":{"default":"","description":"Which command outputs to trim before the agent reads them, cutting the noise a build tool prints without cutting what it said.","type":"string"},"outputHoldout":{"default":0,"description":"What share of commands to leave untrimmed, so the saving can be measured against a real comparison rather than estimated.","type":"number","minimum":0,"maximum":1},"modelRoles":{"default":{},"description":"Which models do which job, one ordered list per job: commit messages, session titles, the safety judge, pipeline fixes, and every other place this sandbox picks a model for you. Tried in order, so one spent account does not take a job down. Nothing is chosen for you: a one-shot job with no list does not run, and a whole session with no list opens on whatever your own chat is set to.","type":"object","propertyNames":{"type":"string","enum":["commit-message","session-title","safety-judge","loop-verdict","model-router","pipeline-fix","deployment-fix","maintenance-chore","documentation-run","acceptance-run","approval-queue","extension-review","loop-iteration"]},"additionalProperties":{"maxItems":10,"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider serves this work."},"model":{"type":"string","minLength":1,"description":"Which of its models. Both halves, because a model name only means anything to the provider that serves it."},"effort":{"description":"How hard this model should think, where it offers a choice. Leave it out to take the model's own default.","type":"string"},"thinking":{"description":"Whether this model reasons before it answers, where that is a choice it offers.","type":"boolean"},"fast":{"description":"Ask for this model's work at a higher rate for a higher price. A request rather than a promise.","type":"boolean"},"harness":{"description":"Which agentic loop runs it. Leave it out to use the provider's own.","type":"string","enum":["native","claude-code"]}},"required":["provider","model"]}}},"autoModelGuidance":{"default":"","description":"What you would tell somebody choosing the model for a new chat on your behalf: which model you want the cheap work on, which account to leave alone, when to reach for the strongest one. Read once per chat, alongside the models and allowances this sandbox can actually run, and it overrides the product's own advice where the two disagree. It cannot invent a model: the answer is still a choice from that list.","type":"string","maxLength":2000},"changelogRepos":{"default":[],"description":"Which repositories keep a changelog, and so get a user-facing note written alongside each merge. A list rather than a switch, and empty by default, because the commit writer's standing rule is to copy the house style rather than impose one, and a repository that has never written such a note gives it nothing to copy.","maxItems":50,"type":"array","items":{"type":"string"}},"agentRetentionDays":{"default":3,"description":"How many days a finished conversation stays on the board before being put away. Zero means never. The one setting here that defaults on, because each card left behind is a real working copy on disk, not just a row.","type":"number","minimum":0,"maximum":365},"limitPolicy":{"default":"wait","description":"What happens to a turn a spent usage limit refused. `wait` holds it for a press. `resend` sends it again by itself once the allowance reopens, which needs a provider that publishes a reset (Grok and Cursor publish none). `move` also tries another connected account of the same provider that still has room, as soon as the refusal lands, and keeps the reset as its fallback. The sandbox-wide default; any one conversation can say otherwise.","type":"string","enum":["wait","resend","move"]},"outagePolicy":{"default":"wait","description":"What happens to a turn the model provider's own failure killed. `wait` holds it for a press. `retry` re-runs it on the shared per-provider breaker, backing off between attempts. The sandbox-wide default; any one conversation can say otherwise. Worth `retry` for a sandbox whose work mostly happens with nobody in the room.","type":"string","enum":["wait","retry"]},"stopPolicy":{"default":"wait","description":"What happens to a turn that stopped short with nothing to repair — a hung runtime, a crashed harness. `wait` holds it for a press. `retry` re-runs the held turn on a short ladder, standing down after three tries that got nowhere rather than looping forever.","type":"string","enum":["wait","retry"]},"limitMoveCarryUnder":{"default":100000,"description":"When a spent usage limit moves a turn to another account, carry the provider session (the model keeps everything, and re-reads all of it once on the other account) while the conversation's context is under this many tokens; at or above it, start a fresh session with the sandbox's measured brief instead. Zero always starts fresh.","type":"integer","minimum":0,"maximum":9007199254740991},"keepWarm":{"description":"Keeping an idle conversation's prompt cache warm, so coming back to it hours later costs a cache read instead of re-sending everything. Any one conversation can be kept warm or let cool by hand whatever `auto` says.","default":{},"type":"object","properties":{"auto":{"default":false,"description":"Keep a Claude conversation's prompt cache warm after each turn a person asked for. Each refresh re-reads the cached context at the cache price and adds nothing to the conversation.","type":"boolean"},"hours":{"default":4,"description":"How long an idle conversation is kept warm after its last turn, in hours, and what a press on one offers first. Shortened where refreshing would cost more than the cold resume it saves, and at midnight where the agent runs, when the date in its prompt changes.","type":"number","minimum":1,"maximum":8},"minTokens":{"default":100000,"description":"Only conversations at least this large, in tokens, are kept warm by `auto`: a small one is cheap to re-read anyway.","type":"integer","minimum":0,"maximum":9007199254740991},"reserve":{"default":15,"description":"How much of an account's usage limit, in percent, keeping conversations warm must leave untouched for real work. Refreshing stops once any limit that account's model spends is fuller than that.","type":"integer","minimum":0,"maximum":90}}},"autoRepair":{"default":true,"description":"Whether main's CI failing is repaired without asking. The first job that fails on main starts one fix agent, without waiting for the rest of the run, and every later failure on main goes to that same agent until a run passes. It gets a few turns; when they are spent, or it finishes without changing anything (a failure that is not in the code), the failure waits for you. A failure on the CI fleet itself is re-run once instead. Off, all of it is only reported.","type":"boolean"},"followOrigin":{"default":true,"description":"Whether each workspace repo keeps up with the remote branch it tracks. Every couple of minutes it is fetched, and what arrived is brought into the main tree: a fast-forward when you have no commits of your own, else a merge commit. Only while it is quiet (no turn working in the main tree, no merge or rebase of yours open), and never half-way: a conflict, or an uncommitted file in the way, leaves the repo exactly as it was until the next try. Nothing is ever pushed. Off, nothing is fetched.","type":"boolean"},"offload":{"default":{"commands":{}},"description":"Which heavy work runs on a runner on one of your machines instead of this sandbox: agents' commands by the kind the heavy-command rules sort them into (tests, typechecks, verify…). The code travels as it stands, uncommitted work included; the output streams back, and any file the command changed comes back with it. A machine that is offline, outdated or busy hands the work back to this sandbox, and the output says so.","type":"object","properties":{"commands":{"default":{},"type":"object","propertyNames":{"type":"string","minLength":1},"additionalProperties":{"type":"string","minLength":1}}}},"continueWhenNeedMet":{"default":true,"description":"Whether a conversation carries on by itself once something it asked a person for arrives: a connection made, a secret given, access allowed, a tool built into the image. Off leaves the answer on the conversation's card until someone sends a message.","type":"boolean"},"autoResumeOnRestart":{"default":false,"description":"Whether a turn killed by the sandbox restarting is re-run once it comes back. A switch rather than one of the policies above, because a restart is the one ending with nobody watching it, so there is no in-chat question to answer. Off to begin with: it would spend your allowance on work you are not watching and edit files while you are still waiting for the sandbox to return. Either way the interruption is recorded rather than silently lost.","type":"boolean"},"adoptedChecks":{"default":{},"description":"Which repositories may run the checks they declare for themselves, and exactly which version of those checks you agreed to. A repository's declaration does nothing until it appears here, the same rule git keeps for hooks, which are never cloned; and a declaration that changes afterwards is held until you look at it again.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"rules":{"default":[],"description":"Standing decisions about the sandbox's own work: land or hold finished work, save a version of what landed. Empty is the default and is exactly the behaviour of a fresh sandbox, because each of those defaults is what no rule matched means at its own moment. A command to run is a repository's own check, declared in its .intentic/checks.json.","maxItems":50,"type":"array","items":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]*$"},"label":{"type":"string","minLength":1,"maxLength":80},"moment":{"type":"string","enum":["file.edited","turn.ending","agent.finished","agent.landed"]},"when":{"type":"object","properties":{"repo":{"type":"string","minLength":1},"paths":{"maxItems":20,"type":"array","items":{"type":"string","minLength":1}},"outcome":{"type":"array","items":{"type":"string","enum":["clean","error","conflict","checks-failed"]}},"sample":{"type":"number","exclusiveMinimum":0,"exclusiveMaximum":1}}},"action":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"command"},"command":{"type":"string","maxLength":500},"timeoutMs":{"default":900000,"type":"number","minimum":60000,"maximum":3600000}},"required":["kind","command"]},{"type":"object","properties":{"kind":{"type":"string","const":"verdict"},"verdict":{"type":"string","enum":["allow","hold"]}},"required":["kind","verdict"]},{"type":"object","properties":{"kind":{"type":"string","const":"builtin"},"name":{"type":"string","enum":["verify-ui-edits","version-landed"]}},"required":["kind","name"]}]},"enabled":{"default":true,"type":"boolean"}},"required":["id","label","moment","action"]}},"automationFailureLimit":{"default":0,"description":"How many failures in a row before an automation switches itself off. Zero means never, which is the default, because the failure is not always the automation's fault and a job disabled at three in the morning is one nobody re-enables. Only real errors count: a guard deciding there was nothing to do, or the sandbox dying mid-run, say nothing about the automation.","type":"number","minimum":0,"maximum":20},"admission":{"description":"Whether work started from outside may run, per kind of trigger: let it, hold it for approval, or refuse it. Composes with each automation's own setting, and the stricter of the two wins, so holding every visitor's message needs no edit to each automation.","default":{},"type":"object","properties":{"schedule":{"default":"allow","type":"string","enum":["allow","hold","deny"]},"event":{"default":"allow","type":"string","enum":["allow","hold","deny"]},"listener":{"default":"allow","type":"string","enum":["allow","hold","deny"]},"webchat":{"default":"allow","type":"string","enum":["allow","hold","deny"]},"issues":{"default":"hold","type":"string","enum":["allow","hold","deny"]},"workspace":{"default":"allow","type":"string","enum":["allow","hold","deny"]},"workflow":{"default":"allow","type":"string","enum":["allow","deny"]}}},"actionRules":{"default":{},"description":"What an agent may do out in the world, per kind of action: go ahead, ask first, or never.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string","enum":["allow","hold","deny"]}},"commandJudge":{"default":"on","description":"Whether a model reads your safety policy before a flagged command runs. Off judges nothing and asks about nothing; Watch judges everything and records it without ever interrupting you, which is how you find out what your policy actually does before you let it stop anything; On lets the verdict decide. Wiping a disk or deleting under /history asks at every setting — that rule is typed rather than judged, and cannot be turned off.","type":"string","enum":["off","watch","on"]},"projectInstalls":{"default":"automatic","description":"What happens when an agent installs a project's packages itself (pnpm add, npm install, uv sync). Automatic lets it run and keep working; Ask first stops for your answer in the chat, once or for the whole conversation; Never refuses, and a dependency the agent added to a manifest is installed when you land its work. A conversation in its own worktree installs into its own copy, and the land review lists every dependency its work adds.","type":"string","enum":["automatic","ask","never"]},"subagentsAtOnce":{"default":20,"description":"How many subagents may work at the same time.","type":"number","minimum":1,"maximum":200},"subagentsPerTurn":{"default":200,"description":"How many a single turn may start in total.","type":"number","minimum":1,"maximum":2000},"subagentDepth":{"default":3,"description":"How many levels deep the delegation may go, since a subagent can start subagents of its own.","type":"number","minimum":1,"maximum":10}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/settings/savings":{"get":{"operationId":"settings.savings","summary":"What the token-saving measures were worth","description":"Measured rather than estimated: what each mechanism actually saved over a range of days. The same day range the spending ledger takes, so one calendar filters both.","tags":["Settings"],"parameters":[{"name":"from","in":"query","schema":{"description":"First day to include, as YYYY-MM-DD in UTC. Leave it out for everything up to the end day.","type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"to","in":"query","schema":{"description":"Last day to include, as YYYY-MM-DD in UTC, and it is included rather than excluded. Leave it out for everything from the start day onwards.","type":"string"},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"input":{"type":"object","properties":{"updatedAt":{"type":"number"},"commands":{"type":"number"},"rawTokens":{"type":"number"},"emittedTokens":{"type":"number"},"savedPct":{"type":"number"},"perCleaner":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"commands":{"type":"number"},"savedTokens":{"type":"number"}},"required":["id","commands","savedTokens"],"additionalProperties":false}},"holdout":{"type":"object","properties":{"cleaned":{"type":"number"},"heldOut":{"type":"number"},"measuredSavedPct":{"type":"number"}},"required":["cleaned","heldOut"],"additionalProperties":false},"gaps":{"type":"array","items":{"type":"object","properties":{"command":{"type":"string"},"commands":{"type":"number"},"tokens":{"type":"number"}},"required":["command","commands","tokens"],"additionalProperties":false}}},"required":["commands","rawTokens","emittedTokens","savedPct","perCleaner","holdout","gaps"],"additionalProperties":false},"search":{"type":"object","properties":{"metrics":{"type":"array","prefixItems":[{"type":"object","properties":{"metric":{"type":"string","enum":["searchCalls","openingSearches","openingListings","callsBeforeTarget","failedCalls","roundTrips","contextPerCall"]},"on":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"off":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"controlTurnsNeeded":{"type":"number"},"marginPct":{"type":"number"},"deltaPct":{"type":"number"},"saved":{"type":"number"}},"required":["metric","on","off"],"additionalProperties":false}],"items":{"type":"object","properties":{"metric":{"type":"string","enum":["searchCalls","openingSearches","openingListings","callsBeforeTarget","failedCalls","roundTrips","contextPerCall"]},"on":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"off":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"controlTurnsNeeded":{"type":"number"},"marginPct":{"type":"number"},"deltaPct":{"type":"number"},"saved":{"type":"number"}},"required":["metric","on","off"],"additionalProperties":false},"minItems":1},"minTurns":{"type":"number"},"sampleUnit":{"type":"string","enum":["turns","conversations","opening turns"]},"cohort":{"type":"string"}},"required":["metrics","minTurns"],"additionalProperties":false},"map":{"type":"object","properties":{"metrics":{"type":"array","prefixItems":[{"type":"object","properties":{"metric":{"type":"string","enum":["searchCalls","openingSearches","openingListings","callsBeforeTarget","failedCalls","roundTrips","contextPerCall"]},"on":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"off":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"controlTurnsNeeded":{"type":"number"},"marginPct":{"type":"number"},"deltaPct":{"type":"number"},"saved":{"type":"number"}},"required":["metric","on","off"],"additionalProperties":false}],"items":{"type":"object","properties":{"metric":{"type":"string","enum":["searchCalls","openingSearches","openingListings","callsBeforeTarget","failedCalls","roundTrips","contextPerCall"]},"on":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"off":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"controlTurnsNeeded":{"type":"number"},"marginPct":{"type":"number"},"deltaPct":{"type":"number"},"saved":{"type":"number"}},"required":["metric","on","off"],"additionalProperties":false},"minItems":1},"minTurns":{"type":"number"},"sampleUnit":{"type":"string","enum":["turns","conversations","opening turns"]},"cohort":{"type":"string"}},"required":["metrics","minTurns"],"additionalProperties":false},"notes":{"type":"object","properties":{"metrics":{"type":"array","prefixItems":[{"type":"object","properties":{"metric":{"type":"string","enum":["searchCalls","openingSearches","openingListings","callsBeforeTarget","failedCalls","roundTrips","contextPerCall"]},"on":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"off":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"controlTurnsNeeded":{"type":"number"},"marginPct":{"type":"number"},"deltaPct":{"type":"number"},"saved":{"type":"number"}},"required":["metric","on","off"],"additionalProperties":false}],"items":{"type":"object","properties":{"metric":{"type":"string","enum":["searchCalls","openingSearches","openingListings","callsBeforeTarget","failedCalls","roundTrips","contextPerCall"]},"on":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"off":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"controlTurnsNeeded":{"type":"number"},"marginPct":{"type":"number"},"deltaPct":{"type":"number"},"saved":{"type":"number"}},"required":["metric","on","off"],"additionalProperties":false},"minItems":1},"minTurns":{"type":"number"},"sampleUnit":{"type":"string","enum":["turns","conversations","opening turns"]},"cohort":{"type":"string"}},"required":["metrics","minTurns"],"additionalProperties":false},"guidance":{"type":"object","properties":{"metrics":{"type":"array","prefixItems":[{"type":"object","properties":{"metric":{"type":"string","enum":["searchCalls","openingSearches","openingListings","callsBeforeTarget","failedCalls","roundTrips","contextPerCall"]},"on":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"off":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"controlTurnsNeeded":{"type":"number"},"marginPct":{"type":"number"},"deltaPct":{"type":"number"},"saved":{"type":"number"}},"required":["metric","on","off"],"additionalProperties":false}],"items":{"type":"object","properties":{"metric":{"type":"string","enum":["searchCalls","openingSearches","openingListings","callsBeforeTarget","failedCalls","roundTrips","contextPerCall"]},"on":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"off":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"controlTurnsNeeded":{"type":"number"},"marginPct":{"type":"number"},"deltaPct":{"type":"number"},"saved":{"type":"number"}},"required":["metric","on","off"],"additionalProperties":false},"minItems":1},"minTurns":{"type":"number"},"sampleUnit":{"type":"string","enum":["turns","conversations","opening turns"]},"cohort":{"type":"string"}},"required":["metrics","minTurns"],"additionalProperties":false},"clearing":{"type":"object","properties":{"metrics":{"type":"array","prefixItems":[{"type":"object","properties":{"metric":{"type":"string","enum":["searchCalls","openingSearches","openingListings","callsBeforeTarget","failedCalls","roundTrips","contextPerCall"]},"on":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"off":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"controlTurnsNeeded":{"type":"number"},"marginPct":{"type":"number"},"deltaPct":{"type":"number"},"saved":{"type":"number"}},"required":["metric","on","off"],"additionalProperties":false}],"items":{"type":"object","properties":{"metric":{"type":"string","enum":["searchCalls","openingSearches","openingListings","callsBeforeTarget","failedCalls","roundTrips","contextPerCall"]},"on":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"off":{"type":"object","properties":{"turns":{"type":"number"},"mean":{"type":"number"}},"required":["turns","mean"],"additionalProperties":false},"controlTurnsNeeded":{"type":"number"},"marginPct":{"type":"number"},"deltaPct":{"type":"number"},"saved":{"type":"number"}},"required":["metric","on","off"],"additionalProperties":false},"minItems":1},"minTurns":{"type":"number"},"sampleUnit":{"type":"string","enum":["turns","conversations","opening turns"]},"cohort":{"type":"string"}},"required":["metrics","minTurns"],"additionalProperties":false}},"required":["input"],"additionalProperties":false}}}}}}},"/settings/system-prompt/{base}":{"get":{"operationId":"settings.builtinPrompt","summary":"Read a built-in system prompt","description":"The actual text behind one of the built-in modes, so a settings screen can show the prompt instead of asking anyone to trust a description of it, and so either can be forked into a custom one.","tags":["Settings"],"parameters":[{"name":"base","in":"path","required":true,"schema":{"type":"string","enum":["intentic","claude"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string"},"version":{"type":"string"}},"required":["text","version"],"additionalProperties":false}}}}}}},"/settings/rule-firings":{"get":{"operationId":"settings.firings","summary":"When each rule last did something","description":"A separate read rather than a field on the settings, because a rule firing is not somebody editing anything: folding it in would turn every firing into a settings write and put a self-changing value inside the object a screen edits.","tags":["Settings"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"}}}}}}}},"/settings/repo-checks":{"get":{"operationId":"settings.repoChecks","summary":"What each repository asks to run on its own code","description":"Every repository that declares its own checks at `.intentic/checks.json`, what it declares, and whether you have switched it on. A repository declares what to run because the command belongs beside the scripts it names; nothing it declares runs until you say so.","tags":["Settings"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"repos":{"type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"Which repository, by its workspace id (\"root\" is the workspace itself)."},"path":{"type":"string","description":"Where the declaration lives, relative to the workspace, whether or not the file exists yet."},"checks":{"type":"array","items":{"type":"object","properties":{"when":{"type":"string","enum":["edit","turn","land"],"description":"When to run it: `edit` on each file as it is written (`{file}` is its path); `turn` once when an isolated turn is about to stop, for at most three minutes, with what it prints on failure said back to the model once. Neither holds or refuses anything. `land` is retired and runs nothing, since CI checks what is pushed."},"run":{"type":"string","minLength":1,"maxLength":500,"description":"The command, run in this repository's own directory, so it reads as it would in a terminal there."},"label":{"description":"What to call it on screen. Absent names it after the command.","type":"string","minLength":1,"maxLength":80},"timeoutMs":{"description":"How long it may take before it is killed and counted as failed.","type":"number","minimum":60000,"maximum":3600000},"paths":{"description":"Only run it when the change touches these paths, written relative to this repository. Absent runs it on every change here.","maxItems":20,"type":"array","items":{"type":"string","minLength":1}}},"required":["when","run"],"additionalProperties":false},"description":"What it declares, in the order the file lists them."},"fired":{"type":"array","items":{"type":["number","null"]},"description":"When each declared check last reported something, in the file's order, as epoch milliseconds; null for one that never has, or for a retired one, which runs nothing."},"adopted":{"type":"boolean","description":"Whether these are running. False means declared and inert: nothing a repository writes runs until the owner switches it on."},"changed":{"type":"boolean","description":"Whether the declaration changed since it was adopted, which holds it until the owner looks again. True only for a repository that was adopted before."},"error":{"description":"Why the file could not be read, when it exists but does not parse. The checks list is empty in that case.","type":"string"}},"required":["repo","path","checks","fired","adopted","changed"],"additionalProperties":false},"description":"Every repository that declares checks, in id order."}},"required":["repos"],"additionalProperties":false}}}}}}},"/settings/field-notes":{"get":{"operationId":"settings.fieldNotes","summary":"The state of this sandbox's field notes","description":"Whether `.intentic/config/field-notes.toon` exists, when it was last rewritten, how much of it the current budget reaches, and whether a monthly rewrite is scheduled.","tags":["Settings"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"present":{"type":"boolean"},"writtenAt":{"type":"number"},"ranksSent":{"type":"number"},"ranksTotal":{"type":"number"},"chars":{"type":"number"},"automation":{"type":"string","enum":["missing","enabled","disabled"]},"nextRunAt":{"type":"number"},"unreadable":{"type":"string"}},"required":["present","automation"],"additionalProperties":false}}}}}}},"/settings/timezone":{"post":{"operationId":"settings.adoptTimezone","summary":"Offer this sandbox a clock, if it has none","description":"Sets which timezone this sandbox's schedules are meant in, but only while it has none set. Already answered, the stored zone wins and comes back unchanged, so any number of browsers can offer theirs without fighting over it. To change a zone that is already set, write the settings.","tags":["Settings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"timezone":{"type":"string","description":"The zone the offering machine is in, as an IANA name like Europe/Warsaw."}},"required":["timezone"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"timezone":{"type":"string","description":"The zone this sandbox's schedules are now read in. Empty only if none could be resolved."},"adopted":{"type":"boolean","description":"Whether this call is what set it. False means it was already answered and the stored zone stands."}},"required":["timezone","adopted"],"additionalProperties":false}}}}}}},"/settings/audience":{"get":{"operationId":"settings.audience","summary":"Which words the editor uses for you here","description":"Whether your editor speaks git's own words (developer) or plain ones (maker) on this sandbox, in every browser and on every device. Absent until you have chosen here.","tags":["Settings"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"audience":{"description":"developer for git's own words, maker for plain ones. Absent until this person has chosen here.","type":"string","enum":["developer","maker"]}},"additionalProperties":false}}}}}},"post":{"operationId":"settings.setAudience","summary":"Choose which words the editor uses for you here","description":"Sets whether your editor speaks git's own words (developer) or plain ones (maker) on this sandbox, for every browser and device you open it on. Other members keep their own. With `offer` it is taken only while you have none kept, and the kept answer comes back unchanged.","tags":["Settings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"audience":{"type":"string","enum":["developer","maker"],"description":"developer for git's own words, maker for plain ones."},"offer":{"description":"Take it only while this person has no answer kept here yet. Absent or false replaces whatever is kept.","type":"boolean"}},"required":["audience"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"audience":{"type":"string","enum":["developer","maker"],"description":"The words this sandbox's editor now uses."},"adopted":{"type":"boolean","description":"Whether this call set it. False means an offer met an answer already kept, which stands."}},"required":["audience","adopted"],"additionalProperties":false}}}}}}},"/settings/repo-checks/adopt":{"post":{"operationId":"settings.adoptRepoChecks","summary":"Switch a repository's own checks on or off","description":"Adopts exactly what that repository declares as it stands now. If the declaration changes afterwards it stops running until you adopt it again, so a command nobody has read cannot inherit the answer given to a different one.","tags":["Settings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"repo":{"type":"string","minLength":1,"description":"Which repository's declaration to switch."},"on":{"type":"boolean","description":"On adopts what it declares as it stands now; off stops running it. Adopting again is how a changed declaration is accepted."}},"required":["repo","on"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/safety/policy":{"get":{"operationId":"safety.policy","summary":"The safety policy this sandbox is judged against","description":"The document that decides when an agent stops to ask you before running something. Prose, not settings: it is read by the model that judges each command. When nobody has written one, this is the text the product ships with, and it describes the behaviour a fresh sandbox already has.","tags":["Safety policy"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","description":"The policy, as the owner wrote it."},"custom":{"type":"boolean","description":"False when nobody has edited it and this is the text this product ships."}},"required":["text","custom"],"additionalProperties":false}}}}}},"post":{"operationId":"safety.setPolicy","summary":"Rewrite the safety policy","description":"Replaces the document whole. Nothing in it can widen what the sandbox is structurally allowed to do: it decides which of the things an agent may already do are worth interrupting you about.","tags":["Safety policy"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","description":"The policy, as you want it written."}},"required":["text"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/safety/log":{"get":{"operationId":"safety.log","summary":"Recent safety verdicts","description":"What was judged lately, what the judge decided, and whether you were interrupted. Newest first. This is where you find out why you were not asked about something, which is the question a policy page otherwise cannot answer.","tags":["Safety policy"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"at":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"When it was judged, epoch milliseconds."},"program":{"type":"string","description":"The command or script, excerpted."},"classes":{"type":"array","items":{"type":"string"},"description":"The kinds of consequence triage matched, which is why a judge looked."},"decision":{"type":"string","enum":["allow","ask","refuse"],"description":"What the judge decided."},"sentence":{"type":"string","description":"The judge's sentence."},"outcome":{"type":"string","enum":["allowed","asked","refused"],"description":"What the gate did in the end."},"answer":{"description":"How the owner answered, when they were asked.","type":"string","enum":["allowed","declined","unanswered"]},"machine":{"description":"Which connected device it was headed for, when it was not this sandbox.","type":"string"}},"required":["at","program","classes","decision","sentence","outcome"],"additionalProperties":false}}}}}}}},"/privacy/shield":{"get":{"operationId":"privacy.status","summary":"The privacy shield and what it covers","description":"Whether personal data is kept from untrusted model providers, which providers are trusted, which local readers are installed, and how many values it has learned.","tags":["Privacy shield"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"policy":{"type":"object","properties":{"mode":{"default":"off","description":"Whether the shield is off, only watching, or masking.","type":"string","enum":["off","watch","on"]},"trusted":{"default":[],"description":"Providers that may read personal data as it is, by provider id (`claude`, `codex`, `endpoint/<id>`). A local model is always trusted.","maxItems":200,"type":"array","items":{"type":"string","minLength":1,"maxLength":200}},"classes":{"default":["person-name","national-id","tax-id","identity-document","bank-account","payment-card","email","phone","address"],"description":"Which kinds of personal data are looked for.","type":"array","items":{"type":"string","enum":["person-name","national-id","tax-id","identity-document","bank-account","payment-card","email","phone","address"]}},"images":{"default":"mask","description":"What an image bound for an untrusted provider becomes.","type":"string","enum":["mask","allow"]},"names":{"default":"dictionary","description":"How names are found.","type":"string","enum":["dictionary","model"]},"allow":{"default":[],"description":"Values never masked: your own company, a public figure, a word the detector keeps mistaking for a name.","maxItems":1000,"type":"array","items":{"type":"string","minLength":1,"maxLength":200}},"conversations":{"default":[],"description":"Providers that may read one conversation's personal data as it is, each granted from that conversation; oldest first.","maxItems":200,"type":"array","items":{"type":"object","properties":{"conversationId":{"type":"string","minLength":1,"maxLength":200},"provider":{"type":"string","minLength":1,"maxLength":200,"description":"Provider id, as the trusted list names it."}},"required":["conversationId","provider"],"additionalProperties":false}}},"required":["mode","trusted","classes","images","names","allow","conversations"],"additionalProperties":false},"known":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Values taught from your datasets, matched exactly wherever they appear."},"tokens":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Values the shield has given a token so far."},"readers":{"type":"object","properties":{"ocr":{"type":"boolean","description":"The local text reader (PaddleOCR) that finds personal data in images is installed."},"model":{"type":"boolean","description":"A local named-entity model for names is installed."}},"required":["ocr","model"],"additionalProperties":false},"providers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Provider id, as the trusted list names it."},"label":{"type":"string"},"shieldable":{"type":"boolean","description":"Its runtime can be put behind the gateway; one that cannot is refused while the shield is on, unless trusted."},"local":{"type":"boolean","description":"It runs on this machine, so it is trusted whatever the list says."}},"required":["id","label","shieldable","local"],"additionalProperties":false}}},"required":["policy","known","tokens","readers","providers"],"additionalProperties":false}}}}}},"post":{"operationId":"privacy.setPolicy","summary":"Change the privacy shield","description":"Replaces the policy whole. Turning the shield on puts every turn that starts from then on, whose runtime can be shielded, behind the gateway, and refuses the turns that cannot be shielded on an untrusted provider; a turn already running keeps the route it started with. A change to what is masked or trusted holds from the next model request.","tags":["Privacy shield"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"default":"off","description":"Whether the shield is off, only watching, or masking.","type":"string","enum":["off","watch","on"]},"trusted":{"default":[],"description":"Providers that may read personal data as it is, by provider id (`claude`, `codex`, `endpoint/<id>`). A local model is always trusted.","maxItems":200,"type":"array","items":{"type":"string","minLength":1,"maxLength":200}},"classes":{"default":["person-name","national-id","tax-id","identity-document","bank-account","payment-card","email","phone","address"],"description":"Which kinds of personal data are looked for.","type":"array","items":{"type":"string","enum":["person-name","national-id","tax-id","identity-document","bank-account","payment-card","email","phone","address"]}},"images":{"default":"mask","description":"What an image bound for an untrusted provider becomes.","type":"string","enum":["mask","allow"]},"names":{"default":"dictionary","description":"How names are found.","type":"string","enum":["dictionary","model"]},"allow":{"default":[],"description":"Values never masked: your own company, a public figure, a word the detector keeps mistaking for a name.","maxItems":1000,"type":"array","items":{"type":"string","minLength":1,"maxLength":200}},"conversations":{"default":[],"description":"Providers that may read one conversation's personal data as it is, each granted from that conversation; oldest first.","maxItems":200,"type":"array","items":{"type":"object","properties":{"conversationId":{"type":"string","minLength":1,"maxLength":200},"provider":{"type":"string","minLength":1,"maxLength":200,"description":"Provider id, as the trusted list names it."}},"required":["conversationId","provider"]}}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/privacy/log":{"get":{"operationId":"privacy.log","summary":"What the privacy shield did lately","description":"Each model request the gateway handled: which provider, whether it was trusted, how many of each kind of personal data it found, and the tokens it gave with the masked text around them. Never the values.","tags":["Privacy shield"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string","description":"When, as an ISO timestamp."},"conversationId":{"type":"string"},"provider":{"type":"string"},"trusted":{"type":"boolean"},"action":{"type":"string","enum":["masked","watched","passed","refused"]},"counts":{"type":"object","propertyNames":{"type":"string","enum":["person-name","national-id","tax-id","identity-document","bank-account","payment-card","email","phone","address"]},"additionalProperties":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"description":"How many of each kind were found in what this request added."},"images":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Images the shield changed: personal data painted over, or held back when they could not be read."},"documents":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Documents replaced by their masked text."},"protocol":{"type":"string","description":"Which wire format the request spoke."},"detail":{"description":"Why it was refused, when it was.","type":"string"},"replacements":{"description":"The first values replaced in what this request added, one per token. Absent on entries written before it was kept.","maxItems":12,"type":"array","items":{"type":"object","properties":{"token":{"type":"string","description":"The token the value became, as the provider read it: Alice."},"class":{"type":"string","enum":["person-name","national-id","tax-id","identity-document","bank-account","payment-card","email","phone","address"]},"excerpt":{"type":"string","description":"The masked text around the token as it left this machine (watching: as it would have). Tokens only, never a value."},"image":{"description":"Found in an image's text, so the token was painted over the picture rather than written.","type":"boolean"}},"required":["token","class","excerpt"],"additionalProperties":false}}},"required":["at","provider","trusted","action","counts","images","documents","protocol"],"additionalProperties":false}}}}}}}},"/privacy/reveal":{"post":{"operationId":"privacy.reveal","summary":"Read tokens back to their values","description":"The value each token stands for, from the vault, so the owner can check what the shield masked and spot a value it should have left alone. Only the owner may ask; a token the vault never gave out is left out.","tags":["Privacy shield"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tokens":{"maxItems":100,"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}},"required":["tokens"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}}}}}}}},"/privacy/dictionary":{"get":{"operationId":"privacy.dictionary","summary":"The name lists the shield finds names by","description":"Every list the dictionary holds (first names, surnames, words that are names only beside other evidence, titles), how many words each has and where they come from. With a query, what the dictionary makes of it as a name, and for one word the listed words starting with it.","tags":["Privacy shield"],"parameters":[{"name":"query","in":"query","schema":{"description":"A word, the start of one, or a full name.","type":"string","maxLength":100},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"lists":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Stable id of the list."},"kind":{"type":"string","enum":["first-name","surname","ambiguous","title","never"],"description":"What a word on it says about a name."},"languages":{"type":"array","items":{"type":"string","enum":["pl","en"]},"description":"The languages its words come from."},"count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many words it holds."},"matching":{"type":"string","enum":["inflected","as-written"],"description":"inflected: matched in every grammatical form of a listed word; as-written: matched only exactly as listed."},"source":{"type":"string","description":"Where the words come from: the register or dataset, or that they were written by hand."},"url":{"description":"The source's page, where it has one.","type":"string"},"license":{"type":"string"}},"required":["id","kind","languages","count","matching","source"],"additionalProperties":false}},"totals":{"type":"object","properties":{"firstNames":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"surnames":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["firstNames","surnames"],"additionalProperties":false,"description":"Distinct words across the first-name lists, and across the surname lists."},"matches":{"type":"array","items":{"type":"object","properties":{"word":{"type":"string"},"lists":{"type":"array","items":{"type":"string"},"description":"Ids of the lists holding it."}},"required":["word","lists"],"additionalProperties":false}},"lookup":{"type":"object","properties":{"text":{"type":"string","description":"The query as a name is written: each word capitalized."},"found":{"type":"boolean","description":"Whether the dictionary alone masks it as a name, written so on its own."},"words":{"type":"array","items":{"type":"object","properties":{"word":{"type":"string"},"firstName":{"type":"boolean","description":"A listed first name, in this form or as an inflection of one."},"surname":{"type":"boolean","description":"A listed surname, in this form or as an inflection of one."},"surnameForm":{"type":"boolean","description":"Shaped like a Polish surname (-ski, -cki, -wicz…), listed or not."},"ambiguous":{"type":"boolean","description":"Also an ordinary word, so found only beside other evidence (a surname, a title)."},"never":{"type":"boolean","description":"Never taken as part of a name (a title, an institution, a function word)."}},"required":["word","firstName","surname","surnameForm","ambiguous","never"],"additionalProperties":false}}},"required":["text","found","words"],"additionalProperties":false}},"required":["lists","totals","matches"],"additionalProperties":false}}}}}}},"/privacy/known":{"get":{"operationId":"privacy.sources","summary":"The datasets taught to the shield","description":"Each source values were taught from, and how many. The values themselves are never sent back.","tags":["Privacy shield"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string","description":"Where the values came from, as whoever taught them named it."},"count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"at":{"type":"string","description":"When they were last taught."}},"required":["source","count","at"],"additionalProperties":false}}}}}}},"post":{"operationId":"privacy.learn","summary":"Teach the shield a dataset's values","description":"Each value is masked wherever it appears from now on, in every form it is written, whether or not the detectors would have found it. Teaching only ever masks more, so the agent may do it.","tags":["Privacy shield"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"source":{"type":"string","minLength":1,"maxLength":200,"description":"Where the values came from: a file and its column, a table."},"values":{"maxItems":50000,"type":"array","items":{"type":"object","properties":{"value":{"type":"string","minLength":2,"maxLength":500},"class":{"type":"string","enum":["person-name","national-id","tax-id","identity-document","bank-account","payment-card","email","phone","address"]}},"required":["value","class"]}}},"required":["source","values"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"added":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"known":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["added","known"],"additionalProperties":false}}}}}}},"/privacy/known/forget":{"post":{"operationId":"privacy.forget","summary":"Forget a taught dataset","description":"Stops matching the values taught from one source. Tokens already given to them still resolve, so earlier conversations keep reading right.","tags":["Privacy shield"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"source":{"type":"string","minLength":1,"maxLength":200}},"required":["source"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"forgotten":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["forgotten"],"additionalProperties":false}}}}}}},"/capabilities":{"get":{"operationId":"capabilities.list","summary":"Everything this sandbox is connected to","description":"Each connection with its live state, the settings that are safe to show, and the names of the credentials it holds. The values of those credentials are never in the answer, on any route but one.","tags":["Capabilities"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"capabilities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The connection's id."},"kind":{"type":"string","enum":["devops","monorepo","mcp","cli","plugin","extension","ssh","vpn","exit","netdisk","docker","browser","identity","device","webext","phone","agent","endpoint","localmodel","wallet","fleet"],"description":"What sort of thing it is."},"status":{"type":"object","properties":{"state":{"type":"string","enum":["active","pending","error","inactive"],"description":"Whether it is live, still coming up, broken, or switched off."},"detail":{"description":"What is wrong, in words a person can act on.","type":"string"},"code":{"description":"A short marker for that reason, for anything deciding what to do about it.","type":"string"},"settling":{"description":"True while something under way will move this on its own: a start, a download, a pairing code waiting to be typed. Absent when only a person can move it, or a pushed change will say when it moved.","type":"boolean"}},"required":["state"],"additionalProperties":false,"description":"Whether it is working."},"config":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number","boolean"]},"description":"Its settings, minus anything secret."},"secrets":{"default":[],"description":"Which credentials it holds, by name. The values are on one route only, and it is not this one.","type":"array","items":{"type":"string"}}},"required":["id","kind","status","config","secrets"],"additionalProperties":false},"description":"What this sandbox is connected to."},"recommendations":{"default":[],"description":"Things worth connecting, worked out from what is actually in the workspace rather than from anything you configured. Re-derived on every read, so one whose evidence has moved simply stops being suggested.","type":"array","items":{"type":"object","properties":{"entry":{"type":"string","description":"Which catalog entry is being suggested."},"evidence":{"type":"string","description":"What was seen that prompted it: a file, a remote, printed verbatim so the claim can be checked rather than believed."},"reason":{"type":"string","description":"The same claim in words, without repeating the evidence into it."},"prefill":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"Settings the scan could read, to fill the form so you supply only the credential. Never a secret, even when one is sitting in a checked-in file: the suggestion points at such a file, it does not absorb what is in it."}},"required":["entry","evidence","reason","prefill"],"additionalProperties":false}}},"required":["capabilities","recommendations"],"additionalProperties":false}}}}}},"post":{"operationId":"capabilities.add","summary":"Connect something, or change a connection","description":"Writes a connection and streams the work of applying it, because some kinds provision real infrastructure and take a while. Sending an id that already exists edits that connection: this is the edit as well as the create. Since a caller is never shown stored credentials, it marks the ones it is leaving alone and the daemon fills them in, which is the only way to change one setting without retyping a key.","tags":["Capabilities"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"devops"},"config":{"type":"object","properties":{}}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"monorepo"},"config":{"type":"object","properties":{}}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"mcp"},"config":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Where the tool server answers."},"token":{"description":"The credential it needs, if any. Stored, never echoed back.","type":"string"}},"required":["url"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"cli"},"config":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which tool to give the agent. The rest of the fields are whatever that tool's own card declares it needs, and are checked against it when you connect."}},"required":["provider"],"additionalProperties":{"type":"string"}}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"plugin"},"config":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"The repository to take the plugin from."},"ref":{"description":"A branch, tag or commit to install from. Leave it out for the default branch. The install pins the commit it names at that moment.","type":"string","minLength":1},"path":{"description":"Where inside the repository the plugin lives, for one that sits in a larger checkout.","type":"string","minLength":1},"token":{"description":"A credential for a private repository. Stored, never echoed back.","type":"string","minLength":1},"commit":{"description":"The exact commit installed, in full, resolved from the branch, tag or commit when the plugin was added. Its hooks run inside the agent's session, so it stays at this commit until it is added again.","type":"string","pattern":"^[0-9a-f]{40}$"}},"required":["url"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"extension"},"config":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"The repository to take the extension from."},"ref":{"type":"string","pattern":"^[0-9a-f]{40}$","description":"The exact commit to install, in full. Required rather than optional because extension code runs with your browser's trust: the owner approves precisely the code that runs, and an update is a deliberate re-install at a new commit."},"path":{"description":"Where inside the repository the extension lives, for one that sits in a larger checkout.","type":"string","minLength":1},"token":{"description":"A credential for a private repository. Stored, never echoed back.","type":"string","minLength":1},"registry":{"description":"Which registry this install came from, which is what update checks and security advisories are read against. Absent falls back to the official one.","type":"string","format":"uri"}},"required":["url","ref"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"ssh"},"config":{"oneOf":[{"type":"object","properties":{"auth":{"type":"string","const":"key","description":"Sign in with a key."},"host":{"type":"string","minLength":1,"description":"The machine's address."},"port":{"default":22,"description":"Which port it listens on.","type":"number"},"user":{"type":"string","minLength":1,"description":"Which user to connect as."},"privateKey":{"type":"string","minLength":1,"description":"The private key, whole. Stored with tight permissions and never echoed back."}},"required":["auth","host","user","privateKey"]},{"type":"object","properties":{"auth":{"type":"string","const":"generated","description":"Sign in with a key the sandbox generated. Its private half never left the sandbox."},"host":{"type":"string","minLength":1,"description":"The machine's address."},"port":{"default":22,"description":"Which port it listens on.","type":"number"},"user":{"type":"string","minLength":1,"description":"Which user to connect as."},"privateKey":{"type":"string","minLength":1,"description":"The private half of the generated key. Stored with tight permissions and never echoed back."}},"required":["auth","host","user","privateKey"]},{"type":"object","properties":{"auth":{"type":"string","const":"password","description":"Sign in with a password."},"host":{"type":"string","minLength":1,"description":"The machine's address."},"port":{"default":22,"description":"Which port it listens on.","type":"number"},"user":{"type":"string","minLength":1,"description":"Which user to connect as."},"password":{"type":"string","minLength":1,"description":"The password. Stored, never echoed back."}},"required":["auth","host","user","password"]}]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"vpn"},"config":{"oneOf":[{"type":"object","properties":{"provider":{"type":"string","const":"wireguard"},"config":{"type":"string","minLength":1},"autoConnect":{"default":"on","type":"string","enum":["on","off"]}},"required":["provider","config"]},{"type":"object","properties":{"provider":{"type":"string","const":"fortinet"},"server":{"type":"string","minLength":1},"port":{"default":443,"type":"integer","minimum":1,"maximum":65535},"username":{"type":"string","minLength":1},"password":{"type":"string","minLength":1},"trustedCert":{"type":"string","minLength":1},"realm":{"type":"string","minLength":1},"autoConnect":{"default":"on","type":"string","enum":["on","off"]}},"required":["provider","server","username","password"]},{"type":"object","properties":{"provider":{"type":"string","const":"ipsec"},"server":{"type":"string","minLength":1},"presharedKey":{"type":"string","minLength":1},"localId":{"type":"string","minLength":1},"remoteId":{"type":"string","minLength":1},"username":{"type":"string","minLength":1},"password":{"type":"string","minLength":1},"ikeVersion":{"default":"1","type":"string","enum":["1","2"]},"pfs":{"default":"on","type":"string","enum":["on","off"]},"dhGroup":{"default":"14","type":"string","enum":["2","5","14","15","16","19","20"]},"aggressive":{"default":"on","type":"string","enum":["on","off"]},"routedNetworks":{"default":"0.0.0.0/0","type":"string"},"autoConnect":{"default":"on","type":"string","enum":["on","off"]}},"required":["provider","server","presharedKey"]}]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"exit"},"config":{"oneOf":[{"type":"object","properties":{"provider":{"type":"string","const":"tor"},"country":{"type":"string","pattern":"^[A-Za-z]{2}$"},"autoStart":{"default":"off","type":"string","enum":["on","off"]}},"required":["provider"]},{"type":"object","properties":{"provider":{"type":"string","const":"vpngate"},"country":{"type":"string","pattern":"^[A-Za-z]{2}$"},"autoStart":{"default":"off","type":"string","enum":["on","off"]}},"required":["provider"]},{"type":"object","properties":{"provider":{"type":"string","const":"wireguard"},"config":{"type":"string","minLength":1},"country":{"type":"string","pattern":"^[A-Za-z]{2}$"},"autoStart":{"default":"off","type":"string","enum":["on","off"]}},"required":["provider","config"]}]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"netdisk"},"config":{"oneOf":[{"type":"object","properties":{"provider":{"type":"string","const":"smb"},"server":{"type":"string","minLength":1,"description":"The NAS or file server: a hostname or address, reachable from the sandbox (through a VPN if it is behind one)."},"share":{"type":"string","minLength":1,"description":"The share name, the first path segment after the server in //server/share."},"path":{"description":"A folder inside the share to mount instead of its root.","type":"string"},"username":{"type":"string","minLength":1,"description":"The account the sandbox mounts as. For a read-only disk, give it an account the server itself limits to reading."},"password":{"description":"Its password. Leave empty for a guest share.","type":"string"},"domain":{"description":"The Windows domain or workgroup, only where the server asks for one.","type":"string"},"access":{"default":"read","description":"Whether the agent may write to it. Read-only is the default.","type":"string","enum":["read","readwrite"]},"version":{"default":"auto","description":"The SMB dialect to insist on. Leave on auto unless the server refuses.","type":"string","enum":["auto","3.1.1","3.0","2.1","1.0"]},"autoMount":{"default":"on","type":"string","enum":["on","off"]}},"required":["provider","server","share","username"]}]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"docker"},"config":{"type":"object","properties":{"gpu":{"default":"off","type":"string","enum":["on","off"]},"registryMirror":{"type":"string","format":"uri"},"insecureRegistries":{"type":"string"},"addressPool":{"type":"string"}}}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"browser"},"config":{"type":"object","properties":{"platform":{"type":"string","minLength":1},"username":{"type":"string"},"password":{"type":"string"},"identity":{"type":"string"},"purpose":{"type":"string"},"openedAt":{"type":"string"},"exit":{"type":"string"}},"required":["platform"],"additionalProperties":{"type":"string"}}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"identity"},"config":{"type":"object","properties":{"email":{"type":"string","minLength":3},"password":{"type":"string"},"mailbox":{"type":"string"},"loginUrl":{"type":"string","format":"uri"},"openAccounts":{"default":"off","type":"string","enum":["on","off"]},"exit":{"type":"string"}},"required":["email"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"device"},"config":{"type":"object","properties":{"shell":{"default":"on","type":"string","enum":["on","off"]},"write":{"default":"off","type":"string","enum":["on","off"]},"screen":{"default":"on","type":"string","enum":["on","off"]},"control":{"default":"off","type":"string","enum":["on","off"]},"sandboxes":{"default":"off","type":"string","enum":["on","off"]},"destructive":{"default":"off","type":"string","enum":["on","off"]},"roots":{"type":"string"},"platform":{"type":"string","minLength":1}},"required":["platform"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"webext"},"config":{"type":"object","properties":{"read":{"default":"on","type":"string","enum":["on","off"]},"act":{"default":"on","type":"string","enum":["on","off"]},"screenshot":{"default":"off","type":"string","enum":["on","off"]},"cookies":{"default":"off","type":"string","enum":["on","off"]},"confirm":{"default":"sensitive","type":"string","enum":["sensitive","always","never"]},"platform":{"type":"string","minLength":1}},"required":["platform"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"phone"},"config":{"type":"object","properties":{"screen":{"default":"on","type":"string","enum":["on","off"]},"control":{"default":"off","type":"string","enum":["on","off"]},"files":{"default":"on","type":"string","enum":["on","off"]},"write":{"default":"off","type":"string","enum":["on","off"]},"notifications":{"default":"off","type":"string","enum":["on","off"]},"apps":{"default":"on","type":"string","enum":["on","off"]},"destructive":{"default":"off","type":"string","enum":["on","off"]},"confirm":{"default":"sensitive","type":"string","enum":["sensitive","always","never"]},"platform":{"type":"string","minLength":1}},"required":["platform"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"agent"},"config":{"type":"object","properties":{"command":{"type":"string","minLength":1},"name":{"type":"string","minLength":1},"env":{"type":"string"},"loginCommand":{"type":"string","minLength":1}},"required":["command"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"endpoint"},"config":{"type":"object","properties":{"baseUrl":{"type":"string","format":"uri"},"protocol":{"default":"openai","type":"string","enum":["openai","anthropic"]},"apiKey":{"type":"string"},"headers":{"type":"string"}},"required":["baseUrl"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"localmodel"},"config":{"type":"object","properties":{"model":{"type":"string","minLength":1},"gpu":{"default":"off","type":"string","enum":["on","off"]},"url":{"type":"string","format":"uri"},"context":{"default":"65536","anyOf":[{"type":"string","enum":["16384","32768","65536","131072"]},{"type":"string","const":"custom"}]},"contextTokens":{"type":"integer","minimum":2048,"maximum":1048576}},"required":["model"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"wallet"},"config":{"type":"object","properties":{"network":{"default":"eip155:8453","type":"string","enum":["eip155:8453","eip155:84532"]},"address":{"type":"string"},"perPaymentMaxUsd":{"default":"1.00","type":"string","pattern":"^\\d+(\\.\\d{1,6})?$"},"autoApproveUnderUsd":{"default":"0","type":"string","pattern":"^\\d+(\\.\\d{1,6})?$"},"dailyCapUsd":{"default":"5.00","type":"string","pattern":"^\\d+(\\.\\d{1,6})?$"},"allow":{"type":"string"},"deny":{"type":"string"}}}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"fleet"},"config":{"type":"object","properties":{"token":{"type":"string","minLength":1,"description":"A provisioning token from Settings ▸ Tokens. Stored, never echoed back."}},"required":["token"]}},"required":["id","kind","config"]}]}}}},"responses":{"200":{"description":"OK","content":{"text/event-stream":{"schema":{"oneOf":[{"type":"object","properties":{"event":{"const":"message"},"data":{"type":"object","properties":{"kind":{"type":"string"}},"required":["kind"],"additionalProperties":{}},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event","data"]},{"type":"object","properties":{"event":{"const":"done"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"error"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]}]}}}}}}},"/capabilities/probe":{"post":{"operationId":"capabilities.probe","summary":"Test a connection's settings without saving them","description":"Dials the service the way this connection would and hands back what it said, before anything is written. The answer is the service's own confirmation or its exact refusal, so a wrong token or an unreachable host is found on the form rather than on a card afterwards.","tags":["Capabilities"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"devops"},"config":{"type":"object","properties":{}}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"monorepo"},"config":{"type":"object","properties":{}}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"mcp"},"config":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Where the tool server answers."},"token":{"description":"The credential it needs, if any. Stored, never echoed back.","type":"string"}},"required":["url"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"cli"},"config":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which tool to give the agent. The rest of the fields are whatever that tool's own card declares it needs, and are checked against it when you connect."}},"required":["provider"],"additionalProperties":{"type":"string"}}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"plugin"},"config":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"The repository to take the plugin from."},"ref":{"description":"A branch, tag or commit to install from. Leave it out for the default branch. The install pins the commit it names at that moment.","type":"string","minLength":1},"path":{"description":"Where inside the repository the plugin lives, for one that sits in a larger checkout.","type":"string","minLength":1},"token":{"description":"A credential for a private repository. Stored, never echoed back.","type":"string","minLength":1},"commit":{"description":"The exact commit installed, in full, resolved from the branch, tag or commit when the plugin was added. Its hooks run inside the agent's session, so it stays at this commit until it is added again.","type":"string","pattern":"^[0-9a-f]{40}$"}},"required":["url"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"extension"},"config":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"The repository to take the extension from."},"ref":{"type":"string","pattern":"^[0-9a-f]{40}$","description":"The exact commit to install, in full. Required rather than optional because extension code runs with your browser's trust: the owner approves precisely the code that runs, and an update is a deliberate re-install at a new commit."},"path":{"description":"Where inside the repository the extension lives, for one that sits in a larger checkout.","type":"string","minLength":1},"token":{"description":"A credential for a private repository. Stored, never echoed back.","type":"string","minLength":1},"registry":{"description":"Which registry this install came from, which is what update checks and security advisories are read against. Absent falls back to the official one.","type":"string","format":"uri"}},"required":["url","ref"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"ssh"},"config":{"oneOf":[{"type":"object","properties":{"auth":{"type":"string","const":"key","description":"Sign in with a key."},"host":{"type":"string","minLength":1,"description":"The machine's address."},"port":{"default":22,"description":"Which port it listens on.","type":"number"},"user":{"type":"string","minLength":1,"description":"Which user to connect as."},"privateKey":{"type":"string","minLength":1,"description":"The private key, whole. Stored with tight permissions and never echoed back."}},"required":["auth","host","user","privateKey"]},{"type":"object","properties":{"auth":{"type":"string","const":"generated","description":"Sign in with a key the sandbox generated. Its private half never left the sandbox."},"host":{"type":"string","minLength":1,"description":"The machine's address."},"port":{"default":22,"description":"Which port it listens on.","type":"number"},"user":{"type":"string","minLength":1,"description":"Which user to connect as."},"privateKey":{"type":"string","minLength":1,"description":"The private half of the generated key. Stored with tight permissions and never echoed back."}},"required":["auth","host","user","privateKey"]},{"type":"object","properties":{"auth":{"type":"string","const":"password","description":"Sign in with a password."},"host":{"type":"string","minLength":1,"description":"The machine's address."},"port":{"default":22,"description":"Which port it listens on.","type":"number"},"user":{"type":"string","minLength":1,"description":"Which user to connect as."},"password":{"type":"string","minLength":1,"description":"The password. Stored, never echoed back."}},"required":["auth","host","user","password"]}]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"vpn"},"config":{"oneOf":[{"type":"object","properties":{"provider":{"type":"string","const":"wireguard"},"config":{"type":"string","minLength":1},"autoConnect":{"default":"on","type":"string","enum":["on","off"]}},"required":["provider","config"]},{"type":"object","properties":{"provider":{"type":"string","const":"fortinet"},"server":{"type":"string","minLength":1},"port":{"default":443,"type":"integer","minimum":1,"maximum":65535},"username":{"type":"string","minLength":1},"password":{"type":"string","minLength":1},"trustedCert":{"type":"string","minLength":1},"realm":{"type":"string","minLength":1},"autoConnect":{"default":"on","type":"string","enum":["on","off"]}},"required":["provider","server","username","password"]},{"type":"object","properties":{"provider":{"type":"string","const":"ipsec"},"server":{"type":"string","minLength":1},"presharedKey":{"type":"string","minLength":1},"localId":{"type":"string","minLength":1},"remoteId":{"type":"string","minLength":1},"username":{"type":"string","minLength":1},"password":{"type":"string","minLength":1},"ikeVersion":{"default":"1","type":"string","enum":["1","2"]},"pfs":{"default":"on","type":"string","enum":["on","off"]},"dhGroup":{"default":"14","type":"string","enum":["2","5","14","15","16","19","20"]},"aggressive":{"default":"on","type":"string","enum":["on","off"]},"routedNetworks":{"default":"0.0.0.0/0","type":"string"},"autoConnect":{"default":"on","type":"string","enum":["on","off"]}},"required":["provider","server","presharedKey"]}]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"exit"},"config":{"oneOf":[{"type":"object","properties":{"provider":{"type":"string","const":"tor"},"country":{"type":"string","pattern":"^[A-Za-z]{2}$"},"autoStart":{"default":"off","type":"string","enum":["on","off"]}},"required":["provider"]},{"type":"object","properties":{"provider":{"type":"string","const":"vpngate"},"country":{"type":"string","pattern":"^[A-Za-z]{2}$"},"autoStart":{"default":"off","type":"string","enum":["on","off"]}},"required":["provider"]},{"type":"object","properties":{"provider":{"type":"string","const":"wireguard"},"config":{"type":"string","minLength":1},"country":{"type":"string","pattern":"^[A-Za-z]{2}$"},"autoStart":{"default":"off","type":"string","enum":["on","off"]}},"required":["provider","config"]}]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"netdisk"},"config":{"oneOf":[{"type":"object","properties":{"provider":{"type":"string","const":"smb"},"server":{"type":"string","minLength":1,"description":"The NAS or file server: a hostname or address, reachable from the sandbox (through a VPN if it is behind one)."},"share":{"type":"string","minLength":1,"description":"The share name, the first path segment after the server in //server/share."},"path":{"description":"A folder inside the share to mount instead of its root.","type":"string"},"username":{"type":"string","minLength":1,"description":"The account the sandbox mounts as. For a read-only disk, give it an account the server itself limits to reading."},"password":{"description":"Its password. Leave empty for a guest share.","type":"string"},"domain":{"description":"The Windows domain or workgroup, only where the server asks for one.","type":"string"},"access":{"default":"read","description":"Whether the agent may write to it. Read-only is the default.","type":"string","enum":["read","readwrite"]},"version":{"default":"auto","description":"The SMB dialect to insist on. Leave on auto unless the server refuses.","type":"string","enum":["auto","3.1.1","3.0","2.1","1.0"]},"autoMount":{"default":"on","type":"string","enum":["on","off"]}},"required":["provider","server","share","username"]}]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"docker"},"config":{"type":"object","properties":{"gpu":{"default":"off","type":"string","enum":["on","off"]},"registryMirror":{"type":"string","format":"uri"},"insecureRegistries":{"type":"string"},"addressPool":{"type":"string"}}}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"browser"},"config":{"type":"object","properties":{"platform":{"type":"string","minLength":1},"username":{"type":"string"},"password":{"type":"string"},"identity":{"type":"string"},"purpose":{"type":"string"},"openedAt":{"type":"string"},"exit":{"type":"string"}},"required":["platform"],"additionalProperties":{"type":"string"}}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"identity"},"config":{"type":"object","properties":{"email":{"type":"string","minLength":3},"password":{"type":"string"},"mailbox":{"type":"string"},"loginUrl":{"type":"string","format":"uri"},"openAccounts":{"default":"off","type":"string","enum":["on","off"]},"exit":{"type":"string"}},"required":["email"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"device"},"config":{"type":"object","properties":{"shell":{"default":"on","type":"string","enum":["on","off"]},"write":{"default":"off","type":"string","enum":["on","off"]},"screen":{"default":"on","type":"string","enum":["on","off"]},"control":{"default":"off","type":"string","enum":["on","off"]},"sandboxes":{"default":"off","type":"string","enum":["on","off"]},"destructive":{"default":"off","type":"string","enum":["on","off"]},"roots":{"type":"string"},"platform":{"type":"string","minLength":1}},"required":["platform"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"webext"},"config":{"type":"object","properties":{"read":{"default":"on","type":"string","enum":["on","off"]},"act":{"default":"on","type":"string","enum":["on","off"]},"screenshot":{"default":"off","type":"string","enum":["on","off"]},"cookies":{"default":"off","type":"string","enum":["on","off"]},"confirm":{"default":"sensitive","type":"string","enum":["sensitive","always","never"]},"platform":{"type":"string","minLength":1}},"required":["platform"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"phone"},"config":{"type":"object","properties":{"screen":{"default":"on","type":"string","enum":["on","off"]},"control":{"default":"off","type":"string","enum":["on","off"]},"files":{"default":"on","type":"string","enum":["on","off"]},"write":{"default":"off","type":"string","enum":["on","off"]},"notifications":{"default":"off","type":"string","enum":["on","off"]},"apps":{"default":"on","type":"string","enum":["on","off"]},"destructive":{"default":"off","type":"string","enum":["on","off"]},"confirm":{"default":"sensitive","type":"string","enum":["sensitive","always","never"]},"platform":{"type":"string","minLength":1}},"required":["platform"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"agent"},"config":{"type":"object","properties":{"command":{"type":"string","minLength":1},"name":{"type":"string","minLength":1},"env":{"type":"string"},"loginCommand":{"type":"string","minLength":1}},"required":["command"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"endpoint"},"config":{"type":"object","properties":{"baseUrl":{"type":"string","format":"uri"},"protocol":{"default":"openai","type":"string","enum":["openai","anthropic"]},"apiKey":{"type":"string"},"headers":{"type":"string"}},"required":["baseUrl"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"localmodel"},"config":{"type":"object","properties":{"model":{"type":"string","minLength":1},"gpu":{"default":"off","type":"string","enum":["on","off"]},"url":{"type":"string","format":"uri"},"context":{"default":"65536","anyOf":[{"type":"string","enum":["16384","32768","65536","131072"]},{"type":"string","const":"custom"}]},"contextTokens":{"type":"integer","minimum":2048,"maximum":1048576}},"required":["model"]}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"wallet"},"config":{"type":"object","properties":{"network":{"default":"eip155:8453","type":"string","enum":["eip155:8453","eip155:84532"]},"address":{"type":"string"},"perPaymentMaxUsd":{"default":"1.00","type":"string","pattern":"^\\d+(\\.\\d{1,6})?$"},"autoApproveUnderUsd":{"default":"0","type":"string","pattern":"^\\d+(\\.\\d{1,6})?$"},"dailyCapUsd":{"default":"5.00","type":"string","pattern":"^\\d+(\\.\\d{1,6})?$"},"allow":{"type":"string"},"deny":{"type":"string"}}}},"required":["id","kind","config"]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"kind":{"type":"string","const":"fleet"},"config":{"type":"object","properties":{"token":{"type":"string","minLength":1,"description":"A provisioning token from Settings ▸ Tokens. Stored, never echoed back."}},"required":["token"]}},"required":["id","kind","config"]}]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"checked":{"type":"boolean","description":"Whether this connection can be tested from here at all. False is not a failure: it is 'no test exists'."},"ok":{"type":"boolean","description":"Whether the service answered as itself."},"message":{"type":"string","description":"What happened, in the words a person standing in front of the form needs: the service's own answer, or its refusal."},"who":{"description":"Who the service said the credential belongs to, when it said.","type":"string"}},"required":["checked","ok","message"],"additionalProperties":false}}}}}}},"/capabilities/ssh-key":{"post":{"operationId":"capabilities.sshKey","summary":"Generate an SSH key for a connection","description":"Makes an ed25519 key pair inside the sandbox and answers with its public half, to authorize on the server, and a one-time token. The private half is never in the answer: it waits in the sandbox until an add sends the token where the private key goes, and lapses if none does within thirty minutes.","tags":["Capabilities"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"publicKey":{"type":"string","description":"The public half, as the one line a server's authorized_keys holds: `ssh-ed25519 AAAA… intentic-<sandbox>`."},"token":{"type":"string","description":"Stands for the private half, which never leaves the sandbox. Sent wrapped as a marker in the private key's place, it installs that key once, and it lapses after thirty minutes."}},"required":["publicKey","token"],"additionalProperties":false}}}}}}},"/capabilities/{id}":{"delete":{"operationId":"capabilities.remove","summary":"Disconnect something","description":"Tears a connection down. The kinds that own real infrastructure refuse, because deleting those would be losing data rather than losing a connection.","tags":["Capabilities"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which connection."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/capabilities/{id}/rename":{"post":{"operationId":"capabilities.rename","summary":"Rename a connection","description":"Carries everything the old name keyed across with it: a browser profile and its logins, an enrolled machine, an extension's copy of its source. Removing and re-adding would lose exactly the state that made the connection worth keeping. Kinds whose name is part of what they are refuse.","tags":["Capabilities"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"}},"required":["to"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/capabilities/{id}/secret":{"post":{"operationId":"capabilities.setSecret","summary":"Replace a stored credential","description":"Swaps one connection's key or token for a new one and re-applies it, without touching any of its other settings.","tags":["Capabilities"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which connection."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"value":{"type":"string","minLength":1,"description":"The new credential. Its other settings are left alone."}},"required":["value"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/capabilities/{id}/status":{"get":{"operationId":"capabilities.status","summary":"Re-check one connection","description":"Probes a single connection right now, for a screen that wants to refresh one row rather than the whole list.","tags":["Capabilities"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which connection."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"state":{"type":"string","enum":["active","pending","error","inactive"],"description":"Whether it is live, still coming up, broken, or switched off."},"detail":{"description":"What is wrong, in words a person can act on.","type":"string"},"code":{"description":"A short marker for that reason, for anything deciding what to do about it.","type":"string"},"settling":{"description":"True while something under way will move this on its own: a start, a download, a pairing code waiting to be typed. Absent when only a person can move it, or a pushed change will say when it moved.","type":"boolean"}},"required":["state"],"additionalProperties":false}}}}}}},"/capabilities/{id}/connection":{"get":{"operationId":"capabilities.connection","summary":"A connection's settings, credentials included","description":"The one call that hands back stored secrets, so an extension's own backend can dial the service behind a connection. Never answered for a signed-in person: only a machine credential reaches it, and an extension's only if its manifest asked for this route out loud at install time, and only for a connection of a kind that extension itself contributes.","tags":["Capabilities"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which connection."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The connection's id."},"kind":{"type":"string","description":"What sort of thing it is."},"config":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"Its settings exactly as stored, credentials included. The field names are its own kind's, which the caller already knows."}},"required":["id","kind","config"],"additionalProperties":false}}}}}}},"/capabilities/marketplace":{"post":{"operationId":"capabilities.marketplace","summary":"Read a plugin marketplace","description":"Resolves a plugin marketplace source into the list of connections you could install from it.","tags":["Capabilities"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"The registry to read."},"token":{"description":"A credential for a private one. Sent as a body rather than in the address, so it never lands in a log.","type":"string","minLength":1}},"required":["url"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"What the registry calls itself."},"plugins":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"version":{"type":"string"},"kind":{"type":"string","enum":["plugin","extension"]},"trust":{"type":"string","enum":["verified","listed","blocked"]},"trustReason":{"type":"string"},"securityReview":{"type":"object","properties":{"sha":{"type":"string","pattern":"^[0-9a-f]{40}$"},"url":{"type":"string","minLength":1},"path":{"type":"string","minLength":1},"policy":{"type":"string","minLength":1},"reviewer":{"type":"string","minLength":1},"reviewedAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"},"runId":{"type":"string","minLength":1},"deterministic":{"type":"object","properties":{"policy":{"type":"string","minLength":1},"scanner":{"type":"string","minLength":1},"version":{"type":"string","minLength":1},"runId":{"type":"string","minLength":1}},"required":["policy","scanner","version","runId"],"additionalProperties":false}},"required":["sha","url","policy","reviewer","reviewedAt","runId","deterministic"],"additionalProperties":false},"admitted":{"type":"boolean"},"securityFix":{"type":"boolean"},"category":{"type":"string"},"art":{"type":"string"},"logo":{"type":"string"},"icon":{"type":"string"},"homepage":{"type":"string"},"install":{"type":"object","properties":{"url":{"type":"string"},"ref":{"type":"string"},"path":{"type":"string"}},"required":["url"],"additionalProperties":false},"stars":{"type":"integer","minimum":0,"maximum":9007199254740991},"pushedAt":{"type":"string"},"checks":{"type":"object","properties":{"sha":{"type":"string"},"manifest":{"type":"string"},"bundle":{"type":"string"},"engines":{"type":"string"}},"required":["sha","manifest","bundle"],"additionalProperties":false}},"required":["name","kind","trust","admitted"],"additionalProperties":false},"description":"What it lists, each with the curated decision, the resolved pointer and what a scan found upstream."}},"required":["name","plugins"],"additionalProperties":false}}}}}}},"/capabilities/refs":{"post":{"operationId":"capabilities.refs","summary":"The versions a repository offers","description":"Asks a git remote what it advertises and hands back every branch and tag with the commit it points at, plus which branch is its default. Nothing is cloned and nothing is written, so this is cheap enough to answer a form as someone types a repository into it.","tags":["Capabilities"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"The repository to ask. http(s) only: an ssh remote would stop on a host-key prompt nobody can answer."},"token":{"description":"A credential for a private one. Sent as a body rather than in the address, so it never lands in a log. A form editing a live connection has never been shown its token: it sends the VAULTED marker here and names the connection in `keeping`, so a private repository still answers without anyone retyping a key.","type":"string","minLength":1},"keeping":{"description":"Which connection a VAULTED token belongs to. Ignored when a real token is sent.","type":"string","minLength":1}},"required":["url"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"defaultBranch":{"description":"The branch the remote advertises as HEAD, the one to offer first. Absent when the remote advertises no symref.","type":"string"},"refs":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"The branch or tag as a person names it: `main`, `v1.4.0`."},"kind":{"type":"string","enum":["branch","tag"]},"sha":{"type":"string","pattern":"^[0-9a-f]{40}$","description":"The commit it points at. An annotated tag is peeled here, so this is always a commit, never a tag object."}},"required":["name","kind","sha"],"additionalProperties":false},"description":"Every branch the remote advertises, then every tag. Which to offer first is the reader's question, not this one's."}},"required":["refs"],"additionalProperties":false}}}}}}},"/capabilities/recommendations/{entry}":{"delete":{"operationId":"capabilities.dismiss","summary":"Stop suggesting this connection","description":"Not needed, for now. Nothing is torn down. The suggestion comes back if what prompted it in the workspace changes, because what is remembered is the evidence, not the refusal.","tags":["Capabilities"],"parameters":[{"name":"entry","in":"path","required":true,"schema":{"type":"string","description":"Which suggestion to stop making."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/capabilities/{id}/login":{"post":{"operationId":"capabilities.login","summary":"Sign in to a connection by hand","description":"Opens the connection's own sign-in in a terminal a person can type into, for the flows that need a code pasted or a device confirmed. The answer names the terminal to attach to.","tags":["Capabilities"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which connection."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"string","description":"The terminal the sign-in is happening in. Attach to it to type."}},"required":["session"],"additionalProperties":false}}}}}}},"/capabilities/{id}/otp":{"get":{"operationId":"capabilities.otp","summary":"Mint a one-time code","description":"Generates a single two-factor code from a stored seed. The one credential-adjacent read an agent is allowed, and it is safe because a code expires in seconds and never reveals the seed, so an agent can answer a prompt without ever holding the factor.","tags":["Capabilities"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which connection."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","description":"The code."},"secondsRemaining":{"type":"number","description":"How long it lasts. Its expiring is what makes handing one to an agent safe, since the seed behind it is never revealed."}},"required":["code","secondsRemaining"],"additionalProperties":false}}}}}}},"/needs/ask":{"post":{"operationId":"needs.ask","summary":"Ask a person for something the task needs","description":"Raises a need in the conversation the calling shell belongs to and holds the call up to `wait` seconds for an answer. Answers `met` when it is usable now (or already was), `open` when it is still waiting, and `refused` when nothing was raised or a person declined. An open need's answer reaches the conversation by itself.","tags":["Needs"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ask":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"capability"},"entry":{"type":"string","minLength":1,"description":"The catalog entry or a connected instance's id."},"target":{"description":"The site, host or address it is for.","type":"string","maxLength":200},"set":{"description":"Settings to fill in, or to change on a connected one. Credentials are refused.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"reconnect":{"description":"It is connected, but its credential is being refused: ask for a new one rather than being told to use it.","type":"boolean"}},"required":["kind","entry"]},{"type":"object","properties":{"kind":{"type":"string","const":"secret"},"name":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,127}$","description":"The name it is stored under, and what `{{secret:NAME}}` will resolve."},"where":{"description":"How it will be used: the header, the command, the site it goes to.","type":"string","maxLength":200},"link":{"description":"Where a person gets one, shown as a link on the card.","type":"string","format":"uri"},"hint":{"description":"What a valid one looks like, so a wrong paste is caught by eye.","type":"string","maxLength":120},"replace":{"description":"One is stored under this name and is being refused: the ask is for a new value in its place.","type":"boolean"}},"required":["kind","name"]},{"type":"object","properties":{"kind":{"type":"string","const":"grant"},"subject":{"type":"string","enum":["capability","folder","shelf","site"],"description":"A connected capability the persona leaves out, a folder outside the conversation's reach, a whole shelf of tools, or a site in the person's own browser, which only their browser extension can allow."},"what":{"type":"string","minLength":1,"maxLength":400}},"required":["kind","subject","what"]},{"type":"object","properties":{"kind":{"type":"string","const":"release"},"subject":{"type":"string","minLength":1}},"required":["kind","subject"]},{"type":"object","properties":{"kind":{"type":"string","const":"environment"},"tool":{"type":"string","minLength":1,"maxLength":64},"steps":{"type":"string","minLength":1,"maxLength":20000}},"required":["kind","tool","steps"]}]},"why":{"type":"string","maxLength":280},"wait":{"description":"Seconds to hold the call for an answer. Absent is 90 for a watched turn and 0 for an unattended one.","type":"integer","minimum":0,"maximum":100}},"required":["ask"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"state":{"type":"string","enum":["met","open","refused"]},"message":{"type":"string","description":"The sentence the CLI prints, written for the agent to act on."},"need":{"type":"object","properties":{"id":{"type":"string","description":"The need's handle, the one `needs cancel` takes."},"conversationId":{"type":"string","description":"The conversation that asked, and the one its answer wakes."},"subject":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"capability"},"entry":{"type":"string","minLength":1,"description":"The catalog entry, as the catalog names it."},"name":{"type":"string","description":"What the catalog calls it, never the agent's spelling."},"mode":{"type":"string","enum":["connect","reconnect","change"],"description":"Connect something new, give a connected one a credential that works again, or change a setting on a connected one."},"instance":{"description":"The connection a reconnect or a change is about.","type":"string"},"target":{"description":"The site, host or address the connection is for, when the entry can hold several.","type":"string"},"prefill":{"description":"Settings the agent could fill in for a new connection, never a credential: the daemon keeps only the entry's own non-secret fields.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"changes":{"description":"For a change: each setting and the value it would take. Never a credential.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"reason":{"description":"The daemon's own sentence on why this is the ask, such as the refusal a connected credential keeps getting.","type":"string"},"reported":{"description":"The agent reported the credential refused while the connection still probes as working, so only a person's word that it is fixed meets it: the probe could not see the refusal in the first place.","type":"boolean"}},"required":["kind","entry","name","mode"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret"},"name":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,127}$","description":"The name it is stored under, and what `{{secret:NAME}}` will resolve."},"where":{"description":"How it will be used: the header, the command, the site it goes to.","type":"string","maxLength":200},"link":{"description":"Where a person gets one, shown as a link on the card.","type":"string","format":"uri"},"hint":{"description":"What a valid one looks like, so a wrong paste is caught by eye.","type":"string","maxLength":120},"replace":{"description":"One is stored under this name and is being refused: the ask is for a new value in its place.","type":"boolean"}},"required":["kind","name"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"grant"},"subject":{"type":"string","enum":["capability","folder","shelf","site"],"description":"A connected capability the persona leaves out, a folder outside the conversation's reach, a whole shelf of tools, or a site in the person's own browser, which only their browser extension can allow."},"what":{"type":"string","minLength":1,"description":"The capability's id, the folder, or the shelf."},"label":{"type":"string","description":"What it is, in the daemon's words: the account and its kind, the folder, the shelf's name."},"persona":{"description":"The persona that withholds it, when one does.","type":"string"},"scope":{"description":"How far the yes went, once there was one.","type":"string","enum":["conversation","persona"]}},"required":["kind","subject","what","label"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"release"},"subject":{"type":"string","minLength":1,"description":"The gated account or connector."},"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it. Anyone else's answer is refused and leaves it waiting."}},"required":["kind","subject","approvers"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"environment"},"tool":{"type":"string","minLength":1,"description":"What the steps install, the name the proposal is filed under."},"steps":{"type":"string","minLength":1,"description":"The Dockerfile steps proposed for the image's custom section: RUN and ENV lines only."},"approvedHash":{"description":"The overlay these steps were approved into; met once the running container was built from it.","type":"string"}},"required":["kind","tool","steps"],"additionalProperties":false}],"description":"What exactly is asked for."},"title":{"type":"string","description":"The one line it leads with, in the daemon's words."},"why":{"description":"The agent's case for it, and the only words on a need that are the agent's.","type":"string","maxLength":280},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."},"createdAt":{"type":"number","description":"When it was raised, in milliseconds."},"updatedAt":{"type":"number","description":"When it last moved, in milliseconds."},"answeredBy":{"description":"Who answered it, as the sandbox verified them.","type":"string"},"outcome":{"description":"How it ended, in the daemon's words, once it has.","type":"string"},"told":{"description":"How the agent heard the outcome, once it has.","type":"string","enum":["call","turn","queued"]},"unattended":{"description":"Raised by a turn nobody was watching, so the card waited for whoever came next.","type":"boolean"}},"required":["id","conversationId","subject","title","status","createdAt","updatedAt"],"additionalProperties":false},"code":{"description":"A refusal's type, for a script to branch on.","type":"string"}},"required":["state","message"],"additionalProperties":false}}}}}}},"/needs/mine":{"get":{"operationId":"needs.mine","summary":"This conversation's needs","description":"Every need the calling shell's conversation raised, newest first, open or answered.","tags":["Needs"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"needs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The need's handle, the one `needs cancel` takes."},"conversationId":{"type":"string","description":"The conversation that asked, and the one its answer wakes."},"subject":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"capability"},"entry":{"type":"string","minLength":1,"description":"The catalog entry, as the catalog names it."},"name":{"type":"string","description":"What the catalog calls it, never the agent's spelling."},"mode":{"type":"string","enum":["connect","reconnect","change"],"description":"Connect something new, give a connected one a credential that works again, or change a setting on a connected one."},"instance":{"description":"The connection a reconnect or a change is about.","type":"string"},"target":{"description":"The site, host or address the connection is for, when the entry can hold several.","type":"string"},"prefill":{"description":"Settings the agent could fill in for a new connection, never a credential: the daemon keeps only the entry's own non-secret fields.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"changes":{"description":"For a change: each setting and the value it would take. Never a credential.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"reason":{"description":"The daemon's own sentence on why this is the ask, such as the refusal a connected credential keeps getting.","type":"string"},"reported":{"description":"The agent reported the credential refused while the connection still probes as working, so only a person's word that it is fixed meets it: the probe could not see the refusal in the first place.","type":"boolean"}},"required":["kind","entry","name","mode"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret"},"name":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,127}$","description":"The name it is stored under, and what `{{secret:NAME}}` will resolve."},"where":{"description":"How it will be used: the header, the command, the site it goes to.","type":"string","maxLength":200},"link":{"description":"Where a person gets one, shown as a link on the card.","type":"string","format":"uri"},"hint":{"description":"What a valid one looks like, so a wrong paste is caught by eye.","type":"string","maxLength":120},"replace":{"description":"One is stored under this name and is being refused: the ask is for a new value in its place.","type":"boolean"}},"required":["kind","name"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"grant"},"subject":{"type":"string","enum":["capability","folder","shelf","site"],"description":"A connected capability the persona leaves out, a folder outside the conversation's reach, a whole shelf of tools, or a site in the person's own browser, which only their browser extension can allow."},"what":{"type":"string","minLength":1,"description":"The capability's id, the folder, or the shelf."},"label":{"type":"string","description":"What it is, in the daemon's words: the account and its kind, the folder, the shelf's name."},"persona":{"description":"The persona that withholds it, when one does.","type":"string"},"scope":{"description":"How far the yes went, once there was one.","type":"string","enum":["conversation","persona"]}},"required":["kind","subject","what","label"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"release"},"subject":{"type":"string","minLength":1,"description":"The gated account or connector."},"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it. Anyone else's answer is refused and leaves it waiting."}},"required":["kind","subject","approvers"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"environment"},"tool":{"type":"string","minLength":1,"description":"What the steps install, the name the proposal is filed under."},"steps":{"type":"string","minLength":1,"description":"The Dockerfile steps proposed for the image's custom section: RUN and ENV lines only."},"approvedHash":{"description":"The overlay these steps were approved into; met once the running container was built from it.","type":"string"}},"required":["kind","tool","steps"],"additionalProperties":false}],"description":"What exactly is asked for."},"title":{"type":"string","description":"The one line it leads with, in the daemon's words."},"why":{"description":"The agent's case for it, and the only words on a need that are the agent's.","type":"string","maxLength":280},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."},"createdAt":{"type":"number","description":"When it was raised, in milliseconds."},"updatedAt":{"type":"number","description":"When it last moved, in milliseconds."},"answeredBy":{"description":"Who answered it, as the sandbox verified them.","type":"string"},"outcome":{"description":"How it ended, in the daemon's words, once it has.","type":"string"},"told":{"description":"How the agent heard the outcome, once it has.","type":"string","enum":["call","turn","queued"]},"unattended":{"description":"Raised by a turn nobody was watching, so the card waited for whoever came next.","type":"boolean"}},"required":["id","conversationId","subject","title","status","createdAt","updatedAt"],"additionalProperties":false},"description":"Newest first."}},"required":["needs"],"additionalProperties":false}}}}}}},"/needs/{id}/withdraw":{"post":{"operationId":"needs.withdraw","summary":"Withdraw a need","description":"Closes one of this conversation's open needs because the task no longer needs it. Its card says so.","tags":["Needs"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which need."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The need's handle, the one `needs cancel` takes."},"conversationId":{"type":"string","description":"The conversation that asked, and the one its answer wakes."},"subject":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"capability"},"entry":{"type":"string","minLength":1,"description":"The catalog entry, as the catalog names it."},"name":{"type":"string","description":"What the catalog calls it, never the agent's spelling."},"mode":{"type":"string","enum":["connect","reconnect","change"],"description":"Connect something new, give a connected one a credential that works again, or change a setting on a connected one."},"instance":{"description":"The connection a reconnect or a change is about.","type":"string"},"target":{"description":"The site, host or address the connection is for, when the entry can hold several.","type":"string"},"prefill":{"description":"Settings the agent could fill in for a new connection, never a credential: the daemon keeps only the entry's own non-secret fields.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"changes":{"description":"For a change: each setting and the value it would take. Never a credential.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"reason":{"description":"The daemon's own sentence on why this is the ask, such as the refusal a connected credential keeps getting.","type":"string"},"reported":{"description":"The agent reported the credential refused while the connection still probes as working, so only a person's word that it is fixed meets it: the probe could not see the refusal in the first place.","type":"boolean"}},"required":["kind","entry","name","mode"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret"},"name":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,127}$","description":"The name it is stored under, and what `{{secret:NAME}}` will resolve."},"where":{"description":"How it will be used: the header, the command, the site it goes to.","type":"string","maxLength":200},"link":{"description":"Where a person gets one, shown as a link on the card.","type":"string","format":"uri"},"hint":{"description":"What a valid one looks like, so a wrong paste is caught by eye.","type":"string","maxLength":120},"replace":{"description":"One is stored under this name and is being refused: the ask is for a new value in its place.","type":"boolean"}},"required":["kind","name"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"grant"},"subject":{"type":"string","enum":["capability","folder","shelf","site"],"description":"A connected capability the persona leaves out, a folder outside the conversation's reach, a whole shelf of tools, or a site in the person's own browser, which only their browser extension can allow."},"what":{"type":"string","minLength":1,"description":"The capability's id, the folder, or the shelf."},"label":{"type":"string","description":"What it is, in the daemon's words: the account and its kind, the folder, the shelf's name."},"persona":{"description":"The persona that withholds it, when one does.","type":"string"},"scope":{"description":"How far the yes went, once there was one.","type":"string","enum":["conversation","persona"]}},"required":["kind","subject","what","label"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"release"},"subject":{"type":"string","minLength":1,"description":"The gated account or connector."},"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it. Anyone else's answer is refused and leaves it waiting."}},"required":["kind","subject","approvers"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"environment"},"tool":{"type":"string","minLength":1,"description":"What the steps install, the name the proposal is filed under."},"steps":{"type":"string","minLength":1,"description":"The Dockerfile steps proposed for the image's custom section: RUN and ENV lines only."},"approvedHash":{"description":"The overlay these steps were approved into; met once the running container was built from it.","type":"string"}},"required":["kind","tool","steps"],"additionalProperties":false}],"description":"What exactly is asked for."},"title":{"type":"string","description":"The one line it leads with, in the daemon's words."},"why":{"description":"The agent's case for it, and the only words on a need that are the agent's.","type":"string","maxLength":280},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."},"createdAt":{"type":"number","description":"When it was raised, in milliseconds."},"updatedAt":{"type":"number","description":"When it last moved, in milliseconds."},"answeredBy":{"description":"Who answered it, as the sandbox verified them.","type":"string"},"outcome":{"description":"How it ended, in the daemon's words, once it has.","type":"string"},"told":{"description":"How the agent heard the outcome, once it has.","type":"string","enum":["call","turn","queued"]},"unattended":{"description":"Raised by a turn nobody was watching, so the card waited for whoever came next.","type":"boolean"}},"required":["id","conversationId","subject","title","status","createdAt","updatedAt"],"additionalProperties":false}}}}}}},"/needs":{"get":{"operationId":"needs.list","summary":"What agents are waiting on people for","description":"Needs across the sandbox, or one conversation's, newest first. Never a secret's value.","tags":["Needs"],"parameters":[{"name":"conversationId","in":"query","schema":{"description":"One conversation's needs. Absent is every conversation's.","type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"open","in":"query","schema":{"description":"Only the ones still waiting.","type":"boolean"},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"needs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The need's handle, the one `needs cancel` takes."},"conversationId":{"type":"string","description":"The conversation that asked, and the one its answer wakes."},"subject":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"capability"},"entry":{"type":"string","minLength":1,"description":"The catalog entry, as the catalog names it."},"name":{"type":"string","description":"What the catalog calls it, never the agent's spelling."},"mode":{"type":"string","enum":["connect","reconnect","change"],"description":"Connect something new, give a connected one a credential that works again, or change a setting on a connected one."},"instance":{"description":"The connection a reconnect or a change is about.","type":"string"},"target":{"description":"The site, host or address the connection is for, when the entry can hold several.","type":"string"},"prefill":{"description":"Settings the agent could fill in for a new connection, never a credential: the daemon keeps only the entry's own non-secret fields.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"changes":{"description":"For a change: each setting and the value it would take. Never a credential.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"reason":{"description":"The daemon's own sentence on why this is the ask, such as the refusal a connected credential keeps getting.","type":"string"},"reported":{"description":"The agent reported the credential refused while the connection still probes as working, so only a person's word that it is fixed meets it: the probe could not see the refusal in the first place.","type":"boolean"}},"required":["kind","entry","name","mode"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret"},"name":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,127}$","description":"The name it is stored under, and what `{{secret:NAME}}` will resolve."},"where":{"description":"How it will be used: the header, the command, the site it goes to.","type":"string","maxLength":200},"link":{"description":"Where a person gets one, shown as a link on the card.","type":"string","format":"uri"},"hint":{"description":"What a valid one looks like, so a wrong paste is caught by eye.","type":"string","maxLength":120},"replace":{"description":"One is stored under this name and is being refused: the ask is for a new value in its place.","type":"boolean"}},"required":["kind","name"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"grant"},"subject":{"type":"string","enum":["capability","folder","shelf","site"],"description":"A connected capability the persona leaves out, a folder outside the conversation's reach, a whole shelf of tools, or a site in the person's own browser, which only their browser extension can allow."},"what":{"type":"string","minLength":1,"description":"The capability's id, the folder, or the shelf."},"label":{"type":"string","description":"What it is, in the daemon's words: the account and its kind, the folder, the shelf's name."},"persona":{"description":"The persona that withholds it, when one does.","type":"string"},"scope":{"description":"How far the yes went, once there was one.","type":"string","enum":["conversation","persona"]}},"required":["kind","subject","what","label"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"release"},"subject":{"type":"string","minLength":1,"description":"The gated account or connector."},"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it. Anyone else's answer is refused and leaves it waiting."}},"required":["kind","subject","approvers"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"environment"},"tool":{"type":"string","minLength":1,"description":"What the steps install, the name the proposal is filed under."},"steps":{"type":"string","minLength":1,"description":"The Dockerfile steps proposed for the image's custom section: RUN and ENV lines only."},"approvedHash":{"description":"The overlay these steps were approved into; met once the running container was built from it.","type":"string"}},"required":["kind","tool","steps"],"additionalProperties":false}],"description":"What exactly is asked for."},"title":{"type":"string","description":"The one line it leads with, in the daemon's words."},"why":{"description":"The agent's case for it, and the only words on a need that are the agent's.","type":"string","maxLength":280},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."},"createdAt":{"type":"number","description":"When it was raised, in milliseconds."},"updatedAt":{"type":"number","description":"When it last moved, in milliseconds."},"answeredBy":{"description":"Who answered it, as the sandbox verified them.","type":"string"},"outcome":{"description":"How it ended, in the daemon's words, once it has.","type":"string"},"told":{"description":"How the agent heard the outcome, once it has.","type":"string","enum":["call","turn","queued"]},"unattended":{"description":"Raised by a turn nobody was watching, so the card waited for whoever came next.","type":"boolean"}},"required":["id","conversationId","subject","title","status","createdAt","updatedAt"],"additionalProperties":false},"description":"Newest first."}},"required":["needs"],"additionalProperties":false}}}}}}},"/needs/{id}/answer":{"post":{"operationId":"needs.answer","summary":"Answer a need","description":"Declines it, or says yes the way its card offered: accept a connection being set up, apply a change, grant for this conversation or the persona, release a gated credential, approve an environment proposal. A release is refused from anyone the gate does not name.","tags":["Needs"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which need."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"answer":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"decline"},"note":{"description":"Why not, passed to the agent.","type":"string","maxLength":500}},"required":["kind"]},{"type":"object","properties":{"kind":{"type":"string","const":"accept"}},"required":["kind"]},{"type":"object","properties":{"kind":{"type":"string","const":"apply"}},"required":["kind"]},{"type":"object","properties":{"kind":{"type":"string","const":"grant"},"scope":{"type":"string","enum":["conversation","persona"],"description":"How far a yes goes: this conversation only, or the persona itself, for every conversation that wears it."}},"required":["kind","scope"]},{"type":"object","properties":{"kind":{"type":"string","const":"release"}},"required":["kind"]},{"type":"object","properties":{"kind":{"type":"string","const":"approve"}},"required":["kind"]}]}},"required":["answer"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The need's handle, the one `needs cancel` takes."},"conversationId":{"type":"string","description":"The conversation that asked, and the one its answer wakes."},"subject":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"capability"},"entry":{"type":"string","minLength":1,"description":"The catalog entry, as the catalog names it."},"name":{"type":"string","description":"What the catalog calls it, never the agent's spelling."},"mode":{"type":"string","enum":["connect","reconnect","change"],"description":"Connect something new, give a connected one a credential that works again, or change a setting on a connected one."},"instance":{"description":"The connection a reconnect or a change is about.","type":"string"},"target":{"description":"The site, host or address the connection is for, when the entry can hold several.","type":"string"},"prefill":{"description":"Settings the agent could fill in for a new connection, never a credential: the daemon keeps only the entry's own non-secret fields.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"changes":{"description":"For a change: each setting and the value it would take. Never a credential.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"reason":{"description":"The daemon's own sentence on why this is the ask, such as the refusal a connected credential keeps getting.","type":"string"},"reported":{"description":"The agent reported the credential refused while the connection still probes as working, so only a person's word that it is fixed meets it: the probe could not see the refusal in the first place.","type":"boolean"}},"required":["kind","entry","name","mode"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret"},"name":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,127}$","description":"The name it is stored under, and what `{{secret:NAME}}` will resolve."},"where":{"description":"How it will be used: the header, the command, the site it goes to.","type":"string","maxLength":200},"link":{"description":"Where a person gets one, shown as a link on the card.","type":"string","format":"uri"},"hint":{"description":"What a valid one looks like, so a wrong paste is caught by eye.","type":"string","maxLength":120},"replace":{"description":"One is stored under this name and is being refused: the ask is for a new value in its place.","type":"boolean"}},"required":["kind","name"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"grant"},"subject":{"type":"string","enum":["capability","folder","shelf","site"],"description":"A connected capability the persona leaves out, a folder outside the conversation's reach, a whole shelf of tools, or a site in the person's own browser, which only their browser extension can allow."},"what":{"type":"string","minLength":1,"description":"The capability's id, the folder, or the shelf."},"label":{"type":"string","description":"What it is, in the daemon's words: the account and its kind, the folder, the shelf's name."},"persona":{"description":"The persona that withholds it, when one does.","type":"string"},"scope":{"description":"How far the yes went, once there was one.","type":"string","enum":["conversation","persona"]}},"required":["kind","subject","what","label"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"release"},"subject":{"type":"string","minLength":1,"description":"The gated account or connector."},"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it. Anyone else's answer is refused and leaves it waiting."}},"required":["kind","subject","approvers"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"environment"},"tool":{"type":"string","minLength":1,"description":"What the steps install, the name the proposal is filed under."},"steps":{"type":"string","minLength":1,"description":"The Dockerfile steps proposed for the image's custom section: RUN and ENV lines only."},"approvedHash":{"description":"The overlay these steps were approved into; met once the running container was built from it.","type":"string"}},"required":["kind","tool","steps"],"additionalProperties":false}],"description":"What exactly is asked for."},"title":{"type":"string","description":"The one line it leads with, in the daemon's words."},"why":{"description":"The agent's case for it, and the only words on a need that are the agent's.","type":"string","maxLength":280},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."},"createdAt":{"type":"number","description":"When it was raised, in milliseconds."},"updatedAt":{"type":"number","description":"When it last moved, in milliseconds."},"answeredBy":{"description":"Who answered it, as the sandbox verified them.","type":"string"},"outcome":{"description":"How it ended, in the daemon's words, once it has.","type":"string"},"told":{"description":"How the agent heard the outcome, once it has.","type":"string","enum":["call","turn","queued"]},"unattended":{"description":"Raised by a turn nobody was watching, so the card waited for whoever came next.","type":"boolean"}},"required":["id","conversationId","subject","title","status","createdAt","updatedAt"],"additionalProperties":false}}}}}}},"/needs/{id}/secret":{"post":{"operationId":"needs.provideSecret","summary":"Give a secret a need asked for","description":"Stores the value under the name the need asked for and meets it. The value goes to the sandbox's secret store and nowhere else: not the answer, not the transcript, not a log.","tags":["Needs"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which need."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"value":{"type":"string","minLength":1,"maxLength":64000,"description":"The secret's value. Stored, never echoed, never written into a transcript."}},"required":["value"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The need's handle, the one `needs cancel` takes."},"conversationId":{"type":"string","description":"The conversation that asked, and the one its answer wakes."},"subject":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"capability"},"entry":{"type":"string","minLength":1,"description":"The catalog entry, as the catalog names it."},"name":{"type":"string","description":"What the catalog calls it, never the agent's spelling."},"mode":{"type":"string","enum":["connect","reconnect","change"],"description":"Connect something new, give a connected one a credential that works again, or change a setting on a connected one."},"instance":{"description":"The connection a reconnect or a change is about.","type":"string"},"target":{"description":"The site, host or address the connection is for, when the entry can hold several.","type":"string"},"prefill":{"description":"Settings the agent could fill in for a new connection, never a credential: the daemon keeps only the entry's own non-secret fields.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"changes":{"description":"For a change: each setting and the value it would take. Never a credential.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"reason":{"description":"The daemon's own sentence on why this is the ask, such as the refusal a connected credential keeps getting.","type":"string"},"reported":{"description":"The agent reported the credential refused while the connection still probes as working, so only a person's word that it is fixed meets it: the probe could not see the refusal in the first place.","type":"boolean"}},"required":["kind","entry","name","mode"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"secret"},"name":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]{0,127}$","description":"The name it is stored under, and what `{{secret:NAME}}` will resolve."},"where":{"description":"How it will be used: the header, the command, the site it goes to.","type":"string","maxLength":200},"link":{"description":"Where a person gets one, shown as a link on the card.","type":"string","format":"uri"},"hint":{"description":"What a valid one looks like, so a wrong paste is caught by eye.","type":"string","maxLength":120},"replace":{"description":"One is stored under this name and is being refused: the ask is for a new value in its place.","type":"boolean"}},"required":["kind","name"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"grant"},"subject":{"type":"string","enum":["capability","folder","shelf","site"],"description":"A connected capability the persona leaves out, a folder outside the conversation's reach, a whole shelf of tools, or a site in the person's own browser, which only their browser extension can allow."},"what":{"type":"string","minLength":1,"description":"The capability's id, the folder, or the shelf."},"label":{"type":"string","description":"What it is, in the daemon's words: the account and its kind, the folder, the shelf's name."},"persona":{"description":"The persona that withholds it, when one does.","type":"string"},"scope":{"description":"How far the yes went, once there was one.","type":"string","enum":["conversation","persona"]}},"required":["kind","subject","what","label"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"release"},"subject":{"type":"string","minLength":1,"description":"The gated account or connector."},"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it. Anyone else's answer is refused and leaves it waiting."}},"required":["kind","subject","approvers"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"environment"},"tool":{"type":"string","minLength":1,"description":"What the steps install, the name the proposal is filed under."},"steps":{"type":"string","minLength":1,"description":"The Dockerfile steps proposed for the image's custom section: RUN and ENV lines only."},"approvedHash":{"description":"The overlay these steps were approved into; met once the running container was built from it.","type":"string"}},"required":["kind","tool","steps"],"additionalProperties":false}],"description":"What exactly is asked for."},"title":{"type":"string","description":"The one line it leads with, in the daemon's words."},"why":{"description":"The agent's case for it, and the only words on a need that are the agent's.","type":"string","maxLength":280},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."},"createdAt":{"type":"number","description":"When it was raised, in milliseconds."},"updatedAt":{"type":"number","description":"When it last moved, in milliseconds."},"answeredBy":{"description":"Who answered it, as the sandbox verified them.","type":"string"},"outcome":{"description":"How it ended, in the daemon's words, once it has.","type":"string"},"told":{"description":"How the agent heard the outcome, once it has.","type":"string","enum":["call","turn","queued"]},"unattended":{"description":"Raised by a turn nobody was watching, so the card waited for whoever came next.","type":"boolean"}},"required":["id","conversationId","subject","title","status","createdAt","updatedAt"],"additionalProperties":false}}}}}}},"/needs/grants":{"get":{"operationId":"needs.grants","summary":"The yeses still standing","description":"What people allowed conversations beyond their persona or area, and the gated credentials released to them, by conversation. Names only, never a value.","tags":["Needs"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"conversations":{"type":"array","items":{"type":"object","properties":{"conversationId":{"type":"string"},"capabilities":{"type":"array","items":{"type":"string"},"description":"Connected capabilities allowed although its persona leaves them out."},"folders":{"type":"array","items":{"type":"string"},"description":"Workspace folders its file tools may touch beyond its fence."},"shelves":{"type":"array","items":{"type":"string","enum":["files","shell","code","web","browser","delegate","sandbox"]},"description":"Shelves of tools opened for it."},"installs":{"default":false,"description":"Whether its own dependency installs run without asking.","type":"boolean"},"secrets":{"default":[],"description":"Secrets it may send past their host guard without asking.","type":"array","items":{"type":"string"}},"everything":{"default":false,"description":"Whether every request an allow-once could settle is allowed without asking.","type":"boolean"},"by":{"description":"Who last allowed one of those.","type":"string"},"updatedAt":{"description":"When one of those last changed, in milliseconds.","type":"number"},"releases":{"type":"array","items":{"type":"object","properties":{"subject":{"type":"string"},"approvedBy":{"type":"string"},"at":{"type":"number"}},"required":["subject","approvedBy","at"],"additionalProperties":false},"description":"Gated credentials released to it, until somebody takes one back."}},"required":["conversationId","capabilities","folders","shelves","installs","secrets","everything","releases"],"additionalProperties":false}}},"required":["conversations"],"additionalProperties":false}}}}}}},"/needs/grants/revoke":{"post":{"operationId":"needs.revokeGrant","summary":"Take a yes back","description":"Takes back one grant or one release. The conversation's next turn runs without it; a turn already running keeps what it mounted.","tags":["Needs"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string"},"kind":{"type":"string","enum":["capability","folder","shelf","release","install","secret","everything"],"description":"Which kind of yes: a grant past the persona or area, a credential's release, letting its installs run unasked, a secret sent past its host guard, or allowing everything."},"what":{"type":"string","description":"The capability id, folder, shelf, released credential or secret it named; empty for installs and everything."}},"required":["conversationId","kind","what"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/secrets":{"post":{"operationId":"secrets.set","summary":"Store a secret","description":"Writes one name and value where the agent's references resolve it, without a restart: desired-state/.env once DevOps is active, the sandbox's own secret store before that.","tags":["Secrets"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","maxLength":128,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$","description":"The name to store it under, which is the name a process will find it by."},"value":{"type":"string","minLength":1,"description":"The value. It goes straight to your sandbox and never through the platform."}},"required":["key","value"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}},"get":{"operationId":"secrets.list","summary":"Names of the stored secrets","description":"Which secrets exist here. Names only, never values.","tags":["Secrets"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"keys":{"type":"array","items":{"type":"string"},"description":"The names that exist here. Only the names: the values never leave the sandbox."}},"required":["keys"],"additionalProperties":false}}}}}}},"/secrets/generate":{"post":{"operationId":"secrets.generate","summary":"Make and store a random secret","description":"Makes a random value and stores it under a new name, where `set` would have put it, for a secret nobody has to find or paste (a session key, a signing secret, a password the task sets up itself). Answers the name and its length, never the value. Refused for a name something here already holds.","tags":["Secrets"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","maxLength":128,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$","description":"The name to store it under: a name nothing here holds yet, since a new value would break whatever uses the old one."},"bytes":{"default":32,"description":"How much randomness, in bytes. 32 unless whatever reads it demands a particular length.","type":"integer","minimum":16,"maximum":128},"format":{"default":"hex","description":"How it is spelled: `hex` (0-9, a-f), `base64url` (letters, digits, - and _), or `alnum` (letters and digits only, for readers that refuse symbols).","type":"string","enum":["hex","base64url","alnum"]}},"required":["key"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","description":"The name it is stored under."},"length":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many characters it is, which a reader's validation may care about."},"stored":{"type":"string","enum":["env","sandbox"],"description":"Where it was kept: desired-state/.env once DevOps is active, the sandbox's own store before that."}},"required":["key","length","stored"],"additionalProperties":false}}}}}}},"/secrets/{key}":{"delete":{"operationId":"secrets.remove","summary":"Delete a secret","description":"Removes one by name.","tags":["Secrets"],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","maxLength":128,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$","description":"Which secret, by name."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/secrets/inventory":{"get":{"operationId":"secrets.inventory","summary":"Every secret this sandbox holds, from everywhere","description":"One view across all the places secrets live here: what exists, where it came from and whether it is working. Never any values. This one always answers, even before there is a store to write to.","tags":["Secrets"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string","description":"What identifies it. Unique across the whole inventory, so several accounts of one provider each get their own entry."},"kind":{"type":"string","enum":["env","generated","capability","provider"],"description":"Where it came from: you set it, the sandbox generated it, a connection needs it, or it is a model account's credential."},"label":{"description":"A friendlier name, for entries that have one.","type":"string"},"status":{"type":"string","enum":["missing","set","connected"],"description":"Whether it exists and, for a connection, whether it is working."},"requiredBy":{"type":"array","items":{"type":"object","properties":{"resourceId":{"type":"string","description":"Which resource."},"type":{"type":"string","description":"What kind of resource it is."}},"required":["resourceId","type"],"additionalProperties":false},"description":"What is waiting on it. Empty for a connection's or an account's own credential."},"storedAt":{"type":"string","description":"Where it actually lives, in words."},"revealable":{"type":"boolean","description":"Whether its value can be shown at all. Everything except a model account's credential can be."},"ci":{"description":"Whether a copy has been given to the build pipeline.","type":"object","properties":{"synced":{"type":"boolean","description":"Whether the pipeline has it."},"pushedAt":{"description":"When it was last sent there.","type":"string"}},"required":["synced"],"additionalProperties":false},"lastUse":{"description":"The last time an agent actually spent this secret. Absent while it never has been, which most never are.","type":"object","properties":{"at":{"type":"number","description":"When, in milliseconds."},"lane":{"type":"string","enum":["shell","code","browser"],"description":"How it was used: a command, a script, or typed into a page."},"detail":{"description":"Where it went: the start of the command or script, or the site. Names and destinations only, never values.","type":"string"},"approvedBy":{"description":"Who released it for that use, when it is gated. Absent when nothing had to be approved.","type":"string"}},"required":["at","lane"],"additionalProperties":false},"gate":{"description":"Who has to release this before the agent can use it, and for how long one release lasts. Absent when it is not gated.","type":"object","properties":{"approvers":{"type":"array","items":{"type":"string"},"description":"Who may release it, by email. Nobody else can, whatever their role."},"scope":{"type":"string","enum":["use","conversation"],"description":"How far one release goes: `use` asks again every single time (one click releases exactly one use), `conversation` covers the rest of this conversation and is forgotten when the daemon restarts."}},"required":["approvers","scope"],"additionalProperties":false},"hosts":{"description":"Its host guard. Absent when none was ever set and no connector sets one: the guard is off.","type":"object","properties":{"guard":{"type":"boolean","description":"Whether a use off the list, or whose destination cannot be read, asks a person first."},"list":{"type":"array","items":{"type":"string"},"description":"The hosts it goes to unasked while the guard is on, each exact or `*.domain`."},"source":{"type":"string","enum":["owner","connector"],"description":"Who set it: the owner, or the connector the credential belongs to, whose guard is on with its own service's hosts until the owner changes it."}},"required":["guard","list","source"],"additionalProperties":false}},"required":["key","kind","status","requiredBy","storedAt","revealable"],"additionalProperties":false},"description":"One entry per secret this sandbox knows about, from every place they live. No values, ever."}},"required":["entries"],"additionalProperties":false}}}}}}},"/secrets/reveal":{"post":{"operationId":"secrets.reveal","summary":"Show one secret's value","description":"The only call that hands a value back, and it is for the owner alone. Sent as a body rather than in the address, so the name never ends up in a log or a browser's history.","tags":["Secrets"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string","maxLength":128,"pattern":"^[A-Za-z_][A-Za-z0-9_]*$","description":"Which secret, by name."}},"required":["key"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"value":{"type":"string","description":"The value itself. The only place in this API one is ever returned."}},"required":["value"],"additionalProperties":false}}}}}}},"/secrets/gates":{"get":{"operationId":"secrets.gates","summary":"Which credentials need somebody's approval","description":"What is gated and who may release it. Names and addresses only, never values, and the agent may read it too: knowing a credential needs Bob is what stops it concluding the account is simply not connected.","tags":["Secrets"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"gates":{"type":"array","items":{"type":"object","properties":{"subject":{"type":"string","minLength":1,"description":"What is gated: a secret's name, or a connected capability's id."},"kind":{"type":"string","enum":["secret","capability"],"description":"Whether this gate covers one stored secret, by the name a reference carries, or one whole connected capability, by its id."},"approvers":{"minItems":1,"type":"array","items":{"type":"string","minLength":3},"description":"Exactly who may release it, by email, from the people on the Access roster. Not a seniority floor: nobody outside this list can release it, the owner included, unless the owner is on it."},"scope":{"type":"string","enum":["use","conversation"],"description":"How far one release goes: `use` asks again every single time (one click releases exactly one use), `conversation` covers the rest of this conversation and is forgotten when the daemon restarts."}},"required":["subject","kind","approvers","scope"],"additionalProperties":false},"description":"Every gate in force. Names, subjects and approver addresses only: this answer never carries a credential."}},"required":["gates"],"additionalProperties":false}}}}}}},"/secrets/gates/{subject}":{"put":{"operationId":"secrets.setGate","summary":"Put a credential behind named approvers","description":"Names exactly who may release one secret or one connected account, and how far a single release goes. The owner's call alone. A signed-in browser or a mounted server cannot be released for one use, so those are always for the rest of the conversation.","tags":["Secrets"],"parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"What is gated: a secret's name, or a connected capability's id."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["secret","capability"],"description":"Whether this gate covers one stored secret, by the name a reference carries, or one whole connected capability, by its id."},"approvers":{"minItems":1,"type":"array","items":{"type":"string","minLength":3},"description":"Exactly who may release it, by email, from the people on the Access roster. Not a seniority floor: nobody outside this list can release it, the owner included, unless the owner is on it."},"scope":{"type":"string","enum":["use","conversation"],"description":"How far one release goes: `use` asks again every single time (one click releases exactly one use), `conversation` covers the rest of this conversation and is forgotten when the daemon restarts."}},"required":["kind","approvers","scope"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}},"delete":{"operationId":"secrets.removeGate","summary":"Stop requiring approval for a credential","description":"Removes one gate, so the agent can use that credential the way it uses any other. The owner's call alone.","tags":["Secrets"],"parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which gate, by the secret name or capability id it covers."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/secrets/hosts":{"get":{"operationId":"secrets.hosts","summary":"Which secrets are host-guarded, and where they may go","description":"Every secret and connected account whose host guard is set, on or off, and its hosts. With the guard on, a use aimed off the list, or anywhere a command's text does not show, asks a person first, whatever the safety judge says. Names and hosts only, never values.","tags":["Secrets"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"guards":{"type":"array","items":{"type":"object","properties":{"subject":{"type":"string","minLength":1,"description":"Which secret, by the name its reference carries, or which connected capability, by its id."},"kind":{"type":"string","enum":["secret","capability"],"description":"Whether this gate covers one stored secret, by the name a reference carries, or one whole connected capability, by its id."},"guard":{"type":"boolean","description":"On: a use off the list, or whose destination cannot be read, asks a person first. Off: it never asks."},"hosts":{"maxItems":64,"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:\\*\\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+|[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*)$","description":"One host, `api.github.com`, or every host under a domain, `*.github.com` (which does not include github.com itself). Lowercase, no scheme or port."},"description":"Where it goes without asking while the guard is on. Empty with the guard on: every use asks. Kept while it is off, for turning it back on."},"source":{"type":"string","enum":["owner","connector"],"description":"Who set it: the owner, or the connector the credential belongs to, whose guard is on with its own service's hosts until the owner changes it."}},"required":["subject","kind","guard","hosts","source"],"additionalProperties":false},"description":"Every secret whose host guard has been set, or that a connector guards by default. One not listed has its guard off. Names and hosts only, never a value."}},"required":["guards"],"additionalProperties":false}}}}}}},"/secrets/hosts/{subject}":{"put":{"operationId":"secrets.setHosts","summary":"Turn a secret's host guard on or off, and set its hosts","description":"Replaces one secret's host guard. Anybody who may use secrets can turn it on or take hosts away; turning it off or adding a host is the owner's: from the agent it raises a card for the owner in the live conversation and waits for their answer.","tags":["Secrets"],"parameters":[{"name":"subject","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which secret, by name, or which connected capability, by id."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"description":"Whether the subject is a secret or a capability. Worked out from the name when absent.","type":"string","enum":["secret","capability"]},"guard":{"type":"boolean","description":"Whether a use off the list, or whose destination cannot be read, must ask a person first."},"hosts":{"maxItems":64,"type":"array","items":{"type":"string","maxLength":253,"pattern":"^(?:\\*\\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+|[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*)$","description":"One host, `api.github.com`, or every host under a domain, `*.github.com` (which does not include github.com itself). Lowercase, no scheme or port."},"description":"The whole new list. Turning the guard on or taking hosts away is open to anybody who may use secrets; turning it off or adding a host is the owner's to approve."},"conversationId":{"description":"Which conversation to ask the owner in, when the change needs them. The CLI fills this from the running turn.","type":"string"}},"required":["guard","hosts"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"guard":{"type":"boolean","description":"Whether the guard is on now."},"hosts":{"type":"array","items":{"type":"string"},"description":"Where it goes without asking while the guard is on."},"approvedBy":{"description":"Who approved the change, when it needed the owner's click. Absent when nobody had to.","type":"string"}},"required":["guard","hosts"],"additionalProperties":false}}}}}}},"/secrets/request":{"post":{"operationId":"secrets.request","summary":"Ask a named person to release a credential","description":"Raises the release card in the live conversation and waits for one of the people named on it. Refused, rather than held, when there is nobody to ask: an unattended turn, no live conversation, or a click with no verified identity behind it.","tags":["Secrets"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string","minLength":1,"description":"What to ask for: the secret's name, or the connected capability's id."},"why":{"description":"One line on what it is for. The only words on the card that are the agent's.","type":"string","maxLength":280},"conversationId":{"description":"Which conversation to raise the card in. The CLI fills this from the running turn.","type":"string"}},"required":["subject"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"granted":{"type":"boolean","const":true,"description":"Always true: a refusal is an error with a sentence, never a `false` here."},"approvedBy":{"type":"string","description":"Who released it."},"message":{"type":"string","description":"What the grant means in practice, and what to do next."}},"required":["granted","approvedBy","message"],"additionalProperties":false}}}}}}},"/vpn":{"get":{"operationId":"vpn.list","summary":"Configured tunnels and which are up","description":"Every stored VPN with its live link state, read back from the operating system rather than from memory, so a tunnel dropped from a shell and one dropped from a screen look the same here.","tags":["VPN"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"links":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Which tunnel."},"provider":{"type":"string","enum":["wireguard","fortinet","ipsec"],"description":"What kind of tunnel it is."},"state":{"type":"string","enum":["connected","connecting","disconnected","unavailable","failed"],"description":"Whether it is up, dialling, resting, failed, or not installable yet because its client needs a rebuild to arrive."},"gateway":{"description":"What it dials. For display only, and never a credential.","type":"string"},"interface":{"description":"The network interface carrying it, once one exists.","type":"string"},"address":{"description":"The address the far end gave this sandbox, which is the single most useful answer to whether you are on the VPN.","type":"string"},"routes":{"default":[],"description":"What goes through it. Everything, when the range covers the whole internet. Empty until it is up.","type":"array","items":{"type":"string"}},"dns":{"default":[],"description":"Name servers it pushed, when it pushed any.","type":"array","items":{"type":"string"}},"since":{"description":"When it came up, in milliseconds. Absent unless it is.","type":"number"},"autoConnect":{"type":"boolean","description":"Whether it dials itself when the sandbox starts."},"detail":{"description":"Why it failed, or a note about a healthy one. Never a credential.","type":"string"}},"required":["id","provider","state","routes","dns","autoConnect"],"additionalProperties":false},"description":"Every configured tunnel with its live state, read back from the operating system each time rather than remembered."}},"required":["links"],"additionalProperties":false}}}}}}},"/vpn/{id}/connect":{"post":{"operationId":"vpn.connect","summary":"Dial a VPN","description":"Brings a stored tunnel up, streaming the client's progress as it authenticates and then sets up routing. Streamed because a dial takes seconds and can fail with something you have to read: a wrong password, a gateway certificate nobody trusts, a code it wants. Connecting one that is already up simply says so.","tags":["VPN"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which tunnel to dial."}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"otp":{"description":"A one-time code, where the gateway wants one. Supplied per dial and never stored; without it such a gateway refuses and says so.","type":"string","minLength":1}}}}}},"responses":{"200":{"description":"OK","content":{"text/event-stream":{"schema":{"oneOf":[{"type":"object","properties":{"event":{"const":"message"},"data":{"type":"object","properties":{"kind":{"type":"string"}},"required":["kind"],"additionalProperties":{}},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event","data"]},{"type":"object","properties":{"event":{"const":"done"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"error"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]}]}}}}}}},"/vpn/{id}/disconnect":{"post":{"operationId":"vpn.disconnect","summary":"Drop a tunnel","description":"Takes the tunnel down. One that was already down is fine: the promise is that it is not up afterwards.","tags":["VPN"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which tunnel."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/vpn/import-forticlient":{"post":{"operationId":"vpn.importForticlient","summary":"Read connections out of an exported config","description":"Turns an exported FortiClient configuration into a list of connections you can add, so somebody holding that file picks from a list instead of retyping a host and port for every tunnel.","tags":["VPN"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"xml":{"type":"string","minLength":1,"description":"The exported configuration file, whole. Nothing is stored: it is read and thrown away."}},"required":["xml"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"connections":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The id it would be added under."},"label":{"type":"string","description":"Its name as the file has it, so somebody recognises the connection they are picking."},"provider":{"type":"string","enum":["wireguard","fortinet","ipsec"],"description":"What kind of tunnel it is."},"server":{"type":"string","description":"Where it dials."},"port":{"type":"number","description":"On which port."},"username":{"description":"The username, but only when the file stored it in the clear. An encrypted one is dropped rather than guessed at.","type":"string"},"description":{"description":"Whatever the file said about it.","type":"string"},"localId":{"description":"An identity some tunnel types need, when the file stored it readably.","type":"string"},"aggressive":{"description":"Which negotiation mode it used.","type":"boolean"},"pfs":{"description":"Whether it asked for forward secrecy.","type":"boolean"},"dhGroup":{"description":"Which key-exchange group it used. Together with the setting above, this is what decides whether the connection can complete at all.","type":"string"},"needs":{"type":"array","items":{"type":"string"},"description":"What you still have to type in before it can dial. Always at least the password, because the export wraps credentials in encryption that cannot be undone here."}},"required":["id","label","provider","server","port","needs"],"additionalProperties":false},"description":"The connections found in the file, ready to be added one at a time."}},"required":["connections"],"additionalProperties":false}}}}}}},"/netdisk":{"get":{"operationId":"netdisk.list","summary":"Configured disks and which are mounted","description":"Every stored network disk with its live mount state, read back from the kernel's mount table rather than from memory, so a disk unmounted from a shell and one unmounted from a screen look the same here.","tags":["Network disks"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"links":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Which disk."},"provider":{"type":"string","enum":["smb"],"description":"What protocol it speaks."},"state":{"type":"string","enum":["mounted","unmounted","unavailable"],"description":"Whether it is mounted, resting, or not mountable yet because its client needs a rebuild to arrive."},"target":{"type":"string","description":"What it mounts, as //server/share. For display only, and never a credential."},"mountPoint":{"type":"string","description":"Where its files appear inside the sandbox."},"access":{"type":"string","enum":["read","readwrite"],"description":"What the card asked for: read, or read and write."},"writable":{"description":"Whether the live mount accepts writes, as the kernel has it. Absent unless mounted.","type":"boolean"},"since":{"description":"When it was mounted, in milliseconds. Absent unless it is.","type":"number"},"autoMount":{"type":"boolean","description":"Whether it mounts itself when the sandbox starts."},"detail":{"description":"Why it is unavailable, or a note about a healthy one. Never a credential.","type":"string"}},"required":["id","provider","state","target","mountPoint","access","autoMount"],"additionalProperties":false},"description":"Every configured disk with its live mount state, read back from the kernel each time rather than remembered."}},"required":["links"],"additionalProperties":false}}}}}}},"/netdisk/{id}/mount":{"post":{"operationId":"netdisk.mount","summary":"Mount a disk","description":"Mounts a stored disk at its place under /mnt/netdisk, streaming progress. Mounting one that is already mounted simply says so. A read-only disk is mounted read-only; the kernel refuses writes to it.","tags":["Network disks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which disk."}}],"responses":{"200":{"description":"OK","content":{"text/event-stream":{"schema":{"oneOf":[{"type":"object","properties":{"event":{"const":"message"},"data":{"type":"object","properties":{"kind":{"type":"string"}},"required":["kind"],"additionalProperties":{}},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event","data"]},{"type":"object","properties":{"event":{"const":"done"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"error"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]}]}}}}}}},"/netdisk/{id}/unmount":{"post":{"operationId":"netdisk.unmount","summary":"Unmount a disk","description":"Takes the disk down. One that was already unmounted is fine: the promise is that it is not mounted afterwards.","tags":["Network disks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which disk."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/exit":{"get":{"operationId":"exit.list","summary":"Ways to come out somewhere else","description":"Every configured exit with its live state, the country it was asked to appear in, and the country it actually appears in. Those last two disagreeing is the whole reason this reports both.","tags":["Exit locations"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"links":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Which exit."},"provider":{"type":"string","enum":["tor","vpngate","wireguard"],"description":"What it runs on."},"state":{"type":"string","enum":["up","starting","down","unavailable","failed"],"description":"Whether it is carrying traffic, coming up, resting, failed, or not installable yet because its client needs a rebuild to arrive."},"proxy":{"type":"string","description":"Where to point traffic that should go through it. Fixed per exit and unchanged by a country switch, which is what lets a long job move country halfway through without reconfiguring anything."},"country":{"description":"Where it was asked to come out. Absent means the provider chose.","type":"string"},"observedCountry":{"description":"Where it actually comes out, as last checked. Kept separate from what was asked for, because those two disagreeing is the most useful fault signal this whole feature has.","type":"string"},"ip":{"description":"The address behind that observation.","type":"string"},"checkedAt":{"description":"When that was checked, in milliseconds, so an old reading can be shown as old.","type":"number"},"interface":{"description":"The network interface, for the kinds that have one.","type":"string"},"since":{"description":"When it came up, in milliseconds.","type":"number"},"autoStart":{"type":"boolean","description":"Whether it starts itself when the sandbox does."},"detail":{"description":"Why it failed, or a note about a healthy one.","type":"string"}},"required":["id","provider","state","proxy","autoStart"],"additionalProperties":false},"description":"Every configured exit, with where it was asked to come out and where it actually does."}},"required":["links"],"additionalProperties":false}}}}}}},"/exit/{id}/countries":{"get":{"operationId":"exit.countries","summary":"Countries one exit can reach","description":"Where this exit can put you, ranked by how much capacity is really there. Asked of the provider when it answers and taken from a built-in list when it does not, and the answer says which of those you got.","tags":["Exit locations"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which exit."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"countries":{"type":"array","items":{"type":"object","properties":{"country":{"type":"string","description":"The country's code."},"countryName":{"type":"string","description":"Its name, spelled out."},"servers":{"type":"number","description":"How many servers this provider has there."},"share":{"description":"How much of the provider's actual capacity is there, from zero to one. This is what a list should be sorted by: a third of the countries on offer are one overloaded machine behind a flag, and a count of servers would rank them first.","type":"number"}},"required":["country","countryName","servers"],"additionalProperties":false},"description":"Where this exit can put you, best-supplied first."},"live":{"type":"boolean","description":"Whether the provider answered, or this came from a built-in list. Said out loud rather than presenting an old list as current."}},"required":["countries","live"],"additionalProperties":false}}}}}}},"/exit/{id}/start":{"post":{"operationId":"exit.start","summary":"Bring an exit up","description":"Starts the exit in the country it was configured for. Streamed, because a first start fetches a catalogue, raises a tunnel and then checks the address, which takes tens of seconds on the free providers and can fail at each step with something worth reading. Starting one that is already up simply says so.","tags":["Exit locations"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which exit."}}],"responses":{"200":{"description":"OK","content":{"text/event-stream":{"schema":{"oneOf":[{"type":"object","properties":{"event":{"const":"message"},"data":{"type":"object","properties":{"kind":{"type":"string"}},"required":["kind"],"additionalProperties":{}},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event","data"]},{"type":"object","properties":{"event":{"const":"done"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"error"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]}]}}}}}}},"/exit/{id}/use":{"post":{"operationId":"exit.use","summary":"Move to another country","description":"Switches the exit's country, starting it first if it was down. It ends by checking where the world actually sees you and fails if that does not match what you asked for. A switch that quietly left your traffic where it was is the exact failure this whole feature exists to rule out.","tags":["Exit locations"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which exit."}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"country":{"description":"Where to come out. Leaving it out means letting the provider choose, so clearing a country is something you can actually say rather than only setting one.","type":"string","pattern":"^[A-Za-z]{2}$"}}}}}},"responses":{"200":{"description":"OK","content":{"text/event-stream":{"schema":{"oneOf":[{"type":"object","properties":{"event":{"const":"message"},"data":{"type":"object","properties":{"kind":{"type":"string"}},"required":["kind"],"additionalProperties":{}},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event","data"]},{"type":"object","properties":{"event":{"const":"done"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"error"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]}]}}}}}}},"/exit/{id}/rotate":{"post":{"operationId":"exit.rotate","summary":"Take a different address, same country","description":"Swaps to another address in the country you are already in. Fails if the address does not actually change, which on a small pool it sometimes cannot.","tags":["Exit locations"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which exit."}}],"responses":{"200":{"description":"OK","content":{"text/event-stream":{"schema":{"oneOf":[{"type":"object","properties":{"event":{"const":"message"},"data":{"type":"object","properties":{"kind":{"type":"string"}},"required":["kind"],"additionalProperties":{}},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event","data"]},{"type":"object","properties":{"event":{"const":"done"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"error"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]}]}}}}}}},"/exit/{id}/check":{"post":{"operationId":"exit.check","summary":"Where the world sees you right now","description":"Looks up the address and country as seen through this exit. Cheap, and the honest answer to whether you are really where you meant to be, which is what every other call here is judged against.","tags":["Exit locations"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which exit."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ip":{"type":"string","description":"The address the world sees, looked up through the exit's own proxy rather than assumed."},"country":{"description":"Which country that address is in. Absent when the lookup gave an address and no country, in which case a switch is judged on the address having changed instead.","type":"string"},"countryName":{"description":"That country's name, spelled out.","type":"string"}},"required":["ip"],"additionalProperties":false}}}}}}},"/exit/{id}/stop":{"post":{"operationId":"exit.stop","summary":"Take an exit down","description":"Shuts the exit off. One that was already down is fine: the promise is that it is not up afterwards, not that it was up before.","tags":["Exit locations"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which exit."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/inventory":{"get":{"operationId":"inventory.list","summary":"Machines and services you have declared","description":"What the deployment configuration says this setup owns and what it wants provisioned.","tags":["Inventory"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"backend","description":"Something you already have: a machine, an account with a hosting provider."},"provider":{"type":"string","enum":["host","cloudflare","github","gitlab","stripe"],"description":"Which provider it is with."},"name":{"type":"string","description":"What to call it, which is also how everything else refers to it."},"values":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number"]},"description":"Its settings. Anything secret is stored separately and referred to here, never written in."}},"required":["kind","provider","name","values"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"service","description":"Something you want provisioned."},"service":{"type":"string","enum":["signoz","outline","paperless","openproject","invoiceninja","infisical"],"description":"Which service."},"name":{"type":"string","description":"What to call it."},"values":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number"]},"description":"Its settings."},"on":{"type":"string","description":"Which of your machines to put it on."},"expose":{"type":"string","description":"How it should be reachable."}},"required":["kind","service","name","values","on","expose"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"app","description":"An app of your own, built from source and deployed."},"name":{"type":"string","description":"What to call it."},"values":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number"]},"description":"Its settings, including the address it should answer on."},"on":{"type":"string","description":"Which of your machines to put it on."},"expose":{"type":"string","description":"How it should be reachable."}},"required":["kind","name","values","on","expose"],"additionalProperties":false}]},"description":"Everything declared: what you have, and what you want provisioned."}},"required":["entries"],"additionalProperties":false}}}}}},"post":{"operationId":"inventory.add","summary":"Declare a machine or service","description":"Writes the entry into the configuration file and commits it, exactly as an agent editing that file by hand would. Answers with the whole updated list, so a screen redraws from one response.","tags":["Inventory"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"backend","description":"Something you already have: a machine, an account with a hosting provider."},"provider":{"type":"string","enum":["host","cloudflare","github","gitlab","stripe"],"description":"Which provider it is with."},"name":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z_][a-zA-Z0-9_]*$"},"values":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number"]},"description":"Its settings. Anything secret is stored separately and referred to here, never written in."}},"required":["kind","provider","name","values"]},{"type":"object","properties":{"kind":{"type":"string","const":"service","description":"Something you want provisioned."},"service":{"type":"string","enum":["signoz","outline","paperless","openproject","invoiceninja","infisical"],"description":"Which service."},"name":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z_][a-zA-Z0-9_]*$"},"values":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number"]},"description":"Its settings."},"on":{"type":"string","description":"Which of your machines to put it on."},"expose":{"type":"string","description":"How it should be reachable."}},"required":["kind","service","name","values","on","expose"]},{"type":"object","properties":{"kind":{"type":"string","const":"app","description":"An app of your own, built from source and deployed."},"name":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z_][a-zA-Z0-9_]*$"},"values":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number"]},"description":"Its settings, including the address it should answer on."},"on":{"type":"string","description":"Which of your machines to put it on."},"expose":{"type":"string","description":"How it should be reachable."}},"required":["kind","name","values","on","expose"]}]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"backend","description":"Something you already have: a machine, an account with a hosting provider."},"provider":{"type":"string","enum":["host","cloudflare","github","gitlab","stripe"],"description":"Which provider it is with."},"name":{"type":"string","description":"What to call it, which is also how everything else refers to it."},"values":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number"]},"description":"Its settings. Anything secret is stored separately and referred to here, never written in."}},"required":["kind","provider","name","values"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"service","description":"Something you want provisioned."},"service":{"type":"string","enum":["signoz","outline","paperless","openproject","invoiceninja","infisical"],"description":"Which service."},"name":{"type":"string","description":"What to call it."},"values":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number"]},"description":"Its settings."},"on":{"type":"string","description":"Which of your machines to put it on."},"expose":{"type":"string","description":"How it should be reachable."}},"required":["kind","service","name","values","on","expose"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"app","description":"An app of your own, built from source and deployed."},"name":{"type":"string","description":"What to call it."},"values":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number"]},"description":"Its settings, including the address it should answer on."},"on":{"type":"string","description":"Which of your machines to put it on."},"expose":{"type":"string","description":"How it should be reachable."}},"required":["kind","name","values","on","expose"],"additionalProperties":false}]},"description":"Everything declared: what you have, and what you want provisioned."}},"required":["entries"],"additionalProperties":false}}}}}}},"/inventory/{name}":{"delete":{"operationId":"inventory.remove","summary":"Undeclare a machine or service","description":"Takes the entry back out of the configuration and commits that too. Answers with the whole updated list.","tags":["Inventory"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","description":"Which entry, by name."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"backend","description":"Something you already have: a machine, an account with a hosting provider."},"provider":{"type":"string","enum":["host","cloudflare","github","gitlab","stripe"],"description":"Which provider it is with."},"name":{"type":"string","description":"What to call it, which is also how everything else refers to it."},"values":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number"]},"description":"Its settings. Anything secret is stored separately and referred to here, never written in."}},"required":["kind","provider","name","values"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"service","description":"Something you want provisioned."},"service":{"type":"string","enum":["signoz","outline","paperless","openproject","invoiceninja","infisical"],"description":"Which service."},"name":{"type":"string","description":"What to call it."},"values":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number"]},"description":"Its settings."},"on":{"type":"string","description":"Which of your machines to put it on."},"expose":{"type":"string","description":"How it should be reachable."}},"required":["kind","service","name","values","on","expose"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"app","description":"An app of your own, built from source and deployed."},"name":{"type":"string","description":"What to call it."},"values":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number"]},"description":"Its settings, including the address it should answer on."},"on":{"type":"string","description":"Which of your machines to put it on."},"expose":{"type":"string","description":"How it should be reachable."}},"required":["kind","name","values","on","expose"],"additionalProperties":false}]},"description":"Everything declared: what you have, and what you want provisioned."}},"required":["entries"],"additionalProperties":false}}}}}}},"/intentic":{"post":{"operationId":"intentic.run","summary":"Run an infrastructure command","description":"Runs the sandbox's own command-line tool and streams its output as it arrives, so progress is visible rather than arriving all at once at the end. A failure surfaces once the stream closes.","tags":["Platform CLI"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"args":{"type":"array","items":{"type":"string"}}},"required":["args"]}}}},"responses":{"200":{"description":"OK","content":{"text/event-stream":{"schema":{"oneOf":[{"type":"object","properties":{"event":{"const":"message"},"data":{"type":"object","properties":{"kind":{"type":"string"}},"required":["kind"],"additionalProperties":{}},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event","data"]},{"type":"object","properties":{"event":{"const":"done"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"error"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]}]}}}}}}},"/intentic/apply":{"post":{"operationId":"intentic.apply","summary":"Bring the infrastructure into line","description":"Starts the long reconcile that makes the running world match what was declared, and answers immediately. It takes minutes, so it runs in a terminal you attach to rather than on a held-open request.","tags":["Platform CLI"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/intentic/apply/events":{"get":{"operationId":"intentic.applyEvents","summary":"Follow the reconcile","description":"The same progress the terminal shows, as structured events, kept on disk so a page refresh does not lose it. It replays from the start of the run and then follows live, closing when the run ends.","tags":["Platform CLI"],"responses":{"200":{"description":"OK","content":{"text/event-stream":{"schema":{"oneOf":[{"type":"object","properties":{"event":{"const":"message"},"data":{"type":"object","properties":{"kind":{"type":"string"}},"required":["kind"],"additionalProperties":{}},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event","data"]},{"type":"object","properties":{"event":{"const":"done"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"error"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]}]}}}}}}},"/accounts/{provider}/login/start":{"post":{"operationId":"accounts.start","summary":"Begin connecting an account","description":"Hands back the page to sign in on, and the code it will ask for where there is one. The sandbox holds the proof and finishes what it can itself: a device sign-in lands in the account list on its own, a paste or a redirect needs one thing brought back to the finishing call.","tags":["Accounts"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","enum":["claude","codex","grok","kimi","gemini","cursor","meta","zai"]}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"variant":{"description":"Which estate to sign in to. Absent takes the provider's default.","type":"string","minLength":1}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"The page to open and sign in on."},"code":{"type":"string","description":"The one-time code the page will ask for, where the vendor issues one. Blank when the page is already addressed to this attempt."},"state":{"type":"string","description":"For a redirect sign-in, the marker in the address the browser lands on, so a pasted URL can be recognised as this attempt's. Blank otherwise."},"flow":{"type":"string","enum":["device","redirect","paste"],"description":"How this attempt ends. A device sign-in finishes by itself and you watch the account list; a redirect needs the address it landed on handed back; a paste needs the code the page showed."},"variant":{"type":"string","description":"Which of the provider's estates this attempt signs in to. Blank for a provider with one."},"handshake":{"type":"string","description":"This attempt's id, for finishing or abandoning it. Not a credential and not redeemable: the proof that completes the sign-in never leaves the sandbox."},"expiresAt":{"type":"number","description":"When this attempt stops being answerable, in milliseconds, so a card can stop waiting instead of spinning."},"catchers":{"description":"Who is watching for where the browser lands, on the machine it is on: a device or a browser of yours. Listed means the sign-in finishes by itself once the page is approved there; the paste stays open for a browser anywhere else. Empty or absent means nothing is watching.","type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["device","browser"]},"label":{"type":"string","minLength":1}},"required":["kind","label"],"additionalProperties":false}}},"required":["url","code","state","flow","variant","handshake","expiresAt"],"additionalProperties":false}}}}}}},"/accounts/{provider}/login/complete":{"post":{"operationId":"accounts.complete","summary":"Finish a sign-in with what the page handed back","description":"Takes the code the page showed, or the address a redirect landed on, and finishes the attempt. Answers with the account where the exchange ends here; otherwise the sandbox still has a mint to do and the row appears in the account list.","tags":["Accounts"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","enum":["claude","codex","grok","kimi","gemini","cursor","meta","zai"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"handshake":{"type":"string","minLength":1,"description":"Which attempt this belongs to."},"code":{"description":"The code the sign-in page showed, for a paste sign-in.","type":"string"},"redirectUrl":{"description":"The address the browser was sent to, whole, for a redirect sign-in. The grant is inside it.","type":"string"},"label":{"description":"What to call the account. Blank derives one from the sign-in.","type":"string"}},"required":["handshake"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"account":{"description":"The account it connected, where the sign-in ends here. Absent means keep watching the account list.","type":"object","properties":{"id":{"type":"string","description":"The account's id, which is what a turn names to spend on it and what disconnecting takes."},"label":{"type":"string","description":"What it is called here, which somebody can change."},"email":{"description":"Who it signs in as, in the provider's own words. Kept beside the label rather than folded into it, so a renamed account can still say whose it is. Absent when the provider says nothing, which is exactly when renaming is the only answer.","type":"string"},"organization":{"description":"Which organisation it belongs to, where the provider says.","type":"string"},"variant":{"description":"Which of the provider's estates it signs in to, for a provider selling more than one (Z.ai's international or mainland plan). With the email and organisation, it is who the account is: a sign-in matching all three lands on this account again, same id.","type":"string"},"scope":{"description":"What the credential is permitted to do, in the provider's terms.","type":"string"},"connectedAt":{"type":"number","description":"When it was connected, in milliseconds."},"needsReauth":{"description":"Its stored credential can no longer be renewed and somebody has to sign in again. Absent means healthy, or not checked yet.","type":"boolean"},"detail":{"description":"Why, in words a person can act on.","type":"string"},"seatRefusal":{"description":"Its organisation has switched it off for this harness (no seat): it still signs in and its plan limits may still read, but no turn can run on it until an admin gives access back. The provider's own sentence; cleared by the next turn that runs on it. Absent means no refusal is on file.","type":"string"},"usage":{"description":"How full its plan limits were when last measured, so a picker can show what is left before committing work to it. Absent until a reading exists, which reads as unknown rather than as nothing left.","type":"object","properties":{"windows":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"label":{"type":"string"},"utilization":{"type":"number"},"resetsAt":{"type":"number"},"gates":{"anyOf":[{"type":"string","const":"all"},{"type":"string","const":"none"},{"type":"object","properties":{"models":{"minItems":1,"type":"array","items":{"type":"string","minLength":1}}},"required":["models"],"additionalProperties":false}]}},"required":["kind","utilization","gates"],"additionalProperties":false}},"measuredAt":{"type":"number"},"unread":{"description":"Present while re-reading this account keeps failing: these windows are the last reading that succeeded, and `measuredAt` will not move until a read succeeds again.","type":"object","properties":{"since":{"type":"number","description":"When re-reading this account first failed, in milliseconds. It has failed on every attempt since."},"reason":{"type":"string","description":"Why, in the provider's own words where it gave some (\"Verify your account to continue.\"). Short enough to print; never a pasted response body."}},"required":["since","reason"],"additionalProperties":false}},"required":["windows","measuredAt"],"additionalProperties":false},"state":{"description":"Whether it can serve a turn now, judged once from the sign-in, the seat, the provider's last refusal and the plan limits: the verdict every picker in the sandbox uses. Read this rather than the fields it was judged from. Absent from a daemon older than this field.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"ready"},"room":{"type":"number","description":"How much of the fullest pool that gates the turn is left, in percent (above 0, up to 100). Pickers take the most room."}},"required":["kind","room"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"spent"},"reopensAt":{"description":"When every full pool has reopened, in epoch seconds, where the plan publishes it. Absent means unknown, never now.","type":"number"}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"blocked"},"fix":{"type":"string","enum":["reconnect","admin","verify","wait"],"description":"Who can make it serve again: `reconnect` (sign in again on this sandbox), `admin` (an organisation admin hands the seat back), `verify` (the account's owner confirms it on the provider's page, at `url`), or `wait` (it lifts by itself, at `until` where known)."},"reason":{"type":"string","description":"Why, in words a person can act on: the provider's own sentence where it gave one."},"until":{"description":"When waiting lifts it, in epoch seconds, for `wait` only.","type":"number"},"url":{"description":"The provider's page where the account's owner lifts it, for `verify` only.","type":"string"}},"required":["kind","fix","reason"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"unknown","description":"Nothing blocks it and nothing has been measured: usable, never read as room."}},"required":["kind"],"additionalProperties":false}]}},"required":["id","label","connectedAt"],"additionalProperties":false}},"additionalProperties":false}}}}}}},"/accounts/{provider}/login/status":{"get":{"operationId":"accounts.status","summary":"Read a sign-in attempt","description":"Whether this exact attempt is still waiting, has connected an account, or failed. Tied to the attempt, not to the account list, so adding a second account is told apart from the first already being there. An attempt that finishes by itself on a device or browser of yours ends here.","tags":["Accounts"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","enum":["claude","codex","grok","kimi","gemini","cursor","meta","zai"]}},{"name":"handshake","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"Which attempt."},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"status":{"type":"string","const":"wait"}},"required":["status"],"additionalProperties":false},{"type":"object","properties":{"status":{"type":"string","const":"ok"},"account":{"description":"The account it connected. Absent where the row lands in the account list a little later.","type":"object","properties":{"id":{"type":"string","description":"The account's id, which is what a turn names to spend on it and what disconnecting takes."},"label":{"type":"string","description":"What it is called here, which somebody can change."},"email":{"description":"Who it signs in as, in the provider's own words. Kept beside the label rather than folded into it, so a renamed account can still say whose it is. Absent when the provider says nothing, which is exactly when renaming is the only answer.","type":"string"},"organization":{"description":"Which organisation it belongs to, where the provider says.","type":"string"},"variant":{"description":"Which of the provider's estates it signs in to, for a provider selling more than one (Z.ai's international or mainland plan). With the email and organisation, it is who the account is: a sign-in matching all three lands on this account again, same id.","type":"string"},"scope":{"description":"What the credential is permitted to do, in the provider's terms.","type":"string"},"connectedAt":{"type":"number","description":"When it was connected, in milliseconds."},"needsReauth":{"description":"Its stored credential can no longer be renewed and somebody has to sign in again. Absent means healthy, or not checked yet.","type":"boolean"},"detail":{"description":"Why, in words a person can act on.","type":"string"},"seatRefusal":{"description":"Its organisation has switched it off for this harness (no seat): it still signs in and its plan limits may still read, but no turn can run on it until an admin gives access back. The provider's own sentence; cleared by the next turn that runs on it. Absent means no refusal is on file.","type":"string"},"usage":{"description":"How full its plan limits were when last measured, so a picker can show what is left before committing work to it. Absent until a reading exists, which reads as unknown rather than as nothing left.","type":"object","properties":{"windows":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"label":{"type":"string"},"utilization":{"type":"number"},"resetsAt":{"type":"number"},"gates":{"anyOf":[{"type":"string","const":"all"},{"type":"string","const":"none"},{"type":"object","properties":{"models":{"minItems":1,"type":"array","items":{"type":"string","minLength":1}}},"required":["models"],"additionalProperties":false}]}},"required":["kind","utilization","gates"],"additionalProperties":false}},"measuredAt":{"type":"number"},"unread":{"description":"Present while re-reading this account keeps failing: these windows are the last reading that succeeded, and `measuredAt` will not move until a read succeeds again.","type":"object","properties":{"since":{"type":"number","description":"When re-reading this account first failed, in milliseconds. It has failed on every attempt since."},"reason":{"type":"string","description":"Why, in the provider's own words where it gave some (\"Verify your account to continue.\"). Short enough to print; never a pasted response body."}},"required":["since","reason"],"additionalProperties":false}},"required":["windows","measuredAt"],"additionalProperties":false},"state":{"description":"Whether it can serve a turn now, judged once from the sign-in, the seat, the provider's last refusal and the plan limits: the verdict every picker in the sandbox uses. Read this rather than the fields it was judged from. Absent from a daemon older than this field.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"ready"},"room":{"type":"number","description":"How much of the fullest pool that gates the turn is left, in percent (above 0, up to 100). Pickers take the most room."}},"required":["kind","room"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"spent"},"reopensAt":{"description":"When every full pool has reopened, in epoch seconds, where the plan publishes it. Absent means unknown, never now.","type":"number"}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"blocked"},"fix":{"type":"string","enum":["reconnect","admin","verify","wait"],"description":"Who can make it serve again: `reconnect` (sign in again on this sandbox), `admin` (an organisation admin hands the seat back), `verify` (the account's owner confirms it on the provider's page, at `url`), or `wait` (it lifts by itself, at `until` where known)."},"reason":{"type":"string","description":"Why, in words a person can act on: the provider's own sentence where it gave one."},"until":{"description":"When waiting lifts it, in epoch seconds, for `wait` only.","type":"number"},"url":{"description":"The provider's page where the account's owner lifts it, for `verify` only.","type":"string"}},"required":["kind","fix","reason"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"unknown","description":"Nothing blocks it and nothing has been measured: usable, never read as room."}},"required":["kind"],"additionalProperties":false}]}},"required":["id","label","connectedAt"],"additionalProperties":false}},"required":["status"],"additionalProperties":false},{"type":"object","properties":{"status":{"type":"string","const":"error"},"error":{"type":"string","minLength":1,"description":"Why it failed, to show as it is."}},"required":["status","error"],"additionalProperties":false}]}}}}}}},"/accounts/{provider}/login/cancel":{"post":{"operationId":"accounts.cancel","summary":"Abandon a sign-in","description":"Stops waiting on a sign-in nobody completed. An abandoned attempt also expires on its own.","tags":["Accounts"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","enum":["claude","codex","grok","kimi","gemini","cursor","meta","zai"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"handshake":{"type":"string","minLength":1,"description":"Which attempt to stop waiting on."}},"required":["handshake"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/accounts/{provider}":{"get":{"operationId":"accounts.accounts","summary":"Connected accounts of a provider","description":"Each connected account with how full its plan limits were when last measured, where the provider publishes any. Ask for a fresh measurement and it takes one before answering, which is slower. The credentials themselves never travel: being in this list is what connected means.","tags":["Accounts"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","enum":["claude","codex","grok","kimi","gemini","cursor","meta","zai"]}},{"name":"force","in":"query","schema":{"description":"Measure the plan limits again before answering, rather than serving a recent reading. Slower, and the right thing when somebody has just changed a plan and is asking whether what they can see is still true.","type":"string"},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"accounts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The account's id, which is what a turn names to spend on it and what disconnecting takes."},"label":{"type":"string","description":"What it is called here, which somebody can change."},"email":{"description":"Who it signs in as, in the provider's own words. Kept beside the label rather than folded into it, so a renamed account can still say whose it is. Absent when the provider says nothing, which is exactly when renaming is the only answer.","type":"string"},"organization":{"description":"Which organisation it belongs to, where the provider says.","type":"string"},"variant":{"description":"Which of the provider's estates it signs in to, for a provider selling more than one (Z.ai's international or mainland plan). With the email and organisation, it is who the account is: a sign-in matching all three lands on this account again, same id.","type":"string"},"scope":{"description":"What the credential is permitted to do, in the provider's terms.","type":"string"},"connectedAt":{"type":"number","description":"When it was connected, in milliseconds."},"needsReauth":{"description":"Its stored credential can no longer be renewed and somebody has to sign in again. Absent means healthy, or not checked yet.","type":"boolean"},"detail":{"description":"Why, in words a person can act on.","type":"string"},"seatRefusal":{"description":"Its organisation has switched it off for this harness (no seat): it still signs in and its plan limits may still read, but no turn can run on it until an admin gives access back. The provider's own sentence; cleared by the next turn that runs on it. Absent means no refusal is on file.","type":"string"},"usage":{"description":"How full its plan limits were when last measured, so a picker can show what is left before committing work to it. Absent until a reading exists, which reads as unknown rather than as nothing left.","type":"object","properties":{"windows":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"label":{"type":"string"},"utilization":{"type":"number"},"resetsAt":{"type":"number"},"gates":{"anyOf":[{"type":"string","const":"all"},{"type":"string","const":"none"},{"type":"object","properties":{"models":{"minItems":1,"type":"array","items":{"type":"string","minLength":1}}},"required":["models"],"additionalProperties":false}]}},"required":["kind","utilization","gates"],"additionalProperties":false}},"measuredAt":{"type":"number"},"unread":{"description":"Present while re-reading this account keeps failing: these windows are the last reading that succeeded, and `measuredAt` will not move until a read succeeds again.","type":"object","properties":{"since":{"type":"number","description":"When re-reading this account first failed, in milliseconds. It has failed on every attempt since."},"reason":{"type":"string","description":"Why, in the provider's own words where it gave some (\"Verify your account to continue.\"). Short enough to print; never a pasted response body."}},"required":["since","reason"],"additionalProperties":false}},"required":["windows","measuredAt"],"additionalProperties":false},"state":{"description":"Whether it can serve a turn now, judged once from the sign-in, the seat, the provider's last refusal and the plan limits: the verdict every picker in the sandbox uses. Read this rather than the fields it was judged from. Absent from a daemon older than this field.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"ready"},"room":{"type":"number","description":"How much of the fullest pool that gates the turn is left, in percent (above 0, up to 100). Pickers take the most room."}},"required":["kind","room"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"spent"},"reopensAt":{"description":"When every full pool has reopened, in epoch seconds, where the plan publishes it. Absent means unknown, never now.","type":"number"}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"blocked"},"fix":{"type":"string","enum":["reconnect","admin","verify","wait"],"description":"Who can make it serve again: `reconnect` (sign in again on this sandbox), `admin` (an organisation admin hands the seat back), `verify` (the account's owner confirms it on the provider's page, at `url`), or `wait` (it lifts by itself, at `until` where known)."},"reason":{"type":"string","description":"Why, in words a person can act on: the provider's own sentence where it gave one."},"until":{"description":"When waiting lifts it, in epoch seconds, for `wait` only.","type":"number"},"url":{"description":"The provider's page where the account's owner lifts it, for `verify` only.","type":"string"}},"required":["kind","fix","reason"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"unknown","description":"Nothing blocks it and nothing has been measured: usable, never read as room."}},"required":["kind"],"additionalProperties":false}]}},"required":["id","label","connectedAt"],"additionalProperties":false},"description":"The connected accounts. Tokens never travel in this shape: being in this list is what connected means."}},"required":["accounts"],"additionalProperties":false}}}}}}},"/accounts/{provider}/rename":{"post":{"operationId":"accounts.rename","summary":"Rename an account","description":"Changes the label one account shows under, so several are tellable apart. Blank restores the one derived from the sign-in.","tags":["Accounts"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","enum":["claude","codex","grok","kimi","gemini","cursor","meta","zai"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"description":"Which account."},"label":{"type":"string","maxLength":80,"description":"The new name. Blank restores the one derived from the sign-in, rather than leaving a nameless row."}},"required":["id","label"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The account's id, which is what a turn names to spend on it and what disconnecting takes."},"label":{"type":"string","description":"What it is called here, which somebody can change."},"email":{"description":"Who it signs in as, in the provider's own words. Kept beside the label rather than folded into it, so a renamed account can still say whose it is. Absent when the provider says nothing, which is exactly when renaming is the only answer.","type":"string"},"organization":{"description":"Which organisation it belongs to, where the provider says.","type":"string"},"variant":{"description":"Which of the provider's estates it signs in to, for a provider selling more than one (Z.ai's international or mainland plan). With the email and organisation, it is who the account is: a sign-in matching all three lands on this account again, same id.","type":"string"},"scope":{"description":"What the credential is permitted to do, in the provider's terms.","type":"string"},"connectedAt":{"type":"number","description":"When it was connected, in milliseconds."},"needsReauth":{"description":"Its stored credential can no longer be renewed and somebody has to sign in again. Absent means healthy, or not checked yet.","type":"boolean"},"detail":{"description":"Why, in words a person can act on.","type":"string"},"seatRefusal":{"description":"Its organisation has switched it off for this harness (no seat): it still signs in and its plan limits may still read, but no turn can run on it until an admin gives access back. The provider's own sentence; cleared by the next turn that runs on it. Absent means no refusal is on file.","type":"string"},"usage":{"description":"How full its plan limits were when last measured, so a picker can show what is left before committing work to it. Absent until a reading exists, which reads as unknown rather than as nothing left.","type":"object","properties":{"windows":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"label":{"type":"string"},"utilization":{"type":"number"},"resetsAt":{"type":"number"},"gates":{"anyOf":[{"type":"string","const":"all"},{"type":"string","const":"none"},{"type":"object","properties":{"models":{"minItems":1,"type":"array","items":{"type":"string","minLength":1}}},"required":["models"],"additionalProperties":false}]}},"required":["kind","utilization","gates"],"additionalProperties":false}},"measuredAt":{"type":"number"},"unread":{"description":"Present while re-reading this account keeps failing: these windows are the last reading that succeeded, and `measuredAt` will not move until a read succeeds again.","type":"object","properties":{"since":{"type":"number","description":"When re-reading this account first failed, in milliseconds. It has failed on every attempt since."},"reason":{"type":"string","description":"Why, in the provider's own words where it gave some (\"Verify your account to continue.\"). Short enough to print; never a pasted response body."}},"required":["since","reason"],"additionalProperties":false}},"required":["windows","measuredAt"],"additionalProperties":false},"state":{"description":"Whether it can serve a turn now, judged once from the sign-in, the seat, the provider's last refusal and the plan limits: the verdict every picker in the sandbox uses. Read this rather than the fields it was judged from. Absent from a daemon older than this field.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"ready"},"room":{"type":"number","description":"How much of the fullest pool that gates the turn is left, in percent (above 0, up to 100). Pickers take the most room."}},"required":["kind","room"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"spent"},"reopensAt":{"description":"When every full pool has reopened, in epoch seconds, where the plan publishes it. Absent means unknown, never now.","type":"number"}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"blocked"},"fix":{"type":"string","enum":["reconnect","admin","verify","wait"],"description":"Who can make it serve again: `reconnect` (sign in again on this sandbox), `admin` (an organisation admin hands the seat back), `verify` (the account's owner confirms it on the provider's page, at `url`), or `wait` (it lifts by itself, at `until` where known)."},"reason":{"type":"string","description":"Why, in words a person can act on: the provider's own sentence where it gave one."},"until":{"description":"When waiting lifts it, in epoch seconds, for `wait` only.","type":"number"},"url":{"description":"The provider's page where the account's owner lifts it, for `verify` only.","type":"string"}},"required":["kind","fix","reason"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"unknown","description":"Nothing blocks it and nothing has been measured: usable, never read as room."}},"required":["kind"],"additionalProperties":false}]}},"required":["id","label","connectedAt"],"additionalProperties":false}}}}}}},"/accounts/{provider}/disconnect":{"post":{"operationId":"accounts.disconnect","summary":"Disconnect an account","description":"Clears one stored credential, and stops any sign-in still in flight for this provider. The others stay connected.","tags":["Accounts"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","enum":["claude","codex","grok","kimi","gemini","cursor","meta","zai"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"description":"Which account."}},"required":["id"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/translator/accounts":{"get":{"operationId":"translator.accounts","summary":"Subscriptions connected through the translator","description":"What is signed in per provider. Each provider can hold several accounts at once, and the translator spreads work across them.","tags":["Routed providers"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"codex":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"},"usage":{"type":"object","properties":{"windows":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"label":{"type":"string"},"utilization":{"type":"number"},"resetsAt":{"type":"number"},"gates":{"anyOf":[{"type":"string","const":"all"},{"type":"string","const":"none"},{"type":"object","properties":{"models":{"minItems":1,"type":"array","items":{"type":"string","minLength":1}}},"required":["models"],"additionalProperties":false}]}},"required":["kind","utilization","gates"],"additionalProperties":false}},"measuredAt":{"type":"number"},"unread":{"description":"Present while re-reading this account keeps failing: these windows are the last reading that succeeded, and `measuredAt` will not move until a read succeeds again.","type":"object","properties":{"since":{"type":"number","description":"When re-reading this account first failed, in milliseconds. It has failed on every attempt since."},"reason":{"type":"string","description":"Why, in the provider's own words where it gave some (\"Verify your account to continue.\"). Short enough to print; never a pasted response body."}},"required":["since","reason"],"additionalProperties":false}},"required":["windows","measuredAt"],"additionalProperties":false},"cooling":{"type":"object","properties":{"until":{"type":"number"},"reason":{"type":"string"},"verify":{"type":"string"}},"additionalProperties":false},"state":{"description":"Whether it can serve a turn now, judged from everything above plus the provider's last refusal. Absent from a daemon older than this field.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"ready"},"room":{"type":"number","description":"How much of the fullest pool that gates the turn is left, in percent (above 0, up to 100). Pickers take the most room."}},"required":["kind","room"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"spent"},"reopensAt":{"description":"When every full pool has reopened, in epoch seconds, where the plan publishes it. Absent means unknown, never now.","type":"number"}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"blocked"},"fix":{"type":"string","enum":["reconnect","admin","verify","wait"],"description":"Who can make it serve again: `reconnect` (sign in again on this sandbox), `admin` (an organisation admin hands the seat back), `verify` (the account's owner confirms it on the provider's page, at `url`), or `wait` (it lifts by itself, at `until` where known)."},"reason":{"type":"string","description":"Why, in words a person can act on: the provider's own sentence where it gave one."},"until":{"description":"When waiting lifts it, in epoch seconds, for `wait` only.","type":"number"},"url":{"description":"The provider's page where the account's owner lifts it, for `verify` only.","type":"string"}},"required":["kind","fix","reason"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"unknown","description":"Nothing blocks it and nothing has been measured: usable, never read as room."}},"required":["kind"],"additionalProperties":false}]}},"required":["name","label"],"additionalProperties":false}},"grok":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"},"usage":{"type":"object","properties":{"windows":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"label":{"type":"string"},"utilization":{"type":"number"},"resetsAt":{"type":"number"},"gates":{"anyOf":[{"type":"string","const":"all"},{"type":"string","const":"none"},{"type":"object","properties":{"models":{"minItems":1,"type":"array","items":{"type":"string","minLength":1}}},"required":["models"],"additionalProperties":false}]}},"required":["kind","utilization","gates"],"additionalProperties":false}},"measuredAt":{"type":"number"},"unread":{"description":"Present while re-reading this account keeps failing: these windows are the last reading that succeeded, and `measuredAt` will not move until a read succeeds again.","type":"object","properties":{"since":{"type":"number","description":"When re-reading this account first failed, in milliseconds. It has failed on every attempt since."},"reason":{"type":"string","description":"Why, in the provider's own words where it gave some (\"Verify your account to continue.\"). Short enough to print; never a pasted response body."}},"required":["since","reason"],"additionalProperties":false}},"required":["windows","measuredAt"],"additionalProperties":false},"cooling":{"type":"object","properties":{"until":{"type":"number"},"reason":{"type":"string"},"verify":{"type":"string"}},"additionalProperties":false},"state":{"description":"Whether it can serve a turn now, judged from everything above plus the provider's last refusal. Absent from a daemon older than this field.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"ready"},"room":{"type":"number","description":"How much of the fullest pool that gates the turn is left, in percent (above 0, up to 100). Pickers take the most room."}},"required":["kind","room"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"spent"},"reopensAt":{"description":"When every full pool has reopened, in epoch seconds, where the plan publishes it. Absent means unknown, never now.","type":"number"}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"blocked"},"fix":{"type":"string","enum":["reconnect","admin","verify","wait"],"description":"Who can make it serve again: `reconnect` (sign in again on this sandbox), `admin` (an organisation admin hands the seat back), `verify` (the account's owner confirms it on the provider's page, at `url`), or `wait` (it lifts by itself, at `until` where known)."},"reason":{"type":"string","description":"Why, in words a person can act on: the provider's own sentence where it gave one."},"until":{"description":"When waiting lifts it, in epoch seconds, for `wait` only.","type":"number"},"url":{"description":"The provider's page where the account's owner lifts it, for `verify` only.","type":"string"}},"required":["kind","fix","reason"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"unknown","description":"Nothing blocks it and nothing has been measured: usable, never read as room."}},"required":["kind"],"additionalProperties":false}]}},"required":["name","label"],"additionalProperties":false}},"kimi":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"},"usage":{"type":"object","properties":{"windows":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"label":{"type":"string"},"utilization":{"type":"number"},"resetsAt":{"type":"number"},"gates":{"anyOf":[{"type":"string","const":"all"},{"type":"string","const":"none"},{"type":"object","properties":{"models":{"minItems":1,"type":"array","items":{"type":"string","minLength":1}}},"required":["models"],"additionalProperties":false}]}},"required":["kind","utilization","gates"],"additionalProperties":false}},"measuredAt":{"type":"number"},"unread":{"description":"Present while re-reading this account keeps failing: these windows are the last reading that succeeded, and `measuredAt` will not move until a read succeeds again.","type":"object","properties":{"since":{"type":"number","description":"When re-reading this account first failed, in milliseconds. It has failed on every attempt since."},"reason":{"type":"string","description":"Why, in the provider's own words where it gave some (\"Verify your account to continue.\"). Short enough to print; never a pasted response body."}},"required":["since","reason"],"additionalProperties":false}},"required":["windows","measuredAt"],"additionalProperties":false},"cooling":{"type":"object","properties":{"until":{"type":"number"},"reason":{"type":"string"},"verify":{"type":"string"}},"additionalProperties":false},"state":{"description":"Whether it can serve a turn now, judged from everything above plus the provider's last refusal. Absent from a daemon older than this field.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"ready"},"room":{"type":"number","description":"How much of the fullest pool that gates the turn is left, in percent (above 0, up to 100). Pickers take the most room."}},"required":["kind","room"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"spent"},"reopensAt":{"description":"When every full pool has reopened, in epoch seconds, where the plan publishes it. Absent means unknown, never now.","type":"number"}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"blocked"},"fix":{"type":"string","enum":["reconnect","admin","verify","wait"],"description":"Who can make it serve again: `reconnect` (sign in again on this sandbox), `admin` (an organisation admin hands the seat back), `verify` (the account's owner confirms it on the provider's page, at `url`), or `wait` (it lifts by itself, at `until` where known)."},"reason":{"type":"string","description":"Why, in words a person can act on: the provider's own sentence where it gave one."},"until":{"description":"When waiting lifts it, in epoch seconds, for `wait` only.","type":"number"},"url":{"description":"The provider's page where the account's owner lifts it, for `verify` only.","type":"string"}},"required":["kind","fix","reason"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"unknown","description":"Nothing blocks it and nothing has been measured: usable, never read as room."}},"required":["kind"],"additionalProperties":false}]}},"required":["name","label"],"additionalProperties":false}},"gemini":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"},"usage":{"type":"object","properties":{"windows":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"label":{"type":"string"},"utilization":{"type":"number"},"resetsAt":{"type":"number"},"gates":{"anyOf":[{"type":"string","const":"all"},{"type":"string","const":"none"},{"type":"object","properties":{"models":{"minItems":1,"type":"array","items":{"type":"string","minLength":1}}},"required":["models"],"additionalProperties":false}]}},"required":["kind","utilization","gates"],"additionalProperties":false}},"measuredAt":{"type":"number"},"unread":{"description":"Present while re-reading this account keeps failing: these windows are the last reading that succeeded, and `measuredAt` will not move until a read succeeds again.","type":"object","properties":{"since":{"type":"number","description":"When re-reading this account first failed, in milliseconds. It has failed on every attempt since."},"reason":{"type":"string","description":"Why, in the provider's own words where it gave some (\"Verify your account to continue.\"). Short enough to print; never a pasted response body."}},"required":["since","reason"],"additionalProperties":false}},"required":["windows","measuredAt"],"additionalProperties":false},"cooling":{"type":"object","properties":{"until":{"type":"number"},"reason":{"type":"string"},"verify":{"type":"string"}},"additionalProperties":false},"state":{"description":"Whether it can serve a turn now, judged from everything above plus the provider's last refusal. Absent from a daemon older than this field.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"ready"},"room":{"type":"number","description":"How much of the fullest pool that gates the turn is left, in percent (above 0, up to 100). Pickers take the most room."}},"required":["kind","room"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"spent"},"reopensAt":{"description":"When every full pool has reopened, in epoch seconds, where the plan publishes it. Absent means unknown, never now.","type":"number"}},"required":["kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"blocked"},"fix":{"type":"string","enum":["reconnect","admin","verify","wait"],"description":"Who can make it serve again: `reconnect` (sign in again on this sandbox), `admin` (an organisation admin hands the seat back), `verify` (the account's owner confirms it on the provider's page, at `url`), or `wait` (it lifts by itself, at `until` where known)."},"reason":{"type":"string","description":"Why, in words a person can act on: the provider's own sentence where it gave one."},"until":{"description":"When waiting lifts it, in epoch seconds, for `wait` only.","type":"number"},"url":{"description":"The provider's page where the account's owner lifts it, for `verify` only.","type":"string"}},"required":["kind","fix","reason"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"unknown","description":"Nothing blocks it and nothing has been measured: usable, never read as room."}},"required":["kind"],"additionalProperties":false}]}},"required":["name","label"],"additionalProperties":false}}},"required":["codex","grok","kimi","gemini"],"additionalProperties":false}}}}}}},"/translator/{provider}/connect":{"post":{"operationId":"translator.connect","summary":"Start connecting a subscription","description":"Begins the sign-in for one provider and says which of the two shapes it is: a code you type into a device page, which finishes by itself in the background, or a redirect whose landing address you hand back afterwards.","tags":["Routed providers"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","enum":["codex","grok","kimi","gemini"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"The page to open."},"code":{"type":"string","description":"The one-time code, where the provider uses one."},"state":{"type":"string","minLength":1,"description":"The handshake's id, which status reads and the finishing call sends back."},"flow":{"type":"string","enum":["device","redirect"],"description":"Which shape this is. A device sign-in finishes by itself and you poll the attempt; a redirect needs the address it landed on handed back. Said outright rather than guessed at from whether a code happens to exist."},"catchers":{"description":"Who is watching for where the browser lands, on the machine it is on: a device or a browser of yours. Listed means the sign-in finishes by itself once the page is approved there; the paste stays open for a browser anywhere else. Empty or absent means nothing is watching.","type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["device","browser"]},"label":{"type":"string","minLength":1}},"required":["kind","label"],"additionalProperties":false}}},"required":["url","code","state","flow"],"additionalProperties":false}}}}}},"get":{"operationId":"translator.status","summary":"Read a subscription connection attempt","description":"Reports whether this exact sign-in attempt is waiting, completed, or failed. Completion is tied to the attempt rather than a change in account count, because signing in to an existing account replaces its credential in place.","tags":["Routed providers"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","enum":["codex","grok","kimi","gemini"]}},{"name":"state","in":"query","required":true,"schema":{"type":"string","minLength":1},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"status":{"type":"string","const":"wait"}},"required":["status"],"additionalProperties":false},{"type":"object","properties":{"status":{"type":"string","const":"ok"}},"required":["status"],"additionalProperties":false},{"type":"object","properties":{"status":{"type":"string","const":"error"},"error":{"type":"string","minLength":1}},"required":["status","error"],"additionalProperties":false}]}}}}}}},"/translator/{provider}/complete":{"post":{"operationId":"translator.complete","summary":"Finish a redirect sign-in","description":"For the providers that redirect somewhere this sandbox cannot receive: hand back the address you landed on and the connection completes.","tags":["Routed providers"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","enum":["codex","grok","kimi","gemini"],"description":"Which provider."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"redirectUrl":{"type":"string","minLength":1,"description":"The address the browser was sent to, whole. The grant is inside it."},"state":{"type":"string","minLength":1,"description":"The handshake this belongs to. A mismatch is refused."}},"required":["redirectUrl","state"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/translator/{provider}/disconnect":{"post":{"operationId":"translator.disconnect","summary":"Disconnect one subscription","description":"Clears a single account by name. Any others under the same provider stay connected.","tags":["Routed providers"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","enum":["codex","grok","kimi","gemini"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1}},"required":["name"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/endpoints/{id}/models":{"get":{"operationId":"endpoints.models","summary":"Models a connected server offers","description":"Asks one configured model server what it serves. There is no built-in list and no fallback: what a server offers is knowable only by asking it, so an empty answer is the honest report that we could not.","tags":["Endpoints"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","description":"Which connection."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"models":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"What to name when asking for this model."},"label":{"type":"string","description":"What to call it on screen."},"efforts":{"description":"The thinking levels it accepts, where the provider says. Empty means use your own defaults.","type":"array","items":{"type":"string"}},"description":{"description":"What it is good for, in the provider's own words. Absent where the provider publishes only ids, which is the honest answer rather than something to paper over with a hand-written table.","type":"string"},"badges":{"description":"What it is known for, where the provider says so.","type":"array","items":{"type":"string","enum":["reasoning","fast"]}},"contextWindow":{"description":"How many tokens this model will accept in one request, where the server publishes it.","type":"number"},"helperOnly":{"description":"Set where this model may write commit messages, titles and other one-shot jobs but never run a chat turn, and why: its server says it cannot call tools, or it is the small local model kept for quick jobs. Absent means nothing has said it cannot.","type":"string","enum":["no-tool-calls","instant-tier"]},"availableAt":{"description":"When this model can be asked again, where every credential that serves it is currently refused. Absent means it can be asked now. A model here is still worth showing, unlike one the plan does not cover at all: the wait is the whole answer.","type":"number"}},"required":["id","label"],"additionalProperties":false},"description":"What this provider serves, in its own preference order, which is not rearranged here. Never empty."},"default":{"type":"string","description":"Which one a fresh conversation starts on. Always present."}},"required":["models","default"],"additionalProperties":false}}}}}}},"/endpoints/trial/status":{"get":{"operationId":"endpoints.trial","summary":"What is left of the free trial","description":"The allowance, what has been used, when it resets, and which model actually answered the last message. Not being available is the ordinary answer rather than a failure: most sandboxes run against a platform that offers no trial at all.","tags":["Endpoints"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"available":{"type":"boolean"},"allowance":{"type":"integer","minimum":0,"maximum":9007199254740991},"used":{"type":"integer","minimum":0,"maximum":9007199254740991},"remaining":{"type":"integer","minimum":0,"maximum":9007199254740991},"health":{"type":"string","enum":["unknown","healthy","degraded","unavailable"]},"resetsAt":{"type":"string"},"retryAt":{"type":"string"},"servedModel":{"type":"string"}},"required":["available","allowance","used","remaining","health"],"additionalProperties":false}}}}}}},"/endpoints/local-model/fit":{"get":{"operationId":"endpoints.localModelFit","summary":"Which local models this machine can actually run","description":"The memory this sandbox may use, whether a GPU reached it, and every curated model priced two ways: whether it fits on one device's free memory and so runs at full speed, and whether it can load at all. The two the connect view offers, one that downloads in a minute and the best this machine runs at full speed, come from the first; a start is refused only on the second.","tags":["Endpoints"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"memoryBytes":{"type":"integer","minimum":0,"maximum":9007199254740991},"memoryCapped":{"type":"boolean"},"gpu":{"type":"string","enum":["granted","unsupported","absent"]},"gpuMemoryBytes":{"type":"integer","minimum":0,"maximum":9007199254740991},"gpuFreeBytes":{"type":"integer","minimum":0,"maximum":9007199254740991},"budgetBytes":{"type":"integer","minimum":0,"maximum":9007199254740991},"fullSpeedBytes":{"type":"integer","minimum":0,"maximum":9007199254740991},"fullSpeedDevice":{"type":"string","enum":["gpu","host"]},"serverReady":{"type":"boolean"},"options":{"type":"array","items":{"type":"object","properties":{"model":{"type":"string"},"label":{"type":"string"},"tier":{"type":"string","enum":["instant","work"]},"weightsBytes":{"type":"integer","minimum":0,"maximum":9007199254740991},"held":{"type":"boolean"},"windows":{"type":"array","items":{"type":"object","properties":{"tokens":{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"totalBytes":{"type":"integer","minimum":0,"maximum":9007199254740991},"fits":{"type":"boolean"},"fullSpeed":{"type":"boolean"}},"required":["tokens","totalBytes","fits"],"additionalProperties":false}}},"required":["model","label","tier","weightsBytes","held","windows"],"additionalProperties":false}},"instant":{"type":"object","properties":{"model":{"type":"string"},"context":{"type":"string"}},"required":["model","context"],"additionalProperties":false},"best":{"type":"object","properties":{"model":{"type":"string"},"context":{"type":"string"}},"required":["model","context"],"additionalProperties":false},"prefetch":{"type":"object","properties":{"model":{"type":"string"},"state":{"type":"string","enum":["idle","downloading","held","failed"]},"receivedBytes":{"type":"integer","minimum":0,"maximum":9007199254740991},"totalBytes":{"type":"integer","minimum":0,"maximum":9007199254740991},"detail":{"type":"string"}},"required":["model","state","receivedBytes","totalBytes"],"additionalProperties":false}},"required":["memoryBytes","memoryCapped","gpu","gpuMemoryBytes","budgetBytes","serverReady","options","prefetch"],"additionalProperties":false}}}}}}},"/endpoints/local-model/prefetch":{"post":{"operationId":"endpoints.localModelPrefetch","summary":"Fetch the small model's weights ahead of being asked","description":"Downloads the curated instant model into the workspace cache so that adding it later costs nothing. Stopping leaves the part file, so a later start resumes from where this one stopped rather than beginning again.","tags":["Endpoints"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["start","stop"]}},"required":["action"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"model":{"type":"string"},"state":{"type":"string","enum":["idle","downloading","held","failed"]},"receivedBytes":{"type":"integer","minimum":0,"maximum":9007199254740991},"totalBytes":{"type":"integer","minimum":0,"maximum":9007199254740991},"detail":{"type":"string"}},"required":["model","state","receivedBytes","totalBytes"],"additionalProperties":false}}}}}}},"/providers":{"get":{"operationId":"providers.list","summary":"Providers a chat can run on here","description":"The installed ACP agents and model endpoints, which are the providers this sandbox adds to the fixed native list. A read for anyone who may watch or drive a turn: it names what a message can be addressed to, not what credential stands behind it.","tags":["Providers"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"native":{"type":"array","items":{"type":"string","enum":["claude","codex","grok","kimi","gemini","cursor","meta","zai"]},"description":"Native providers with a working credential here, by id; never what holds it."},"agents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"}},"required":["id","label"],"additionalProperties":false},"description":"ACP agents installed here. The id is the provider id itself, the label its display name."},"endpoints":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"kind":{"type":"string","enum":["endpoint","localmodel"]}},"required":["id","label","kind"],"additionalProperties":false},"description":"Model endpoints, already prefixed `endpoint/`, including the daemon-provisioned free trial."}},"required":["native","agents","endpoints"],"additionalProperties":false}}}}}}},"/providers/{provider}/models":{"get":{"operationId":"providers.models","summary":"Models one provider offers","description":"Every model this provider serves and which one it defaults to. Never empty: it is discovered live with a stored list behind it. The order is the provider's own preference and is not rearranged here.","tags":["Providers"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","enum":["claude","codex","grok","kimi","gemini","cursor","meta","zai"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"models":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"What to name when asking for this model."},"label":{"type":"string","description":"What to call it on screen."},"efforts":{"description":"The thinking levels it accepts, where the provider says. Empty means use your own defaults.","type":"array","items":{"type":"string"}},"description":{"description":"What it is good for, in the provider's own words. Absent where the provider publishes only ids, which is the honest answer rather than something to paper over with a hand-written table.","type":"string"},"badges":{"description":"What it is known for, where the provider says so.","type":"array","items":{"type":"string","enum":["reasoning","fast"]}},"contextWindow":{"description":"How many tokens this model will accept in one request, where the server publishes it.","type":"number"},"helperOnly":{"description":"Set where this model may write commit messages, titles and other one-shot jobs but never run a chat turn, and why: its server says it cannot call tools, or it is the small local model kept for quick jobs. Absent means nothing has said it cannot.","type":"string","enum":["no-tool-calls","instant-tier"]},"availableAt":{"description":"When this model can be asked again, where every credential that serves it is currently refused. Absent means it can be asked now. A model here is still worth showing, unlike one the plan does not cover at all: the wait is the whole answer.","type":"number"}},"required":["id","label"],"additionalProperties":false},"description":"What this provider serves, in its own preference order, which is not rearranged here. Never empty."},"default":{"type":"string","description":"Which one a fresh conversation starts on. Always present."}},"required":["models","default"],"additionalProperties":false}}}}}}},"/usage/rollup":{"get":{"operationId":"usage.rollup","summary":"What was spent, grouped","description":"The spending record over a range of days, grouped by day, provider, account and model. Everything a cost screen shows is a rearrangement of this one answer, so nothing needs a second call. Read-only: rows are written by the sandbox as turns end, which is what makes it worth trusting.","tags":["Usage"],"parameters":[{"name":"from","in":"query","schema":{"description":"First day to include, as YYYY-MM-DD in UTC. Leave it out for everything up to the end day.","type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"to","in":"query","schema":{"description":"Last day to include, as YYYY-MM-DD in UTC, and it is included rather than excluded. Leave it out for everything from the start day onwards.","type":"string"},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string","description":"The day, as YYYY-MM-DD in UTC."},"provider":{"type":"string","description":"Which model provider."},"account":{"description":"Which account. Absent for work run on a plain key.","type":"string"},"model":{"description":"Which model.","type":"string"},"harness":{"type":"string","description":"Which agentic loop."},"conversationId":{"description":"Which conversation.","type":"string"},"turns":{"type":"number","description":"Turns in this group."},"inputTokens":{"type":"number","description":"Uncached input tokens, excluding cache reads and cache writes."},"outputTokens":{"type":"number","description":"Tokens received."},"cacheReadTokens":{"type":"number","description":"Tokens served from cache."},"cacheCreationTokens":{"type":"number","description":"Tokens written to cache."},"costUsd":{"type":"number","description":"What the group cost, in dollars."},"costKnown":{"description":"False when the cost includes unpriced turns and is a lower bound rather than the true total.","type":"boolean"},"durationMs":{"type":"number","description":"Time spent, in milliseconds."}},"required":["day","provider","harness","turns","inputTokens","outputTokens","cacheReadTokens","cacheCreationTokens","costUsd","durationMs"],"additionalProperties":false},"description":"Spending grouped by day, provider, account, model and conversation. Everything a cost screen shows is a rearrangement of these rows, which is why there is no second call for any of it."}},"required":["rows"],"additionalProperties":false}}}}}}},"/usage/plan-limits/refresh":{"post":{"operationId":"usage.refreshPlanLimits","summary":"Measure every account's plan limits again","description":"Reads how full each connected account's plan limits are, for every provider, and records it. Forced, it measures even accounts read a moment ago, which is the right thing when a plan was just changed and the question is whether the number on screen is still true. Answers with the accounts it could not read because the provider is rate-limiting them, and when each may be asked again: those keep the reading they already had, so a number that does not move is explained rather than silent.","tags":["Usage"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"force":{"default":false,"description":"Measure again even if a reading was taken a moment ago.","type":"boolean"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true},"held":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","description":"Which provider is holding the read off."},"account":{"type":"string","description":"The account as its provider's list names it: an account id, or a routed auth file's name."},"resumesAt":{"type":"number","description":"Unix seconds: when this account may be read again, the provider's own retry-after."}},"required":["provider","account","resumesAt"],"additionalProperties":false},"description":"Accounts whose plan limits could not be read now because the provider is rate-limiting them."}},"required":["ok","held"],"additionalProperties":false}}}}}}},"/usage/limit-reset/{account}":{"get":{"operationId":"usage.limitReset","summary":"Whether this account's session window can be reopened now","description":"Asks the provider whether it will reopen this account's spent session window immediately, which some plans grant once a week. Only worth asking about an account that has actually been refused: the answer is the provider's judgement at this moment, it is not cached, and an account with no such grant answers plainly that it has none.","tags":["Usage"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which account."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"available":{"type":"boolean","description":"Whether the provider will reopen this account's session window right now. The only thing a button may be drawn from."},"reason":{"description":"Why not, in the provider's own word, when it gave one. Absent when it is available, or when the provider said nothing.","type":"string"},"nextAvailableAt":{"description":"When the next reset may be claimed, in epoch seconds, where the provider publishes it. Absent means unknown, never 'now'.","type":"number"},"weeklyResetsAt":{"description":"When the weekly allowance itself reopens, in epoch seconds, where the provider publishes it.","type":"number"}},"required":["available"],"additionalProperties":false}}}}}}},"/usage/limit-reset/{account}/claim":{"post":{"operationId":"usage.claimLimitReset","summary":"Reopen this account's session window now","description":"Spends one of the account's weekly resets to reopen its session window immediately. The weekly allowance is untouched and still binds. Answers with what the provider actually did: only `reset` changed anything, and it is the cue to send the refused turn again.","tags":["Usage"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string","minLength":1,"description":"Which account."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"result":{"type":"string","enum":["reset","already_used","not_limited","ineligible","unavailable","error"],"description":"What the provider did. Only `reset` reopened the window; every other value means nothing changed."},"nextAvailableAt":{"description":"When another reset may be claimed, in epoch seconds, where the provider published it.","type":"number"},"detail":{"description":"What went wrong, in words, for the two outcomes that are this sandbox's fault rather than the plan's.","type":"string"}},"required":["result"],"additionalProperties":false}}}}}}},"/ci/runs":{"get":{"operationId":"ci.runs","summary":"Pipeline runs across the repos","description":"What the forges are reporting for every workspace repo that has a remote, served from a cache and filled in on demand. Repos whose notifications are not wired up say so.","tags":["Pipelines"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"repos":{"type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"Which workspace repository."},"host":{"type":"string","enum":["github","gitlab"],"description":"Which forge it lives on."},"project":{"type":"string","description":"The project there."},"url":{"type":"string","description":"Its page on the forge."},"hookWarning":{"description":"Present when the sandbox could not register for instant notifications, with what happened. Without them the sandbox polls instead, so this costs a couple of minutes' delay rather than the feature.","type":"string"},"hookRecipe":{"description":"What to paste into the repository's webhook settings by hand, secret included. Shown to a maintainer or the owner only.","type":"string"}},"required":["repo","host","project","url"],"additionalProperties":false},"description":"Which workspace repositories are wired to a forge, and how each one's notifications are set up."},"runs":{"type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"Which workspace repository it belongs to."},"host":{"type":"string","enum":["github","gitlab"],"description":"Which forge is running it."},"project":{"type":"string","description":"The project there, as that forge names it."},"runId":{"type":"number","description":"The forge's own id for the run, which is what re-running and cancelling take."},"title":{"description":"The run's headline, usually the commit subject or the pull request's title. Absent means falling back to the branch and commit.","type":"string"},"authorName":{"description":"Who the forge credits for setting it off.","type":"string"},"authorAvatarUrl":{"description":"Their picture, hosted by the forge. Absent means drawing their initials instead.","type":"string"},"trigger":{"description":"What set it off, in the forge's own word rather than flattened into a shared vocabulary, because the forge's word is the precise one.","type":"string"},"workflow":{"description":"Which workflow it is a run of, where a push starts several. Absent where a commit has one pipeline.","type":"string"},"branch":{"type":"string","description":"Which branch."},"sha":{"type":"string","description":"Which commit."},"status":{"type":"string","enum":["queued","running","success","failed","canceled","skipped"],"description":"How it is going. Queued means the forge has accepted it and nothing is executing it yet, which is a different thing to wait on than a run actually in progress."},"url":{"type":"string","description":"Its page on the forge."},"createdAt":{"type":"number","description":"When it started, in milliseconds."},"durationSeconds":{"description":"How long it took.","type":"number"},"failedJobs":{"description":"What broke, by name. Fetched only for failed runs, so that a notification or a screen can say what went wrong rather than just that something did.","type":"array","items":{"type":"string"}}},"required":["repo","host","project","runId","branch","sha","status","url","createdAt"],"additionalProperties":false},"description":"Runs across all of them, newest first."},"failures":{"description":"Every main-line branch failing right now, with the fix agent on it. Absent from a daemon that keeps none.","type":"array","items":{"type":"object","properties":{"repo":{"type":"string","description":"Which workspace repository."},"branch":{"type":"string","description":"Which main-line branch."},"since":{"type":"number","description":"When its first job failed, in milliseconds."},"runId":{"type":"number","description":"The newest run that failed on it."},"jobs":{"type":"array","items":{"type":"string"},"description":"The jobs failing on it now, by name: the newest failed run's failures, and any that failed since."},"fixer":{"description":"The one conversation working on it, which every failure goes to until the branch passes. Absent while nobody is on it.","type":"string"},"decision":{"description":"The latest thing decided about it: `fix-up` while the fix agent has it, `spent` once it waits for you (its turns are used up, or it stopped without a fix), `reported` when repairs are off.","type":"object","properties":{"kind":{"type":"string","enum":["fix-up","reported","spent"],"description":"What was decided."},"reason":{"description":"Why the fix agent handed it back, on a `spent` decision.","type":"string","enum":["turns","no-change","stopped","interrupted","turn-failed","gone","refused"]},"conversationId":{"description":"The conversation working on it, when one is.","type":"string"},"at":{"type":"number","description":"When that was decided, in milliseconds."},"detail":{"description":"One short sentence on why, in the sandbox's words.","type":"string"}},"required":["kind","at"],"additionalProperties":false}},"required":["repo","branch","since","runId","jobs"],"additionalProperties":false}}},"required":["repos","runs"],"additionalProperties":false}}}}}}},"/ci/runs/rerun":{"post":{"operationId":"ci.rerun","summary":"Run a pipeline again","description":"Asks the forge to re-run one pipeline. The daemon only passes the request along.","tags":["Pipelines"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"repo":{"type":"string","description":"Which workspace repository. The project behind it is resolved fresh each call, so a stale screen cannot act on one the workspace no longer maps to."},"runId":{"type":"number","description":"Which run, by the forge's own id."}},"required":["repo","runId"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/ci/runs/cancel":{"post":{"operationId":"ci.cancel","summary":"Cancel a pipeline run","description":"Asks the forge to stop a run in progress.","tags":["Pipelines"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"repo":{"type":"string","description":"Which workspace repository. The project behind it is resolved fresh each call, so a stale screen cannot act on one the workspace no longer maps to."},"runId":{"type":"number","description":"Which run, by the forge's own id."}},"required":["repo","runId"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/ci/runs/jobs":{"post":{"operationId":"ci.jobs","summary":"The steps inside one pipeline run","description":"Each job in a run with its outcome, which is where you look to find out what actually broke.","tags":["Pipelines"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"repo":{"type":"string","description":"Which workspace repository. The project behind it is resolved fresh each call, so a stale screen cannot act on one the workspace no longer maps to."},"runId":{"type":"number","description":"Which run, by the forge's own id."}},"required":["repo","runId"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"jobs":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"The job's name."},"status":{"type":"string","enum":["queued","running","success","failed","canceled","skipped"],"description":"How it went."},"stage":{"description":"Which stage it belongs to, where the pipeline groups its jobs that way.","type":"string"},"needs":{"description":"Which jobs in this run it declared it waits on: the real shape of the pipeline. Absent means nothing could be read, which is different from an empty list, which is the claim that it waits on nothing.","type":"array","items":{"type":"string"}},"startedAt":{"description":"When it began, in milliseconds. Absent while it is queued.","type":"number"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"},"durationSeconds":{"description":"How long it took.","type":"number"},"webUrl":{"description":"Its page on the forge, which is the shortest path from this step failed to the log that says why.","type":"string"}},"required":["name","status"],"additionalProperties":false},"description":"The steps inside one run. Fetched separately from the run list, so that list stays cheap."}},"required":["jobs"],"additionalProperties":false}}}}}}},"/ci/fix":{"post":{"operationId":"ci.fix","summary":"Put an agent on a broken pipeline","description":"Opens a fresh isolated conversation already holding the failure: which job, which repo, what it said. The answer names the conversation so you can open it.","tags":["Pipelines"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"repo":{"type":"string","description":"Which workspace repository. The project behind it is resolved fresh each call, so a stale screen cannot act on one the workspace no longer maps to."},"runId":{"type":"number","description":"Which run, by the forge's own id."},"pick":{"description":"Which model to open the conversation on, when somebody chose one. Leave it out for the sandbox's own choice, which is the ordinary path.","type":"object","properties":{"agent":{"type":"string","minLength":1,"description":"Which provider."},"model":{"type":"string","minLength":1,"description":"Which of its models. Both or neither, because a model name only means anything to the provider that serves it."},"account":{"description":"Which connected account of that provider pays, by its daemon-minted id. Leave it out to take whichever account can serve with the most room.","type":"string"},"harness":{"description":"Which agentic loop runs it. Leave it out to use the provider's own.","type":"string","enum":["native","claude-code"]},"effort":{"description":"How hard that model should think, where it offers a choice. Leave it out to take the model's own default.","type":"string"},"thinking":{"description":"Whether this model reasons before it answers, where that is a choice it offers.","type":"boolean"},"fast":{"description":"Ask for this model's work at a higher rate for a higher price. A request rather than a promise.","type":"boolean"}},"required":["agent","model"]},"fallback":{"description":"The model a new chat opens on for whoever pressed, for when nobody chose one and no model set for fixing pipelines can run. Used while this sandbox can serve its provider; left out, or not servable, the sandbox takes its default provider when connected, else the first one connected.","type":"object","properties":{"agent":{"type":"string","minLength":1,"description":"Which provider."},"model":{"type":"string","minLength":1,"description":"Which of its models. Both or neither, because a model name only means anything to the provider that serves it."},"account":{"description":"Which connected account of that provider pays, by its daemon-minted id. Leave it out to take whichever account can serve with the most room.","type":"string"},"harness":{"description":"Which agentic loop runs it. Leave it out to use the provider's own.","type":"string","enum":["native","claude-code"]},"effort":{"description":"How hard that model should think, where it offers a choice. Leave it out to take the model's own default.","type":"string"},"thinking":{"description":"Whether this model reasons before it answers, where that is a choice it offers.","type":"boolean"},"fast":{"description":"Ask for this model's work at a higher rate for a higher price. A request rather than a promise.","type":"boolean"}},"required":["agent","model"]},"mode":{"description":"What to do about the attempt already made at this run, when there is one. `continue` carries on in that conversation; `start-over` stops it if running, files it away, and opens the next attempt on a clean worktree. Leave it out for the plain press: an attempt that ended is continued, a fresh failure gets attempt 1, and one still in play answers CONFLICT with why.","type":"string","enum":["continue","start-over"]},"force":{"description":"Open the conversation even when every failed job died in its runner's own setup, which is the fleet's fault and nothing an agent on the code can repair. Left out, such a run is refused with that sentence.","type":"boolean"}},"required":["repo","runId"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","description":"The conversation that was opened, already holding the failure. Open it to watch, or attach to its turn."}},"required":["conversationId"],"additionalProperties":false}}}}}}},"/offload/runners/{runner}":{"get":{"operationId":"offload.target","summary":"Whether a runner can take a heavy command","description":"Answers whether the runner a kind of heavy work is sent to is connected and able to run it, before anything is copied to it. When it is not, the command runs in this sandbox and says why.","tags":["Offloaded work"],"parameters":[{"name":"runner","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"runner":{"type":"string","minLength":1},"name":{"type":"string","minLength":1},"ready":{"type":"boolean"},"why":{"type":"string"}},"required":["runner","name","ready"],"additionalProperties":false}}}}}}},"/offload/runs":{"post":{"operationId":"offload.run","summary":"Run a heavy command on a runner","description":"Hands one command to a runner on one of your machines, together with a snapshot of the code as it stands, and streams its output back as it comes. It ends with the exit code, every file the command changed and any report it wrote.","tags":["Offloaded work"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"runId":{"type":"string","pattern":"^[a-z0-9][a-z0-9-]{7,63}$"},"repo":{"type":"string","minLength":1},"ref":{"type":"string","format":"starts_with","pattern":"^refs\\/intentic-offload\\/.*"},"cwd":{"type":"string"},"command":{"type":"string","minLength":1,"maxLength":64000},"env":{"default":{},"type":"object","propertyNames":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"additionalProperties":{"type":"string"}},"exports":{"default":[],"maxItems":8,"type":"array","items":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]*$"}},"label":{"type":"string","minLength":1,"maxLength":80},"timeoutMs":{"type":"integer","exclusiveMinimum":0,"maximum":21600000},"runner":{"type":"string","minLength":1}},"required":["runId","repo","ref","cwd","command","label","runner"]}}}},"responses":{"200":{"description":"OK","content":{"text/event-stream":{"schema":{"oneOf":[{"type":"object","properties":{"event":{"const":"message"},"data":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"status"},"text":{"type":"string"}},"required":["kind","text"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"output"},"stream":{"type":"string","enum":["stdout","stderr"]},"text":{"type":"string"}},"required":["kind","stream","text"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"exit"},"code":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"signal":{"type":"string"},"failure":{"type":"string"},"ran":{"default":true,"type":"boolean"},"patchBase64":{"type":"string"},"files":{"default":{},"type":"object","propertyNames":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"additionalProperties":{"type":"string"}}},"required":["kind","code","ran","files"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"refused"},"why":{"type":"string"}},"required":["kind","why"],"additionalProperties":false}]},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event","data"]},{"type":"object","properties":{"event":{"const":"done"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"error"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]}]}}}}}},"get":{"operationId":"offload.runs","summary":"Recent offloaded commands","description":"The heavy commands this sandbox sent to runners lately, newest first, with where they ran and how they ended.","tags":["Offloaded work"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"runs":{"type":"array","items":{"type":"object","properties":{"runId":{"type":"string"},"runner":{"type":"string"},"name":{"type":"string"},"label":{"type":"string"},"command":{"type":"string"},"startedAt":{"type":"number"},"endedAt":{"type":"number"},"code":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"failure":{"type":"string"}},"required":["runId","runner","name","label","command","startedAt"],"additionalProperties":false}}},"required":["runs"],"additionalProperties":false}}}}}}},"/offload/runs/{runId}/cancel":{"post":{"operationId":"offload.cancel","summary":"Stop an offloaded command","description":"Stops a command running on a runner, with everything it started there.","tags":["Offloaded work"],"parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/offload/kinds":{"get":{"operationId":"offload.kinds","summary":"Kinds of heavy work that can run elsewhere","description":"The kinds this sandbox sorts heavy commands into (tests, typechecks, verify…), each of which can be sent to a runner on one of your machines instead of running here.","tags":["Offloaded work"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"kinds":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"pattern":{"type":"string"}},"required":["id","pattern"],"additionalProperties":false}}},"required":["kinds"],"additionalProperties":false}}}}}}},"/public":{"get":{"operationId":"public.list","summary":"What is published to the internet","description":"Everything currently in the outbox and the address it answers on. There is no call to read a published file back: it is served openly to anyone with the link, which is the entire point of having put it there.","tags":["Outbox"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"description":"Your public address, which every file's own hangs off. Absent on a sandbox with nowhere to publish to.","type":"string"},"files":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"Where it sits inside the outbox."},"size":{"type":"number","description":"Size in bytes."},"modifiedAt":{"type":"number","description":"When it last changed, in milliseconds."},"url":{"description":"Its public address. Absent when this sandbox has no outside address, or when the file is being refused.","type":"string"},"blocked":{"description":"Why a file sitting in the outbox is not being served: a hidden name, a credential-shaped name, contents that look like a token, or sheer size. Only the publisher sees this; a stranger asking for the same file gets the same nothing every other miss gets.","type":"string"}},"required":["path","size","modifiedAt"],"additionalProperties":false},"description":"What the outbox holds."}},"required":["files"],"additionalProperties":false}}}}}}},"/public/publish":{"post":{"operationId":"public.publish","summary":"Put a file on the internet","description":"Copies a workspace file or folder into the outbox, where it is served to anyone with the link and no sign-in. Answers with the address.","tags":["Outbox"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","minLength":1,"description":"What to publish, as a workspace path. It is copied rather than moved, so a repository does not lose its build output because somebody shared it."}},"required":["path"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","description":"Where it landed inside the outbox."},"url":{"description":"Its public address. Absent on a sandbox with nowhere to publish to.","type":"string"}},"required":["path"],"additionalProperties":false}}}}}}},"/public/unpublish":{"post":{"operationId":"public.unpublish","summary":"Take something off the internet","description":"Withdraws one published entry. When the last one goes, the outbox goes with it, so its existing at all always means something is published.","tags":["Outbox"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","minLength":1,"description":"What to withdraw, as a path inside the outbox rather than a workspace path."}},"required":["path"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/share":{"get":{"operationId":"share.list","summary":"Conversations published as pages","description":"Every conversation that has been turned into a read-only page, with its link. There is no call to read one back: the page itself is the read, and it answers to anyone who has the link.","tags":["Sharing"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"shares":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The share's own id, minted fresh each time, so sharing one conversation twice gives two links. Deliberately not the conversation's id, which is memorable by design and would make a page's address guessable."},"conversationId":{"type":"string","description":"Which conversation it was taken from."},"title":{"type":"string","description":"The title on the page, which is the sharer's choice rather than the conversation's own."},"detail":{"type":"string","enum":["messages","everything"],"description":"How much travels: the two speakers' words alone, or the whole record including the agent's work and thinking, which necessarily publishes the code and command output in it."},"sharedAt":{"type":"number","description":"When the snapshot was taken, in milliseconds. A share is frozen, so this dates what a recipient can see rather than when the conversation happened."},"messages":{"type":"number","description":"How many messages are behind the link."},"url":{"description":"The page's address. Absent on a sandbox with nowhere to publish to.","type":"string"}},"required":["id","conversationId","title","detail","sharedAt","messages"],"additionalProperties":false},"description":"Every conversation currently published as a page."}},"required":["shares"],"additionalProperties":false}}}}}},"post":{"operationId":"share.create","summary":"Publish a conversation","description":"Renders a conversation into a page anybody with the link can read, without signing in. Answers with the link, so nothing has to be listed again to find it.","tags":["Sharing"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"conversationId":{"type":"string","minLength":1,"description":"Which conversation to publish."},"title":{"type":"string","minLength":1,"maxLength":80,"description":"The title for the page. The conversation's own name is only what a dialog would open with."},"detail":{"type":"string","enum":["messages","everything"],"description":"How much to publish. Two levels rather than a set of switches, because every extra toggle is another thing to get wrong about a link that cannot be recalled."}},"required":["conversationId","title","detail"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The share's own id, minted fresh each time, so sharing one conversation twice gives two links. Deliberately not the conversation's id, which is memorable by design and would make a page's address guessable."},"conversationId":{"type":"string","description":"Which conversation it was taken from."},"title":{"type":"string","description":"The title on the page, which is the sharer's choice rather than the conversation's own."},"detail":{"type":"string","enum":["messages","everything"],"description":"How much travels: the two speakers' words alone, or the whole record including the agent's work and thinking, which necessarily publishes the code and command output in it."},"sharedAt":{"type":"number","description":"When the snapshot was taken, in milliseconds. A share is frozen, so this dates what a recipient can see rather than when the conversation happened."},"messages":{"type":"number","description":"How many messages are behind the link."},"url":{"description":"The page's address. Absent on a sandbox with nowhere to publish to.","type":"string"}},"required":["id","conversationId","title","detail","sharedAt","messages"],"additionalProperties":false}}}}}}},"/share/update":{"post":{"operationId":"share.update","summary":"Refresh a published page","description":"Re-renders an existing page from the conversation as it stands now. Same link, newer contents.","tags":["Sharing"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"description":"Which share to re-take. Its link stays the same, which matters because it has already been sent."}},"required":["id"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"The share's own id, minted fresh each time, so sharing one conversation twice gives two links. Deliberately not the conversation's id, which is memorable by design and would make a page's address guessable."},"conversationId":{"type":"string","description":"Which conversation it was taken from."},"title":{"type":"string","description":"The title on the page, which is the sharer's choice rather than the conversation's own."},"detail":{"type":"string","enum":["messages","everything"],"description":"How much travels: the two speakers' words alone, or the whole record including the agent's work and thinking, which necessarily publishes the code and command output in it."},"sharedAt":{"type":"number","description":"When the snapshot was taken, in milliseconds. A share is frozen, so this dates what a recipient can see rather than when the conversation happened."},"messages":{"type":"number","description":"How many messages are behind the link."},"url":{"description":"The page's address. Absent on a sandbox with nowhere to publish to.","type":"string"}},"required":["id","conversationId","title","detail","sharedAt","messages"],"additionalProperties":false}}}}}}},"/share/remove":{"post":{"operationId":"share.remove","summary":"Unpublish a conversation","description":"Takes the page down, so the link stops answering.","tags":["Sharing"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"description":"Which share to take down."}},"required":["id"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/approvals":{"get":{"operationId":"approvals.list","summary":"Things waiting for your yes","description":"Everything an agent has prepared and would like to do: posts to publish, actions to carry out. Nothing here has happened yet.","tags":["Approvals"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"approvals":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"post","description":"A post to publish somewhere."},"platform":{"type":"string","minLength":1,"description":"Where it should go. A plain name, so a new site needs no change here; an unknown one simply fails when it tries to post."},"content":{"type":"string","minLength":1,"description":"The post itself."},"title":{"description":"A title, where the site wants one.","type":"string"},"target":{"description":"Where on the site: a community, a channel. Or the address of the thing this replies to, in which case it is a reply, and on some sites the difference between a thread's address and one comment's is the difference between talking to the room and answering the person.","type":"string"},"media":{"description":"Anything to attach, as workspace paths.","type":"array","items":{"type":"string"}},"actsAs":{"description":"Whose name it acts under. Needed for anything that requires being logged in, because an unwatched turn naming nobody is allowed no account at all. Never guessed: one site can be connected five times over, and picking for you means picking wrong in public with no undo.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"scheduledAt":{"description":"When it should happen, in milliseconds. An agent may propose without one and you set it when approving; an approved item with no time goes after a short countdown you can still stop.","type":"number"},"status":{"default":"proposed","description":"Where it is: proposed by the agent, approved by you, being carried out, done, or failed. Rejecting is deleting it; retrying is approving a failed one again.","type":"string","enum":["proposed","approved","running","done","failed"]},"createdAt":{"description":"When it was written, in milliseconds.","type":"number"},"startedAt":{"description":"When it started being carried out, in milliseconds. Needed to tell a run that is under way from one whose turn died mid-flight, which the scheduled time cannot.","type":"number"},"finishedAt":{"description":"When it was done, in milliseconds.","type":"number"},"result":{"description":"What came back, when something did: the post's own address, a confirmation number. The one thing a finished item can offer that reading it cannot.","type":"string"},"error":{"description":"Why it failed, written as a sentence for a person to read rather than as a code.","type":"string"},"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The approval's id."}},"required":["kind","platform","content","status","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"action","description":"Something the agent will do once you say so."},"summary":{"type":"string","minLength":1,"maxLength":200,"description":"What will happen, in one line: the row's headline and the confirm dialog's item."},"details":{"description":"The specifics, as Markdown: everything you would want to see before saying yes.","type":"string"},"instructions":{"type":"string","minLength":1,"description":"What to do once approved, written for the fresh turn that will do it: names, ids and steps, since it has none of this conversation."},"actsAs":{"description":"Whose name it acts under. Needed for anything that requires being logged in, because an unwatched turn naming nobody is allowed no account at all. Never guessed: one site can be connected five times over, and picking for you means picking wrong in public with no undo.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"scheduledAt":{"description":"When it should happen, in milliseconds. An agent may propose without one and you set it when approving; an approved item with no time goes after a short countdown you can still stop.","type":"number"},"status":{"default":"proposed","description":"Where it is: proposed by the agent, approved by you, being carried out, done, or failed. Rejecting is deleting it; retrying is approving a failed one again.","type":"string","enum":["proposed","approved","running","done","failed"]},"createdAt":{"description":"When it was written, in milliseconds.","type":"number"},"startedAt":{"description":"When it started being carried out, in milliseconds. Needed to tell a run that is under way from one whose turn died mid-flight, which the scheduled time cannot.","type":"number"},"finishedAt":{"description":"When it was done, in milliseconds.","type":"number"},"result":{"description":"What came back, when something did: the post's own address, a confirmation number. The one thing a finished item can offer that reading it cannot.","type":"string"},"error":{"description":"Why it failed, written as a sentence for a person to read rather than as a code.","type":"string"},"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The approval's id."}},"required":["kind","summary","instructions","status","id"],"additionalProperties":false}]},"description":"The queue."},"invalid":{"type":"array","items":{"type":"string"},"description":"Files that could not be read at all, or name a kind this daemon does not know. Listed rather than skipped, because an agent writes these files directly and a malformed one would otherwise never run and never say why."}},"required":["approvals","invalid"],"additionalProperties":false}}}}}},"post":{"operationId":"approvals.upsert","summary":"Approve, edit or retry one","description":"All three are the same act with a different field changed, so they share one call. Send the item back as you want it.","tags":["Approvals"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"post","description":"A post to publish somewhere."},"platform":{"type":"string","minLength":1,"description":"Where it should go. A plain name, so a new site needs no change here; an unknown one simply fails when it tries to post."},"content":{"type":"string","minLength":1,"description":"The post itself."},"title":{"description":"A title, where the site wants one.","type":"string"},"target":{"description":"Where on the site: a community, a channel. Or the address of the thing this replies to, in which case it is a reply, and on some sites the difference between a thread's address and one comment's is the difference between talking to the room and answering the person.","type":"string"},"media":{"description":"Anything to attach, as workspace paths.","type":"array","items":{"type":"string"}},"actsAs":{"description":"Whose name it acts under. Needed for anything that requires being logged in, because an unwatched turn naming nobody is allowed no account at all. Never guessed: one site can be connected five times over, and picking for you means picking wrong in public with no undo.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"scheduledAt":{"description":"When it should happen, in milliseconds. An agent may propose without one and you set it when approving; an approved item with no time goes after a short countdown you can still stop.","type":"number"},"status":{"default":"proposed","description":"Where it is: proposed by the agent, approved by you, being carried out, done, or failed. Rejecting is deleting it; retrying is approving a failed one again.","type":"string","enum":["proposed","approved","running","done","failed"]},"createdAt":{"description":"When it was written, in milliseconds.","type":"number"},"startedAt":{"description":"When it started being carried out, in milliseconds. Needed to tell a run that is under way from one whose turn died mid-flight, which the scheduled time cannot.","type":"number"},"finishedAt":{"description":"When it was done, in milliseconds.","type":"number"},"result":{"description":"What came back, when something did: the post's own address, a confirmation number. The one thing a finished item can offer that reading it cannot.","type":"string"},"error":{"description":"Why it failed, written as a sentence for a person to read rather than as a code.","type":"string"},"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The approval's id."}},"required":["kind","platform","content","id"]},{"type":"object","properties":{"kind":{"type":"string","const":"action","description":"Something the agent will do once you say so."},"summary":{"type":"string","minLength":1,"maxLength":200,"description":"What will happen, in one line: the row's headline and the confirm dialog's item."},"details":{"description":"The specifics, as Markdown: everything you would want to see before saying yes.","type":"string"},"instructions":{"type":"string","minLength":1,"description":"What to do once approved, written for the fresh turn that will do it: names, ids and steps, since it has none of this conversation."},"actsAs":{"description":"Whose name it acts under. Needed for anything that requires being logged in, because an unwatched turn naming nobody is allowed no account at all. Never guessed: one site can be connected five times over, and picking for you means picking wrong in public with no undo.","type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$"},"scheduledAt":{"description":"When it should happen, in milliseconds. An agent may propose without one and you set it when approving; an approved item with no time goes after a short countdown you can still stop.","type":"number"},"status":{"default":"proposed","description":"Where it is: proposed by the agent, approved by you, being carried out, done, or failed. Rejecting is deleting it; retrying is approving a failed one again.","type":"string","enum":["proposed","approved","running","done","failed"]},"createdAt":{"description":"When it was written, in milliseconds.","type":"number"},"startedAt":{"description":"When it started being carried out, in milliseconds. Needed to tell a run that is under way from one whose turn died mid-flight, which the scheduled time cannot.","type":"number"},"finishedAt":{"description":"When it was done, in milliseconds.","type":"number"},"result":{"description":"What came back, when something did: the post's own address, a confirmation number. The one thing a finished item can offer that reading it cannot.","type":"string"},"error":{"description":"Why it failed, written as a sentence for a person to read rather than as a code.","type":"string"},"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The approval's id."}},"required":["kind","summary","instructions","id"]}]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/approvals/{id}":{"delete":{"operationId":"approvals.remove","summary":"Reject one","description":"Throws it away undone.","tags":["Approvals"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"Which approval."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/approvals/hooks":{"get":{"operationId":"approvals.hookRequests","summary":"Hooks waiting for your yes","description":"Hook sets a turn found in Claude Code's settings files, in a skill's or subagent's definition, or in a plugin the turn loads that the sandbox does not ship (its hooks and its hooks module, code that runs inside Claude Code), that nobody has approved in that exact form. Until one is approved, turns in this workspace run with every hook switched off; the sandbox's own safeguards are not hooks of this kind and keep working.","tags":["Approvals"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"requests":{"type":"array","items":{"type":"object","properties":{"digest":{"type":"string","pattern":"^[0-9a-f]{64}$","description":"The set's fingerprint: every hook as declared plus the bytes of every file they run. Approving it approves exactly this, and nothing that differs from it by a character."},"seenAt":{"type":"number","description":"When a turn first found this set, in milliseconds."},"conversationId":{"description":"The conversation whose turn found it, when one did.","type":"string"},"hooks":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string","enum":["user","project","plugin"],"description":"Whose configuration declares it: the sandbox's own (~/.claude), the workspace's (.claude/ in the project), or a Claude Code plugin the turn loads."},"plugin":{"description":"The plugin that declares it, by the name the request's plugin list gives it. Present only for a plugin's hook.","type":"string"},"declaredIn":{"description":"The skill, subagent or command whose frontmatter declares it, spelled like a script path. Absent when it comes from the settings.json of its source.","type":"string"},"event":{"type":"string","description":"When it runs, in Claude Code's own words: before a tool, after one, when a prompt is sent, when a session starts."},"matcher":{"description":"Which tools it is limited to, when it is limited at all.","type":"string"},"type":{"type":"string","description":"What kind of hook it is: a shell command, an address it calls, a prompt it asks a model, or a module: a plugin's code that runs inside Claude Code itself."},"run":{"type":"string","description":"Exactly what it runs: the command line, the address, the prompt."}},"required":["source","event","type","run"],"additionalProperties":false},"description":"Every hook in the set. Until it is approved, turns run with all of them off, and so with every other hook the agent would load."},"scripts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"A file one of the hooks runs, spelled the way the hook names it: inside the workspace as $CLAUDE_PROJECT_DIR/…, under the home directory as ~/…, otherwise absolute."},"sha256":{"type":"string","description":"Its contents when the hooks were found. The approval covers these bytes, so editing the file asks again, the same as editing the command would."}},"required":["path","sha256"],"additionalProperties":false},"description":"The files those hooks run by name, and a plugin module's code, which the approval pins byte for byte."},"plugins":{"description":"The plugins whose hooks or modules are in the set, and those Claude Code's settings enable. Absent when none is.","type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"The plugin, as the sandbox lists it: the connection, extension or persona that brought it, or the name settings enable it under."},"from":{"type":"string","enum":["plugin","extension","persona","skills","settings"],"description":"What brought it into the turn: a plugin connection, an extension, a persona's kit, a plugin folder among Claude Code's skills, or Claude Code's own settings enabling it."},"source":{"description":"Which settings enable it or hold it, the sandbox's own or the workspace's. Present only for a plugin settings or a skills folder bring in.","type":"string","enum":["user","project"]},"dir":{"description":"Where its files were read. Absent for one settings enable that this sandbox has not installed.","type":"string"},"module":{"description":"Its hooks module, code that runs inside Claude Code with everything the agent's session holds. Absent when it has none.","type":"object","properties":{"path":{"type":"string","description":"The plugin's hooks module, as its hooks.json names it."},"hooks":{"type":"array","items":{"type":"string"},"description":"The moments it acts on, in Claude Code's own words: a tool call, a prompt being sent, the system prompt being put together. Empty when it could not be read."},"calls":{"type":"array","items":{"type":"string"},"description":"What it reaches for while it runs, as Claude Code names its methods: starting programs, network requests, reading and writing files. Empty when it could not be read, or when it reaches for nothing."},"unreadable":{"description":"Why the sandbox could not tell what the module does. The approval still covers its code byte for byte. Present only when that is the case.","type":"string"}},"required":["path","hooks","calls"],"additionalProperties":false}},"required":["name","from"],"additionalProperties":false}},"marketplaces":{"description":"Plugin marketplaces Claude Code's settings add, which it may install enabled plugins from. Absent when none is.","type":"array","items":{"type":"object","properties":{"source":{"type":"string","enum":["user","project"],"description":"Whose settings add it: the sandbox's own or the workspace's."},"name":{"type":"string","description":"The marketplace's name in those settings."},"location":{"type":"string","description":"Where Claude Code fetches it from, as the settings spell it."}},"required":["source","name","location"],"additionalProperties":false}},"dismissed":{"description":"Kept off on purpose: no longer counted as waiting, and still approvable. Present only when it was dismissed.","type":"boolean"}},"required":["digest","seenAt","hooks","scripts"],"additionalProperties":false},"description":"Hook sets waiting for a yes, newest first, then the dismissed ones."},"ledgerUnreadable":{"description":"The record of what was approved could not be read, so no settings-file hook runs anywhere until a set is approved again. Present only when that is the case.","type":"boolean"}},"required":["requests"],"additionalProperties":false}}}}}}},"/approvals/hooks/{digest}/approve":{"post":{"operationId":"approvals.approveHooks","summary":"Let a hook set run","description":"Approves exactly this set, commands and the bytes of the files they run, plugin modules included, from the next turn on. Any later change to either is a new set and asks again. Owner and maintainers only, and never through a token a program holds.","tags":["Approvals"],"parameters":[{"name":"digest","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{64}$","description":"Which hook set, by its fingerprint."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/approvals/hooks/{digest}/dismiss":{"post":{"operationId":"approvals.dismissHooks","summary":"Keep a hook set off without being asked again","description":"Takes the set off the list. Its hooks stay switched off; a change to them is a new set, which asks again.","tags":["Approvals"],"parameters":[{"name":"digest","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{64}$","description":"Which hook set, by its fingerprint."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/issues":{"get":{"operationId":"issues.list","summary":"Bugs your users have reported","description":"Everything that has crashed or been written in, grouped so a crash that hit a thousand people is one row with a count.","tags":["Issues"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"issues":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["crash","report","detection"]},"title":{"type":"string","minLength":1,"maxLength":300,"description":"The one line this is listed under."},"culprit":{"description":"The frame it came from, when the stack named one.","type":"string","maxLength":300},"automationId":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"Which intake received it."},"origin":{"description":"Which site it came from.","type":"string","maxLength":400},"firstSeen":{"type":"number","description":"When it first happened, in milliseconds."},"lastSeen":{"type":"number","description":"When it last happened, in milliseconds."},"count":{"type":"number","description":"How many times this exact thing has arrived."},"status":{"default":"open","description":"Where it stands with you.","type":"string","enum":["open","investigating","resolved","ignored"]},"statusAt":{"description":"When the status last changed, in milliseconds.","type":"number"},"release":{"description":"The build the latest one came from.","type":"string","maxLength":200},"sample":{"type":"object","properties":{"kind":{"type":"string","enum":["crash","report","detection"],"description":"A crash the SDK caught, something a person wrote in, or a problem the SDK noticed on its own."},"message":{"type":"string","minLength":1,"maxLength":1000,"description":"The error's own message, or the headline of what a person reported."},"stack":{"description":"The stack, verbatim from the browser.","type":"string","maxLength":20000},"url":{"description":"Where it happened: the page's address, or a screen name in an app.","type":"string","maxLength":2000},"release":{"description":"Which build it came from: a commit sha or a tag. With it the agent reads your real source rather than minified frames.","type":"string","maxLength":200},"userAgent":{"description":"What the browser said it was.","type":"string","maxLength":400},"description":{"description":"What the person typed, when a person is the one reporting.","type":"string","maxLength":5000},"reporter":{"description":"Who says they are reporting it. Unverified by construction.","type":"object","properties":{"email":{"description":"An address they typed, to reach them about it. Unverified.","type":"string","maxLength":320},"name":{"description":"A name they typed. Unverified, and never identity.","type":"string","maxLength":200}},"additionalProperties":false},"breadcrumbs":{"description":"What happened just before, oldest first.","maxItems":40,"type":"array","items":{"type":"object","properties":{"at":{"type":"number","description":"When, in milliseconds."},"kind":{"type":"string","maxLength":40,"description":"What sort of thing it was: a console line, a request, a click, a route change."},"message":{"type":"string","maxLength":300,"description":"What it said, already truncated by the SDK."}},"required":["at","kind","message"],"additionalProperties":false}},"context":{"description":"Whatever else the app attached: a route, a version, a locale.","type":"object","propertyNames":{"type":"string","maxLength":60},"additionalProperties":{"type":"string","maxLength":300}},"fingerprint":{"description":"Group by this instead of by the stack, when your app knows better than the stack does.","type":"string","maxLength":200}},"required":["kind","message"],"additionalProperties":false,"description":"The most recent one, in full."},"firedAt":{"description":"What the count stood at the last time this woke an agent.","type":"number"},"runs":{"description":"The turns started for it.","maxItems":20,"type":"array","items":{"type":"object","properties":{"conversationId":{"type":"string","description":"The conversation this run became."},"at":{"type":"number","description":"When it started, in milliseconds."},"atCount":{"type":"number","description":"How many times it had happened when this run started."}},"required":["conversationId","at","atCount"],"additionalProperties":false}},"id":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"The issue's id, which is its fingerprint."}},"required":["kind","title","automationId","firstSeen","lastSeen","count","status","sample","id"],"additionalProperties":false},"description":"The inbox, most recently seen first."},"invalid":{"type":"array","items":{"type":"string"},"description":"Files in the issues directory that could not be read at all."}},"required":["issues","invalid"],"additionalProperties":false}}}}}}},"/issues/{id}/status":{"post":{"operationId":"issues.status","summary":"File one away, or reopen it","description":"Moves one issue between open, resolved and ignored. Resolving does not close anything upstream: it is your own inbox.","tags":["Issues"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"Which issue."}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["open","resolved","ignored"],"description":"Where it now stands with you."}},"required":["status"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/issues/{id}/investigate":{"post":{"operationId":"issues.investigate","summary":"Put an agent on it now","description":"Starts a turn on this issue with the crash, its stack and what led up to it as the brief. Answers straight away and runs detached; the issue goes to 'being looked at'.","tags":["Issues"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"Which issue."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/issues/{id}":{"delete":{"operationId":"issues.remove","summary":"Throw one away","description":"Forgets an issue entirely. It will come back as new if it happens again, which is usually what you want.","tags":["Issues"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"Which issue."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/issues/installs/{automationId}":{"get":{"operationId":"issues.installs","summary":"Which sites have loaded the reporter","description":"The sites whose pages actually loaded this intake's script, and the ones that were turned away. The answer to 'did the snippet land?', which an empty inbox cannot give you.","tags":["Issues"],"parameters":[{"name":"automationId","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":60,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*$","description":"Which intake."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"origins":{"type":"array","items":{"type":"object","properties":{"origin":{"type":"string"},"allowed":{"type":"boolean"},"lastSeenAt":{"type":"number"},"loads":{"type":"number"}},"required":["origin","allowed","lastSeenAt","loads"],"additionalProperties":false}}},"required":["origins"],"additionalProperties":false}}}}}}},"/info":{"get":{"operationId":"system.info","summary":"What this sandbox is","description":"The sandbox's own identity and state: which workspace it holds, which image it runs, what it is called, and the list of calls it actually implements. Start here, because a browser is routinely newer than the sandbox it is talking to and this is how it finds out what is there.","tags":["System"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"description":"What this sandbox is called.","type":"string"},"image":{"description":"The image it is running.","type":"string"},"version":{"description":"The version of that image.","type":"string"},"latest":{"description":"The newest published version on its channel.","type":"string"},"updateAvailable":{"description":"Whether those two differ.","type":"boolean"},"runtimes":{"description":"Which agent runtimes can serve a turn right now, keyed by runtime. Absent until the first check has run, which reads the same as every entry being unknown.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"object","properties":{"state":{"type":"string","enum":["ready","unavailable","unknown"],"description":"Whether this runtime can serve a turn. Unknown is a real answer rather than a soft no: a check that could not run must not grey out a provider you can in fact use."},"detail":{"description":"Why it cannot, and what to do about it. Absent when it can.","type":"string"},"checkedAt":{"type":"number","description":"When it was last checked, in milliseconds."}},"required":["state","checkedAt"],"additionalProperties":false}},"channel":{"description":"Which release channel this sandbox follows.","type":"string"},"previousImage":{"description":"The image the last update replaced, which is what a rollback would return to. Absent means there is nothing to go back to.","type":"string"},"updateNotes":{"description":"What is in the update, in the words of the people it is for, newest first. Absent or empty whenever there is nothing worth saying, which reads on screen exactly as it did before there were notes at all.","type":"array","items":{"type":"string"}},"moreUpdateNotes":{"description":"How many further notes there are beyond the ones sent, for a sandbox left alone a long time. Absent or zero means you have all of them.","type":"number"},"breakingNotes":{"description":"What the update takes away, uncapped, because a warning that fell off a shortened list is a breaking update taken unwarned. Absent for the overwhelming majority, which break nothing.","type":"array","items":{"type":"string"}},"staged":{"description":"An update already downloaded and built on the machine running this container, waiting only for the restart that applies it. That restart is seconds, where an unprepared update is minutes, which is a different decision entirely. Absent when nothing is waiting.","type":"object","properties":{"version":{"description":"What the downloaded build says it is. Absent means ready but unnamed, never that nothing is ready.","type":"string"},"channel":{"type":"string","description":"Which channel it was taken from. Not necessarily the one this sandbox follows: downloading a beta build is not the same as moving onto beta."},"at":{"type":"number","description":"When the download finished, in milliseconds, which answers whether this is still the update being offered."},"plan":{"description":"What the downloaded build's first boot would convert in this sandbox's stored files. Absent when no plan could be had, which says nothing either way.","type":"object","properties":{"plan":{"type":"number","const":1,"description":"The format of this line. A reader refuses any other rather than guessing at its fields."},"version":{"type":"string","description":"The release of the image that planned it; 0.0.0 for a development build."},"engine":{"type":"number","description":"The conversion count builds before the digest compared. Reported, never decided by."},"digest":{"type":"string","description":"What identifies the planning build's conversion set: its episodes resume only under the same one."},"ok":{"type":"boolean","description":"False when a conversion would fail on this sandbox's files, which refuses the update before anything is touched."},"downgrade":{"type":"boolean","description":"A newer release than the planning build ran here: it opens what it cannot read read-only."},"failures":{"type":"array","items":{"type":"object","properties":{"document":{"type":"string","description":"The stored file whose conversion would fail, or the structural step that would."},"detail":{"type":"string","description":"Why, in the conversion's own words."}},"required":["document","detail"],"additionalProperties":false},"description":"Each conversion or step that would fail, and why."},"steps":{"type":"array","items":{"type":"object","properties":{"document":{"type":"string","description":"The stored file, workspace-relative, or `<volume>:<path>` for one on another volume; a structural step's id."},"change":{"type":"string","description":"What is done to it, in one line."},"detail":{"description":"What was particular about this one: a conflict's losing value, a retired entry, a mapped value.","type":"string"}},"required":["document","change"],"additionalProperties":false},"description":"What the first boot changes on disk: documents moved, structural steps run."},"converts":{"description":"What the build's conversions change as its stores read these files, written by each store's next save. Absent from a build before it.","type":"array","items":{"type":"object","properties":{"document":{"type":"string","description":"The stored file, workspace-relative, or `<volume>:<path>` for one on another volume; a structural step's id."},"change":{"type":"string","description":"What is done to it, in one line."},"detail":{"description":"What was particular about this one: a conflict's losing value, a retired entry, a mapped value.","type":"string"}},"required":["document","change"],"additionalProperties":false}},"files":{"type":"array","items":{"type":"string"},"description":"Every file the first boot writes, workspace-relative where it can be."}},"required":["ok"],"additionalProperties":false}},"required":["channel","at"],"additionalProperties":false},"preparing":{"description":"A download of the next update running on the machine right now, and how far it has got. Absent when nothing is downloading, or when the machine stopped saying it is.","type":"object","properties":{"channel":{"type":"string","description":"Which channel it is being taken from."},"startedAt":{"type":"number","description":"When the download began, in milliseconds."},"at":{"type":"number","description":"When the machine last said it is still working on it, in milliseconds."},"phase":{"type":"string","description":"What it is doing: download (pulling the new image), build (building this sandbox's environment on it), or check (checking this sandbox's stored files against it)."},"percent":{"description":"How far through the download it is, from 0 to 100. Absent for a step with no measure of its own.","type":"number","minimum":0,"maximum":100}},"required":["channel","startedAt","at","phase"],"additionalProperties":false},"lastUpdate":{"description":"What the machine running this sandbox last did about its version: an update that took, one it gave up on and why, and until when the previous version stays ready. Absent when that machine has never said.","type":"object","properties":{"result":{"type":"string","enum":["updated","kept","restored","rolled-back"],"description":"What happened. Updated: the new version passed its first health check and runs, with the previous one kept ready until keepUntil. Kept: that probation ended and the new version stays. Restored: the new version never came up, so the previous container was put back at once. Rolled back: the new version came up and then failed its probation (it kept crashing, never became ready, or lost its tunnel), so the host went back to the previous one by itself."},"verb":{"description":"What was asked for: update, rollback, rebuild, dev, reshape, or the probation watch acting on its own.","type":"string"},"at":{"type":"number","description":"When it happened, in milliseconds."},"from":{"description":"The version (or, when it would not say, the image) that ran before.","type":"string"},"to":{"description":"The version (or image) that was moved onto, or that was tried and given up on.","type":"string"},"reason":{"description":"Why the host gave up on the new version, in plain words. Absent when nothing went wrong.","type":"string"},"log":{"description":"Where the host kept the full log of the swap, as a path on the machine that runs the sandbox.","type":"string"},"keepUntil":{"description":"Until when the previous version stays parked and ready, in milliseconds. While it does, going back takes seconds and nothing is downloaded or rebuilt; after it, going back uses the pinned image.","type":"number"}},"required":["result","at"],"additionalProperties":false},"withdrawn":{"description":"Set when the version this sandbox runs was withdrawn after it shipped, which is the moment to go back to the one before it. Absent for every version still standing.","type":"object","properties":{"version":{"type":"string","description":"The withdrawn version, which is the one this sandbox is running."},"reason":{"description":"Why it was withdrawn, as the people who withdrew it put it.","type":"string"}},"required":["version"],"additionalProperties":false},"skippedVersion":{"description":"A release the owner chose to skip. While it is the newest, no update is offered; a newer one is. Absent when nothing is skipped.","type":"string"},"autoUpdate":{"description":"Whether and when this sandbox takes a downloaded update by itself, and what it is waiting on. Absent where it cannot: a hosted sandbox, one built from a checkout, or one older than automatic updates.","type":"object","properties":{"enabled":{"type":"boolean","description":"Whether this sandbox takes downloaded updates by itself. On unless the owner turned it off."},"phase":{"type":"string","description":"Where it stands: idle (nothing downloaded to take, or turned off), waiting (an update is downloaded and waits for the holds to clear), countdown (it restarts at startsAt unless somebody stops it), or updating (the machine is restarting this sandbox onto it now)."},"version":{"description":"The downloaded version it will take. Absent while idle, or when the download did not say.","type":"string"},"holds":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","description":"What it waits on: agents (an agent is mid-turn, or a subagent, workflow or land is running), people (somebody is using the editor), terminal (a terminal was busy in the last few minutes), schedule (an automation or a scheduled message is due shortly), paused (the owner asked it to wait), machine (the machine that runs this sandbox is not connected), retry (the last try did not take), or consent (the release changes something developers build on, so a person takes it). A newer sandbox may name others."},"names":{"description":"Who or what, by name: the agents still working, the people at the editor. Absent when there is nobody to name.","type":"array","items":{"type":"string"}},"until":{"description":"When this lifts by itself, in milliseconds, for a hold with a moment of its own: a pause, a retry, an automation's next run.","type":"number"}},"required":["kind"],"additionalProperties":false},"description":"Everything keeping it waiting right now, the one most likely to last first. Empty unless waiting."},"startsAt":{"description":"During a countdown, when the restart begins, in milliseconds.","type":"number"},"pausedUntil":{"description":"The owner's pause: no automatic update before this moment, in milliseconds. Absent when not paused.","type":"number"},"failure":{"description":"Why the last automatic try did not take, in the machine's own words, while that is still the news.","type":"string"},"lastApplied":{"description":"The last update this sandbox took by itself: when it began and which version it moved onto.","type":"object","properties":{"at":{"type":"number"},"to":{"type":"string"}},"required":["at"],"additionalProperties":false}},"required":["enabled","phase","holds"],"additionalProperties":false}},"additionalProperties":false}}}}}}},"/system/update/skip":{"post":{"operationId":"system.skipUpdate","summary":"Stop offering one release","description":"Stops offering the named release as an update, typically one this sandbox already tried and went back from. A newer release is offered as usual. Null offers the newest release again.","tags":["System"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"version":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}],"description":"The release to stop offering, or null to offer the newest release again. A newer release than the skipped one is always offered."}},"required":["version"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/system/update/auto":{"post":{"operationId":"system.autoUpdate","summary":"Decide how updates are taken","description":"Turns taking a downloaded update by itself on or off, pauses it until a moment, or takes the downloaded update right now. A sandbox taking updates by itself waits for a quiet moment: no agent mid-turn, nobody at the editor, terminals quiet. Answers with where it stands afterwards.","tags":["System"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"description":"Turn taking downloaded updates by itself on or off.","type":"boolean"},"pausedUntil":{"description":"Hold automatic updates until this moment, in milliseconds; null lifts a pause. A moment already past lifts it too.","type":["number","null"]},"applyNow":{"description":"Take the downloaded update now, without waiting for a quiet moment or counting down. Refused when nothing is downloaded.","type":"boolean","const":true}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Whether this sandbox takes downloaded updates by itself. On unless the owner turned it off."},"phase":{"type":"string","description":"Where it stands: idle (nothing downloaded to take, or turned off), waiting (an update is downloaded and waits for the holds to clear), countdown (it restarts at startsAt unless somebody stops it), or updating (the machine is restarting this sandbox onto it now)."},"version":{"description":"The downloaded version it will take. Absent while idle, or when the download did not say.","type":"string"},"holds":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","description":"What it waits on: agents (an agent is mid-turn, or a subagent, workflow or land is running), people (somebody is using the editor), terminal (a terminal was busy in the last few minutes), schedule (an automation or a scheduled message is due shortly), paused (the owner asked it to wait), machine (the machine that runs this sandbox is not connected), retry (the last try did not take), or consent (the release changes something developers build on, so a person takes it). A newer sandbox may name others."},"names":{"description":"Who or what, by name: the agents still working, the people at the editor. Absent when there is nobody to name.","type":"array","items":{"type":"string"}},"until":{"description":"When this lifts by itself, in milliseconds, for a hold with a moment of its own: a pause, a retry, an automation's next run.","type":"number"}},"required":["kind"],"additionalProperties":false},"description":"Everything keeping it waiting right now, the one most likely to last first. Empty unless waiting."},"startsAt":{"description":"During a countdown, when the restart begins, in milliseconds.","type":"number"},"pausedUntil":{"description":"The owner's pause: no automatic update before this moment, in milliseconds. Absent when not paused.","type":"number"},"failure":{"description":"Why the last automatic try did not take, in the machine's own words, while that is still the news.","type":"string"},"lastApplied":{"description":"The last update this sandbox took by itself: when it began and which version it moved onto.","type":"object","properties":{"at":{"type":"number"},"to":{"type":"string"}},"required":["at"],"additionalProperties":false}},"required":["enabled","phase","holds"],"additionalProperties":false}}}}}}},"/system/manifest-problems":{"get":{"operationId":"system.manifestProblems","summary":"Settings files the sandbox could not read","description":"Anything the daemon tripped over in its own configuration on disk: a file it had to fall back from, a key it did not recognise, an entry it skipped. Separate from the identity call because it goes stale for a different reason, namely a file changing.","tags":["System"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The file, as a workspace path, or as its absolute path on the daemon's own history volume for one kept there (the conversation registry). The file is the unit somebody fixes, which is why problems are grouped by it."},"problems":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["unreadable","unknownKey","invalidEntry"],"description":"What to do about it. Unreadable means the whole file is being ignored and everything in it is at its default. An unknown key means only that key is ignored. An invalid entry means one item of a list was skipped and the rest is fine."},"reason":{"description":"Why it could not be read: the file could not be opened (io), it is not JSON, a conversion to this version's shape failed, or its contents are not what this version expects (rejected). A rejected file after a newer version ran is usually that version's, not a broken one.","type":"string","enum":["io","not-json","conversion-failed","rejected"]},"detail":{"type":"string","description":"What exactly was wrong, as one sentence and nothing else. Never the remedy: that is `fix`."},"suggestion":{"description":"The name it was probably meant to be, when one is close enough to guess honestly.","type":"string"},"fix":{"description":"What to do about it, when that is something other than 'correct the file'. Absent whenever the file itself is the thing to edit.","type":"string"}},"required":["kind","detail"],"additionalProperties":false},"description":"Everything currently wrong with it. A file with nothing wrong is absent rather than present and empty."}},"required":["path","problems"],"additionalProperties":false}}}}}}}},"/system/manifest-problems/repair":{"post":{"operationId":"system.repairManifest","summary":"Take a stray setting out of a file","description":"Removes a key the sandbox does not recognise from one of its settings files, or renames it to the one it was probably meant to be, keeping the value. Only the files a person hand-edits can be named, and only a key — never a value — so this can only ever remove something already being ignored. Renaming onto a key the file already has is refused instead of overwriting it.","tags":["System"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"path":{"type":"string","description":"The file to repair, as the workspace path the problem was reported under. Only the handful of manifests a person hand-edits can be named; anything else is refused."},"key":{"type":"string","description":"The stray top-level key, exactly as it was reported. Absent from the file already means there is nothing to do."},"to":{"description":"Rename the key to this instead of removing it, carrying its value across. Absent means remove it. Naming a key that is already in the file is refused rather than silently overwriting what is there.","type":"string"}},"required":["path","key"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/system/session":{"post":{"operationId":"system.session","summary":"Trade a sign-in for a session","description":"Exchanges a verified sign-in, or a session that has not expired yet, for a fresh session the daemon minted. That session is the credential every other call carries, and calling this again with a live one renews it.","tags":["System"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","description":"The credential every other call carries. Present it as a bearer token."},"expiresAt":{"type":"number","description":"When it stops working, in milliseconds, so a caller can renew ahead of it without reading the token."},"email":{"type":"string","description":"Who the sandbox verified you as."}},"required":["token","expiresAt","email"],"additionalProperties":false}}}}}}},"/events":{"get":{"operationId":"system.events","summary":"The live event stream","description":"A stream held open for as long as you want it, carrying heartbeats so a caller notices the sandbox dying at once, batches of file changes so a tree or an editor can refresh itself, and the roster of who else is looking. Give it an id for this connection to appear in that roster; leave it out and you watch without being seen.","tags":["System"],"parameters":[{"name":"clientId","in":"query","schema":{"type":"string"},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"text/event-stream":{"schema":{"oneOf":[{"type":"object","properties":{"event":{"const":"message"},"data":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"hello"},"workspaceId":{"type":"string"},"routes":{"type":"array","items":{"type":"string"}},"shapes":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"build":{"type":"string"},"boot":{"type":"object","properties":{"ready":{"type":"boolean"},"startedAt":{"type":"number"},"steps":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"state":{"type":"string","enum":["pending","running","done","failed"]},"ms":{"type":"number"}},"required":["key","label","state"],"additionalProperties":false}}},"required":["ready","startedAt","steps"],"additionalProperties":false},"projectDir":{"type":"string"},"surface":{"type":"string","enum":["sandbox","folder"]}},"required":["kind","workspaceId"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"heartbeat"},"rev":{"type":"number"}},"required":["kind","rev"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"boot"},"ready":{"type":"boolean"},"startedAt":{"type":"number"},"steps":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"state":{"type":"string","enum":["pending","running","done","failed"]},"ms":{"type":"number"}},"required":["key","label","state"],"additionalProperties":false}}},"required":["kind","ready","startedAt","steps"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"workspaceChanged"},"paths":{"type":"array","items":{"type":"string"}}},"required":["kind","paths"],"additionalProperties":false},{"type":"object","properties":{"from":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The generation this applies on top of. A reader holding any other fetches the tree afresh instead."},"generation":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The generation it leaves the tree at."},"dirs":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"The folder, as a workspace path; empty for the workspace root."},"entries":{"type":"array","items":{"$ref":"#/$defs/__schema0"},"description":"Its entries now, without their contents: a folder whose own entries also changed comes as an item of its own."}},"required":["path","entries"],"additionalProperties":false},"description":"Each folder that changed, parents before the folders inside them."},"barren":{"description":"The barren folders now, present only when that list moved.","type":"array","items":{"type":"string"}},"kind":{"type":"string","const":"treeChanged"}},"required":["from","generation","dirs","kind"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"derivedChanged"},"paths":{"type":"array","items":{"type":"string"}}},"required":["kind","paths"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"reposChanged"},"repos":{"type":"array","items":{"type":"string"}}},"required":["kind","repos"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"refsChanged"},"repos":{"type":"array","items":{"type":"string"}}},"required":["kind","repos"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"runtimeChanged"},"domains":{"type":"array","items":{"type":"string"}}},"required":["kind","domains"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"presence"},"users":{"type":"array","items":{"type":"object","properties":{"clientId":{"type":"string"},"email":{"type":"string"},"name":{"type":"string"},"picture":{"type":"string"},"role":{"type":"string","enum":["guest","viewer","collaborator","writer","maintainer","owner"]},"idle":{"type":"boolean"},"away":{"type":"boolean"},"view":{"type":"string"},"sessionId":{"type":"string"},"path":{"type":"string"}},"required":["clientId","email","role","idle"],"additionalProperties":false}}},"required":["kind","users"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"agents"},"agents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The conversation id, which is how every other call addresses it."},"sessionId":{"description":"The provider session behind the last turn. It is retired whenever the model or account changes.","type":"string"},"title":{"description":"What to call it: the first prompt cut to one line, unless somebody renamed it.","type":"string"},"titleAction":{"description":"One word for the kind of work the naming pass read in it (fix, audit, redesign). Never part of the displayed name: a board tints and glyphs a card by it. Absent for a title nothing named an action for.","type":"string"},"status":{"type":"string","enum":["idle","running","awaiting","stopping","dismissing","stopped","resuming","landing","ready","landed","conflict","error","interrupted"],"description":"What it is doing. Stopping and stopped are the two halves of somebody pressing stop, because a cancel is not instant; dismissing is the same window for a question waved away, which ends the turn too but owes the user nothing; resuming means the sandbox is already putting right whatever killed the turn; landing means its work is being carried into the workspace right now, and nothing may act on its branch until that settles."},"failure":{"description":"Why the last turn failed, in the words it died on. Absent unless it did, and cleared the moment it runs again. Carried here because the word error on its own is not an answer, least of all for a run nobody was watching.","type":"string"},"failureCode":{"description":"Which kind of failure it was, as the turn's own error frame coded it. Absent for a failure nothing could classify, which reads as the plain red line it is.","type":"string"},"limitResetsAt":{"description":"When the spent allowance reopens, in epoch seconds. Absent when the provider publishes no instant.","type":"number"},"limitHeld":{"description":"Whether the refused turn is held whole, so sending again re-runs it instead of appending to it.","type":"boolean"},"limitScheduled":{"description":"Whether the held turn is already booked to go again at the reset, so nobody has to press anything.","type":"boolean"},"limitMoving":{"description":"The account the held turn is being moved to by the owner's policy, while that move is booked.","type":"string"},"provider":{"type":"string","minLength":1,"description":"Which model provider it runs on."},"harness":{"type":"string","enum":["native","claude-code"],"description":"Which agentic loop it runs on."},"runner":{"description":"The runner this conversation runs on. Absent means this sandbox.","type":"string"},"startIn":{"description":"Which folder it opened in, relative to the workspace root. Absent means the root.","type":"string"},"actsAs":{"description":"Which persona its first turn acted as. Absent for an ordinary chat.","type":"string"},"lastActsAs":{"description":"Which persona its last turn acted as: each turn runs as the persona it names, so this is who the conversation speaks as now, and what a view groups it under. Absent for an ordinary chat.","type":"string"},"model":{"description":"What its last turn ran with. Kept per conversation so opening it restores the choices made in it, rather than whatever some other tab last picked.","type":"string"},"effort":{"description":"How hard that turn was told to think.","type":"string"},"thinking":{"description":"Whether that turn showed its reasoning.","type":"boolean"},"fast":{"description":"Whether that turn asked for higher speed. What was asked for, not what was served.","type":"boolean"},"account":{"description":"Which connected account paid for it.","type":"string"},"branch":{"description":"The branch its private copy works on. Absent for a conversation that works directly in the shared tree.","type":"string"},"autoLand":{"description":"This conversation's own answer to whether its work merges automatically. Absent means it follows the sandbox-wide setting, which is the common case.","type":"boolean"},"limitPolicy":{"type":"string","enum":["wait","resend","move"]},"outagePolicy":{"type":"string","enum":["wait","retry"]},"stopPolicy":{"type":"string","enum":["wait","retry"]},"landRequested":{"description":"A collaborator has asked a maintainer to merge this work. Cleared by whichever merge or discard answers it. Absent means nobody is waiting.","type":"object","properties":{"email":{"type":"string","description":"Who asked."},"name":{"description":"Their display name.","type":"string"},"at":{"type":"number","description":"When they asked, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"reactions":{"description":"What people have marked this conversation with, one entry per emoji, in the order the emoji were first used. Absent means nobody has marked it, which is most conversations.","type":"array","items":{"type":"object","properties":{"emoji":{"type":"string","description":"The mark itself, one emoji, carried as the character rather than as a name that would need a table on both sides."},"by":{"minItems":1,"type":"array","items":{"type":"object","properties":{"email":{"type":"string","description":"Who it was, as the sandbox verified them."},"name":{"description":"What to call them, when their sign-in carried a name. Absent leaves the address to stand for them.","type":"string"},"at":{"type":"number","description":"When they marked it, in milliseconds."}},"required":["email","at"],"additionalProperties":false},"description":"Everyone wearing this mark, oldest first, each by name. Carried whole rather than as a count, because a chip reading 3 that cannot say whose is a number nobody can answer. Never empty: the last person taking theirs back takes the whole chip with it."}},"required":["emoji","by"],"additionalProperties":false}},"origin":{"description":"Where the conversation came from when nobody typed it: a chat mention, a visitor's message, a webhook. Absent means a person started it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"startedBy":{"description":"Who asked for the first turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a child another conversation spawned. Absent when nothing was verified (a wake, a loopback caller).","type":"string"},"areas":{"description":"Which named areas of the workspace this conversation was started within, latched from whoever asked for the first turn. Absent means its starter held the whole workspace, which is why a fenced member is not shown it.","type":"array","items":{"type":"string"}},"owner":{"description":"The member answerable for this conversation: set from whoever started it, inherited from the parent by a spawned child, moved by handing it over. Absent means nobody has claimed it yet.","type":"object","properties":{"email":{"type":"string","description":"Who answers for this conversation, as the sandbox verified them."},"name":{"description":"What to call them, when the sign-in that made them its owner carried a name. Absent leaves the address to stand for them.","type":"string"},"since":{"type":"number","description":"When they became its owner, in milliseconds."}},"required":["email","since"],"additionalProperties":false},"forkedFrom":{"description":"The conversation this one was cut from. Recorded once and never cleared: it is the relationship, not a pending state.","type":"object","properties":{"conversationId":{"type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"},"index":{"type":"integer","minimum":0,"maximum":9007199254740991},"files":{"type":"string","enum":["then","now"]}},"required":["conversationId","index","files"],"additionalProperties":false},"base":{"description":"The commit its private copy started from, shortened.","type":"string"},"costUsd":{"description":"What it has cost so far, in dollars. A subagent's spend is its own and is not folded in here.","type":"number"},"inputTokens":{"description":"Uncached input tokens, excluding cache reads and cache writes.","type":"number"},"outputTokens":{"description":"Tokens received.","type":"number"},"contextTokens":{"description":"How much of the window the conversation currently fills.","type":"number"},"contextWindow":{"description":"How large that window is.","type":"number"},"promptCache":{"description":"When this conversation's prompt cache was last kept alive and how long it lasts, which together say when picking the conversation up stops being cheap. Absent when the provider publishes nothing to ground it on.","type":"object","properties":{"at":{"type":"number","description":"When its last request touched the provider's prompt cache, in milliseconds."},"ttlMs":{"type":"number","description":"How long that entry lives from `at`, in milliseconds."},"rollsAt":{"description":"When the date written into the agent's prompt next changes, in milliseconds (midnight where the agent runs). Past it the next turn sends a different prompt, so nothing kept before it is read again.","type":"number"},"keepableUntil":{"description":"Present when this sandbox can keep this cache warm (its provider can replay the last turn's prefix): the furthest instant a hold can reach, in milliseconds, where refreshing would cost more than the cold resume it saves or the date in the prompt changes, whichever comes first.","type":"number"}},"required":["at","ttlMs"],"additionalProperties":false},"keepWarm":{"description":"Whether the sandbox is keeping this conversation's prompt cache warm while it sits idle, how that is going, or why it stopped. Absent when nobody asked for it.","type":"object","properties":{"since":{"type":"number","description":"When keeping it warm started, in milliseconds."},"until":{"type":"number","description":"When it stops by itself, in milliseconds: the time asked for, shortened to what the sandbox can honestly keep, which is never past the point where refreshing costs more than re-reading, nor past the date change that rewrites the prompt."},"refreshes":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Refreshes sent so far."},"readTokens":{"description":"How much the last refresh read back from the provider's cache, in tokens: the proof the cache was still there.","type":"number"},"ended":{"description":"Why keeping it warm stopped before anyone picked the conversation up. Absent while it is still being kept.","type":"object","properties":{"at":{"type":"number","description":"When it stopped, in milliseconds."},"reason":{"type":"string","enum":["elapsed","allowance","changed","cold","failed"],"description":"Why: `elapsed` the time asked for ran out; `allowance` the account reached the reserve kept for real work, or the provider refused a refresh for its limit; `changed` what the next turn would send no longer matches the cache (a part of the prompt, the date in it, or the conversation's session or account); `cold` the cache was gone, expired before a refresh could run or found missing by one; `failed` a refresh failed."},"detail":{"description":"The specifics, when there are any: which parts of the prompt changed, how full the account was, or the failure's own words.","type":"string"}},"required":["at","reason"],"additionalProperties":false}},"required":["since","until","refreshes"],"additionalProperties":false},"activity":{"description":"What it is doing at this moment.","type":"object","properties":{"tool":{"description":"The last tool it reached for.","type":"string"},"target":{"description":"What it reached for that tool with: a file, a command, a URL.","type":"string"},"todo":{"description":"The item on its own list that it is working through.","type":"string"}},"additionalProperties":false},"checklist":{"description":"How far it is through its own checklist. Absent for a conversation that kept no list, which is most short ones.","type":"object","properties":{"done":{"type":"number","description":"Items it has completed."},"total":{"type":"number","description":"Items on the list. Never zero: a conversation that kept no list carries no clause at all."}},"required":["done","total"],"additionalProperties":false},"landedMessageDraft":{"description":"The whole story of this merge's commit message being written: which models were asked, how long each took, what refused and in what words. Forgotten on restart, which is right, because a restart also killed the drafting it describes.","type":"object","properties":{"startedAt":{"type":"number","description":"When the drafting began, in milliseconds."},"steps":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","minLength":1,"description":"Which provider was asked."},"model":{"type":"string","minLength":1,"description":"Which of its models."},"status":{"type":"string","enum":["asking","answered","refused","skipped"],"description":"How this one went. Skipped means it was not asked at all, because it refused a few minutes ago and the walk stepped over it."},"at":{"description":"When it started being asked, in milliseconds. Absent for one that was skipped, which cost no time.","type":"number"},"ms":{"description":"How long it took. Absent while it is still being asked.","type":"number"},"reason":{"description":"Why it refused, in its own words.","type":"string"}},"required":["provider","model","status"],"additionalProperties":false},"description":"Each model that was asked, in the order they were spent, so the list is the timeline. Empty with no outcome means the diff is still being read."},"outcome":{"description":"How it ended. Absent means it is still going.","type":"string","enum":["written","failed"]},"reason":{"description":"The one-line account of a failure, for a screen with one line to spend. The steps carry each model's own words.","type":"string"},"finishedAt":{"description":"When it ended, in milliseconds.","type":"number"}},"required":["startedAt","steps"],"additionalProperties":false},"landedMessage":{"description":"What this conversation's merged work is called, once the drafting above has finished. It arrives on the same push that ends the draft, so the promise and the answer travel together.","type":"object","properties":{"subject":{"type":"string","description":"One line saying what the merged work did, read off the code rather than off the opening request. A conversation that asks for an audit and then spends four turns fixing what it found needs a subject about the fixes."},"note":{"description":"The same change said to somebody who uses the product, for a repository that keeps a changelog. Usually absent, because most changes are not ones a user would notice.","type":"string"},"breaking":{"description":"What this change takes away, for anything already relying on it. Nearly always absent: it is for removals, not for additions.","type":"string"},"testNote":{"description":"Why a test this change weakened is meant to be weaker, in the conversation's own words. Nearly always absent.","type":"string"},"allows":{"description":"Exceptions the conversation declared for this change, each `<check> — <reason>` in its own words. Nearly always absent.","type":"array","items":{"type":"string"}}},"required":["subject"],"additionalProperties":false},"startedAt":{"description":"When the running turn started, in milliseconds. Absent when none is running.","type":"number"},"run":{"description":"The run under way right now: what a stop names, so it cannot cancel a turn that started after it was pressed. Absent when none is running, and for a turn with no run to attach to.","type":"string"},"queue":{"description":"Messages waiting for its next turn, and whether they are held. Absent for a conversation nothing has ever waited for.","type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message's id: what its sender named it, or what the sandbox did."},"text":{"type":"string","description":"The words, as they will go out."},"attachments":{"description":"Files that go with it, as workspace paths.","type":"array","items":{"type":"string"}},"voice":{"type":"string","enum":["person","sandbox","agent"],"description":"Who it is from: a person, the sandbox itself, or another agent."},"queuedAt":{"type":"number","description":"When it joined the queue, in milliseconds."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"The queue's revision when this message was last written. An edit or a removal names it, and is refused if the message has changed since."}},"required":["id","text","voice","queuedAt","revision"],"additionalProperties":false},"description":"What waits, in the order it goes out."},"revision":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Moves with every change to the queue, so of two copies the higher is the newer."},"paused":{"description":"Why nothing goes out by itself: somebody stopped the turn, the turn these messages started was refused before it ran, or they were scheduled for a time or for after another conversation's work lands. Resuming lets them go, and so does sending another message.","type":"string","enum":["stopped","refused","scheduled"]},"until":{"description":"When scheduled messages go out by themselves, in milliseconds. Only on a queue paused as `scheduled`.","type":"number"},"after":{"description":"The conversation whose finished work must land before scheduled messages go out by themselves. Only on a queue paused as `scheduled`, in place of `until`.","type":"string","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$"}},"required":["items","revision"],"additionalProperties":false},"updatedAt":{"type":"number","description":"When it last did something, in milliseconds. Reading it does not count."},"seenAt":{"description":"When somebody last opened it, in milliseconds. Newer activity than this is what makes it unread. Kept by the sandbox rather than by a browser, so clearing site data or picking up a phone does not resurrect every badge.","type":"number"},"unsentAt":{"description":"Since when somebody's composer has held a message for it that they have not sent yet, in milliseconds. While set, the sandbox never archives it on its own for being idle.","type":"number"},"attention":{"type":"object","properties":{"plan":{"type":"boolean","description":"It has proposed a plan and is waiting for a yes."},"question":{"type":"boolean","description":"It has asked you something."},"permission":{"type":"boolean","description":"It wants to use a tool it needs permission for."},"capability":{"type":"boolean","description":"It needs something connected that is not connected yet."},"credential":{"type":"boolean","description":"It is waiting for a named person to release a credential. The one pause that may not be yours to clear, whatever your role."},"conflict":{"type":"boolean","description":"Its work cannot be merged without somebody resolving a clash."},"need":{"description":"It asked a person for something it still needs: a connection, a secret, wider reach, a tool. Absent from a daemon older than needs.","type":"boolean"}},"required":["plan","question","permission","capability","credential","conflict"],"additionalProperties":false,"description":"Which kinds of waiting-for-you it is doing."},"permissionAsk":{"description":"The oldest permission its running turn is waiting on, so it can be answered where the conversation is listed. Absent when none waits, and from a sandbox older than it.","type":"object","properties":{"requestId":{"type":"string","description":"Which request this is: the id an answer to it names."},"ask":{"type":"string","description":"What it asks to do, on one line: the runtime's own sentence, else the tool's short name."}},"required":["requestId","ask"],"additionalProperties":false},"landFailure":{"description":"The last attempt to bring its work into the workspace failed, and why. Cleared by the next land that goes through. Absent when nothing failed, and from a sandbox older than it.","type":"object","properties":{"reason":{"type":"string","description":"Why it failed, in the words it failed with."},"code":{"description":"Which kind of failure it was, when the sandbox could tell: unlinked when its copy lost its link to the workspace. Absent otherwise.","type":"string"},"at":{"type":"number","description":"When it failed, in milliseconds."}},"required":["reason","at"],"additionalProperties":false},"delivery":{"description":"What became of its last land in the folder on the owner's computer, for a project folder attached to this computer's sandbox. Absent for any other land, and from a sandbox older than it.","type":"object","properties":{"state":{"type":"string","enum":["delivered","partial","waiting","failed","too-large"]},"at":{"type":"number","description":"When this state was reached, in milliseconds."},"project":{"type":"string"},"folder":{"description":"The folder on the owner's computer, as its machine reported it.","type":"string"},"applied":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Files written, merged ones included."},"conflicts":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"reason":{"type":"string","enum":["edited","link","outside","missing-git"]}},"required":["path","reason"],"additionalProperties":false}},"point":{"description":"The machine's restore point taken before writing.","type":"string"},"reason":{"description":"Why it failed or waits, in a sentence.","type":"string"}},"required":["state","at","project","applied","conflicts"],"additionalProperties":false},"conflictCauses":{"description":"Why its work will not merge, and so who can clear it: your own uncommitted edits, which only you can commit or stash, against a moved main line or an unmergeable binary, which the conversation can redo on its own copy. Absent unless it is refusing to merge.","type":"array","items":{"type":"string","enum":["workspace","diverged","binary"]}},"unfinished":{"description":"What its last turn left open: steps it never completed. Absent for a turn that finished what it started.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"steps":{"description":"The agent's own checklist where that turn left it. Absent for a conversation that kept no list.","type":"object","properties":{"open":{"type":"number","description":"Items on it that were never completed."},"total":{"type":"number","description":"Items on the whole list."},"next":{"description":"The one it would have done next: what it was working through, or the first still waiting.","type":"string"}},"required":["open","total"],"additionalProperties":false},"check":{"description":"The end-of-turn check that was still failing when the turn ended, by name. No longer written: what a check finds as a turn ends is said back to the model and never leaves its work unfinished.","type":"string"}},"required":["at"],"additionalProperties":false},"proof":{"description":"What its last turn showed of its work: whether a check it ran passed after its last edit, and whether it looked at interface files it changed. Absent until a turn that edited or checked anything has ended.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"verification":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Verified: a check it ran passed after its last edit to code. Failing: the last one it ran failed. Unproven: it changed code and ran nothing that checked it. No-code: it changed nothing a check could speak to."},"check":{"description":"The command that spoke, when one did, so a targeted test is never read as the whole suite.","type":"string"},"unviewed":{"description":"How many rendered files it changed (pages, components, styles) without looking at the result afterwards. Absent when none.","type":"number"}},"required":["at","verification"],"additionalProperties":false},"reach":{"description":"Where its last turn's work went when it went somewhere besides its own branch: files changed live outside it, clones of its own holding work that will not land, pushes it made itself. Absent when everything it did is on its branch.","type":"object","properties":{"at":{"type":"number","description":"When the turn that left this ended, in milliseconds."},"live":{"description":"Files it changed in place where every conversation reads them, outside its own branch: an installed extension's copy, the sandbox's shared state, the owner's own checkout. Live the moment they were written, never reviewed, and an installed extension's next update replaces them.","maxItems":10,"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"What it wrote, as it named it: relative to the workspace for the sandbox's shared state, absolute for the owner's own checkout."},"extension":{"description":"The extension, by its manifest id, when what it changed is the installed copy of one. Absent for anything else.","type":"string"}},"required":["path"],"additionalProperties":false}},"liveMore":{"description":"How many more such files there were beyond the ones listed. Absent when all are listed.","type":"number"},"stranded":{"description":"Repositories it cloned inside its own copy and left work in. A clone of its own never rides its land, so what is in one stays there.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"type":"string","description":"The clone, as a folder relative to its own copy of the workspace."},"uncommitted":{"type":"number","description":"Files changed in it and never committed."},"unpushed":{"type":"number","description":"Commits in it its upstream does not have, or that no remote has when it follows none."}},"required":["dir","uncommitted","unpushed"],"additionalProperties":false}},"strandedMore":{"description":"How many more such clones there were beyond the ones listed. Absent when all are listed.","type":"number"},"published":{"description":"Pushes it made with git that went through: work it sent out of the sandbox itself, never through a land.","maxItems":10,"type":"array","items":{"type":"object","properties":{"dir":{"description":"The folder it pushed from, as the command named it. Absent for its own working folder.","type":"string"},"remote":{"description":"The remote it pushed to, as the command named it. Absent when the command named none.","type":"string"},"branch":{"description":"The branch it pushed to, as the command named it. Absent when the command named none.","type":"string"},"command":{"type":"string","description":"The push itself, trimmed to one line."}},"required":["command"],"additionalProperties":false}},"publishedMore":{"description":"How many more such pushes there were beyond the ones listed. Absent when all are listed.","type":"number"}},"required":["at"],"additionalProperties":false},"turns":{"description":"Turns it has finished.","type":"number"},"toolUses":{"description":"Tools it has used, over its whole life.","type":"number"},"subagents":{"description":"Subagents this one started, in-process and spawned alike. Absent means it never has, which is most conversations. Their spend is their own and is not folded into this conversation's cost.","type":"object","properties":{"running":{"type":"number","description":"Subagents working right now."},"total":{"type":"number","description":"Subagents it has started over its whole life."}},"required":["running","total"],"additionalProperties":false},"diff":{"description":"Everything it has written, measured from where it started. Independent of how much has been merged.","type":"object","properties":{"files":{"type":"number","description":"Files touched."},"insertions":{"type":"number","description":"Lines added."},"deletions":{"type":"number","description":"Lines removed."}},"required":["files","insertions","deletions"],"additionalProperties":false},"landedPresence":{"description":"Present only when some of what it merged has since been thrown away. Absent is the steady state: its presence is the signal, so an ordinary card spends no line on it.","type":"object","properties":{"landed":{"type":"number","description":"Paths this conversation merged in."},"present":{"type":"number","description":"How many of them are still there, either pending or committed."},"removedBy":{"description":"Who took them out, when the sandbox could tell: an agent working in the workspace, or a person throwing the changes away. Absent when it could not tell, and from a sandbox older than it.","oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"agent"},"id":{"type":"string","description":"The conversation that took them out."},"title":{"description":"What that conversation is called.","type":"string"}},"required":["kind","id"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"person"},"email":{"description":"Who it was, as the sandbox verified them. Absent when the request carried no identity.","type":"string"},"name":{"description":"What to call them, when their sign-in carried a name.","type":"string"}},"required":["kind"],"additionalProperties":false}]}},"required":["landed","present"],"additionalProperties":false},"loop":{"description":"The loop driving this conversation, if one is. Absent for an ordinary conversation, which is nearly all of them.","type":"object","properties":{"state":{"type":"string","enum":["running","done","exhausted","stalled","overspent","stopped","error","unpriced"],"description":"How the loop is going."},"iteration":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"Which round it is on."},"maxIterations":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many rounds it will attempt before giving up."},"goal":{"type":"string","description":"What it is looping towards."}},"required":["state","iteration","maxIterations","goal"],"additionalProperties":false},"workflow":{"description":"The workflow run this conversation is a step of. Without it, a four-step run reads as four unrelated conversations that happen to have started together.","type":"object","properties":{"runId":{"type":"string","description":"The run this belongs to, which is how a board groups its steps together."},"name":{"type":"string","description":"The workflow's name."},"step":{"type":"string","description":"Which step this conversation is on now. It moves when steps are chained."},"index":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"This step's place in the workflow, counting from one."},"total":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How many steps the workflow has."}},"required":["runId","name","step","index","total"],"additionalProperties":false},"awaitingWake":{"description":"Whether this conversation runs again by itself with nobody pressing anything: a watch is armed on it, or words the sandbox or another agent sent wait for it. A finished-looking card that is awaiting a wake is not finished yet.","type":"boolean"},"watches":{"description":"Outside conditions this conversation is parked on, each of which will wake it. Absent means none, which is nearly every conversation: an armed watch is why a finished-looking agent starts working by itself, and why a hosted machine will not go idle.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this watch, the same one the agent was given when it armed it."},"note":{"type":"string","description":"The agent's own line on what it is waiting for."},"intervalSeconds":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"How often the check runs."},"deadlineAt":{"type":"number","description":"When it gives up and wakes the conversation anyway, in milliseconds. Every watch has one."}},"required":["id","note","intervalSeconds","deadlineAt"],"additionalProperties":false}},"needs":{"description":"What it is waiting on people for and has not got yet, oldest first. Absent means nothing: an open need is why an idle-looking agent still needs you.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["capability","secret","grant","release","environment"],"description":"What is being asked for: a connection, a secret's value, wider reach, a gated credential, or a tool in the image."},"title":{"type":"string"},"status":{"type":"string","enum":["open","working","met","declined","cancelled"],"description":"Where it stands: open (waiting on a person), working (a person said yes and it is being set up), met, declined, or cancelled (the agent withdrew it, or its conversation went away)."}},"required":["id","kind","title","status"],"additionalProperties":false}},"jobs":{"description":"Commands this conversation left running in the background, and how the most recent ones ended. Absent means none since the daemon started. A job is running exactly while it has no end.","type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The daemon's handle for this job, the one its transcript row names."},"label":{"type":"string","description":"What the job is, in the agent's own words when it gave any, else its command on one line."},"session":{"type":"string","description":"The terminal session its pane runs in, which is what opening it focuses."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When the command exited, in milliseconds. Absent while it runs.","type":"number"},"exitCode":{"description":"The code it exited with. Absent while it runs, or when its exit left none.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"watch":{"description":"The watch its exit wakes this conversation through. Present once the turn that left it running has ended and the conversation is waiting on it.","type":"string"},"handed":{"description":"Left running for the person: the agent kept it for them with the `keep` tool (or it holds a port this conversation already left them), so it outlives the turn, wakes nothing and is theirs to stop.","type":"boolean"},"ports":{"description":"The ports it was listening on when its turn ended.","type":"array","items":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"stoppedBy":{"description":"Who stopped it: the sandbox, when the turn that used it ended without handing it over; a person; or the agent itself. Present from the moment the stop is asked. Absent for a job that exited by itself.","type":"string","enum":["turn","person","agent"]},"inputWait":{"description":"Present while it sits blocked reading its terminal with nothing moving: a prompt nobody in the sandbox will answer, which only stopping it or a person typing into its terminal ends. Absent while it runs on, and once it ends.","type":"object","properties":{"since":{"type":"number","description":"When it went still, in milliseconds."},"program":{"type":"string","description":"The process blocked reading the terminal, as its command line."}},"required":["since","program"],"additionalProperties":false}},"required":["id","label","session","startedAt"],"additionalProperties":false}},"archivedAt":{"description":"When it was put away, in milliseconds. Nothing was lost: its branch, its record and every counter stayed, and bringing it back gives it a fresh working copy. Absent means it is live on the board.","type":"number"}},"required":["id","status","provider","harness","updatedAt","attention"],"additionalProperties":false}},"rev":{"type":"number"}},"required":["kind","agents","rev"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"accountUsage"},"provider":{"type":"string"},"account":{"type":"string"},"usage":{"type":"object","properties":{"windows":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"label":{"type":"string"},"utilization":{"type":"number"},"resetsAt":{"type":"number"},"gates":{"anyOf":[{"type":"string","const":"all"},{"type":"string","const":"none"},{"type":"object","properties":{"models":{"minItems":1,"type":"array","items":{"type":"string","minLength":1}}},"required":["models"],"additionalProperties":false}]}},"required":["kind","utilization","gates"],"additionalProperties":false}},"measuredAt":{"type":"number"},"unread":{"description":"Present while re-reading this account keeps failing: these windows are the last reading that succeeded, and `measuredAt` will not move until a read succeeds again.","type":"object","properties":{"since":{"type":"number","description":"When re-reading this account first failed, in milliseconds. It has failed on every attempt since."},"reason":{"type":"string","description":"Why, in the provider's own words where it gave some (\"Verify your account to continue.\"). Short enough to print; never a pasted response body."}},"required":["since","reason"],"additionalProperties":false}},"required":["windows","measuredAt"],"additionalProperties":false}},"required":["kind","provider","account"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"providerRefusal"},"provider":{"type":"string"},"refusal":{"type":"object","properties":{"at":{"type":"number","description":"When it refused, in milliseconds."},"kind":{"type":"string","enum":["limit","auth","entitlement"],"description":"Three different noes, kept apart because what fixes each is different. A spent allowance is answered by waiting; a refused credential by signing in again; and an entitlement refusal, where somebody has switched this off for your seat, by neither of those. That last one authenticates fine and reports healthy limits the whole time it refuses everything."},"message":{"type":"string","description":"The provider's own words, verbatim. The only part that says which limit or which credential."},"account":{"description":"Which account was serving, where that is known.","type":"string"},"model":{"description":"Which model the refused turn was on, where that is known.","type":"string"},"resetsAt":{"description":"When the provider said to try again, in epoch seconds, for a spent allowance where it named one. Until then the refusal stands whatever a reading says; after it, it is over.","type":"number"}},"required":["at","kind","message"],"additionalProperties":false}},"required":["kind","provider"],"additionalProperties":false}],"$defs":{"__schema0":{"type":"object","properties":{"name":{"type":"string","description":"Just this entry's own name."},"path":{"type":"string","description":"Its full path from the workspace root, which feeds straight back into the file routes."},"type":{"type":"string","enum":["file","dir"],"description":"What it is. For a link, what it points at, so a link to a folder opens like a folder."},"size":{"description":"Size in bytes, for a file.","type":"number"},"ignored":{"description":"Tooling ignores it: installed packages, git internals, anything the ignore rules exclude. Usually drawn greyed out.","type":"boolean"},"link":{"description":"Present when this entry is a link.","type":"object","properties":{"to":{"type":"string","description":"What the link says, verbatim, rather than where it ends up. That is what the person who made it wrote, and what they would edit."},"state":{"description":"Absent for an ordinary link. Broken means there is nothing at the other end, and it is listed anyway because a dangling link is worth seeing. Outside means it leads out of the workspace, so it is shown and refused.","type":"string","enum":["broken","outside"]}},"required":["to"],"additionalProperties":false},"children":{"description":"What is inside a folder. Absent means it was not opened, either because it is ignored or because the walk ran out of budget above it, so ask for it separately. An empty list means it really is empty.","type":"array","items":{"$ref":"#/$defs/__schema0"}}},"required":["name","path","type"],"additionalProperties":false}}},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event","data"]},{"type":"object","properties":{"event":{"const":"done"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"error"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]}]}}}}}}},"/system/presence":{"post":{"operationId":"system.presence","summary":"Say what you are looking at","description":"Reports which view, conversation or file this connection is on, or that it has gone idle. The daemon fans it back out on the event stream so everyone else's roster updates.","tags":["System"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"clientId":{"type":"string","description":"This connection's own id, the same one it gave the event stream."},"idle":{"type":"boolean","description":"Whether the person has stopped doing anything."},"away":{"description":"Whether the window is on screen but nobody has touched it for several minutes. Absent from a page older than it, which reads as not away.","type":"boolean"},"view":{"description":"Which view they are on.","type":"string"},"sessionId":{"description":"Which conversation they have open.","type":"string"},"path":{"description":"Which file they are looking at. Sent whole rather than merged: leaving a field out clears it, so a tab that closes a file drops the path in the same report.","type":"string"}},"required":["clientId","idle"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/system/usage":{"get":{"operationId":"system.usage","summary":"What has been spent","description":"Token and cost totals per account, added up from the record of every finished turn.","tags":["System"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"accounts":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string"},"account":{"type":"string"},"turns":{"type":"number"},"inputTokens":{"type":"number"},"outputTokens":{"type":"number"},"cacheReadTokens":{"type":"number"},"cacheCreationTokens":{"type":"number"},"costUsd":{"type":"number"}},"required":["provider","account","turns","inputTokens","outputTokens","cacheReadTokens","cacheCreationTokens","costUsd"],"additionalProperties":false}}},"required":["accounts"],"additionalProperties":false}}}}}}},"/system/metrics":{"get":{"operationId":"system.metrics","summary":"What the sandbox is using right now","description":"CPU and memory for the sandbox as a whole, for the daemon that runs it, for each kind of process, and for each conversation's own processes. Measured when you ask and never in between, so CPU is the use since the previous reading: the first reading after a quiet spell has memory and no CPU, and the next one a few seconds later has both.","tags":["System"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"at":{"type":"number","description":"When this reading was taken, in milliseconds."},"windowMs":{"description":"How long the CPU figures were measured over, in milliseconds. Absent on a first reading, and then so is every CPU figure.","type":"number"},"sandbox":{"type":"object","properties":{"cpuPercent":{"description":"CPU the whole sandbox used over the window, as a percentage of all it may use (`cores`). Absent on a first reading.","type":"number"},"cores":{"type":"number","description":"How many cores the sandbox may use: its CPU quota, or every core it is allowed to run on when it has none."},"memoryBytes":{"type":"number","description":"Memory counted against the limit, as the daemon admits work by it: resident memory less the file cache the kernel takes back on demand, and once swap is full (`swapFull`) what was pushed to swap as well."},"memoryLimitBytes":{"type":"number","description":"The memory limit work is admitted against: where the kernel starts throttling the container (memory.high), else its hard limit, else the machine's memory."},"swapBytes":{"description":"What was pushed out to swap, counted in `memoryBytes` only once swap is full. Absent where the sandbox cannot see its own memory.","type":"number"},"swapLimitBytes":{"description":"What swap can hold. Absent where nothing bounds it, and from a daemon that predates it.","type":"number"},"swapFull":{"description":"Swap is nearly at `swapLimitBytes`: nothing more can be parked there, so what is swapped counts against the limit too.","type":"boolean"},"memoryRoom":{"description":"What admission reads off the same reading. Absent from a daemon that predates it.","type":"object","properties":{"freeBytes":{"description":"The limit less what is used and what work admitted in the last minute and a half still holds. Absent where nothing measures it.","type":"number"},"reservedBytes":{"type":"number","description":"What work admitted in the last minute and a half holds before it shows in `memoryBytes`."},"personNeedBytes":{"type":"number","description":"What a person's turn needs free to start without a warning: below it, the sandbox is short."},"stallPercent":{"type":"number","description":"Percent of the last ten seconds in which everything in the sandbox waited on memory (pressure `full`)."},"stallLimitPercent":{"type":"number","description":"The stall at or past which the sandbox counts as short of memory, whatever `freeBytes` says."},"stallSustainedPercent":{"description":"Percent of the last minute in which everything in the sandbox waited on memory. Absent from a daemon that predates it.","type":"number"},"stallSustainedLimitPercent":{"description":"The minute's stall at or past which the sandbox counts as short of memory. Absent from a daemon that predates it.","type":"number"}},"required":["reservedBytes","personNeedBytes","stallPercent","stallLimitPercent"],"additionalProperties":false},"diskBytes":{"description":"Space used on the volume the workspace lives on. Absent when the volume would not say.","type":"number"},"diskTotalBytes":{"description":"That volume's size. Absent when the volume would not say.","type":"number"},"loadAverage":{"type":"array","prefixItems":[{"type":"number"},{"type":"number"},{"type":"number"}],"items":false,"minItems":3,"maxItems":3,"description":"The load average over 1, 5 and 15 minutes. It is the machine's, so other sandboxes on it count too."},"machineCores":{"description":"Cores the machine's load average reads against: every core the sandbox may be scheduled on, before any quota. Absent from a daemon that predates it.","type":"number"},"processes":{"type":"number","description":"How many processes are running in the sandbox."},"pressure":{"description":"How much work waited on CPU, memory or disk lately. Absent where the kernel does not report it.","type":"object","properties":{"cpu":{"type":"number","description":"Percent of the last ten seconds in which something was waiting for a CPU."},"memory":{"type":"number","description":"Percent of the last ten seconds in which something was waiting on memory: reclaim, swap-in, or a refault."},"io":{"type":"number","description":"Percent of the last ten seconds in which something was waiting on disk."}},"required":["cpu","memory","io"],"additionalProperties":false}},"required":["cores","memoryBytes","memoryLimitBytes","loadAverage","processes"],"additionalProperties":false,"description":"The sandbox as a whole."},"daemon":{"type":"object","properties":{"rssBytes":{"type":"number","description":"The daemon's own resident memory."},"heapUsedBytes":{"type":"number","description":"Of that, JavaScript objects in use."},"cpuPercent":{"description":"CPU the daemon itself used over the window, as a percentage of one core. Absent on a first reading.","type":"number"},"eventLoopPercent":{"description":"How much of the window the daemon spent busy rather than waiting. Near 100, every request queues behind whatever it is doing. Absent on a first reading.","type":"number"}},"required":["rssBytes","heapUsedBytes"],"additionalProperties":false,"description":"The daemon that runs it, which none of the other figures include."},"sessions":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"object","properties":{"processes":{"type":"number","description":"How many processes."},"rssBytes":{"type":"number","description":"Their resident memory added up, in bytes. Memory two processes share is counted in each, so this can exceed what they cost together."},"cpuPercent":{"description":"CPU its processes used over the window, as a percentage of one core, so a conversation busy on four cores reads 400. Counts commands that finished inside the window too. Absent on a first reading, which has nothing earlier to measure from.","type":"number"}},"required":["processes","rssBytes"],"additionalProperties":false},"description":"What each conversation's processes use, by conversation id: the agent's own process and everything it started. Only conversations with processes running, and only those the caller may see."},"roles":{"type":"object","propertyNames":{"type":"string","enum":["languageServer","searchEngine","agentRuntime","browser","git","translator","extension","terminal","toolchain","localModel","container","other"]},"additionalProperties":{"type":"object","properties":{"processes":{"type":"number","description":"How many processes."},"rssBytes":{"type":"number","description":"Their resident memory added up, in bytes. Memory two processes share is counted in each, so this can exceed what they cost together."}},"required":["processes","rssBytes"],"additionalProperties":false},"description":"Every process in the sandbox but the daemon, by what kind of work it is. A kind with nothing running is absent."}},"required":["at","sandbox","daemon","sessions","roles"],"additionalProperties":false}}}}}}},"/system/storage":{"get":{"operationId":"system.storage","summary":"What is filling the disk","description":"The last measurement of the sandbox's disk, by what the space is for: conversations, checkouts, restore points, caches, logs, the trash and the rest, each with its biggest parts and whether it can be cleaned from here. Reading it measures nothing; ask for a scan to measure again.","tags":["System"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"scan":{"description":"The last scan that finished. Absent until one has, and again after the daemon restarts.","type":"object","properties":{"startedAt":{"type":"number","description":"When the scan began, in milliseconds."},"finishedAt":{"type":"number","description":"When it ended, in milliseconds: the moment these sizes describe."},"outcome":{"type":"string","enum":["complete","partial"],"description":"`partial` when the scan hit its time limit first, so every size is at least what it says rather than exactly it."},"disk":{"description":"The volume as a whole. Absent when the volume would not say.","type":"object","properties":{"usedBytes":{"type":"number","description":"Space used on the volume the workspace lives on."},"totalBytes":{"type":"number","description":"That volume's size."}},"required":["usedBytes","totalBytes"],"additionalProperties":false},"categories":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","enum":["workspace","conversations","checkouts","restorePoints","repositories","engines","indexes","extensions","docker","state","other","trash","backups","exports","artifacts","browserCaptures","browserProfiles","modelWeights","logs","scratch","packageStores","buildCaches"]},"cleanability":{"type":"string","enum":["none","safe","confirm"],"description":"Whether this category can be cleaned from here, and whether cleaning it asks first."},"bytes":{"type":"number","description":"Its size in bytes."},"files":{"type":"number","description":"How many files it holds."},"cleanableBytes":{"description":"What cleaning it would free right now: only what is old enough and not in use. Absent where nothing here may be cleaned, and for a package store, whose own tool decides what no project needs.","type":"number"},"items":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"Where it is, as an absolute path inside the sandbox."},"bytes":{"type":"number","description":"Its size in bytes."}},"required":["path","bytes"],"additionalProperties":false},"description":"Its biggest parts, largest first, at most eight."}},"required":["id","cleanability","bytes","files","items"],"additionalProperties":false},"description":"Every category that holds anything, largest first."},"unreadable":{"type":"number","description":"Files and folders the scan could not read, and so did not count."}},"required":["startedAt","finishedAt","outcome","categories","unreadable"],"additionalProperties":false},"scanning":{"type":"boolean","description":"Whether a scan is running now."}},"required":["scanning"],"additionalProperties":false}}}}}}},"/system/storage/scan":{"post":{"operationId":"system.scanStorage","summary":"Measure what is filling the disk","description":"Walks the sandbox's volumes and answers with the new measurement once it is done. A scan already running is joined rather than doubled. It stops at a time limit and says so, since a size it could not finish is still worth reading. A cancelled scan answers with the previous measurement.","tags":["System"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"scan":{"description":"The last scan that finished. Absent until one has, and again after the daemon restarts.","type":"object","properties":{"startedAt":{"type":"number","description":"When the scan began, in milliseconds."},"finishedAt":{"type":"number","description":"When it ended, in milliseconds: the moment these sizes describe."},"outcome":{"type":"string","enum":["complete","partial"],"description":"`partial` when the scan hit its time limit first, so every size is at least what it says rather than exactly it."},"disk":{"description":"The volume as a whole. Absent when the volume would not say.","type":"object","properties":{"usedBytes":{"type":"number","description":"Space used on the volume the workspace lives on."},"totalBytes":{"type":"number","description":"That volume's size."}},"required":["usedBytes","totalBytes"],"additionalProperties":false},"categories":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","enum":["workspace","conversations","checkouts","restorePoints","repositories","engines","indexes","extensions","docker","state","other","trash","backups","exports","artifacts","browserCaptures","browserProfiles","modelWeights","logs","scratch","packageStores","buildCaches"]},"cleanability":{"type":"string","enum":["none","safe","confirm"],"description":"Whether this category can be cleaned from here, and whether cleaning it asks first."},"bytes":{"type":"number","description":"Its size in bytes."},"files":{"type":"number","description":"How many files it holds."},"cleanableBytes":{"description":"What cleaning it would free right now: only what is old enough and not in use. Absent where nothing here may be cleaned, and for a package store, whose own tool decides what no project needs.","type":"number"},"items":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string","description":"Where it is, as an absolute path inside the sandbox."},"bytes":{"type":"number","description":"Its size in bytes."}},"required":["path","bytes"],"additionalProperties":false},"description":"Its biggest parts, largest first, at most eight."}},"required":["id","cleanability","bytes","files","items"],"additionalProperties":false},"description":"Every category that holds anything, largest first."},"unreadable":{"type":"number","description":"Files and folders the scan could not read, and so did not count."}},"required":["startedAt","finishedAt","outcome","categories","unreadable"],"additionalProperties":false},"scanning":{"type":"boolean","description":"Whether a scan is running now."}},"required":["scanning"],"additionalProperties":false}}}}}},"delete":{"operationId":"system.cancelStorageScan","summary":"Stop measuring the disk","description":"Stops a running scan. Whoever was waiting on it gets the previous measurement back; nothing is lost but the time.","tags":["System"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/system/storage/clean":{"post":{"operationId":"system.cleanStorage","summary":"Free the space one category holds","description":"Removes what one cleanable category holds, and says how much space that gave back. Only what is old enough and not in use goes: today's logs, a browser that is open, the weights a running model reads and a pack still being written all stay. Nothing outside the sandbox's own volumes, and nothing a category may not hold, is ever removed. Categories that cannot be cleaned are refused.","tags":["System"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"string","enum":["workspace","conversations","checkouts","restorePoints","repositories","engines","indexes","extensions","docker","state","other","trash","backups","exports","artifacts","browserCaptures","browserProfiles","modelWeights","logs","scratch","packageStores","buildCaches"],"description":"The category to clean; one whose cleanability is `none` is refused."}},"required":["category"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"string","enum":["workspace","conversations","checkouts","restorePoints","repositories","engines","indexes","extensions","docker","state","other","trash","backups","exports","artifacts","browserCaptures","browserProfiles","modelWeights","logs","scratch","packageStores","buildCaches"]},"freedBytes":{"type":"number","description":"Space the removals gave back, in bytes. A file that is still linked elsewhere frees nothing and is not counted."},"removed":{"type":"number","description":"How many items were removed."},"kept":{"type":"number","description":"How many were left in place: changed too recently, in use by a running program, or no longer this category's."},"failed":{"type":"number","description":"How many removals the filesystem refused."}},"required":["category","freedBytes","removed","kept","failed"],"additionalProperties":false}}}}}}},"/system/terminals":{"get":{"operationId":"system.terminals","summary":"Open terminals","description":"The terminal sessions this sandbox is holding, which is what a terminal panel rebuilds its tabs from after a reload. The live typing and output run over a separate socket; this is the list.","tags":["System"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"sessions":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Its id, and what the close route takes."},"label":{"description":"What to call it on screen.","type":"string"},"kind":{"type":"string","enum":["shell","panel","agent","job","process"],"description":"What sort of thing it is: a terminal somebody opened, a repository's dev server, where an agent's commands run, a job the sandbox started, or a background process that is watched rather than typed into."},"running":{"type":"boolean","description":"Whether it is alive. A finished one-shot job leaves a dead shell behind, which reads as false and is how it gets swept up."},"activityAt":{"type":"number","description":"When it last produced output, in milliseconds. Zero means it did not say, which is unknown rather than 1970."},"exitCode":{"description":"How the last thing in it ended. Absent while that pane is still alive.","type":"number"},"command":{"description":"What is running in it right now. Absent when it is sitting at a prompt. Not a second spelling of whether it is alive: this says whether anything is happening, which is what a close button should ask about before it ends something.","type":"string"},"extensionId":{"description":"Which extension declared this process, when one did.","type":"string"},"processName":{"description":"Which of that extension's processes it is, which together with the id above addresses its start and stop routes.","type":"string"},"help":{"description":"The agent has stopped at something only a person can clear, and is waiting at this terminal. Present only while it is waiting.","type":"object","properties":{"requestId":{"type":"string","description":"What to send back when you answer, through the agent reply route."},"message":{"type":"string","description":"What the agent needs, in its own words."},"requestedAt":{"type":"number","description":"When it asked, in milliseconds."}},"required":["requestId","message","requestedAt"],"additionalProperties":false}},"required":["name","kind","running","activityAt"],"additionalProperties":false},"description":"Every live surface the sandbox is holding, in one list, because the question they all answer is the same one."}},"required":["sessions"],"additionalProperties":false}}}}}}},"/system/terminals/{name}":{"delete":{"operationId":"system.killTerminal","summary":"Close a terminal","description":"Destroys one terminal session and whatever was running inside it.","tags":["System"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","description":"Which terminal."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/system/terminals/{name}/scrollback":{"get":{"operationId":"system.terminalScrollback","summary":"A terminal's history as plain text","description":"What has scrolled past in one terminal, as text you can select and copy. The live view is a picture of a screen on the far side of a socket, with nothing in the page to select, so scrolling back and copying is this call rather than a gesture.","tags":["System"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","description":"Which terminal."}},{"name":"lines","in":"query","schema":{"default":20000,"description":"How far back to ask for. Clamped to the history that actually exists.","type":"number","minimum":1,"maximum":100000},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Which terminal this is from."},"text":{"type":"string","description":"The history, oldest line first, with wrapped lines rejoined so a copied address or path comes back whole."},"lines":{"type":"number","description":"How many lines you got."},"truncated":{"type":"boolean","description":"It stopped because you asked for that many, not because the history ran out."}},"required":["name","text","lines","truncated"],"additionalProperties":false}}}}}}},"/system/browsers":{"get":{"operationId":"system.browsers","summary":"Browsers the agent has open","description":"Every browser a conversation currently has running and the pages inside each one. The picture of what they are showing comes over a separate socket; this is the roster.","tags":["System"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"sessions":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Its id, and what the close route takes."},"label":{"type":"string","description":"What to call it on screen: the open page's title, or its site, or which browser this is."},"server":{"type":"string","description":"Which browser drives it: the credential-free one, or a signed-in account's. The difference between a throwaway page and one logged in as you, which is worth saying out loud."},"running":{"type":"boolean","description":"Whether it is still open. A closed one is listed for a while with the pages it had, as the record of where the agent went."},"activityAt":{"type":"number","description":"When it last did anything, in milliseconds."},"finishedAt":{"description":"When it closed, in milliseconds. Absent while it is open.","type":"number"},"help":{"description":"The agent has hit something only a person can clear: a captcha, a password it does not hold, a check on your phone. Present only while it is waiting.","type":"object","properties":{"requestId":{"type":"string","description":"What to send back when you answer, through the agent reply route."},"message":{"type":"string","description":"What the agent needs, in its own words."},"requestedAt":{"type":"number","description":"When it asked, in milliseconds."}},"required":["requestId","message","requestedAt"],"additionalProperties":false},"dialog":{"description":"A dialog a page has open and is waiting on. You or the agent answers it; present only while it is open.","type":"object","properties":{"pageId":{"type":"string","description":"Which page opened it."},"kind":{"type":"string","enum":["alert","confirm","prompt","beforeunload"],"description":"What it asks: an alert wants dismissing, a confirm a yes or no, a prompt a text."},"message":{"type":"string","description":"What the page says."},"defaultValue":{"description":"A prompt's prefilled answer.","type":"string"}},"required":["pageId","kind","message"],"additionalProperties":false},"pages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Stable for the life of the page, which is what lets a tab survive a refresh of this list. Its address changes as the agent navigates and its position changes when a sibling closes."},"title":{"description":"The page's title. Absent mid-navigation, which is exactly when a tab still has to be drawn.","type":"string"},"url":{"type":"string","description":"Where it is."},"active":{"type":"boolean","description":"The one the agent last touched, or for a finished session, the one it ended on. Exactly one page has this."}},"required":["id","url","active"],"additionalProperties":false},"description":"Every page it has open. A browser holds several at once, which is the reason it is listed apart from the terminals."}},"required":["name","label","server","running","activityAt","pages"],"additionalProperties":false},"description":"Every browser the agents have running, open or recently closed."}},"required":["sessions"],"additionalProperties":false}}}}}}},"/system/browsers/{name}":{"delete":{"operationId":"system.closeBrowser","summary":"Shut a browser down","description":"Closes one of the agent's browsers. Its next attempt to use that browser then fails as though it had crashed, which is the honest account of somebody pulling the plug.","tags":["System"],"parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","description":"Which browser."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/system/desktop":{"get":{"operationId":"system.desktop","summary":"The sandbox's own desktop","description":"Whether the sandbox's desktop is up, which display it is, and how many windows are open on it. The live picture of it comes over a separate socket; this says whether there is anything to see.","tags":["System"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"running":{"type":"boolean","description":"Whether the desktop is up. It starts the first time anyone looks at it, you or an agent, and stays up until the sandbox restarts."},"display":{"description":"The X display it is, present while it runs: a program started with DISPLAY set to this opens its window there.","type":"string"},"windows":{"description":"How many windows are open on it. None is an empty desktop. Absent while it is not running, or when it has no window manager to ask.","type":"integer","minimum":0,"maximum":9007199254740991}},"required":["running"],"additionalProperties":false}}}}}}},"/system/subagents":{"get":{"operationId":"system.subagents","summary":"Subagents the agents have started","description":"Every subagent that conversations the caller can see have delegated work to, whichever tool started it, with what each one is doing: all that are still working, and the most recent that have settled.","tags":["System"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"sessions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The id of the tool call that started it (an SDK child) or the child's own conversation id (a spawned one); either way both sides already hold it, so a card links to its subagent with the id it has and the subagent points back the same way."},"kind":{"type":"string","enum":["subagent","spawned"],"description":"How it was started: in-process by the runtime's own Agent/Task tool, or spawned by the daemon as a conversation of its own, on any provider. It changes where its record is read from and what else can be done with it, never what it is."},"conversationId":{"type":"string","description":"The conversation whose turn started it, and the way back to the chat it belongs to."},"agentType":{"description":"What kind of subagent it is.","type":"string"},"description":{"description":"What it was asked to do, in one line.","type":"string"},"model":{"description":"Which model it runs on: the exact id its provider served once its own record says so, where the call that started it named only an alias or nothing.","type":"string"},"effort":{"description":"How hard it was told to think: its own definition's tier, else the one it inherited from its parent's turn.","type":"string"},"provider":{"description":"Which provider serves it, for a subagent spawned across providers.","type":"string"},"spawnDepth":{"description":"How deep in the chain it sits, where one means the turn itself started it. A subagent can start subagents, and a flat list that could not say so would read as though the turn started all of them.","type":"number"},"background":{"description":"The parent carried on working instead of waiting for it. Such a subagent is otherwise invisible until its result lands, sometimes minutes later.","type":"boolean"},"status":{"type":"string","enum":["pending","running","blocked","completed","failed","killed","paused"],"description":"How it is going. Blocked means it needs an answer, which a parent and an operator act on differently from it simply working."},"startedAt":{"type":"number","description":"When it started, in milliseconds."},"endedAt":{"description":"When it finished, in milliseconds. Absent while it works.","type":"number"},"activityAt":{"type":"number","description":"When it last did anything, in milliseconds."},"tokens":{"description":"What it has spent. Its own, so a parent's cost and the sum of its subagents' are two different true numbers.","type":"number"},"toolUses":{"description":"How many tools it has used.","type":"number"},"lastTool":{"description":"The last one it reached for.","type":"string"},"summary":{"description":"Its report: what it concluded, without opening its record. The question a finished subagent gets read for.","type":"string"},"error":{"description":"Why it failed, when it did.","type":"string"},"verification":{"description":"Whether anything proved the work its report describes.","type":"object","properties":{"state":{"type":"string","enum":["verified","unproven","failing","no-code"],"description":"Whether anything proved its work: a check passed after its last edit, it changed code and nothing checked it, a check ran and failed, or it changed no code at all."},"paths":{"description":"The code files it changed, most recent last. The first few; the record holds the rest.","type":"array","items":{"type":"string"}},"check":{"description":"The command that spoke: the one that cleared it, or the one that failed. Named rather than summarised, so a targeted test is not read as the whole suite.","type":"string"}},"required":["state"],"additionalProperties":false}},"required":["id","kind","conversationId","status","startedAt","activityAt"],"additionalProperties":false},"description":"The subagents conversations the caller can see have started, in-process and spawned alike: every one still working, and the most recent that have settled. Working ones first, then the most recently active."}},"required":["sessions"],"additionalProperties":false}}}}}}},"/system/devices":{"get":{"operationId":"system.devices","summary":"The machines you have connected","description":"Every computer this sandbox can see, whether it reached it through desktop sync or through a connected device, in one row per machine: what it says about itself, which sandboxes it holds, and what stopped it answering when nothing came back.","tags":["System"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"devices":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"},"sync":{"type":"object","properties":{"machine":{"type":"string"},"mode":{"type":"string","enum":["sync","mirror"]},"seenAt":{"type":"number"},"machineId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$"},"environment":{"type":"string"}},"required":["machine","mode"],"additionalProperties":false},"hostId":{"type":"string"},"card":{"type":"string"},"machineId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$"},"online":{"type":"boolean"},"platform":{"type":"string"},"facts":{"type":"object","properties":{"machineId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$"},"os":{"type":"string"},"arch":{"type":"string"},"shell":{"type":"string"},"home":{"type":"string"},"roots":{"type":"array","items":{"type":"string"}},"engine":{"type":"object","properties":{"memoryBytes":{"type":"number"},"cpus":{"type":"number"}},"required":["memoryBytes","cpus"],"additionalProperties":false},"hostname":{"type":"string"},"wsl":{"type":"object","properties":{"distro":{"type":"string"}},"required":["distro"],"additionalProperties":false},"wslDistros":{"type":"array","items":{"type":"string"}},"links":{"type":"object","properties":{"total":{"type":"number"},"unreachable":{"type":"number"},"unreachableSince":{"type":"number"}},"required":["total","unreachable"],"additionalProperties":false},"features":{"type":"array","items":{"type":"string"}},"icOutOfDate":{"type":"string"},"upkeep":{"type":"object","properties":{"at":{"type":"number"},"found":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"}},"fixed":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"}},"skipped":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"what":{"type":"string"},"why":{"type":"string"}},"required":["kind","what","why"],"additionalProperties":false}}},"required":["at","found","fixed","skipped"],"additionalProperties":false}},"required":["os","arch","shell","home","roots"],"additionalProperties":false},"agentVersion":{"type":"string"},"lastSeen":{"type":"number"},"report":{"type":"object","properties":{"machineId":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$"},"hostname":{"type":"string"},"os":{"type":"string"},"wsl":{"type":"object","properties":{"distro":{"type":"string"}},"required":["distro"],"additionalProperties":false},"pairings":{"type":"array","items":{"type":"object","properties":{"sandboxId":{"type":"string"},"mode":{"type":"string","enum":["sync","mirror"]},"localDir":{"type":"string"},"remoteDir":{"type":"string"},"projectsHost":{"type":"boolean"},"deliver":{"type":"string","enum":["auto","off"]},"mirroring":{"type":"string","enum":["on","off"]},"mutagenStatus":{"type":"string"},"conflicts":{"type":"integer","minimum":0,"maximum":9007199254740991},"conflictedPaths":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"local":{"type":"string","enum":["created","modified","deleted","untracked"]},"sandbox":{"type":"string","enum":["created","modified","deleted","untracked"]},"nature":{"type":"string","enum":["derived-leftover","both-edited"]}},"required":["path"],"additionalProperties":false}},"paused":{"type":"boolean"},"backupStatus":{"type":"string"}},"required":["sandboxId","mode"],"additionalProperties":false}},"ports":{"type":"array","items":{"type":"object","properties":{"port":{"type":"integer","minimum":1,"maximum":65535},"host":{"type":"string","enum":["127.0.0.1","::1"]},"sandboxId":{"type":"string"},"state":{"type":"string","enum":["mirrored","held-by-sandbox","busy","ignored"]},"heldBy":{"type":"string"},"command":{"type":"string"}},"required":["port","host","sandboxId","state"],"additionalProperties":false}},"agent":{"type":"object","properties":{"running":{"type":"boolean"},"pid":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"installed":{"type":"string"},"build":{"type":"string"},"lastTickAt":{"type":"number"}},"required":["running"],"additionalProperties":false},"capturedAt":{"type":"number"}},"required":["hostname","os","pairings","ports","agent","capturedAt"],"additionalProperties":false},"sandboxes":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"container":{"type":"string"},"name":{"type":"string"},"running":{"type":"boolean"},"image":{"type":"string"},"tunnelRunning":{"type":"boolean"},"resources":{"type":"object","properties":{"memoryBytes":{"type":"number"},"cpus":{"type":"number"},"privileged":{"type":"boolean"},"gpu":{"type":"boolean"},"hostRuntime":{"type":"array","items":{"type":"string"}},"overlayRuntime":{"type":"array","items":{"type":"string"}},"shape":{"type":"object","properties":{"memoryGib":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"cpus":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"privileged":{"type":"boolean"},"gpu":{"type":"boolean"}},"required":["memoryGib","cpus","privileged","gpu"],"additionalProperties":false},"desired":{"type":"object","properties":{"memoryGib":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"cpus":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"privileged":{"type":"boolean"},"gpu":{"type":"boolean"}},"required":["memoryGib","cpus","privileged","gpu"],"additionalProperties":false},"saved":{"type":"object","properties":{"memoryGib":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"cpus":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"privileged":{"type":"boolean"},"gpu":{"type":"boolean"}},"additionalProperties":false}},"required":["privileged","gpu","hostRuntime","overlayRuntime"],"additionalProperties":false},"staged":{"type":"object","properties":{"image":{"type":"string"},"version":{"type":"string"},"channel":{"type":"string"}},"required":["image"],"additionalProperties":false},"version":{"type":"string"},"parked":{"type":"boolean"},"probationUntil":{"type":"number"},"lastUpdate":{"type":"object","properties":{"result":{"type":"string","enum":["updated","kept","restored","rolled-back"],"description":"What happened. Updated: the new version passed its first health check and runs, with the previous one kept ready until keepUntil. Kept: that probation ended and the new version stays. Restored: the new version never came up, so the previous container was put back at once. Rolled back: the new version came up and then failed its probation (it kept crashing, never became ready, or lost its tunnel), so the host went back to the previous one by itself."},"verb":{"description":"What was asked for: update, rollback, rebuild, dev, reshape, or the probation watch acting on its own.","type":"string"},"at":{"type":"number","description":"When it happened, in milliseconds."},"from":{"description":"The version (or, when it would not say, the image) that ran before.","type":"string"},"to":{"description":"The version (or image) that was moved onto, or that was tried and given up on.","type":"string"},"reason":{"description":"Why the host gave up on the new version, in plain words. Absent when nothing went wrong.","type":"string"},"log":{"description":"Where the host kept the full log of the swap, as a path on the machine that runs the sandbox.","type":"string"},"keepUntil":{"description":"Until when the previous version stays parked and ready, in milliseconds. While it does, going back takes seconds and nothing is downloaded or rebuilt; after it, going back uses the pinned image.","type":"number"}},"required":["result","at"],"additionalProperties":false},"rollbackTargets":{"type":"array","items":{"type":"object","properties":{"image":{"type":"string","description":"The local image a rollback would run, pinned under a tag no other flow writes."},"version":{"description":"What that image says it is. Absent when it would not say.","type":"string"},"download":{"description":"The local pin is gone (pruned outside ic); going back downloads the published image of `version`.","type":"boolean"}},"required":["image"],"additionalProperties":false}},"keptElsewhere":{"type":"string"},"keptElsewhereName":{"type":"string"},"adoptedFrom":{"type":"string"},"keeperSilentSince":{"type":"number"}},"required":["slug","container","running","image"],"additionalProperties":false}},"gap":{"type":"string","enum":["offline","scope-off","unreported"]}},"required":["key","label"],"additionalProperties":false}}},"required":["devices"],"additionalProperties":false}}}}}}},"/system/devices/{id}/sandboxes/{slug}":{"post":{"operationId":"system.manageDeviceSandbox","summary":"Drive a sandbox on one of your own devices","description":"Start, stop, restart, update, rebuild, roll back, reshape (its memory and CPU caps, privileged, GPU) or remove a sandbox running on a machine you own, relayed over the connection that machine holds open. The answer is a stream because the slowest of these takes minutes, and it is the same stream whichever you ask for. The daemon adds no opinion: the machine enforces its own permissions and a refusal arrives as the last line, in the machine's words, naming the switch to flip.","tags":["System"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"slug","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"op":{"type":"string","enum":["start","stop","restart","prepare","prepare-background","update","rebuild","rollback","reshape","set-shape","forget-shape","remove","logs","reconnect","create","runner-up","runner-remove"]},"hash":{"type":"string"},"to":{"type":"string","minLength":1},"shape":{"type":"object","properties":{"memoryGib":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"cpus":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"privileged":{"type":"boolean"},"gpu":{"type":"boolean"}},"required":["memoryGib","cpus","privileged","gpu"],"additionalProperties":false},"when":{"type":"string","enum":["now","nextRestart"]},"resources":{"type":"object","properties":{"memoryGib":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"cpus":{"anyOf":[{"type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},{"type":"null"}]},"privileged":{"type":"boolean"},"gpu":{"type":"boolean"}},"additionalProperties":false},"later":{"type":"boolean"},"parentUrl":{"type":"string"},"pair":{"secret":true,"type":"string"},"setupCode":{"secret":true,"type":"string"},"platformUrl":{"type":"string"},"definition":{"type":"string"},"overlay":{"type":"string"},"overlayHash":{"type":"string"},"resumeTurns":{"type":"boolean"}},"required":["op"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"text/event-stream":{"schema":{"oneOf":[{"type":"object","properties":{"event":{"const":"message"},"data":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"line"},"text":{"type":"string"}},"required":["kind","text"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"result"},"message":{"type":"string"}},"required":["kind","message"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"error"},"message":{"type":"string"}},"required":["kind","message"],"additionalProperties":false}]},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event","data"]},{"type":"object","properties":{"event":{"const":"done"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"error"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]}]}}}}}}},"/system/devices/{id}/commands/{command}":{"post":{"operationId":"system.runDeviceCommand","summary":"Run one of your device's own CLI actions","description":"Performs a named action on a machine you own by running its own intentic-machine command there — turning that device's port mirroring off, say — over the connection it holds open. The set of actions is fixed and the command line is built here from the name, never sent by the caller. The machine enforces its own permissions and a refusal comes back as its own sentence, naming the switch to flip.","tags":["System"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"command","in":"path","required":true,"schema":{"type":"string","enum":["mirror-off","mirror-on","mirror-ignore","mirror-unignore","sync-pause","sync-resume","sync-unpair","sync-clean","dev-restart","dev-rebuild","dev-rebuild-log","sync-install"]}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"sandboxId":{"type":"string","maxLength":200,"pattern":"^[A-Za-z0-9][A-Za-z0-9._-]*$"},"mode":{"type":"string","enum":["sync","mirror"]},"localDir":{"type":"string","minLength":1,"maxLength":4096,"pattern":"^(?:~|\\/|[A-Za-z]:[\\\\/])[^\"'`$;|&\\n\\r]*$"},"port":{"type":"integer","minimum":1,"maximum":65535}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"message":{"type":"string"},"output":{"type":"string"},"refused":{"type":"boolean"}},"required":["ok","message","refused"],"additionalProperties":false}}}}}}},"/system/devices/{id}/agent/{op}":{"post":{"operationId":"system.runDeviceAgentFlow","summary":"Update, restart, or clean up the links of the agent on one of your own devices","description":"Updates a machine you own to the current intentic-machine agent, restarts the loop it is running, or drops the links it holds to sandboxes that have stopped answering — over the connection that machine holds open. The answer is a stream of the run's own output — and it normally stops mid-run, because the agent's loop is what carries this connection: the work is detached from it first, so it finishes regardless, and the device's reported version is what confirms it. Takes the machine's \"Run commands\" permission, the same one a command typed there would.","tags":["System"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","minLength":1}},{"name":"op","in":"path","required":true,"schema":{"type":"string","enum":["upgrade","restart","forget-unreachable"]}}],"responses":{"200":{"description":"OK","content":{"text/event-stream":{"schema":{"oneOf":[{"type":"object","properties":{"event":{"const":"message"},"data":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"line"},"text":{"type":"string"}},"required":["kind","text"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"result"},"message":{"type":"string"}},"required":["kind","message"],"additionalProperties":false},{"type":"object","properties":{"kind":{"type":"string","const":"error"},"message":{"type":"string"}},"required":["kind","message"],"additionalProperties":false}]},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event","data"]},{"type":"object","properties":{"event":{"const":"done"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]},{"type":"object","properties":{"event":{"const":"error"},"data":{},"id":{"type":"string"},"retry":{"type":"number"}},"required":["event"]}]}}}}}}},"/activity":{"get":{"operationId":"activity.list","summary":"What the agent has done out in the world","description":"The audit trail of actions taken on outside services. Read-only on purpose: entries are written by the sandbox alone, which is what makes it a record worth trusting.","tags":["Activity"],"parameters":[{"name":"provider","in":"query","schema":{"description":"Narrow it to one outside service.","type":"string"},"allowEmptyValue":true,"allowReserved":true},{"name":"limit","in":"query","schema":{"default":100,"description":"How many entries to return.","type":"number","minimum":1,"maximum":500},"allowEmptyValue":true,"allowReserved":true},{"name":"before","in":"query","schema":{"description":"Only entries older than this timestamp, so paging walks backwards through the feed.","type":"number"},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The entry's own id."},"at":{"type":"number","description":"When it happened, in milliseconds. Also what you page by."},"provider":{"description":"Which outside service, when one was involved. Absent for the sandbox's own events.","type":"string"},"account":{"description":"Which account handled it. Absent for the sandbox's own events and for work run on a provider's default.","type":"string"},"direction":{"type":"string","enum":["in","out","system"],"description":"Whether something arrived, something went out, or the sandbox did it to itself."},"type":{"type":"string","description":"Exactly what happened: a message received or sent, a reaction, a turn starting or ending, a rule doing something. A rule that ran and passed says nothing here, because a feed of passes is one the eye learns to skip."},"channelId":{"description":"Which channel or thread it happened in.","type":"string"},"author":{"description":"Who sent it, for something that arrived.","type":"string"},"actor":{"description":"Who asked for the turn, as the sandbox verified it: a member's email, token:<label> for a program's control token, or agent:<conversation id> for a parent conversation's child. Absent for a wake nothing asked for.","type":"string"},"content":{"description":"The message, in full, whichever direction it went.","type":"string"},"method":{"description":"The verb of an outgoing call.","type":"string"},"endpoint":{"description":"The address of an outgoing call. Credentials travel in headers, so they are never here.","type":"string"},"sessionId":{"description":"The provider session behind it.","type":"string"},"turnId":{"description":"Ties one turn's entries together. A turn writes several, and read as separate rows they say one thing several times, so a feed groups on this.","type":"string"},"conversationId":{"description":"Which conversation. This, rather than the provider session, is what the same agent means across a feed, because a session is retired whenever the model changes.","type":"string"},"title":{"description":"What that conversation was called at the time. Copied in rather than looked up, because an audit entry must still read as words years later, after the conversation has been renamed or pruned.","type":"string"},"origin":{"description":"What woke the conversation from outside, when something did. It is how a turn gets filed under the chat service that caused it rather than under the model that served it.","type":"object","properties":{"automationId":{"type":"string"},"provider":{"type":"string"},"channelId":{"type":"string"},"author":{"type":"string"}},"required":["automationId","provider"],"additionalProperties":false},"automationIds":{"description":"Which automations were involved.","type":"array","items":{"type":"string"}},"outcome":{"description":"How it ended.","type":"string","enum":["ok","error"]},"error":{"description":"What went wrong, when something did.","type":"string"},"extra":{"description":"Whatever else the source had to say: attachments, participants, a recording's path. Shape varies by source.","type":"object","propertyNames":{"type":"string"},"additionalProperties":{}}},"required":["id","at","direction","type"],"additionalProperties":false},"description":"The audit entries, newest first."}},"required":["events"],"additionalProperties":false}}}}}}},"/activity/status":{"get":{"operationId":"activity.status","summary":"Whether the audit trail is being kept","description":"Which sources are feeding the record and whether each is working.","tags":["Activity"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"connections":{"type":"array","items":{"type":"object","properties":{"capabilityId":{"type":"string","description":"Which connection."},"provider":{"type":"string","description":"Which service it is."},"gateway":{"type":"string","enum":["ready","connecting","pairing","disconnected","idle"],"description":"Idle means it is up but has nothing to listen for, which is different from a connection that should be up and is not. Pairing means somebody started a sign-in and never finished it, which no amount of waiting will fix."},"lastError":{"description":"The most recent thing that went wrong on it.","type":"string"}},"required":["capabilityId","provider","gateway"],"additionalProperties":false},"description":"Each source feeding the record, and whether it is working. Probed now rather than remembered."},"voice":{"description":"A voice call the sandbox is currently in, when it is in one.","type":"object","properties":{"channelId":{"type":"string","description":"Which channel."},"channelName":{"type":"string","description":"What it is called."},"startedAt":{"type":"number","description":"When it joined, in milliseconds."},"participants":{"type":"array","items":{"type":"string"},"description":"Who else is in it."}},"required":["channelId","channelName","startedAt","participants"],"additionalProperties":false}},"required":["connections"],"additionalProperties":false}}}}}}},"/logs":{"get":{"operationId":"logs.list","summary":"Logs the sandbox keeps","description":"Every log file the daemon owns: captured terminal output, command runs, and the daemon's own log. Read-only, because only the sandbox writes them.","tags":["Logs"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"files":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Its name, which is what the read route takes."},"sizeBytes":{"type":"number","description":"Size in bytes."},"modifiedAt":{"type":"number","description":"When it last changed, in milliseconds."}},"required":["name","sizeBytes","modifiedAt"],"additionalProperties":false},"description":"Every log the sandbox keeps: captured terminal output, command runs, and its own log."}},"required":["files"],"additionalProperties":false}}}}}}},"/logs/file":{"get":{"operationId":"logs.read","summary":"Read part of a log","description":"A window of one log file's text. A window rather than the whole thing, because a busy log outgrows any single answer.","tags":["Logs"],"parameters":[{"name":"name","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"Which log. It travels in the query rather than the address, because log names contain slashes."},"allowEmptyValue":true,"allowReserved":true},{"name":"bytes","in":"query","required":false,"schema":{"default":65536,"description":"How much of the end to read. The newest bytes win when the file is larger.","type":"number","minimum":1,"maximum":1048576},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Which log this is from."},"sizeBytes":{"type":"number","description":"How large the whole file is."},"text":{"type":"string","description":"The end of it, as text."},"truncated":{"type":"boolean","description":"There is more before what you got."}},"required":["name","sizeBytes","text","truncated"],"additionalProperties":false}}}}}}},"/logs/client":{"post":{"operationId":"logs.report","summary":"Report what the browser saw","description":"Errors the app caught, stalls it measured, and recoveries it performed, written to a log of their own. The browser is the only witness to these, so without it a bug someone hit in their own browser leaves no record at all.","tags":["Logs"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"events":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"seenAt":{"type":"number","description":"When the browser saw it, in milliseconds."},"level":{"type":"string","enum":["warn","error"],"description":"How bad it was."},"event":{"type":"string","minLength":1,"maxLength":100,"description":"What kind of thing it was, as a stable name."},"message":{"type":"string","maxLength":2000,"description":"What it said."},"route":{"description":"Which page they were on.","type":"string","maxLength":300},"requestId":{"description":"Which daemon call it belonged to, when it belonged to one.","type":"string","maxLength":100},"build":{"description":"Which build of the app was running.","type":"string","maxLength":100},"fields":{"description":"Whatever else was worth keeping.","type":"object","propertyNames":{"type":"string","maxLength":60},"additionalProperties":{"anyOf":[{"type":"string","maxLength":4000},{"type":"number"},{"type":"boolean"}]}}},"required":["seenAt","level","event","message"]},"description":"What the browser has to report, oldest first."}},"required":["events"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"recorded":{"type":"number","description":"How many were written down."}},"required":["recorded"],"additionalProperties":false}}}}}}},"/push/config":{"get":{"operationId":"push.config","summary":"What a device needs to subscribe","description":"The public key and settings a browser or app needs before it can register for notifications from this sandbox.","tags":["Push notifications"],"parameters":[{"name":"id","in":"query","schema":{"description":"Which device is asking. Without it the answer can only speak for the sandbox as a whole, which is rarely the question.","type":"string","minLength":1},"allowEmptyValue":true,"allowReserved":true}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"publicKey":{"type":"string","description":"The key a browser needs in order to subscribe. Native apps ignore it."},"subscribed":{"type":"boolean","description":"Whether the asking device is already registered, so a toggle can show its real state instead of trusting the device's own permission, which can be granted with nothing behind it."}},"required":["publicKey","subscribed"],"additionalProperties":false}}}}}}},"/push/subscribe":{"post":{"operationId":"push.subscribe","summary":"Send notifications to this device","description":"Registers one device. The sandbox only interrupts you on the three moments where attention is genuinely wanted: a turn has finished, the agent is stuck on a question, and something is waiting for approval.","tags":["Push notifications"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"webpush","description":"A browser, which the sandbox can reach directly and encrypt end to end."},"endpoint":{"type":"string","format":"uri","description":"Where that browser's push service accepts sends. It also identifies the device everywhere else in this group."},"keys":{"type":"object","properties":{"p256dh":{"type":"string","minLength":1,"description":"The browser's public key, for encrypting what is sent."},"auth":{"type":"string","minLength":1,"description":"The browser's secret, for the same."}},"required":["p256dh","auth"],"description":"What the browser handed you when it subscribed. Post it back exactly as it came; nothing reshapes it."}},"required":["kind","endpoint","keys"]},{"type":"object","properties":{"kind":{"type":"string","const":"relay","description":"A native app, whose operating system only accepts sends from the app's publisher, so the sandbox posts through a relay instead. The message passes through that relay readable, which is the price of the publisher having to be in the loop."},"url":{"type":"string","format":"uri","description":"Where to post a send. Recorded rather than assumed, so the sandbox need not know any platform by name."},"deviceId":{"type":"string","minLength":1,"description":"The device's id, which also identifies this registration everywhere else in this group."},"secret":{"type":"string","minLength":1,"description":"Proof that this sandbox may notify this device. The relay never learns which sandbox is calling."}},"required":["kind","url","deviceId","secret"]}]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/push/unsubscribe":{"post":{"operationId":"push.unsubscribe","summary":"Stop notifying a device","description":"Removes one registered device. Others keep receiving.","tags":["Push notifications"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"description":"Which device: a browser's push address, or a native install's device id."}},"required":["id"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true. A route that answers this either did the thing or refused with a status; there is no third outcome to report."}},"required":["ok"],"additionalProperties":false}}}}}}},"/push/test":{"post":{"operationId":"push.test","summary":"Send a test notification","description":"Proves the whole chain end to end. Worth having, because there are four separate places a notification can be lost that nobody can inspect from the outside: the device's permission, its registration, the sandbox's key, and the delivery service.","tags":["Push notifications"],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"delivered":{"type":"integer","minimum":0,"maximum":9007199254740991,"description":"How many devices actually accepted it. A count rather than a yes, because this button exists to prove a chain nobody can inspect, and the sandbox having accepted the request is not the question being asked."}},"required":["delivered"],"additionalProperties":false}}}}}}}},"components":{"securitySchemes":{"session":{"type":"http","scheme":"bearer","description":"A signed-in person's session, minted by `POST /system/session` and presented as `authorization: Bearer …`. It reaches everything the owner reaches."},"control":{"type":"apiKey","in":"header","name":"x-intentic-control","description":"A program's credential, minted by the owner with a scope fixed at that moment. The raw `ict_…` value is returned exactly once; only its hash is stored, and it can be revoked per token.\n\nScopes, widening downward:\n- `editor` — One conversation: run a turn, answer a card it parked on, read transcripts, search the tree. What an editor bridge holds. It cannot see the fleet and it cannot land work.\n- `read` — Everything a viewer sees: the fleet, transcripts, files, git state, CI runs, listening ports. Changes nothing. The one genuinely narrow rung, which is why it exists separately rather than as a politeness.\n- `drive` — Everything read sees, plus what a collaborator does: start, answer, steer and stop turns, rename and archive agents. Stops short of anything that moves code into the main tree. A stolen token at this rung is the agent's reach.\n- `land` — Everything drive does, plus merging a conversation's worktree into the main tree, and discarding or purging one. Separate because the usual arrangement is a program that works and a person who decides."}}}}