jg-agent-hq / api
Public API
Import the named exports from jg-agent-hq. Internal module paths are private.
Intentional Node API failures use Exception from jg-exception. The error reference (opens in a new tab) defines their named string codes and stable messages. PUBLIC_ERROR_CODES and PUBLIC_ERROR_MESSAGES are available from jg-agent-hq/contracts for the seven safe client classifications. SQLite-worker responses retain the originating application code; arbitrary worker failures become JG_AGENT_HQ_STORE_FAILED. Browser and IPC error bodies expose only their established safe classification and message.
Lock lifecycle
acquireHqLock(root, command, options?)acquires.runtime/hq.lockand returns an asynchronous release function. The caller owns the trusted state root and supplies a non-empty, single-line operation label. Surrounding whitespace is trimmed. The returned release function is idempotent and refuses a replaced owner. Acquisitions in one process queue by their normalized lock path, so later status polling cannot overtake a waiting checkpoint. Cross-process exclusion still uses the same file and exact owner; no cross-process ordering is promised. Timed-out or failed waiters leave the local queue.withHqLock(root, command, operation, options?)runs an asynchronous operation under the lock. It releases on success or failure; operation failures propagate unchanged. If cleanup also fails, the original operation error stays primary and cleanup failure is reported on stderr.readHqLockState(root, now?)reports free, held or unreadable without acquiring or reclaiming the lock. Held state includes the owner, liveness and age.getOwnerLiveness(pid)reports alive, dead or unknown. An unverifiable owner is not evidence that recovery is safe.releaseHqLock(root, nonce)is deliberate operator recovery. Confirm first that the inspected owner and its children have stopped. It checks the inspected nonce rather than liveness, because a reused PID must not prevent recovery.
IHqLockOptions retains the wait timeout, setup window and poll interval. Defaults are 120 seconds, 16 seconds and 200 milliseconds. Durations must be finite and non-negative; the poll interval must be positive. Invalid acquisition arguments fail before filesystem writes. An empty freshly created file is tolerated only within the setup window. Owner records are bounded to 16,384 UTF-8 bytes. The private directory and lock must belong to the current user and have no group/other access.
IHqLockOwner, IOwnerLiveness, IHqLockOptions and IHqLockState describe the contracts. RUNTIME_DIRECTORY and HQ_LOCK_FILE expose the established names.
All operations may expose the relevant intentional contracts in errors (opens in a new tab). Filesystem failures outside those classifications propagate to the trusted Node caller. The returned acquisition release function can expose the ownership-changed contract.
Shared storage
hq.storage returns an independent IStorageDescriptor containing the canonical databasePath, schemaHash, reservedPrefix (hq_), and required connectionSettings: foreignKeys true, busyTimeoutMs 5000, journalMode wal, and synchronous full. Exported Node types are IStorageDescriptor and IStorageMaintenanceOperation<T>. No database handle, storage callback, raw SQL command or storage path is exposed by browser contracts or IPC.
For node:sqlite, open the host connection with new DatabaseSync(storage.databasePath, { timeout: storage.connectionSettings.busyTimeoutMs, enableForeignKeyConstraints: storage.connectionSettings.foreignKeys }). These options enable foreign keys and a 5,000 ms busy timeout; the descriptor itself is not a DatabaseSync options object. See the Node 24 SQLite API (opens in a new tab). Keep WAL mode and FULL synchronization; do not change database-wide settings while HQ is running. Hosts own their table prefix, definitions, connection lifetime and user_version. HQ-owned tables and indexes are reserved. A nonempty database requires the current singleton hq_schema stamp, which records the schema hash, the SHA-256 fingerprint of action.key and the package version that wrote the database. Startup rejects any other schema with JG_AGENT_HQ_STORAGE_INVALID before opening a writer; its message names that package version, or reports another jg-agent-hq version when the stamp records none. Rejected storage remains unchanged. A different valid-length key fails with JG_AGENT_HQ_ACTION_KEY_UNAVAILABLE before a writer opens. Fresh initialization is transactional.
await hq.withStorageMaintenance(operation) passes the storage descriptor to the async operation and returns its result. It closes admission before draining accepted HQ operations, allows their awaited nested calls and required native evidence to finish, and checks for active native execution or prepared plan writes before invoking the callback. Concurrent maintenance, new HQ requests, browser host effects and shutdown conflict while maintenance is held. During the callback, native event persistence waits in its existing bounded lane, and an already accepted scheduled stop waits for release. The callback must not call HQ, start native work or leave transactions open. Its errors propagate unchanged; it owns rollback and reconciliation for its SQL effects. After success or failure, HQ admission resumes. Calls after shutdown use JG_AGENT_HQ_RUNTIME_NOT_RUNNING.
The host must coordinate every application connection that can write during maintenance. A shared file gives SQLite transaction isolation, not an atomic transaction spanning host SQL and HQ API calls. HQ does not expose its worker connection. Snapshot scheduling and remote backup operations belong to the host application.
Snapshot and restore
Import createHqSnapshot, inspectHqSnapshot, restoreHqSnapshot, packHqSnapshot and unpackHqSnapshot from jg-agent-hq in a trusted Node host. They are absent from browser contracts, IPC and agent tools. The exported types are IHqSnapshotInspection, IHqRestoreOptions and IHqRestoreResult.
| Operation | Contract |
|---|---|
createHqSnapshot(hq, destination) |
Capture a running runtime while its agents keep working into an absent absolute destination under an existing canonical parent. Host connections must not write host tables until it resolves. Returns IHqSnapshotInspection. |
inspectHqSnapshot(directory) |
Validate the complete private snapshot without changing it. Returns IHqSnapshotInspection. |
packHqSnapshot(snapshotDirectory, archivePath, key) |
Inspect the snapshot, then encrypt it into one new private file at an absent absolute path under an existing canonical parent. Returns the snapshot's IHqSnapshotInspection. |
unpackHqSnapshot(archivePath, destination, key) |
Decrypt and verify an archive, then write it into an absent snapshot directory and inspect it. Returns IHqSnapshotInspection. |
restoreHqSnapshot({ snapshotDirectory, destination }) |
Validate before creating an absent recovery destination, copy and validate staged files, then expose state and plan recovery directories. Returns IHqRestoreResult. All proposed destination connections must be stopped. |
Capture runs as admitted HQ work, so host maintenance excludes it, and holds the coordination metadata lock throughout, so HQ-mediated plan writes and coordination.json wait for it under the lock's 120-second default. Native turns, ownership changes and evidence keep flowing: the store worker backs up the database through its own connection in bounded steps. Each registered plan is then copied under its own shared document locks, one plan at a time; a locked document conflicts. Host connections must not write host tables for the complete capture, because a write from another connection restarts or splits SQLite's backup. Prepared document write intents and source mismatches are retained. Attached databases and external files are outside this coherence contract.
SQLite's worker-owned backup API includes committed WAL data and every table in the main database, including application tables and exact hq_report payloads. The resulting database needs no WAL sidecar. The snapshot also contains the private 32-byte action.key and every present registered plan.md, milestones.md and coordination.json. Capture binds the original canonical state root and checks the key against its startup hash, rejecting a key replaced while the runtime is open. The indexed hq_plan_registration records durable references from plans, task/grant scope and requests. Enumeration reads directory seek pages of 128 from the captured database, so a plan registered after the backup belongs to the next capture; file copy and SHA-256 use 65,536-byte buffers. There is no repository scan, transcript copy or artifact cache.
The one inventory format is manifest.jsonl: header, database, action-key and ordered plan records. Each plan's generated directory is plans/<SHA-256 of source directory>. The header records the schema hash and the package version that captured it. Plan records retain the exact source directory, observed plan ID, the plan revision computed from the copied files, the distinct revisions the captured database's thread bindings record, companion byte counts/hashes and bounded diagnostics. A copied revision that differs from a recorded one adds the plan-changed diagnostic: the plan changed during capture or after its threads bound. Missing source plans, parser observations and corrupt coordination remain visible evidence; capture does not repair them or require checker conformance. Registered plans outside the runtime's current allowed roots retain an unavailable record without reading their files. A final complete.json binds the complete manifest hash and record count. Directories are private (0700), files are private (0600), and destinations are created exclusively. Runtime sockets, owner records, process IDs, bridge credentials and provider authentication are excluded.
Inspection streams the inventory, verifies each listed regular file and every expected/absent companion, rejects links and unsafe paths, checks the current HQ schema stamp and database/key pairing, SQLite integrity/foreign keys, exact report hashes and complete registered-plan coverage. It rechecks captured plan identities, revisions and diagnostics. A snapshot whose header or database comes from another schema is refused with STORAGE_INVALID, naming the package version that wrote it, before any restore output is reserved. Full database verification runs in a dedicated worker. Inspection returns bounded directory, header, hash and count metadata, including changedPlanCount; it does not load a growing inventory into memory. Hashes detect corruption and bind content; they do not authenticate a snapshot supplied by an untrusted party.
Restore leaves every existing destination untouched, including an empty directory, repository or live state root. Use a new directory. It validates the source before reserving that directory and validates the staged copy independently against the initially inspected manifest hash before changing recovery authority. On success, stateRoot points to destination/state, recoveryRoot to destination/plans, recovery.jsonl retains the source inventory and recovered.json records completion. Caught failures remove only the operation's newly created output. A crash can leave incomplete output; do not open it as a recovered runtime.
Restored task and agent lifecycles are paused unless already stopped, automatic dispatch is disabled, all skill grants are revoked, live approvals expire and queued input loses readiness. Submitted actions retain uncertainty. Managed native identities become uncertain and require explicit ownership reconciliation. IDs, stored reports, waiting child sets/continuation reservations, delivery links, historical receipts and exact prepared document intents remain intact. No native operation runs during restore.
Thread bindings of plans marked plan-changed also lose their execution scope, so that work resumes only under fresh authority. Call createAgentHq with the returned stateRoot and an explicitly selected allowedRoots array. Runtime construction never imports source root permissions from the snapshot and always assigns a new epoch; stored execution and revision leases cannot authorize work in it. Native source workspace paths and plan bindings remain evidence and are not rewritten. Recover repository/native files separately. Inspect recovery.jsonl and copy selected companions into explicitly chosen plan locations only after comparing current files; restore never overwrites a source repository. Rebind task plan references and obtain fresh plan/execution authority where needed, reconcile pending document writes using their exact IDs/baselines, and make explicit current-revision lifecycle/native ownership decisions before enabling dispatch. Source paths outside the newly selected roots remain unavailable.
These exports use SNAPSHOT_INVALID for incomplete, corrupt, unsafe or occupied snapshot, archive or recovery selections, for a key that is not 32 bytes and for an archive that fails authentication, and SNAPSHOT_FAILED for operational copy, backup, lock-release, worker or cleanup failures. Capture additionally exposes ACTION_CONFLICT while host maintenance runs, when the metadata lock wait expires or when a plan document is locked. Inspection/restore expose STORAGE_INVALID for a snapshot from another schema. Operational messages retain private diagnostic causes; no snapshot exception is exposed through public RPC.
An archive is a fixed format identifier, a fresh 12-byte IV, the AES-256-GCM ciphertext of every snapshot file in manifest order, each behind its path and length, and the 16-byte authentication tag. The key is a 32-byte Buffer the host supplies and keeps; HQ never stores it. Unpacking decrypts the whole archive into a private temporary file beside the destination and verifies the tag before trusting any byte or creating the destination, so a wrong key, a modified byte or a truncation is refused with SNAPSHOT_INVALID and writes nothing. It then writes only the manifest's listed paths at exactly their listed sizes, refuses unlisted, repeated or missing entries, and runs inspectHqSnapshot on the result; any failure removes the destination and the temporary file. Packing removes a partial archive on failure. Both stream in 65,536-byte chunks.
Snapshot directories are plaintext local state; archives are the encrypted form for off-machine copies. The host owns the key and its recovery, scheduling, uploads, retention and last-success status. A disk failure can lose every change after the last successful off-machine backup. A local snapshot alone provides no remote durability. See the recovery workflow (opens in a new tab).
Native snapshot qualification on 2026-10-23 used Codex 0.164.0 (gpt-6.1-sol) and Claude Code 2.1.309 (claude-opus-5-5). Both produced exact HQ reports from controlled native sessions. Quiescent capture, offline restore and explicit root selection retained their report bytes/hashes, changed the runtime epoch, paused task dispatch and left both providers idle on the recovered runtime. Claude read-tool permissions received exact explicit replies. These observations do not establish a native version admission rule.
Runtime lifecycle
createAgentHq({ stateRoot, allowedRoots }) starts one controller and returns IAgentHq: its canonical stateRoot, a closed lifetime promise, request(command), idempotent close(), a trusted Node-only storage descriptor and withStorageMaintenance(callback). Paths must be absolute. Allowed roots must exist and are canonicalized at startup. A client-selected workspace must resolve inside an allowed root. State is a real directory owned by the current OS user with no group/other access. The controller creates it privately when absent.
The controller exclusively creates hq-owner.json. A live, inactive, incomplete or unverifiable existing owner prevents another controller from starting. There is no automatic takeover. Before deliberate recovery, establish that the inspected host, its worker and native process group have stopped. Runtime shutdown sends interruption requests across the bounded session cohort concurrently, then closes IPC connections, SQLite and owned native processes before removing the exact owner. Failed cleanup preserves ownership evidence. A new runtime receives a new epoch.
requestAgentHq(stateRoot, command, signal?) is a one-command IPC client. It verifies the private owner and socket, binds the command to the runtime epoch and checks the matching response. Cancellation closes that client's socket and reports transport failure; it never undoes an admitted action. Closing a client does not close the host. stop accepts a shutdown request; the SDK caller must await runtime.closed when it owns the runtime. The CLI confirms owner removal before reporting state: stopped.
The socket accepts at most 32 simultaneous connections. Requests are bounded to 65,536 UTF-8 bytes; native and IPC response frames to 8,388,608 bytes. Structured transports reject malformed UTF-8/JSON, oversized frames and incomplete tails. Each native connection has at most 32 pending calls with a 30-second response deadline. IPC connections have a 35-second deadline. The SQLite worker bounds its queue to 128 commands, accommodating client reads, native lifecycle observations and action dispatch; collection operations remain limited to one page. Database work runs off the host event loop. The state directory is a same-user trust boundary, not a sandbox against other code running as that user.
Provider adapters and capabilities
createAgentHq accepts optional providerFactories, an array of additional trusted IAgentProviderFactory objects. The built-in codex and claude registrations remain available. Each factory has a validated id and asynchronous create(context); duplicate IDs or invalid registrations use JG_AGENT_HQ_REQUEST_INVALID. Registration is construction-time Node code. Browser and IPC clients cannot supply factories, executable paths or modules.
Factories receive canonical allowed roots, the runtime epoch, a cancellation signal and runtime-owned event callbacks. They return IAgentProvider, which owns native protocol validation, processes, authentication, capabilities, history, skill invocation spelling and concrete session controls. Each operation receives a bounded IProviderCall.signal. Factories must clean up partial initialization before rejecting; close() must be idempotent and confirm that owned resources stopped. HQ retains ownership evidence when cleanup cannot be confirmed. Failed construction is not automatically replayed.
IAgentProviderEvents carries normalized observations, items, deltas, prompts, outcomes, observed session states and connection loss. session(nativeId, state) reports the live state of a session another client drives inside the allowed workspaces; HQ ignores it for sessions it controls. An adapter that reports such sessions may implement the optional readObservedThread(nativeId), returning the session's summary with the workspace it runs in; without it, those sessions are left out of active-sessions. validateInput rejects unsupported media/output options before admission and again at dispatch. startTurn receives content-bound IProviderInputFile bytes read by HQ. A turn-completed observation may carry a validated complete result whose state must match that observation. Registration binds provider identity. Echo each supplied request ID unchanged; HQ derives its action identity and verifies the exact receipt, runtime, provider, native session and workspace. A late create/fork/resume/unarchive/revert outcome also supplies verified native session metadata. Unknown control states cannot establish success or idle ownership. Callback metadata may grow additively; known identity and state fields remain validated. Native permission requests keep their exact reply channel and validation. An unsupported request is denied rather than approved.
{ kind: 'capabilities', provider, threadId? } returns IProviderCapabilities. The optional managed thread must belong to that provider and an allowed workspace. The response includes provider/session scope, observation time, concrete operation support, and supported HQ input/configuration schemas. Each operation is supported, unsupported, unavailable or unknown, with a structured reason when support cannot be established. Supported settings describe whether a change is live or requires an idle restart. Configuration schemas describe typed session and turn settings, their live or idle-restart change mode, and native permission/sandbox choices. Model and effort values come from the native model catalog. Capabilities are observations, not permission grants or execution authority.
The current built-in surfaces map to these native entry points:
| HQ surface | Codex | Claude |
|---|---|---|
| Create/adopt/resume | thread/start, thread/read, thread/resume |
Structured session creation/resume and idle/session-ID fences |
| Rename | thread/name/set |
rename_session control request |
| Message/queue | turn/start with explicit native input |
Structured user messages with matching acknowledgements |
| Steer | turn/steer for the exact active turn |
Explicitly unsupported by the structured interface |
| Interrupt | turn/interrupt |
interrupt control request; completion remains separate |
| Close/recover | Native background-terminal cleanup/verification and thread/unsubscribe |
Confirmed process-group shutdown and explicit session release/resume |
| History | thread/list, thread/read, thread/turns/list, thread/items/list |
Bounded worker-owned indexing of native transcripts |
| Skills | skills/list and native skill input |
Initialization command metadata and native invocation text |
| Fork | thread/fork with verified source lineage |
Native --resume, --fork-session and distinct --session-id |
| Archive/unarchive | Native archive/unarchive, with explicit resume afterward | Explicitly unsupported |
| Compact | thread/compact/start, correlated with native turn events |
Native /compact, correlated with input and result events |
| Revert | thread/revert before an exact turn, without undoing files |
Explicitly unsupported |
| Models/settings | model/list, config/read, native startup and next-turn parameters |
list_models, get_settings, native startup flags and apply_flag_settings |
| Usage | account/rateLimits/read |
get_usage control request, with explicit unavailable observations |
| Account | account/read, ChatGPT login start/cancel and native logout |
Initialization account metadata and native auth logout; interactive login is unavailable |
| Skill reload | skills/list with native forceReload |
reload_skills, followed by complete initialization command metadata |
| MCP inventory | mcpServerStatus/list, including tools and resources |
mcp_status; tool input schemas and resource catalogs are unavailable |
| MCP controls | config/mcpServer/reload, server OAuth login |
mcp_reconnect, mcp_toggle; structured server login is unavailable |
| Plugin reload | Under-development native plugin management is unavailable | reload_plugins for existing native configuration, retaining held outcomes |
Availability depends on the selected native connection and, for session metadata, that session's initialization. Native settings, hooks, plugins and authentication remain with the harness. Codex protocol details are in the official app-server reference (opens in a new tab); Claude flags and programmatic behavior are in the CLI reference (opens in a new tab) and programmatic guide (opens in a new tab). Public-API integration tests exercise both native boundaries and a third provider through real HQ persistence and IPC, including compatible version labels, missing-provider isolation, duplicate registration and malformed evidence.
Local qualification on 2026-10-23 used Codex 0.163.0 and Claude Code 2.1.309 with native settings and authentication in a disposable workspace. Both created and named sessions with selected models and startup instructions, acknowledged a text message, reported native completion, exposed live presentation and completed compaction. Both resumed the same native identity after runtime restart, applied next-turn model/effort selections with native readback, and forked stored history through the public HQ action API. Codex also completed public history revert and archive/unarchive, leaving the unarchived child inactive. Claude resumed its compacted transcript after HQ excluded native API diagnostic blobs from conversation indexing. Both also accepted a PNG and UTF-8 document through the HQ file-reference path and returned a validated structured READY result with per-turn token observations (Codex gpt-6.1-sol; Claude claude-haiku-4-5-20251001). Owned controllers and runtimes shut down cleanly. This checks those controls on this host; other controls and platforms require their own qualification.
Third-party adapter failures use JG_AGENT_HQ_PROVIDER_FAILED, retaining safe diagnostic causes. Canonical request/access/state errors and JG_AGENT_HQ_CAPABILITY_UNAVAILABLE retain their meaning. An explicit native rejection may carry nativeRejection: true; use it only when native rejection is proven. Timeouts and lost responses remain uncertain and are not retried with a new identity. Capability reads normally return availability data; an unusable descriptor does not disable unrelated session controls. Quota reads return an unavailable observation when no trustworthy measurement can be obtained. Essential malformed control data quarantines the affected provider. Lifecycle queues are bounded per provider; overflow marks that provider uncertain and stops it while other providers remain usable. SQLite failures still affect the shared store.
Read commands
Import HqRequestSchema, HqResponseSchema and their inferred types from jg-agent-hq/contracts. Requests are closed contracts. Responses tolerate additive fields. Provider fields use ProviderIdSchema, a bounded lowercase identifier. codex and claude are built in; trusted Node factories can add providers. Status returns a bounded providers array of { provider, state, version }, where state is idle, ready or unavailable. Reading status does not start a provider. The diagnostic version/user-agent string may be null and never decides compatibility.
| Command | Required fields | Result |
|---|---|---|
status |
None | Runtime epoch, host PID, start time, worker availability and native transport availability |
capabilities |
provider; optional managed threadId |
Provider/session-scoped support, input/configuration schemas and unavailable reasons |
models |
provider |
Paged native model identities, effort choices and defaults when observed |
configuration |
provider, workspaceRoot; optional managed threadId |
Known effective native settings and paged agent/profile choices |
stop |
None | Accepted shutdown and runtime epoch |
threads |
provider, workspaceRoot |
Paged native discovery; optional search |
sessions |
provider, workspaceRoot |
Byte-bounded discovery with exact IDs; optional search |
thread |
provider, workspaceRoot, nativeId |
One native thread's metadata |
turns |
Same target fields | Paged native turn metadata without hydrating items |
items |
Same target fields | Newest-first persisted native items; optional turnId, textLimit and nativeItemId |
item-detail |
Same target fields, turnId, nativeItemId |
One item with its command, tool call, file diffs and images, or null |
item-image |
Same target fields, turnId, nativeItemId, index |
One image of an item as { mediaType, data }, its bytes in base64 |
session-evidence |
Same target fields | Newest-first bounded evidence, exact references and full normalized text continuation |
account |
provider |
Safe native account metadata with explicit unavailable observations |
mcp-servers |
Managed threadId |
Paged configured native MCP server identity, connection and auth metadata |
mcp-tools / mcp-resources |
Managed threadId, serverName |
Paged native descriptions; no resource content is fetched |
provider-actions |
provider |
Paged global action metadata, including authentication receipts |
login |
actionId |
Live authentication presentation or known terminal state |
usage |
provider |
Actual subscription observations |
Paged commands accept limit (default 32, maximum 128) and an opaque cursor. Continue with exactly the same command, target, filters and limit. The cursor binds those fields to the provider continuation. Codex requests threads by last activity (updated_at), descending, with descending turns and descending items. Claude orders discovery by last activity, the later of a transcript's modification time and its newest record, then native UUID, descending, turns by native chain position ascending and items by native chain position descending. Workspace authorization is rechecked for each request, independently of cursor validation, before selected-thread items are fetched.
items reads newest first, its only order, and its cursor continues toward older items. textLimit, from 1 to 65,536 and 65,536 by default, cuts each item's text to that many UTF-16 code units on a code-point boundary and marks it isTruncated. nativeItemId with its turnId, and without a cursor, returns that item alone, cut at textLimit like any page; a hidden item, an unknown one, or one outside that turn returns an empty page. Codex has no lookup of one item, so it reads that turn's items, 128 at a time, until it finds the item.
item-detail returns IItemDetail for one item of a turn, or null for a hidden or unknown item, or one outside that turn. item is the item as an items read of it returns it. command is { command, output, isOutputStartCut, exitCode, durationMs } or null: a Codex commandExecution gives every field, a Claude Bash call its command, its result the command and its output, from the result's stdout and stderr or else its text, and a ! shell input or output the same; Claude records no exit code or duration. tool is { server, name, input, result, isResultStartCut } or null, with input and result as text: JSON for Codex mcpToolCall and dynamicToolCall, and for a Claude call its input as JSON and for its result the text of its content, an MCP tool's name split into its server and tool. A Claude result takes its command or tool name from the call it answers, which HQ looks for among its 32 nearest ancestors in its turn. files lists each changed file with path, added, removed and its unified diff, from Codex fileChange and Claude Edit and Write results; a created or deleted file without hunks is its whole content, every line added or removed. images lists every image the item holds or names, at most 64, in order: { index, mediaType, byteCount }, where mediaType is image/png, image/jpeg, image/gif or image/webp when its first bytes are one of them, and both are null for an image the item only names, such as a Codex imageView or localImage path or a URL. Output and results longer than 65,536 UTF-16 code units keep their last 65,536, on a code-point boundary, with isOutputStartCut or isResultStartCut set.
item-image returns the image at index, from 0 to 63, in the item's images, as { mediaType, data } with its bytes in base64. A Claude image comes from an image block with a base64 source, in the record's message or in a tool result's content. A Codex image comes from an imageView item's path, a user message's localImage path, or an image part's base64 data: URL; no other URL is fetched. A path must be absolute. Its file is opened without following it when it is itself a symbolic link, and without waiting on a pipe, and it is read only when it is a regular file; directories in the path are resolved as usual. An image is returned only when its first bytes are PNG, JPEG, GIF or WebP, which set mediaType, and it is at most 4 MiB, so its base64 fits in one IPC response. Anything else, including a hidden or unknown item, one outside that turn, or an index past its images, fails with the image-unavailable kind.
Threads expose native state, the live session state described under session states, historyState (available or unavailable), ownership (external, managed or uncertain) and nullable managedId. A managed thread uses its HQ name; an external thread retains its nullable native name. Timestamps use Unix milliseconds. Discovery projections are not authoritative transcript history and never cache a conversation body. Repeated Codex history reads go to its native APIs so a completed turn whose items have not flushed can be read again later. There is no resume-to-read behavior or Codex transcript-file parser.
Bounded session evidence
sessions and session-evidence enforce a 65,536-byte total serialized response budget, including the IPC envelope. Both accept limit (default 32, maximum 128) and an opaque cursor. A request inspects at most eight source items, so it may return fewer than its limit. Continue until nextCursor is null, including when a filtered page is empty. Discovery preserves every matching identity rather than selecting an agent by an ambiguous native label. Native display names are limited to 1,024 UTF-8 bytes with isNameTruncated; exact managed/native IDs remain unchanged.
session-evidence selects provider, workspaceRoot, nativeId, and optional turnId, nativeItemId and kinds (the history item kinds, matching the kinds items presents). Items arrive newest first. Each fragment has reference: { provider, nativeId, turnId, itemId, sha256 }, text, offsetBytes, totalBytes, isComplete, contentState, native type, kind and provenance. The hash identifies the complete normalized UTF-8 text, not the fragment or raw transcript record. Non-text native blocks retain explicit labels; this API does not return raw provider payloads or binary attachments. isComplete is true only on the final fragment of supported, untruncated normalized text. contentState distinguishes available content, unsupported native content and a provider preview. Concatenate fragments for one exact reference in byte-offset order and verify its total length and hash. Empty native history returns an empty terminal page; missing or unverifiable history reports its provider's unavailable error.
Text chunks contain at most 32,768 UTF-8 bytes and shrink when escaping or metadata consumes the response budget. Cursors bind all query fields and the content of a partially consumed native page. Changed content, changed query scope or a changed Claude chain revision uses CURSOR_INVALID; start a fresh read rather than joining different evidence. New native pages use the provider's continuation, not an immutable transcript snapshot. Newly arriving or removed history may therefore require a fresh discovery/evidence read. Authorization is rechecked on every request before content access, and no alternate raw-file path bypasses a denial.
HistoryItemSchema and evidence fragments expose provenance: { origin, source, nativeInputId, nativeInputSha256, actionId, contentBoundary: 'untrusted', grantsAuthority: false }. Origins are operator, agent, tool, system or unknown. Native assistant/tool kinds establish only their observed category. A native user role, quoted operator text or an imported message cannot establish an operator. HQ correlates a submitted input only when its exact native message/client identity, thread/turn and nativeInputSha256 match the acknowledged action ledger. A provider's user message carries nativeInputSha256, the SHA-256 of the text its native input sent: a Codex message's first text part, or a Claude record's string content or first text block, with a slash command or skill, which Claude records as command markup, read back as the /name args it sent. It is hashed before the parts the same input added after the text, such as a skill or attached files, and before a page cuts the text, so those messages correlate too. Programmatic host submissions have source host-action and unknown actor. An authenticated browser submission additionally requires the same selected thread and text fingerprint, yielding authenticated-host-action and operator provenance. A message without a native input identity, or whose sent text differs from the action's, stays unknown. Provenance remains historical evidence and grants no current approval, execution authority or tool permission; quotations remain inside its untrusted content boundary.
Codex uses its native descending item API with unloaded thread metadata. It never reads Codex transcript files. Claude uses indexed current-chain byte locations, exact UUID lookups and separate bounded seeks per selected kind. Its cold index uses 16 MiB batches and a 30-second operation budget; selected evidence reads parse at most 4 MiB of records beyond the first. Continuation revalidates and rereads one bounded native page to detect content changes rather than caching transcript bodies. Work and memory per read are bounded independently of history growth; reconstructing a large body may reread that source page. No conversation-body cache is maintained.
Native accounts and extensions
account reads only native account metadata: provider, state, nullable accountType, email, organization, subscription, isAuthenticationRequired, observedAt, and source: 'native'. This describes configured authentication; it is not a network-validity guarantee. Codex uses account/read without token refresh. Claude reads fresh initialization account metadata in an owned read process. Missing optional metadata yields unavailable without disabling session control. Configuration files, environment values, headers and credentials are never returned.
mcp-servers, mcp-tools and mcp-resources require the exact managed session and an authorized workspace. They return provider, state (available, partial, unavailable, unsupported), observedAt, unavailableCount, data and nextCursor. Default and maximum page sizes are 32 and 128. Native leaf inventories are bounded to 4096 entries and 1 MiB before paging; malformed optional entries are counted without inventing metadata. Claude server and all leaf continuation use a content revision and stable identity, so catalog changes invalidate continuation. Codex retains its native server cursor. Query binding is checked before native retrieval. Unknown server metadata remains null. Claude's native status surface omits tool input schemas and does not enumerate resources; HQ reports null schemas and an unsupported resource catalog. Metadata reads never fetch resource contents or call tools.
Concrete action commands use the existing { kind: 'action', action: { id, runtimeEpoch, command } } envelope:
skills-reload,mcp-reloadandplugins-reloadtakethreadId.mcp-reconnectandmcp-loginalso takeserverName.mcp-set-enabledtakesthreadId,serverNameandisEnabled.account-loginandaccount-logouttakeproviderand nonemptyoperatorConfirmation;account-login-canceladditionally takes the initiatingloginActionId.
Capabilities distinguish each operation. Extension changes require an idle owned session and cannot bypass a permission wait. Codex MCP reload queues a native refresh for all loaded sessions, so HQ requires those sessions to be idle and fences new execution until its outcome is known. Claude reconnect and enable/disable target one session server. Skill reload uses the runtime-authorized managed workspace and retains explicit-only native commands. Plugin reload preserves native configuration and reports held when the native interface defers the change. A reload applied with native plugin errors retains controlState: 'applied' and completion: 'failed'. HQ does not install plugins, forward arbitrary RPC, replace MCP configuration, disable normal hooks or inject global settings.
Receipts include nullable provider and controlState (accepted, applied, held). Native login initiation is acknowledged as accepted, with null completion; it does not claim authentication succeeded. Exact native completion sets completed, failed or interrupted. A matching cancellation acknowledgement records interruption even when its failure notification arrives first. OAuth completion matches the exact native login ID and, for MCP, the server and session as well. Unknown completion identities do not resolve another action. Native permission approval and plan authority remain separate.
login returns state (pending, completed, failed, canceled, unavailable), nullable authorizationUrl, canCancel and nullable observedAt. URLs are sensitive live presentation: HQ never opens them automatically or persists them, login IDs or tokens. Codex exposes its native ChatGPT browser login and cancellation, logout and configured MCP OAuth. Claude's interactive login and MCP OAuth commands have no qualified structured challenge surface and remain explicitly unsupported. Claude logout uses the native CLI, discards human-oriented output, confirms process-group shutdown and invalidates account-derived observations. Unconfirmed logout cleanup remains owned by the provider and prevents HQ from releasing runtime ownership.
Close the selected provider's managed sessions before changing its account. Pending or uncertain provider-wide changes fence new execution in that runtime; the other provider remains usable. status reports lazy native connection health without starting a provider; ready transport does not prove credentials are valid. provider-actions pages global receipt metadata in descending creation time/UUID order; thread-scoped controls remain in actions. Inspect selected receipts after interrupted requests instead of resending them. Restart retains receipts, loses sensitive login presentation, and never restarts authentication or inference. Native account reads cannot prove an interrupted sign-out's exact effect. A new account action requires fresh explicit operator intent after runtime ownership recovery.
Catalog observations are coalesced only while a read is in flight, with no retained performance cache. Native changes invalidate those joins; subsequent reads obtain fresh metadata. Native rejection is recorded as failed, while timeouts and lost responses remain uncertain until exact late evidence arrives. A late acknowledgement clears only its own pending control and does not resume queued work or grant authority.
Native qualification on 2026-10-23 used Codex 0.163.0 and Claude Code 2.1.309. Both exposed account metadata, configured MCP servers/tools and explicit-only skills after native reload. Codex exposed resource/template metadata and completed native MCP reload; Claude completed reconnect, disable/enable and plugin reload without inference. Disposable native account configuration verified Codex browser-login initiation/cancellation and both providers' logout. An owned loopback OAuth server verified Codex MCP challenge initiation and exact terminal denial through its native callback. User credential storage was not altered. These observations qualify the listed operations on this host; they do not establish other platforms or remote authentication success.
Native configuration
models returns native model IDs, display metadata, nullable effort choices/defaults and bounded continuation. Model/effort names are not fixed to a CLI release. Codex retains its native cursor; Claude uses an inventory revision and stable model key. Changed catalogs invalidate continuation.
configuration accepts provider, authorized workspaceRoot, optional managed threadId, limit and cursor. Session selection must match both provider and workspace. The result includes nativeId, workingDirectory, known effective settings, bounded instructionSources with isComplete, and one page of native choices (agent or profile). Null means unknown or unavailable, never a fabricated default. A null choices value means the optional native inventory is unavailable; an empty array means a known empty inventory. Effective settings remain readable when Claude omits its optional agent catalog. Native inventory is bounded to 4096 entries; choice continuation binds the query and inventory revision. Instruction source paths are metadata only; HQ does not load those files through this operation. Claude uses an owned session's settings or a disposable workspace-scoped read process with no user prompt. At most four workspace read processes run concurrently; cancellation and runtime shutdown confirm their exit, including reads still initializing. Unconfirmed cleanup retains runtime ownership. Whole configuration documents, source layers and credentials are not returned.
thread-create and thread-resume accept optional settings: model, effort, outputSchema, permissionMode, systemInstructions, developerInstructions, plus Codex sandbox or Claude nativeAgent. The workspace is the native working directory. Codex system/developer instructions map to native base/developer overrides. Claude system instructions use --system-prompt, and developer instructions use --append-system-prompt. Omitted fields keep native defaults. These are explicit native settings, separate from HQ plan authority and native tool approvals. The action envelope byte limit still applies.
Startup settings require an idle restart, using close followed by explicit resume; they cannot be attached to active input. Codex may retain a loaded idle thread after unsubscribe and ignores resume overrides there. HQ rejects startup overrides on such a thread; restart the HQ runtime's native connection before applying them. It never acknowledges an ignored model change. Claude opens a new native process for explicit resume, preserving the exact native session ID. Native profile selection and Codex native agent selection are not exposed as startup overrides; discovery does not imply a supported setting change.
A message or queue accepts optional settings: { model?, effort?, outputSchema? }, applied at delivery. Codex accepts an output schema per turn; Claude requires it to match the process schema, except that HQ can install its shared directed-request envelope through an idle restart of the same native session. Codex uses native turn parameters; Claude acknowledges a native flag-layer update before sending that input. Startup instructions, permission settings and agent selection are excluded from this live path. Queue entries retain explicit settings; omitted options inherit native settings at delivery. Native rejection, partial control failure or lost acknowledgement never causes automatic input replay. Ordinary settings are not written to native configuration files. Confirmed startup instruction selections stay in the owned controller's memory; settings unavailable from native observation after reconnect are null.
Managed threads expose nativeLineage: { sessionId, parentThreadId, forkedFromId } | null, nullable forkedFromThreadId, and isArchived. Native IDs are kept exactly as observed; a session ID can differ from the thread ID. Unknown later fields never erase confirmed immutable lineage. A contradiction uses JG_AGENT_HQ_PROVIDER_FAILED. Native Claude history omits the fork source, so confirmed HQ fork metadata enriches that read. A fork whose creation is uncertain is never automatically created again; explicit replacement recovery uses the recorded source. Attaching an uncertain fork requires native evidence of its source relationship.
File inputs and complete results
Text and native skill inputs accept optional files: [{ path, mediaType }]. Paths may be absolute or relative to the managed workspace and must resolve inside an operator-authorized root. HQ opens a checked regular-file descriptor, validates UTF-8 or the declared format signature, and binds the canonical path, byte count and SHA-256 before admission. It rereads and verifies that binding immediately before native delivery. Missing, changed or escaping queued files fail without sending input. Symlinks cannot redirect delivery outside the trusted roots. URLs are never fetched.
The fixed limits are 8 references, 2,097,152 aggregate file bytes and 65,536 bytes per UTF-8 text file. Empty files, NUL-containing or invalid UTF-8 text, wrong signatures, unsupported media and oversized input are rejected. The 32,768-byte action envelope contains references, never base64. File bytes are encoded only inside the native 8 MiB frame; the file limits leave room even for worst-case JSON escaping.
Codex supports PNG, JPEG, WebP and UTF-8 text. Images use native image data inputs; text files become complete text inputs labeled with their canonical path and hash. Claude supports those formats plus GIF and PDF, using native image/document blocks. Media signatures establish the declared format, not complete image/PDF validity; native decoder or model rejection remains a provider failure. capabilities.inputSchema includes supportedMediaTypes, maxFiles, maxFileBytes and maxTextFileBytes. Native model-specific media limitations remain native errors.
Select a bounded JSON outputSchema in session settings or, for Codex, on a message/queue entry. Schemas are limited to 8,192 serialized UTF-8 bytes, 16 levels and 1,024 visited nodes, and must be supported by the installed Zod JSON Schema validator. External references are rejected and no remote schema is retrieved. Codex sends the selected schema as turn/start.outputSchema. Claude starts its owned structured process with --json-schema; a different schema requires close and explicit resume of the same native session. Its capability descriptor marks this as idle-restart.
Selected action receipts expose nullable result: { state, format, text, structuredOutput, isComplete, failure, usage }. Results come from the complete native final message/result, independently of the 65,536-character history/live preview. The serialized result budget is 8,257,536 UTF-8 bytes, leaving 128 KiB of the 8 MiB IPC frame for action, plan and transport metadata. Structured results retain the complete parsed value with text: null, avoiding a second escaped copy of the same JSON. This admits a 1 MiB UTF-8 report even when JSON escaping expands its content. HQ validates native structured output against the requested schema without stripping fields. Invalid or missing schema output fails the turn; oversized capture fails explicitly with no saved excerpt. Invalid but bounded JSON remains untrusted evidence in structuredOutput. The result is saved with native turn completion, survives a runtime restart and is omitted from action-list reads.
failure is null or native-error, invalid-structured-output, result-too-large, or missing-result. A native turn can complete without a final assistant message; without a requested schema, that remains completed with isComplete: false and missing-result. It cannot supply a complete report. Interrupted native execution remains interrupted unless capture itself exceeds the result budget. None of these outcomes causes automatic replay.
usage is nullable and contains known inputTokens, outputTokens, cachedInputTokens and accounting scope. Built-in adapters expose per-turn observations, not billable counters; missing or malformed optional usage is unavailable without invalidating essential completion evidence. Native MCP/dynamic tool calls and Claude tool blocks appear as bounded tool items. Truncation and live gaps remain explicit. Complete final results are authoritative retained records, not a parallel conversation cache.
Claude history
Claude connects on its first read through claude --print --input-format stream-json --output-format stream-json --verbose. No user prompt, bare mode, permission bypass or SDK dependency is used. Runtime status reports this adapter as idle, ready or unavailable, with its observed CLI version. This session-less process closes after 5 minutes without a read and opens again on the next one; while it is closed, status reports idle with the last observed version. An unavailable native lane requires restarting the host after resolving the native problem.
The structured CLI interface has no session-history listing operation. The Claude-owned reader therefore uses direct UUID JSONL files in the native project's directory beneath CLAUDE_CONFIG_DIR, or the normal .claude directory. CLI and transcript version labels do not gate reads. Required session identity, workspace, parent links, timestamps and message roles are validated, while additive fields and mixed version labels are tolerated. Claude Code can write a hidden record, such as an attachment or a scheduled-task event, a few lines before the record it follows; the reader holds it back until that parent is indexed, and one whose parent the read doesn't reach starts a turn of its own. A message whose parent is unknown is never accepted. Encoded project keys longer than 200 characters, directory/file links, malformed required fields and records larger than 8 MiB report unavailable. Discovery never recurses into subdirectories, including excluded directories. Unsupported discovered sessions have historyState: unavailable; they do not prevent supported sessions from being listed.
Discovery verifies the workspace recorded inside each transcript before returning its identity or title. A matching native directory name alone is insufficient because punctuation replacement can produce colliding directory keys. Native working directories may be the selected workspace or its subdirectories, including removed subdirectories. The nearest existing path ancestor must resolve inside the workspace without excluded segments; sibling paths and symlink escapes are rejected. Foreign workspaces are omitted. If a file's workspace cannot be established, discovery reports unavailable rather than inventing an empty result.
The existing SQLite worker incrementally records file identity, complete-line offsets, message identities and parent links. It does not save message bodies. File identity includes the current reader projection revision and a native prefix hash, so source changes invalidate the derived projection. Each worker batch inspects at most 128 directory entries and reads at most 16 MiB of new transcript bytes, one file at a time, which fits the largest record. The adapter continues batches with a host-event-loop yield between them, within a 30-second operation budget. If that budget is reached, the read reports unavailable and subsequent reads continue indexing. At most four incomplete discovery iterators remain open. Shutdown closes them. Discovery continuations use that projection without starting another directory scan; start a fresh first page to refresh discovery. Removed files are discarded from the projection after a complete refresh. No additional cache infrastructure or browser cache is used.
Current-chain membership and turn starts are indexed as metadata. Ordinary item and turn continuations seek those indexes; they do not walk the full chain per page. A changed branch rebuilds membership before exposing that revision. Repeated native message identities can replace content while retaining their parent; a changed parent for the same identity is an unsupported format.
Selected history reads refresh only that file, retain incomplete tails, and detect truncation or replaced identity. Changed branch/file revisions invalidate item and turn continuations. Only the current parent-linked conversation chain is returned. Text is reread from the native file for the selected page and normalized as plain text. Thinking blocks are left out, and a record holding only thinking is hidden, as are <task-notification> records. Compaction summaries, <local-command-stdout> output and interruption markers are notice items, and a slash command is a user-message reading /name args. A ! shell input and its output are command items, the output's callId being the input's UUID. A tool call shows its native name and input, with the block's ID as callId, which is null for a record calling several tools; its result is a tool item carrying that ID, failed when it has is_error and succeeded otherwise. An Edit or Write result whose metadata has a filePath and a structuredPatch is a file-change item reading the path and its patch lines, or a created file's content, every line of which counts as added. A result Claude Code saved to a file reads Output too large (<size>), saved to a file followed by its preview, without the file's path. Other non-text content has explicit native block labels. The index stores each entry's presented kind and visibility, so evidence kind filters match items and hidden records never shorten a page. An unknown record containing content is returned as an unsupported item when its identity and chain are trustworthy; it cannot imply an agent response, idle state or completion. Each selected page reads at most 4 MiB of record bytes beyond its first record and returns at most 4 MiB of serialized item presentation, so a large-text page can contain fewer rows than its requested limit.
File timestamps and records do not prove that a native process is idle or a turn completed. Claude file-backed thread and turn states are therefore unknown, with no invented completion timestamp. Explicit native custom titles are preserved; a missing title remains null. A turn starts at a message the operator sends, including a slash command; task notifications, compaction summaries, local command output, interruption markers and ! shell records belong to the turn before them. Grouping follows parent links and is not an authoritative execution boundary.
Thread control and live input
Send { kind: 'action', action: { id, runtimeEpoch, command } }. Generate one UUID per user intent, take the epoch from status, and retain the complete envelope until its receipt can be read. The validated envelope is limited to 32,768 UTF-8 bytes. It is recorded before native writes. Reusing the UUID with the identical envelope returns the saved receipt without dispatch; a changed envelope conflicts. This check also works for an original request read after restart. A new UUID with an expired epoch cannot execute.
| Action | Fields and behavior |
|---|---|
thread-fork |
Source threadId, child name. Requires an idle owned source or an inactive managed source. Creates a distinct native session and retains forkedFromThreadId and exact native lineage without inheriting plan authority. |
thread-archive |
Idle owned threadId. Confirms native background cleanup, archives natively and releases control. |
thread-unarchive |
Inactive archived threadId. Unarchives the exact native session and leaves it inactive. |
thread-compact |
Idle owned threadId. Records native admission separately from its correlated turn completion. Clears the live presentation and requires a fresh history read. |
thread-revert |
Idle owned threadId, exact beforeTurnId. Removes that native turn and later history; files and historical HQ receipts remain unchanged. Clears the live presentation and requires a fresh history read. |
thread-create |
provider (a registered provider ID), workspaceRoot, name. Creates an empty native thread and sets its readable name before reporting acknowledgement. |
thread-adopt |
The same fields plus nativeId, operatorConfirmation. Explicitly records an external thread as managed. Codex accepts it unloaded or idle, between turns of a terminal that still holds it, and resumes it without settings overrides; Claude requires qualified workspace history and the native idle/session-identity checks, and refuses while a terminal runs it. |
thread-resume |
threadId, operatorConfirmation. Resumes an inactive managed thread; HQ does not fetch its complete conversation bodies. |
thread-recover |
threadId, operatorConfirmation, recovery. The recovery is { kind: 'resume' } for a known native identity, { kind: 'attach', nativeId } for an inspected identity after uncertain creation, or { kind: 'create' } to explicitly create a fresh empty thread when no native identity was recorded. The latter can leave an empty orphan from the uncertain creation; it never repeats a message. |
thread-rename |
threadId, name. Changes the name only after the matching native acknowledgement. |
thread-close |
threadId. Requires an idle thread and pauses its queue. Codex cleans and verifies its native terminal list before unsubscribe. Claude closes the owned CLI and proves process-group exit before releasing control. |
message |
threadId, input. Starts a turn only while idle. |
steer |
threadId, expectedTurnId, text. Codex uses native turn/steer. Claude rejects this operation; it never substitutes interruption. A finished or changed turn cannot become a new turn. |
queue |
threadId, expectedTurnId, input. Saves an explicit next-turn message or skill invocation, never a permission or question answer. |
queue-send |
queuedActionId. Explicitly authorizes a still-undelivered queue entry, including a queue paused by restart or interruption. |
queue-cancel |
queuedActionId. Cancels only an undelivered queue entry and clears its saved body. |
interrupt |
threadId, expectedTurnId. A native request acknowledgement leaves the thread stopping until native completion evidence arrives. |
native-reply |
threadId, expectedTurnId, approvalId, response. Answers one exact current native request; cannot consume queue text. |
Message and queue input is a closed union: { kind: 'text', text } or { kind: 'skill', skillId, arguments }. Text is nonempty and at most 65,536 characters; skill arguments may be empty and cap at 16,384 characters. The envelope's UTF-8 limit still applies. A selected skill is resolved through the target thread's current native catalog before admission. Unavailable, missing or disabled selections cannot dispatch. Normal text needs no catalog read. Steering retains its text field and exact active-turn target.
Names use lowercase ASCII letters, digits and hyphens, start with a letter or digit, and have at most 32 characters. Names are unique without regard to case in one state directory. Creation and adoption resolve collisions atomically with ordinal suffixes such as build-usage-2, truncating the base to keep the complete name within 32 characters. Explicit rename rejects an occupied or pending name. Provider/native-session pairs remain unique. At most 32 native sessions are controlled concurrently; close idle sessions to release capacity. Native discovery remains independent of that limit. Confirm that other native CLI controllers are closed before adoption, resume or recovery. An unloaded native status cannot prove another CLI has stopped. HQ provides exclusive coordination within one canonical state directory; it does not impose a global lock on independent CLIs. Recovery is permitted only after the producing runtime has stopped and its ownership evidence has been inspected.
Codex may keep an idle native thread loaded after thread/unsubscribe, as when a terminal still holds it. An explicitly confirmed resume may reacquire that thread when its known HQ control is inactive and managed, without settings overrides. Active native threads, uncertain HQ ownership and already controlled idle sessions remain blocked. Other adoption and recovery checks are unchanged.
Receipts separate delivery (queued, submitted, acknowledged, uncertain, failed, canceled) from nullable native turn completion (completed, failed, interrupted). Acknowledgement means the exact native request was accepted, not that the turn or tool completed. Permission replies remain submitted after a pipe write and become acknowledged only when Codex resolves the exact native request or Claude returns a tool-result block for the exact tool-use identity. Claude cancellation expires the prompt and leaves an unacknowledged written reply uncertain. Resolution does not prove a tool ran. The receipt includes HQ/native identity, request phase, digest, timestamps, safe failure classification and optional related action. Only selected receipts expose undelivered queuedInput and the nullable complete native result; collection reads omit both. Queued input retains prepared text, native skill identity, content-bound file references and explicit turn settings, never file bytes or skill content. It is cleared after delivery acknowledgement or cancellation.
A missing acknowledgement never triggers an automatic resend. Exact late responses reconcile delivery, including completion that arrived first. Native output loss marks the affected provider controls and submitted work uncertain. A Claude process failure affects its own session; a Codex transport failure affects its Codex cohort. Restart quarantines controls from an interrupted runtime; explicit recovery preserves the HQ identity and name. Successful shutdown proves native process-group exit before marking controls inactive. Unknown creation without a native identity remains uncertain.
Normal native turn completion may advance ready queued entries once, in creation/UUID order. Failure, interruption, explicit interrupt admission or restart pauses the queue. Permission prompts do not advance it. An unacknowledged start or metadata change blocks dependent starts until its outcome is reconciled or the stopped runtime is recovered explicitly. A turn's completion does not settle an unresolved steering receipt.
Native replies use one of these closed response forms:
{ kind: 'decision', decision: 'accept' | 'acceptForSession' | 'decline' | 'cancel' }for command/file approval, restricted to the choices Codex offers.{ kind: 'permission', decision: 'accept' | 'decline', scope: 'turn' | 'session' }for additional permissions. Acceptance copies only the complete displayed native scope. Unknown permission fields cannot be approved.{ kind: 'answers', answers: { [questionId]: [answer] } }for native questions. Every requested question must have an answer;allowsMultipleindicates whether several answers are allowed. Codex questions accept one answer, and predefined choices are enforced when free text is unavailable.{ kind: 'elicitation', action: 'accept' | 'decline' | 'cancel', content }for MCP forms and URL requests. Accepted forms require an object matching the exact native JSON Schema. URL acceptance, decline and cancellation requirecontent: null. Responses never open a URL or fetch a schema.
Approval records expose replyActionId as soon as a reply is admitted, so reconnecting clients can read its receipt and avoid offering another answer while native resolution is pending. approvals accepts threadId, optional state, limit (default 32, maximum 128) and a query-bound cursor; it returns { data, nextCursor } in descending (createdAt, id) order. Indexed seek reads do not load the complete history. State filters are live views, so refresh the first page to observe new records or transitions above a continuation position.
Each record contains identity, kind, isBlocking, state, replyActionId, timestamps and nullable presentation. Presentation contains the complete native title, detail, decisions, questions and optional elicitation metadata. It is available only for a matching live pending request. Resolved or expired records retain metadata after reconnect and restart, with presentation: null. Restart and native disconnect expire pending requests; they never recreate response authority. expired means the request is no longer answerable, not that a submitted response was rejected. Prompt bodies and answers remain in memory. SQLite retains only native request identities scoped to their managed thread, scope fingerprints and reply receipts. Fingerprints use a per-store HMAC key in the private 32-byte action.key. Keep that file with the database; a missing, invalid or mismatched key on a nonempty store fails closed. Ordinary input bodies are not saved by HQ. Undelivered queued inputs are saved until acknowledgement or cancellation. Complete native final results are retained on the selected action receipt; streamed token and tool-output bodies remain native.
Claude native control
Each controlled session has one native claude process in the authorized workspace while it is in use. A session idle for 60 minutes since its last turn ended, or since it opened, gives up its process: HQ reads its native settings once, closes the process and releases its agent bridge, and the session stays idle with no state transition. Its skills and configuration reads answer from what it kept, an interrupt is refused as for any idle session, and stopping or releasing it needs no process. Input, renames, extension control and MCP reads first reopen it with --resume, or --session-id when it never had input, its kept settings and a new agent bridge; a collaboration request reopens it with the HQ envelope directly. A parked session keeps its controlled-thread slot, of 32 per runtime, and a runtime restart treats it like any other managed Claude session. A running or waiting session is never stopped. HQ adds structured input/output, replayed user-message acknowledgements, partial-message output and --permission-prompt-tool=stdio. It applies only explicitly selected startup options, including model, instructions or an output schema. It preserves normal native settings, session persistence and permission handling. Native skills remain available. Native idle initialization, session identity and matching acknowledgements establish capability. Version labels and missing diagnostic version metadata do not disable compatible controls or file history.
Creation chooses a native UUID without inference, then names that exact session. Resume uses native --resume and verifies the session-id fence on the name control before marking the session idle. The operator's handoff confirmation establishes that another CLI controller is closed; file timestamps and file-backed unknown states do not establish this. Independently running CLIs are outside HQ's state-directory coordination boundary.
An ordinary message is submitted only while idle. Its native UUID becomes the turn identity. A matching native replay or result acknowledges delivery, and the native result establishes completion. A request-success envelope can acknowledge a name change or interruption without a response body. HQ never writes queued follow-up input into Claude's own busy queue; the shared HQ queue dispatches it only after the previous result. Interruption acknowledgement alone does not establish a stop. An error result is classified as interrupted only when the qualified native interruption marker was observed for an explicitly interrupted input. Other terminal errors remain failed.
Claude questions use exact question text as their answer identity and expose whether multiple selections are supported. Claude can_use_tool permissions use response { kind: 'decision', decision: 'accept' | 'decline' }; question replies use { kind: 'answers', answers }. The response kind permission with a turn/session scope belongs to Codex additional permissions. Tool permissions offer accept or decline for the displayed input only. Complete request details include native policy hints and reasons as untrusted display data; none authorizes a response. Unsupported permission fields or interaction surfaces receive an unsupported-request error. Requests requiring a native interaction card cannot be answered with generic approval; supported AskUserQuestion cards use their typed answer contract. Session-wide permission changes are unavailable; HQ does not apply native permission suggestions automatically. A tool request for the session's own agent bridge, named mcp__<bridge server>__<tool>, is allowed at once with its unchanged input and records no approval. A reply is written through the separate control channel and acknowledged only by the matching tool result. Tool execution can still fail after that acknowledgement. Reused raw request IDs in different CLI processes have distinct controller scopes. Prompt bodies and replies remain out of SQLite.
Both providers support turn-correlated MCP form and url elicitation. Forms use bounded local JSON Schema validation (8 KiB, 16 levels, 1,024 nodes); responses stay within the action envelope’s 32 KiB budget. Unknown modes, unsupported schemas and Codex requests without a native turn are denied. Elicitation URLs are HTTP(S) links without embedded credentials; applications decide how to display or open them. Never render native strings as HTML or terminal instructions, and never infer a permission decision from model output or form metadata. Codex acknowledges an elicitation reply through its exact native request-resolution event. Claude supplies no equivalent form-reply acknowledgement: a successful write stays submitted, and terminal completion or cancellation expires the request and makes its unresolved reply uncertain. Retrying that same action returns its receipt without sending again.
Partial text uses temporary live identities until the final native message UUID arrives; that replacement shares the existing global live-item budget. Live frames and streaming previews follow the history presentation: thinking is left out, task notifications aren't shown, and a live Edit or Write result is a file change. Persisted history reconciles matching final items through the same content normalizer. Closing the session proves owned process-group exit, including native background children, before HQ releases control.
Native skill catalog
skills accepts threadId, optional search, limit (default 32, maximum 128) and cursor. It operates on a controlled session and never adopts or resumes an external session. Codex uses native skills/list for that thread's authorized workspace, plus the skills in the operator's Codex extra roots; Claude uses that session's native initialization command list, which includes explicit-only skills. Its refresh endpoint omits those skills, so HQ does not use it. Close and explicitly resume the session to refresh Claude's catalog. Inactive or unsupported sessions report unavailable, with no successful-empty fallback. Native discovery errors produce partial and an unavailableCount. observedAt records when the native inventory was observed; it stays fixed for a Claude session and is null when unavailable.
Rows contain an opaque id, native name, description, nullable argumentHint, kind, path, scope, prerequisites and requiresExplicitInvocation, plus native availability as isEnabled. Codex supplies skill identity, source/scope, enabled state and declared tool prerequisites. Claude supplies native command names, descriptions, argument hints and a built-in marker when available. Missing fields are null or kind: 'unavailable'; HQ does not inspect skill files to infer them. The one exception is Codex extra roots, which native discovery does not list: HQ reads each skill's single-line name and description frontmatter from <root>/<directory>/SKILL.md, reports it with scope host, skips linked directories and files, and counts unreadable entries in unavailableCount. A native skill with the same name takes precedence. Declared prerequisites are metadata, not proof that an integration is connected. Invocation rules remain with the CLI, including explicit-only skills.
Both native inventory interfaces are unpaged. HQ accepts at most 4,096 native rows and 1 MiB of normalized catalog metadata per read, then provides stable ID seek pages guarded by the complete inventory revision and query. A changed inventory invalidates continuation. Only metadata is read, never skill bodies or complete conversation histories. Concurrent reads for the same thread share their in-flight native request; Codex results are not retained as an HQ cache. Claude's native initialization metadata remains attached to its owned connection until that session closes. Native refresh and discovery caches remain provider-owned.
skill-preview accepts threadId, skillId and arguments. Its result is ready, missing, disabled or unavailable, with the selected metadata and exact text only when ready. Codex previews $name arguments and receives an explicit native skill selection containing its native name/path alongside that text. Claude previews /name arguments and performs native slash-command loading. Native same-name precedence applies. Preview never sends user input. Explicit message and queue actions share the established action ledger, ownership checks and delivery/uncertainty rules. They grant no tool permissions and do not expand the invoked workflow's authority.
Trusted runtime construction also accepts codexExtraSkillRoots?: string[] and claudeAdditionalDirectories?: string[]. Paths must resolve to existing absolute directories. Codex receives them as capability roots of each thread HQ starts, so other sessions on the shared daemon, including terminal sessions, never see them. Forks inherit them. A session Codex later reloads from disk loses native discovery of those skills, while explicit invocation through the HQ catalog still works, because it names the skill file. Claude receives native --add-dir arguments, which also grant native file access to those directories. These inputs belong to the operator and cannot be changed through IPC commands. The installed serve command exposes repeated --codex-extra-skill-root and --claude-additional-directory flags. No global skill installation or coding-instruction change is performed.
Control reads and reconnect
| Command | Fields | Result |
|---|---|---|
managed-threads |
workspaceRoot, optional limit, cursor |
Creation/UUID seek page of HQ records, including inactive and uncertain states. |
managed-thread |
threadId |
One HQ record or null. |
managed-thread-name |
name |
One HQ record or null, with case-insensitive lookup and the same workspace authorization. |
suggest-thread-name |
topic |
An editable lowercase label, with a currently free suffix when needed. This read does not reserve it. |
receipt |
actionId |
One receipt or null. |
actions |
threadId, optional limit, cursor |
Creation/UUID seek page of action metadata without queued bodies. |
events |
threadId, optional after (default 0), limit |
Ascending durable sequence page and hasMore; continue after the final sequence. |
approvals |
threadId, optional state, limit, cursor |
Durable approval metadata pages with nullable live presentation. |
live-items |
threadId |
Active/awaiting-flush item window, hasGap, revision and unchanged. Optional afterRevision omits bodies for an unchanged window. |
The installed CLI exposes managed-thread-name --name <name> and suggest-thread-name --topic <topic>. Suggestions normalize punctuation to hyphens, keep at most 32 characters, and use session when the topic contains no ASCII letters or digits. Creation checks availability again inside its admission transaction. All action targets, histories and relationships continue to use immutable HQ/native identities. Name lookup never adopts, resumes or starts a native session.
Control pages default to 32 and cap at 128. Seek cursors are bound to their complete query and ordering. Reads recheck allowed workspaces. Rejected controls retain their known thread association, and native availability failures appear in each affected thread's replay. Lifecycle replay contains metadata, not token or tool output. Each provider has a bounded native lifecycle backlog; overflow stops that source and marks its state uncertain rather than losing events silently. Other providers retain their independent connection and admission state.
Live item presentation is bounded to 128 items per cached thread, 32 windows and 4 MiB of serialized presentation across the runtime, including escaped text and identifiers. This leaves room inside the IPC response limit. Display text uses the same truncation contract as native history. Eviction, an unavailable window or missing chunks sets hasGap. Completed items remain awaiting-flush after thread closure until a native history page holds the same item, with exactly matching normalized content or, for an item with an outcome, the same final outcome, or the bounded cache evicts them. The outcome is enough because Codex shortens long command output when it saves it. Read persisted history and the live window together after reconnect; a process restart cannot reconstruct unflushed in-memory content.
Items expose only normalized presentation fields, and one mapping per provider presents items, live items and session-evidence alike, as the provider's CLI shows them. Kinds are user-message, agent-message, command, tool, file-change, notice and unsupported. callId pairs a tool call with its result, or is null. outcome is succeeded, failed, or null when unknown or still running. files lists each file a file change touched, at most 128, with its added and removed lines, and is empty on other items. outcome and files come from the full native record, so textLimit never changes them. Unknown native kinds report unsupported without copying arbitrary payloads. Display text is bounded to 65,536 UTF-16 code units without splitting a surrogate pair; isTruncated reports omissions. An unsupported or oversized native transport frame fails explicitly. createdAt is the item's time in Unix milliseconds, or null when the provider records none. A Claude item takes its record's timestamp, and a live one the time its frame arrived. A Codex item takes its turn's times, which Codex records across all of its history: an operator message takes its turn's start and every other item its turn's end, or its start while the turn runs. A Codex history page reads the turns of its items newest first, 128 at a time and at most 8 times, so a recent page takes one read and an item whose turn lies further back has no time. A live Codex item takes its running turn's start.
Codex leaves reasoning items out as it reads them, so a Codex page can hold fewer items than its limit. contextCompaction is a notice with empty text, because Codex records no summary. webSearch and imageView are tool items reading the query or the image path. A command, file change or tool call takes its outcome from its status: completed succeeded, and failed or declined failed. files counts each change's diff, and an added or deleted file without hunks counts its whole content. A Codex item holds its call and its result together, so its callId is null. Claude's rules are under Claude history.
Session states
Every session has a live state: running, waiting (on the operator), idle, closed, failed or unknown. A managed session reports it from its HQ record. A session another client drives is observed and listed with ownership: 'external'; HQ sends it no input until thread-adopt records it as managed, and never stops, wakes or drives a session a terminal runs. A managed session HQ closed is observed again, so a terminal that continues it shows its state.
A Claude session's summary carries terminal: { kind: 'interactive' }, or { kind: 'background', attachId } with the short ID claude attach takes, while the latest claude agents --json run lists it with a pid, and null otherwise; Codex summaries always carry null. While it is set, thread-adopt and thread-resume of the session, and input to it, are refused with JG_AGENT_HQ_ACTION_CONFLICT, so a session HQ runs that a terminal resumed takes nothing from HQ until the terminal closes it. HQ's agent tools never join a Codex session it adopted, so a collaboration request to one is refused with JG_AGENT_HQ_CAPABILITY_UNAVAILABLE and waits with dispatchBlockedReason: 'provider-unavailable'. An approval request of a Codex turn reaches every client the daemon serves; when another answers first, HQ's prompt expires.
- Codex. The shared daemon broadcasts every thread's status. HQ records the states of sessions inside the allowed workspaces, reading each session's workspace once, and their items stay readable with
items. A session already running when the connection opens reports no change, so each time the connection opens, including after a reconnect, HQ reads the daemon's loaded threads (thread/loaded/list, 128 a page, at most 8 pages) and records each one's state from its status. An ephemeral session, which Codex doesn't save, such as a helper session Codex starts itself, is never observed. Observation runs while the Codex connection is open; the connection opens on the first Codex request. - Claude. While the runtime runs, HQ runs
claude agents --jsonevery 30 seconds, or every 5 seconds at thelivepace, each run limited to 10 seconds and 65,536 bytes of output, and joins its entries to Claude discovery bysessionId. An entry without apidisn't running, whatever itsstatesays, so it counts as missing. Otherwisestatus: "busy"and a background session'sstate: "working"are running,status: "waiting"(a question or a permission prompt, for either kind of session) and an interactivestatus: "blocked"are waiting,status: "idle"is idle, and anything else is unknown; an entry missing from a run is closed. Entries whosepidis a native process this runtime started, its managed sessions and its metadata process, are left out. A failed, late, oversized or malformed run changes nothing. Sessions HQ manages keep their managed records.
{ kind: 'session-states', afterSequence?, limit? } returns state transitions in sequence order after afterSequence (default 0), at most limit per page (default 32, maximum 128), with hasMore. Continue after the last returned sequence. Each transition has sequence, provider, nullable nativeId, nullable threadId (managed sessions only), ownership, nullable previousState, state, reason and recordedAt.
| Reason | When |
|---|---|
opened |
A session became idle without completing a turn: it was created, adopted, resumed or first observed idle. |
turn-started |
A turn started. |
turn-completed |
A running or waiting session became idle. |
waiting-for-input |
The session waits on an approval, a question or input. |
operator-stopped |
An operator interrupt or close stopped a running or waiting managed session. |
failed |
The session failed. |
disappeared |
An observed session vanished while running or waiting, or the native connection or HQ ownership was lost and its state is unknown. |
closed |
An idle session closed. |
Transitions survive restarts. Each new transition deletes up to 128 transitions older than seven days, and closed observed sessions are forgotten after seven days. A managed session's stopping phase records no transition of its own.
{ kind: 'active-sessions' } returns every running and waiting session, managed and observed, at most 128 and newest state change first, with isTruncated when more are active. Each comes as the summary threads returns, with its workspace root, name and timestamps. A managed session reads them from its HQ record. An observed Claude session's workspace is the cwd the latest claude agents --json run reported, so it is left out until the first run after the runtime starts lists it; its name and timestamps come from its indexed transcript, and are null and its state's change time while the transcript isn't indexed. An observed Codex session's summary comes from thread/read, and other providers' observed sessions from their optional readObservedThread. A session outside the allowed workspaces, or one its provider can't read, is left out.
{ kind: 'unstarted-threads', provider, workspaceRoot } returns that workspace's managed threads that no turn has started, at most 32 and newest first, with isTruncated when more exist: each was created empty with thread-create, has a native ID and no active or last turn, and is neither archived nor closed. Forks and adopted sessions start with native history, so they never count. Each comes as the summary threads returns, from its HQ record. thread answers for such a thread from its record too, and items with an empty page, so its session opens before the provider has written any history.
{ kind: 'observation-pace', pace: 'live' | 'background' } sets the Claude poll to every 5 seconds or every 30, taking effect with an immediate run; the runtime starts at background. A host sets live while a client follows sessions live.
When the Codex daemon restarts, HQ's connection ends: its sessions become uncertain, observed Codex sessions become unknown, and nothing is sent again. The provider reports unavailable until the next Codex read reconnects; recover uncertain sessions with thread-recover.
Subscription observations
The usage metadata is optional, so a provider may omit it. Built-in adapters populate unknown metadata with null. Windows expose period (five-hour, seven-day or other), nativeKind, group and scope. Scope contains a nullable model with id/displayName and a nullable surface. Codex derives period from the actual duration; Claude normalizes recognized session and weekly groups to 300 and 10080 minutes. Primary/secondary remains the native position, not the period's identity. Existing bucket and window IDs remain stable.
credits contains independent bucket observations with hasCredits, isUnlimited and an exact nullable balance string. Its null value means unknown, never zero; do not add balances across buckets or assume a currency. resetCredits contains a nonnegative safe availableCount, independent of the provider's potentially capped reset detail list. Native account IDs and reset detail rows are not exposed. Credit/reset metadata can be available even when quota windows are unavailable; it grants no permission and provides no reset-redemption or purchase operation.
Codex prefers a valid nonempty rateLimitsByLimitId map and falls back to rateLimits when the map is absent, null or empty, without merging both views. A single rateLimits bucket without a native limitId uses single as its bucket ID. Missing or malformed optional credit/reset metadata becomes unknown while valid windows remain visible. Invalid quota-map content does not bypass boundary validation through the fallback.
Usage retains actual bucket/window identities, optional native labels, reported used percentage, remaining percentage, window duration and reset time. Percentages are never estimated from token counts or summed across sessions. Reported usage above 100 is preserved; remaining percentage floors at zero. ordinaryUsageAllowed is nullable when the native response does not report permission.
available means a usable observation exists. exhausted requires Codex's native permission/reached signal or a fully used Claude meter that the native response marks active. Neither label authorizes a model operation. unavailable covers missing windows or an observation that could not be obtained. Failed refreshes preserve a measured snapshot as stale, with its original observedAt. The complete prior observation, including credit/reset metadata and scopes, shares this freshness; an account change clears every field and invalidates responses started for the prior account. Observations older than 60 seconds or past a reported reset are stale. Codex account/rate-limit notifications trigger a coalesced refresh. Claude auth notifications discard account measurements and invalidate responses started for that account; rate-limit events mark the observation stale until the current or next structured refresh succeeds.
Claude uses the experimental get_usage control with skip_behaviors: true, and validates the native rate_limits.limits rows rather than inferring quotas from session costs or reading credentials. The published provider control types (opens in a new tab) describe this protocol; HQ invokes the installed CLI directly. Independent model/surface windows stay separate. Null percentages are omitted; zero is retained when reported. Unsupported shapes are unavailable. Claude does not report ordinary-usage permission, so it remains null. Recognized session and weekly groups have normalized five-hour and seven-day periods; unknown groups retain their native identity with period other and a null duration. No private OAuth endpoint, credential scrape or status-line execution is used.
Every usage read requests current native observations; the last successful snapshot is retained only to explain freshness after a failure. There is no TTL cache that suppresses a native refresh. Multiple simultaneous reads share one pending refresh.
CLI
jg-agent-hq serve --state-root <directory> --workspace-root <directory> starts the host. Repeat --workspace-root to authorize several roots. Client commands use --state-root; history commands also use one --workspace-root and, where required, --native-id. History and usage accept --provider codex|claude (default codex). Paged commands support --limit and --cursor; items support --turn-id; thread discovery supports --search. CLI paths resolve from the caller's working directory. There are no shell commands or raw native RPC passthroughs.
Output is one JSON line with schemaVersion: 1. Read output includes the command kind and result. Startup reports kind: ready. Confirmed shutdown reports kind: stop with result.state: stopped. Failure output goes to stderr with a safe error kind/message and exit status 1. The CLI never prints diagnostic causes or native payloads in an error response.
jg-agent-hq session-states --state-root <directory> [--after-sequence <sequence>] [--limit <1..128>] reads one page of session state transitions. jg-agent-hq active-sessions --state-root <directory> reads the active sessions, jg-agent-hq unstarted-threads --state-root <directory> --workspace-root <path> [--provider <provider-id>] reads the unstarted threads, and jg-agent-hq observation-pace --state-root <directory> --pace live|background sets the Claude poll's pace.
jg-agent-hq request --state-root <directory> reads one complete HQ request from stdin, validates it, and prints the usual response. It accepts no extra target flags. This exposes action and reconnect operations without putting message text or question answers in command-line arguments. Its stop response acknowledges admission; use the dedicated stop command for confirmed shutdown.
Plan coordination
computePlanRevision({ body, metadata, milestones }) is the pure Node API for execution identity. metadata contains JSON-safe extensions: every frontmatter field except the administrative ones. Object keys use strict deterministic JSON, so their order is not contractual; array order is. The exact parsed body, including its line endings, is contractual.
Send { kind: 'plan', command } through the existing local request API. Every command selects target: { directory }, an absolute directory inside an allowed workspace. HQ reads plans through jg-plans observations and keeps every frontmatter field, including plan id, in extensions, except the administrative ones. Only created_at, updated_at, revision and status are administrative. Malformed, non-mapping or non-JSON metadata fails closed. Plans without protocol: "0.2" get no preferred-document conformance gate. A plan that declares protocol 0.2 must pass the jg-plans check at each stage that grants authority: plan approval needs plan.md to conform on its own, while finalization, sequence approval, authorization and execution checks need plan.md and milestones.md to conform together. A nonconforming plan stays readable, so status, inspection and revision workflows continue. approve, finalize and authorize reject it with JG_AGENT_HQ_PLAN_INVALID, and check-execution and admission stay closed.
Milestone contracts retain every requirement byte outside observed status/identity lines, plus preamble and all intervening/trailing shared sections. Only recognized Overview table spans and the three valid jg_skills binding fields are excluded; other milestone metadata remains contractual. HQ does not regenerate Overview tables. Statuses must be exactly pending, in progress or done, and milestone IDs/order participate in sequence identity. Ordinal renumbering alone does not change a milestone's identity. jg-breakdown assigns every milestone ID through jg-plans; HQ never assigns one. When present, a skills binding must match the current plan id, revision and fingerprint excluding exactly the four administrative keys. A matching binding never grants HQ approval or execution permission.
| Command kind | Required fields and result |
|---|---|
status |
target. Returns current execution revisions, full inspection hashes, skillsBinding (absent, current or stale), approval state and a bounded coordination summary with the completion count and unmatchedEvidence: current receipts whose milestone ID no longer appears in the sequence. |
milestones |
target, optional limit (default 32, maximum 128) and cursor. Returns a bounded ordered seek page. |
register |
target. Records the directory so snapshots capture its plan files, after checking it is an allowed directory outside excluded archive/backup paths. It takes no document locks and reads no plan file, so the plan may not exist yet. Returns { directory }. |
finalize |
target, action, expected. Requires every milestone to carry the ID jg-breakdown assigned, records the current revision and returns the unchanged milestone file. Receipts of a milestone the sequence no longer marks done stop counting. |
begin-revision |
target, action, expected, intent: 'plan' | 'breakdown', threadIds, checkouts. Closes execution admission and starts owned interruption/checkpoint work. |
revision-invoke |
target, action, expected, revisionId, skill: 'jg-plan' | 'jg-breakdown'. Explicitly opens the next native planning invocation after proven quiescence. It sends no model input itself. |
complete |
target, action, expected, milestoneId, actual checks and selected checkouts. Retains the verified contract/check references and actual worktree fingerprints, then advances the marker. |
approve |
target, action, expected, scope: 'plan' | 'sequence', presentationHash. The hash must match the exact presented plan or finalized sequence. Sequence approval also requires current plan approval. |
revoke |
target, action, expected, scope: 'plan' | 'sequence' | 'execution' | 'all'. Revoking a plan also revokes sequence and execution authority; revoking a sequence also revokes execution authority. |
authorize |
target, action, expected, mode: 'supervised' | 'auto', selected unique milestoneIds (empty only for a plan without a breakdown). Records permission for this exact execution scope without starting an agent. |
check-execution |
target, expected, authorizationId, nullable milestoneId, mode. Rejects missing, revoked, stale, wrong-mode or out-of-scope authority. It sends no native input. |
expected is the exact { planDigest, breakdownDigest } returned by inspection. Each mutating action has a UUID id, operatorId and source: { kind: 'user-action' | 'user-message', reference }. These are trusted local operator inputs. The module never infers approval from natural-language claims, an agent response or imported feedback. Its owner-only local IPC and files do not isolate hostile code executing as the same OS user. Native permissions remain separate action kinds with their own exact native request identities.
Milestones are optional. Finalizing a plan with no milestone file returns content: null and does not create a file. That unit needs plan approval and explicit execution authorization, with milestoneIds: []; execution checks, completion and native execution scopes select it with milestoneId: null. A real breakdown requires a nonempty selection of its stable IDs and its separate sequence approval. Null cannot select a milestone or bypass sequence approval. Whole-plan completion retains its exact plan contract/digest, actual check references and checkout evidence, with null shared-sequence digest. Completed work cannot be authorized again under that unchanged contract.
Skills bindings are optional for generic HQ plans. When present, their syntax must be valid and their plan ID, revision and structural fingerprint must match before finalization or execution. A valid but stale binding remains visible in status: plan approval and explicit revision invocations remain available so jg-plan can retain the old sequence until jg-breakdown refreshes its coverage. Malformed bindings fail as invalid artifacts. A stale binding never supplies sequence approval or completion, even when semantic execution digests are unchanged.
Every milestone has one **Milestone ID:** <uuid>, written by jg-breakdown, immediately below its **Status:** line. Blank lines may separate the heading from its status; no intervening requirement prose is allowed. In a protocol 0.2 sequence, a milestone's **Acceptance criteria:** coverage line follows its ID line and is milestone contract prose. Finalization rejects a missing, duplicate, malformed or misplaced ID and inconsistent framing, changes no file and returns the complete file for presentation before approval. Outside a revision, a changed finalized contract must begin a revision before it is finalized again. A raw or changed sequence cannot inherit finalized status or sequence authority. A done marker remains visible but blocks execution without a current completion receipt for its milestone ID.
The milestone contract includes its stable UUID, title without its ordinal, all requirement prose, shared sequence requirements and the approval statement. Status and identity marker lines are excluded from the prose; only the derived Overview table is excluded from shared text. Prose next to that table remains contractual. Sequence order is significant. Renumbering preserves execution identity; changed requirements, dependencies, scope or order require applicable approval. Full-file hashes preserve exact UTF-8 bytes and are distinct from semantic execution revisions. Administrative frontmatter, session labels, status advancement and coordination receipts do not change execution digests.
Plan and milestone artifacts are bounded to 1 MiB each; coordination evidence is bounded to 8 MiB and a sequence to 1,024 milestones. Reads do not scan planning trees. Milestone pages use ordinal seek ordering with unique ascending ordinals. Cursors bind the selected directory, page size, execution revisions and exact milestone inspection hash. Any changed artifact requires a fresh page; renumbering or status-only edits still preserve approval. A SQLite projection is rebuilt only when the selected milestone file's full hash changes. Every read verifies its source file; the projection is replaceable and never grants authority.
Mutations acquire the HQ metadata lock at <coordinationRoot>/.runtime/hq.lock (coordinationRoot defaults to stateRoot), then the public jg-plans document locks in plan-before-milestones order. Use serve --coordination-root <directory> when coordination ownership is separate from runtime storage. Locks cover bounded observation, preparation, journaling and file application; no model turn or native stop wait holds them. A busy document lock becomes JG_AGENT_HQ_ACTION_CONFLICT with the safe document-locked reason. There is no automatic stale-lock takeover.
SQLite persists exact prepared plan/milestone sources and timestamps before application. A plan source of null means keep; milestone operations are explicitly keep, write with source, or remove. A null recorded before-plan hash permits only coordination work. Replay accepts only each recorded baseline or its exact desired state. Markdown is applied before coordination. Every HQ replacement uses jg-fs checked atomic writes with explicit 0600; absent targets are created exclusively and privately before content is written. Strict bounded, same-handle, single-link reads remain in HQ. Inspection alone never changes file permissions.
After file durability succeeds, document locks must release successfully before SQLite marks the intent completed and clears its write bodies; HQ metadata ownership remains held through completion. A write, sync or release failure retains the prepared payload and closes admission. A crash after release and before completion is replayable. A failed completion acknowledgement is reconciled against the durable ledger: confirmed completion succeeds, an uncommitted intent stays pending, and an unknown outcome remains closed. Retry only the same original action and payload after inspection; changed files or payloads conflict. Nonparticipating writers still have a final comparison-to-replacement/removal race; these operations are not a multi-file transaction.
Accepted implementation input prepares aggregate and selected pending milestone progress before native admission. Message, queue and steering requests record accepted implementation scope; an in-progress marker does not assert that a provider has started or completed a turn. Progress is journaled and released before current authority is checked again for native admission. A retry of the same native action can finish its retained progress intent without regenerating timestamps; it still needs current runtime/authorization checks. Native control and permission actions do not advance progress.
Only accepted completion evidence advances a milestone to done. Aggregate done requires the complete current execution contract, applicable current approval/finalization and verified completion; administrative done markers alone do not provide them. Progress-only plan changes preserve revision and set updated_at once in the saved prepared source. An unchanged plan preserves its timestamp. The current finalization receipt retains the operator event, semantic revision, nullable canonical file hash and timestamp independently of write recovery bodies. Status reads expose pendingActionId for unresolved writes and never approve or resume work.
The installed plan-status and plan-milestones commands accept --state-root and --directory; the latter also accepts --limit and --cursor. Use request for the closed JSON mutation commands. The result is a tagged status, milestones, registered, finalized or execution-check object within the outer plan response. A retried finalization returns current status when its finalized artifact no longer exists or has changed.
Revision and progress recovery
Begin revision before invoking a planning skill. Execution admission closes under the shared short lock. The runtime interrupts exactly the affected managed threads and verifies their native background cleanup before granting an editing lease. It retains a checkpoint of contracts, UUIDs/status, original completion records, selected checkout claims and actual Git/file fingerprints. Metadata locks are released during native planning. Failed or uncertain cleanup keeps editing blocked. Stop jobs belong to the runtime and survive a client disconnect. A runtime restart requires an explicit new planning invocation; it never adopts the editing lease automatically.
The local host supplies checkout evidence as { repository, directory, claimReference, paths }. Paths are exact relative task paths. The core streams their contents, records link targets without following them, includes missing paths as deletions, and captures the current Git branch/HEAD. It does not stage or reset files and rejects traversal, parent-link escapes, .git and excluded archive/backup directories. Check commands and references are trusted operator evidence; this primitive does not execute arbitrary submitted commands or treat an imported agent claim as verification.
After native jg-plan revises the contract and an explicitly requested jg-breakdown regenerates a needed sequence, close the planning controller before finalization. jg-breakdown owns milestone identity and progress: it keeps the ID and status of a milestone whose requirements still hold and assigns new IDs to new work. Finalization carries completion evidence by milestone ID, so rewording keeps a verified completion without new checks. A milestone the new sequence no longer marks done has its receipts reopened, and done credit returns only with a new completion. Evidence whose milestone ID left the sequence stays in coordination.json and appears in status as unmatchedEvidence until the ID returns. A done marker without a current receipt for its ID blocks the plan.
A whole-plan checkpoint retains null milestones and an empty milestone inventory. When a revision removes a sequence because the remaining scope no longer needs one, its milestone evidence becomes unmatched; absence alone never resets progress, and whole-plan execution still needs plan approval and fresh authorization.
Finalization is journaled like other plan writes; retry the same action and payload after a partial write. Present the complete milestone file before sequence approval. Applicable plan and sequence approvals are required again after digest changes. Approval does not resume work: send a fresh authorize with the intended mode and scope. Execution authority is also bound to the current runtime epoch.
Guided native input adds plan: { kind: 'execution', authorizationId, milestoneId, mode, revision } or { kind: 'planning', revisionId, invocationId } to its action envelope. The controller binds this context atomically to the managed thread. Planning starts with the exact authorized native skill. A live thread cannot switch scopes; close it before an explicit resume under a different scope. Queued input retains its original scope and cannot run under stale approval or during revision. Ordinary input on a bound thread retains that exact scope and remains subject to its current gate, including after revocation or completion. It cannot silently become unscoped input. Renaming and explicit replies to an existing native permission/question remain separate, revision-guarded controls; neither starts a new implementation turn or restores execution authority. Native permissions still require the exact pending request.
Selected status/admission/queue observations detect external contract edits or milestone deletion, persist a closed gate and request exact bound-thread interruption. These observations grant no operator authority and report uncertain in-flight effects. The finalized baseline retains identities and progress, so a later revision checkpoint starts from them after direct CLI edits. Invalid artifacts fail explicitly. No directory scan or filesystem watcher is used. Status projects Revising, approval/breakdown requirements, Ready, In progress, Completed and Blocked independently of provider process state.
coordination.json has a hard size and record bound and rejects growth beyond it without discarding evidence.
Task collaboration
Use { kind: 'collaboration', actor, command } through hq.request, requestAgentHq, or the CLI's JSON request command. The response is { kind: 'collaboration', result }. These are trusted same-user host/IPC operations. Applications authenticate operators and supply { kind: 'host', operatorRef }; HQ compares that reference with the task owner. A host acting for a managed agent supplies { kind: 'agent', agentId }. This actor field is not an authentication mechanism for an untrusted client. Agent IDs are immutable managed-thread UUIDs, separate from provider/native IDs and display names.
Every mutation has an id UUID. Creation uses that same UUID for the new task, stream, grant or message/request. Mutations return { kind: 'operation', operation }, retaining the normalized command digest, actor, task, command kind and exact original result. Repeating the same identity and command returns that receipt; changing its payload conflicts. operation-read selects a receipt without replaying the effect. Reads return their selected result directly. Current membership/owner checks apply to replays and selected reads.
task-create requires workspaceRoot, title, policy and nullable plan. The policy specifies providers, workspaceRoots, effects, canSpawn, canManagePeerSubscriptions, canExecuteRequests, automaticDispatch, positive concurrencyLimit, nonnegative spawnLimit and automaticTurnLimit. No execution limits are inferred from a role. The policy must include the task workspace. Workspace roots and plan directories resolve through the runtime's allowed roots; metadata path prefixes are normalized relative paths, with '' selecting a whole workspace. References identify evidence and do not grant filesystem access.
Commands scoped to an existing task require taskId:
| Commands | Additional fields and behavior |
|---|---|
task-read, task-policy-set, task-plan-set, task-state-set |
Setters are owner-only and require expectedRevision plus policy, nullable plan, or state. Policy/plan share policyRevision; lifecycle uses lifecycleRevision. Active, paused and stopped states remain separate from native session state. |
tasks-list |
Uses workspaceRoot instead of taskId; returns only the host owner's tasks. |
agent-add, agent-read, agents-list, agent-policy-set, agent-state-set, agent-revoke |
Membership changes are owner-only. Add selects an existing agentId, nullable parentAgentId, nullable role, and permissions. Provider/workspace come from the managed thread. Set/revoke require current expectedRevision; reinstating a revoked membership also requires it. Child permissions cannot exceed their parent's current effective permissions when added or changed. |
grant-issue, grant-read, grants-list, grant-revoke |
Owner-only issue/revoke; read selects grantId, revoke also requires expectedRevision. Issue fields are nullable eligibleAgentIds (null means task-wide), exact skills: [{name, mode}], effects, scopes: [{workspaceRoot, pathPrefixes}] and nullable plan. Grants start at revision 1; revocation increments revision. |
stream-create, stream-read, streams-list, stream-archive |
Create supplies title/purpose. Read/archive select streamId; archive supplies isArchived and requires the host owner or creating agent. List optionally filters isArchived or the calling agent's subscribed: true streams. Archiving preserves history and outstanding requests. |
subscription-set, subscription-read, subscription-advance |
Set/read select streamId and agentId. Set supplies isActive, notify, and optional historyLimit from 1 to 128 when joining. Agents manage themselves or peers only with current peer-management permission; the host owner can manage either. New subscriptions start at the stream's current boundary. Explicit historical notification is bounded. Advance selects streamId and monotonic throughSequence for the calling agent. Unsubscribe preserves existing assignments and delivery records. |
message-publish, messages-list, messages-search, message-read, message-ack |
Publication fields are described below. List/search optionally select streamId; search requires a literal nonempty query. Selected read/ack use messageId. Only a delivery recipient can acknowledge. |
request-read, requests-list, request-claim, request-resolve, request-cancel, request-reassign |
Selected operations use requestId. List optionally filters agentId (target or assignee) and state. Claim requires an eligible active agent; claimable work additionally requires an active stream subscription. Resolve requires an assigned, non-dispatched request, outcome: 'completed' | 'failed' and 1–16 evidence references. Cancellation is available to the requester, assignee or owner. Reassign is requester/owner-only and requires expectedAssigneeAgentId plus targetAgentId; it requires no active or uncertain dispatch. Canceling dispatched work retains its binding until terminal collection and requests interruption of the exact native turn. |
deliveries-list, status, fanout-recover, operation-read |
Delivery list optionally selects recipientAgentId and state: 'unacknowledged' | 'undelivered' | 'all', defaulting to unacknowledged; agents can read only their own deliveries. Status reports capped counts and hasMore, including undelivered and unacknowledged counts, current revisions and background fan-out/dispatch failure. Owner-only recovery schedules another bounded pass. Receipt read selects operationId, returning null when absent. |
List commands accept limit (default 32, maximum 128) and cursor. Pages return summaries with nextCursor; they omit message bodies, grant scopes, member permissions and request arguments. Cursors bind the exact actor and filters and preserve a fixed high-water boundary. Lists are newest-first by record sequence; subscribed-stream discovery follows subscription sequence. Searches match a literal phrase in summary/body or stored reports through task/stream-scoped FTS5, rather than accepting FTS operators from the caller. No automatic retention deletion or message summarization runs.
message-publish requires streamId, messageKind: 'information' | 'request' | 'reply' | 'status', summary, body, references, nullable replyToMessageId and nullable request. HQ derives the author from the trusted actor. Summaries are at most 1,024 UTF-8 bytes, bodies at most 8,192 UTF-8 bytes, and references at most 16. Reply publications require their same-task message link. Archived streams accept linked replies to outstanding requests, but reject ordinary new traffic. Message content remains immutable.
A request publication supplies exactly one assignment mode: a targetAgentId with claimable: false, or null with claimable: true. Its other fields are nullable skill: {name, mode}, nullable grantId/grantRevision, bounded arguments, effects, scopes, nullable plan, targetReferences and optional returnStreamId. A selected skill requires an exact unrevoked grant revision. Publication and claim check current policy and scopes; claim also checks the receiving agent's eligibility. Requests without a skill require no invented skill grant. jg-execute auto requests must explicitly cover commit and push effects. Grants do not replace workflow approval, native permissions or actual dispatch admission.
Artifact references select a report ID/hash, repository path with commit/hash and optional line range, native provider/session/turn/item/hash, or exact plan revision/milestone. Selected message-read returns the exact body and a bounded reference page, with byte/reference offsets and optional continuation. Collaboration responses fit within 65,536 serialized bytes; commands leave room inside the existing 65,536-byte IPC request envelope. Oversized input fails without partial publication.
Broadcast notifications do not assign each subscriber work. Notification intent, native delivery (nativeActionId/deliveredAt), explicit acknowledgedAt, subscription read position, request claim and resolution remain independent. Information, reads and acknowledgements do not submit native input. Authorized directed request intent can wake the separate dispatcher. Directed intent persists even for an unsubscribed recipient. Subscriber fan-out runs in batches of at most 32 with transactionally retained progress; restart resumes it. A failed batch leaves durable intent and exposes JG_AGENT_HQ_STORE_FAILED in status.fanoutFailure until explicit recovery or runtime restart. The host owner can inspect all task metadata; revoked members cannot read or replay operations. Paused participants retain historical reads, while fresh claims require active task/member state.
Authorization failures use JG_AGENT_HQ_COLLABORATION_FORBIDDEN; conflicting command identities, assignments, lifecycle revisions and terminal transitions use JG_AGENT_HQ_COLLABORATION_CONFLICT. IPC maps these to the existing safe access-denied/state-conflict errors. Native execution and plan approval remain separate contracts.
Directed requests and exact reports
An idle directed recipient runs when policy.automaticDispatch is enabled, or when the owner commits request-dispatch with requestId. A claimed request is eligible through its one assignee. Busy requests retain pendingActionId and a visible dispatchBlockedReason; HQ submits at a safe native boundary. Admission atomically reserves one active dispatch per agent across tasks, task concurrency and one cumulative automaticTurnCount for an automatic attempt. Explicit host dispatch/input/resumption does not consume that automatic budget. Already admitted failed or uncertain attempts retain their consumed count. No timer or model turn polls for work.
Each dispatch uses the existing native action ledger and an immutable selected invocation context. currentActionId stays held through terminal outcome collection, so ordinary queued input cannot run ahead of it. lastActionId, lastReportId, dispatchCount, requiredAuthority, failureReason and blocked reasons remain inspectable on the request. Known ownership, exact assignment, current task/member policy, selected grant revision and plan authority are checked again before native submission. Read-only plan assessment verifies the selected ID/revision without replacing the recipient's implementation binding. Writable planned work requires the existing exact approved native plan scope. Requests do not approve a plan or answer native permissions.
The native response is a closed object:
{
requestId: string | null;
outcome: 'completed' | 'waiting-children' | 'needs-input' | 'paused' | 'failed';
childRequestIds: string[];
report: string;
requiredAuthority: 'requester' | 'operator' | null;
}Delegated work uses the exact request UUID. Only needs-input specifies requiredAuthority; other outcomes use null. Only waiting-children specifies a nonempty unique childRequestIds array; other outcomes use an empty array. The normal Markdown report stays inside report. HQ validates the envelope against the exact native action, assignee, provider, session and turn, without another model. Completed means the assignee claims completion. It is not approval or acceptance. Failed assignee reports remain exact evidence. Missing, malformed, wrongly bound or oversized output produces a labeled runtime failure, never a manufactured agent report. Native interruption retains interrupted state; uncertain native delivery retains the unresolved binding and never automatically resends.
The shared native schema omits an external dialect reference because the qualified Claude CLI rejects that reference during initialization. Codex selects this schema per turn. Claude installs it through one confirmed idle process restart, retaining native identity, instructions, permission settings, model and effort. An empty session reuses its same session ID; a session with input resumes its existing history. No global native configuration changes. Subsequent ordinary Claude conversations return requestId: null and HQ unwraps their report into the normal text result. Explicit unrelated output schemas retain their normal native selection contract.
| Command | Required selection and behavior |
|---|---|
request-dispatch |
Owner-only requestId; explicitly schedules eligible pending work even when automatic dispatch is disabled. |
request-input |
requestId, exact expectedActionId and bounded body. Only the owner or the recorded requester may answer a requester checkpoint; operator checkpoints require the owner. The linked immutable input message reserves one next action. Agent answers require active membership and normal automatic policy/limits. |
request-resume |
Owner-only requestId, expectedActionId, current expectedAgentRevision, current expectedTaskRevision and body. Resumes interrupted work or reactivates a paused parent wait under current authority, preserving its continuation reservation; a paused task must first be activated explicitly. |
request-authorize |
Owner-only requestId, nullable exact expectedActionId, nullable grantId/grantRevision and nullable plan. Refreshes the selected authority at an input/interruption/child-wait checkpoint, or before the first queued attempt. Null action selects work with no prior attempt. Skill, mode, effects, arguments and scopes stay selected; this command grants no plan approval. |
request-collect |
Owner-only requestId and actionId; selects already saved native evidence. Returns the immutable collection or null while evidence is unsettled. It accepts no model-provided completion text and never submits input. |
invocation-read |
Agent-only taskId; returns the one active verified invocation or null. It includes selected request scope, action/turn binding, compact grant issuer/revision, immutable parent/return route, bounded child-wait state, continuation generation/result references and linked input identity/author. Read request and input message bodies separately. |
report-read |
reportId and optional cursor, scoped by taskId. Returns metadata, exact UTF-8 text, offsetBytes, isComplete and nextCursor. |
Collection atomically commits the request transition, exact report, bounded reply, return delivery intent and immutable receipt. Repeated collection returns the original receipt. A canceled request keeps its canceled state if terminal evidence arrives later. Explicit idle ownership recovery or reattachment (thread-recover or thread-resume) may close canceled uncertain work, recording recoveryActionId; the native receipt remains uncertain. Reports and replies remain readable after task pause or grant revocation, subject to current task visibility. Runtime diagnostics use a runtime author. Native input acceptance sets nativeActionId/deliveredAt; it neither acknowledges a message nor resolves its request.
Reports have a fixed 1,048,576 UTF-8 byte ceiling. HQ rejects malformed UTF-16 or oversize output rather than replacing characters or storing a truncated report. Metadata records immutable report/request/action/reply IDs, server-derived agent author, native provenance, byte count and SHA-256. Replies carry a labeled excerpt and report ID/hash. report-read uses bounded database substring reads with an actor/task/report/hash-bound cursor, keeping the entire response within 65,536 serialized bytes. Native history availability does not affect these reads.
messages-search merges bounded message and report FTS5 seeks in deterministic message sequence order. A report hit returns its associated reply and optional reportMatch: {reportId, sha256, excerpt}. Searches do not load full report bodies into Node or scan all reports; SQLite generates excerpts only for selected fixed-capacity indexed hits. There is no report cache, transcript mirror or general artifact subsystem. status.dispatchFailure exposes a failed recovery pass; owner fanout-recover also schedules dispatch recovery.
Parent waits and continuations
A delegated message-publish supplies parentRequestId matching the publishing agent's active assigned invocation in the same task. HQ records that parent and its exact native action immutably. Child effects, paths and plan scope cannot exceed the active parent selection; recipient task permissions and selected skill grants are checked separately. Root publications omit this field. A host or an unrelated invocation cannot forge a child relationship.
A waiting-children envelope names 1–32 unique direct children and retains every still-incomplete child from the recorded wait. HQ stores childRequestIds, an incremented waitGeneration and one continuationActionId on the parent. It releases native-turn capacity while preserving the assignee, incomplete request and plan binding. Registering a wait and collecting a child result check readiness in the same SQLite transaction. An early result cannot be lost. Resolved success, resolved failure and cancellation satisfy terminal waits; interruption and input questions do not. Reporting completed while a direct child remains unfinished produces children-pending, with a failed runtime outcome rather than parent completion. Invalid wait relationships produce invalid-child-wait.
All terminal children, or an unconsumed requester clarification from a named child, can reserve one next native action for that generation. The existing action ledger owns admission, busy queueing, uncertainty and replay. Continuation packets carry bounded child states, action/report IDs and hashes, and short excerpts; full payloads use hq_report_read. Native acceptance marks only the included report deliveries and clarification triggers as delivered/consumed. Later questions remain pending for the next wait generation. An agent may answer its direct child's requester checkpoint with hq_request_input and yield again. Operator-only questions require the host and never wake a parent to supply approval.
The parent retains its scope and rechecks current lifecycle, grants, plan revision and resource limits at admission. Paused, interrupted, revoked, canceled or resolved parents do not restart from child traffic. Current host authorization/resumption may unblock a saved wait; it does not replace an uncertain action. Restart recovers recorded wait conditions and reservations without starting a native controller. Explicit native reattachment remains necessary after shutdown. Ending a parent does not cancel its children, and late reports remain readable. Continuation acceptance, acknowledgement and replies never become new child-result triggers. hq_identity.waiting returns at most 32 authorized wait summaries plus hasMore; use bounded hq_requests_list for further inspection.
Native qualification on 2026-10-23 exercised complete Codex-to-Claude, Claude-to-Codex and Codex-to-Codex parent/child journeys without skills. Each child completed once; each parent yielded and completed on its second turn. Reports were checked byte-for-byte and by SHA-256. Native tool permissions were answered only for the selected HQ task operations.
Agent tools
Controlled sessions receive the same closed tools through provider-bound Codex dynamic callbacks or Claude's session-specific stdio MCP configuration. operations.agentTools reports availability separately from other controls. Codex dynamic tools are experimental. New HQ-created sessions declare them at thread/start; the native resume/fork interfaces retain their own tool configuration. An adopted or reconnected Codex session without observed registration reports unknown support until a native callback establishes it. No version label controls this decision. Claude adds one server using --mcp-config, preserving normal settings and other configured servers. Native permission prompts still require explicit replies.
hq_identity({taskId?, limit?, cursor?}) returns {identity, pending}. Identity contains the immutable managed agentId, provider/native ID, runtime epoch, workspace, bounded task summaries, selected task, membership (including parent/role), child purpose and effective permissions. The compact invocation identifies active assigned work and its blocked reason. Active invocation selects its task by default; otherwise identity selects an unambiguous single membership; otherwise the caller selects an authorized taskId. The selector grants nothing. Pending work uses the task status counts. Identity is reconstructed from current durable records after compaction and explicit resume, without injecting stream history.
| Tools | Inputs and results |
|---|---|
hq_agents_list, hq_streams_list, hq_stream_create, hq_subscription_set |
The corresponding collaboration command fields without kind or an actor. |
hq_messages_list, hq_messages_search, hq_message_read, hq_message_publish, hq_message_ack |
The corresponding bounded collaboration command fields; HQ supplies the author. |
hq_request_read, hq_requests_list, hq_request_claim, hq_request_resolve |
Current request relationships/waits and explicit non-dispatched resolution. |
hq_invocation_read, hq_report_read, hq_request_input |
Verified invocation, exact report chunks and linked requester input through the corresponding collaboration commands. |
hq_deliveries_list |
taskId, optional limit and cursor; returns only the calling agent's unacknowledged inbox. |
hq_sessions_list, hq_session_evidence_read |
The corresponding native read fields plus taskId, checked against effective read/provider/workspace permissions. |
hq_agent_spawn |
id, taskId, explicit provider, workspaceRoot and a nonempty bounded purpose; returns {taskId, agentId, receipt} from the native action ledger. |
Native connection identity or an opaque bridge credential supplies the sender. No tool accepts an authoritative actor, runtime epoch or sender ID, issues host grants, approves a plan, expands directories or changes native permissions. Each call checks current membership and policy. During an active dispatch, non-identity tools also recheck its selected grant and scope. Session reads and spawning cannot expand its selected workspace access. Tool arguments are closed schemas; safe results contain one text block with exact bounded JSON and isError. Core payloads fit within 65,536 serialized bytes, and native text wrappers account separately for JSON escaping. Calls are bounded to 32 outstanding operations and a 30-second gateway deadline; cancellation never proves an admitted native effect failed.
Spawn capacity is reserved transactionally with the new managed thread, parent link and copied effective task permissions, narrowed to the parent's active invocation when present. The task's spawnCount is cumulative and cannot exceed policy.spawnLimit. A rejected admission consumes no slot; an admitted failed or uncertain native creation retains its reservation. Reuse the same id and arguments to inspect that receipt. HQ never resends an uncertain creation. A new child has a distinct managed ID and no inherited plan approval or exact-agent skill eligibility. Either provider may create either provider. Native subagent calls resolve to their owner unless native lineage proves an independent managed binding; a shared MCP credential cannot distinguish native children.
The stdio entry is jg-agent-hq agent-bridge. Its only credential input is JG_AGENT_HQ_BRIDGE_FILE, a private short-lived file path supplied in the session MCP environment. The opaque token stays out of argv, prompts, SQLite, native configuration and snapshots. Files are 0600 under an owned 0700 system temporary directory, revoked and removed when their session closes, and rotated on reconnect/runtime restart. Runtime shutdown drains tool calls and removes its files/directory. The installed @modelcontextprotocol/server owns stdio framing, cancellation and negotiation. Stdout is protocol-only. Trusted factory contexts receive IProviderAgentTools, with JSON descriptors, a provider-bound callback and a revocable MCP lease; the Node package exports its descriptor/lease types.
Native qualification on 2026-10-23 used Codex 0.164.0 and Claude Code 2.1.309. Each called hq_identity and created an authorized child with the other provider through hq_agent_spawn; HQ confirmed the native creation receipts and parent memberships. Both then resumed their same managed/native identities after runtime restart, compacted, and called hq_identity again with the new runtime epoch, confirmed through native tool evidence. Claude's tool permissions received explicit per-call replies. Owned controllers, bridges and credential files shut down cleanly. These observations qualify those paths on this host; version labels remain diagnostics.
Directed-request qualification on 2026-10-23 used Codex 0.164.0 and Claude Code 2.1.309. Each called hq_invocation_read under its selected request, completed in one native turn, and returned an exact report retained by HQ. Native tool evidence confirmed the request/action binding; Claude's read-only MCP permission received an explicit reply. Report reads survived controller shutdown/restart without another model turn. The shared envelope uses no external schema-dialect reference; native version labels remain diagnostic.
Native skill delegation
A directed request may select an installed native skill by exact name, mode and current host-issued grant. HQ reads the recipient's native catalog and formats the invocation through that provider. Codex receives its native skill name/path selection; Claude receives its native slash invocation. Missing, disabled, ambiguous or unreadable metadata produces a retained skill-unavailable dispatch reason without sending plain text instead. Reload or repair the native catalog, then explicitly retry dispatch or allow an authorized recovery event. HQ never reads or rewrites skill instructions.
The immutable action command binds the coordination packet, while private preparation adds native skill presentation. Full arguments and mode come from the verified request, rather than being copied into each continuation. The session-bound hq_invocation_read returns the exact task, request, assignee, action, selected grant and current scope/plan/lifecycle. Verify it before acting. Native user-role framing, environment variables, quoted feedback and pasted grant IDs prove no operator authority.
| Workflow need | HQ mapping |
|---|---|
| Verify delegated entry and mode | hq_invocation_read; use request.specification and the selected grant, not packet claims. |
| Read exact session evidence | hq_sessions_list and hq_session_evidence_read with authorized references, bounded pages/chunks and provenance. Denial never permits raw-history fallback. |
| Read review findings | hq_message_read or hq_report_read for the exact authorized reference; report hashes bind content. |
| Finish a read-only assessment | Return the native structured envelope. HQ stores/transports the report without a mutating tool or another inference turn. |
| Wait for child work | Return waiting-children with unique direct childRequestIds; continuation carries bounded states and report references. |
| Require selection/approval | Return needs-input with requiredAuthority operator; requester is for ordinary clarification only. |
| Pause/resume | Return paused under a pause grant. The host explicitly resumes against current lifecycle revisions; child results and earlier grants cannot resume it. |
Only the outermost delegated workflow wraps its normal report. Nested workflows, including jg-execute auto's review/push steps, retain their reports and require their selected effects. A workflow entry grant approves no new plan or later command selection. Both vendors may implement, review, challenge or assess work; same-vendor requests are supported. Skills are optional, and standalone skills retain native-history lookup and manual feedback handoff without an HQ dependency.
For ordinary explicit native skill messages, optional expectedPlan guards the captured revision under the plan lock; explicit null requires an unbound thread. It applies only to message/queue, grants no execution scope and answers no native permissions. A selected jg-feedback assessment with matching expectedPlan retains a neutral revision guard after execution is revoked or completed, preserving the existing implementation binding. Queued assessment keeps the same guard and native target; revision gates pause stale input. Read-only collaboration requests use their selected plan reference without inheriting implementation authority. Delivery or a completed assessment never means findings were accepted.
The public examples/review-handoff.ts helper uses an existing task/stream and selected memberships/grants with automaticDispatch false. Present each exact review/feedback specification through confirmRequest. The reviewer report returns as an immutable ID/hash reference for a separately confirmed author assessment. The helper waits through bounded host reads for up to 30 seconds; timeout never publishes a replacement request. Inspect the retained IDs after a timeout or checkpoint. No provider pairing, implicit feedback invocation or permission approval is built in.
Native qualification on 2026-10-23 exercised all fourteen explicit workflows in Codex 0.164.0 (gpt-6.1-sol) and Claude Code 2.1.309 (claude-opus-5-5), with recorded native configuration. Both providers preserved planning/selection checkpoints, read-only report returns, standalone feedback, denied managed-evidence boundaries and explicit pause/resume. Skill-based parent/child journeys ran in both directions with two parent turns and one child turn, checking exact report hashes. Claude also exercised auto review and signed publication against a disposable local bare remote. Codex’s workspace sandbox kept Git metadata read-only, so its publication attempt reported the native restriction without bypassing it. These are qualification observations, not version admission rules.
Browser host integration
jg-agent-hq/contracts exports closed BrowserActionSchema, BrowserCommandSchema and BrowserReceiptSchema contracts, plus BrowserThreadDetailSchema, a bounded view of one managed thread with its plan context, pending approvals, live items and recent actions. These schemas contain generic identities, not a deployment registry or private handler implementation. They do not expose native RPC, executables or provider configuration. Standalone IPC continues to support only its existing owned commands.
A trusted Node host calls runtime.performBrowserAction(request, operatorId, execute) after validating its signed operator identity. The host supplies one dispatcher function in process. It must validate every target against its registry, check operation-specific revisions/turns, and normalize approval provenance to that authenticated operator. Never accept a callback, operator identity or handler name from HTTP input. The request has a stable UUID, current runtime epoch and one closed command: native or plan. The complete serialized request is limited to 2 MiB; nested native commands retain their tighter limits.
SQLite records an HMAC digest and receipt before invoking the dispatcher. The same UUID and identical normalized request returns the stored receipt without invoking it again. Changed payloads or operators conflict. New requests require the current epoch. Replaying a saved request after restart reconciles it without granting execution authority. Submitted receipts from another runtime become uncertain. Dispatcher failure also leaves the receipt uncertain because its effects may already have committed. This state never automatically resends an effect.
The dispatcher receives a server-owned nativeActionId; use this ID for a native child action, preserving the validated runtime and command. readBrowserAction(id, operatorId) returns only that operator's receipt and joins its exact native receipt. Host acknowledgement means the handler completed, not that a native turn completed. An unknown or differently owned ID returns null. Host receipts retain admission metadata rather than arbitrary handler output. The selected joined native receipt includes its queued input and complete bounded final result; paged domain reads omit those bodies. At most 32 host jobs are admitted concurrently. Runtime close rejects new host work and waits for admitted work before closing its worker.
Live reads accept afterRevision. A matching revision returns unchanged: true with an empty data array; retain the displayed window. A changed revision returns the complete bounded window. Revisions belong to one runtime. After reconnect to a new runtime, discard the revision and refresh native history. Token bodies stay in the existing bounded memory window and are never copied into lifecycle or browser-action tables.
thread-plan accepts a managed threadId and returns its nullable ActionPlanContextSchema. The host verifies the thread's registered checkout before exposing it. It is read-only and grants no execution authority.
Qualification
The operation matrix describes the implemented native interfaces and their explicit unavailable surfaces. Executed qualification is narrower than every possible model, configuration and account state. Native versions below are observations, never admission rules.
| Area | Executed evidence | Boundary |
|---|---|---|
| Provider integration | Public contract tests route a third provider, isolate unavailable providers and tolerate additive fields and future version labels. Native lifecycle, configuration, files, account and extension checks are listed in their sections above. | Each additional adapter needs its own native qualification. |
| Directed collaboration | Codex 0.164.0 and Claude Code 2.1.309 completed both vendor directions. A parent yielded before its child started, then finished on its second turn. Same-vendor delegation answered a child clarification and finished with three parent turns and two child turns. | Reading, subscription and information messages grant no execution authority. |
| Identity and native interaction | Both providers retained managed/native identities after compaction and reconnect with a fresh runtime epoch. Codex interruption and continuation passed. Claude AskUserQuestion and scoped MCP permission replies used exact native identities. | A timed-out compaction retained uncertain delivery even when completion later arrived; it was not resent. Explicit resume followed confirmed controller shutdown. |
| Reports and recovery | Six final reports matched complete native outcomes byte for byte. A 46,464-byte report was found through indexed search and read in two chunks. After removing all eight qualification transcripts, offline recovery retained those reports and the host preference; tasks were paused and providers stayed idle. | Native transcripts, repository files and authentication remain separate recovery responsibilities. |
| Optional skills | All fourteen explicit workflows ran in both providers, including verified delegation and standalone use. Skill-based parent returns ran in both directions. An unissued grant was rejected without creating its request. | Later approvals remain required. Claude signed publication used a disposable bare remote; the qualified Codex sandbox denied Git metadata writes. |
| Shared Codex daemon | Codex 0.164.0: a terminal codex session ran on the shared daemon and appeared in thread/list. Without subscribing, HQ saw its thread/status/changed; after rejoining with thread/resume, HQ received the terminal's live turn and item notifications. A thread HQ created appeared in the terminal's codex resume picker, and closing HQ's connection left the daemon and the terminal session running. The daemon confirmed HQ's interrupt and close. Per-thread skill roots, dynamic agent tools and output schemas worked on HQ threads, while another client's catalog and thread never saw the roots. Sessions read their own thread ID from CODEX_THREAD_ID. |
A terminal started with -c overrides runs its thread in its own process (source: cli): it is listed, but a second writer is refused, so it cannot be followed live. The restart check ran against a separate app-server on its own socket rather than the shared daemon, which was serving other work: within one keepalive interval the session became uncertain and nothing was resent. A session Codex reloads from disk loses its capability roots. |
| Claude active sessions | Claude Code 2.1.310 answered claude agents --json in 0.16 s. Interactive entries carry status (busy, waiting, idle) and pid; background entries carry id and state (blocked). Sessions HQ starts in structured mode are listed as interactive under their HQ name and disappear after close. |
The list describes running processes only; history still comes from transcripts. |
| Host coverage | Linux x86_64 package checks passed on Node 24.15.0 and 24.22.1. Integrated delegation ran on the floor; identity, native interaction and recovery also ran on 24.22.1. | No macOS host was available for this run. Windows is outside the declared host contract. |
The integrated delegation, identity and recovery checks used gpt-6.1-sol with medium effort and claude-opus-5-5 with medium effort in disposable workspaces on 2026-10-23. The shared-daemon and Claude active-session checks ran on 2026-10-25 with the installed CLIs' default models in disposable workspaces. The Node release index (opens in a new tab) identified 24.22.1 as the current LTS patch at qualification time. Local checks do not establish a published npm release or a deployed website.
Native model/output budgets also bound structured results. A generation that ends with incomplete JSON remains failed and creates no successful report; its bounded native result stays on the action receipt. The report byte limit does not increase the native model's output budget. Claude output schemas belong to session startup or explicit idle resume, as described in the configuration contract. Its unavailable steering, archive/revert, resource catalog and interactive authentication surfaces remain explicit in the operation matrix.