@jini-ai/daemon 0.6.8 → 0.6.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CHANGELOG.md +148 -0
  2. package/dist/agent-executor/antigravity-permissions.d.ts +11 -0
  3. package/dist/agent-executor/antigravity-permissions.js +64 -0
  4. package/dist/agent-executor/contracts.d.ts +37 -6
  5. package/dist/agent-executor/event-translation.d.ts +6 -1
  6. package/dist/agent-executor/event-translation.js +3 -1
  7. package/dist/agent-executor/events.d.ts +5 -0
  8. package/dist/agent-executor/events.js +143 -87
  9. package/dist/agent-executor/launch.d.ts +5 -3
  10. package/dist/agent-executor/launch.js +315 -217
  11. package/dist/agent-executor/mcp-bridge-probe.js +51 -35
  12. package/dist/agent-executor/mcp-bridge.d.ts +18 -8
  13. package/dist/agent-executor/mcp-bridge.js +105 -65
  14. package/dist/agent-executor/run-output.d.ts +20 -0
  15. package/dist/agent-executor/run-output.js +80 -0
  16. package/dist/attachment-content.js +18 -15
  17. package/dist/core/read-only-tools.d.ts +15 -0
  18. package/dist/core/read-only-tools.js +29 -1
  19. package/dist/core/tool-audit.d.ts +3 -1
  20. package/dist/core/tool-audit.js +35 -22
  21. package/dist/core/tool-gaps.d.ts +60 -0
  22. package/dist/core/tool-gaps.js +199 -0
  23. package/dist/delegated-result-correlation.d.ts +26 -0
  24. package/dist/delegated-result-correlation.js +68 -0
  25. package/dist/delegated-tool-bridge.d.ts +6 -14
  26. package/dist/delegated-tool-bridge.js +155 -155
  27. package/dist/event-log.d.ts +45 -2
  28. package/dist/event-log.js +83 -4
  29. package/dist/http/attachments.d.ts +26 -1
  30. package/dist/http/attachments.js +129 -65
  31. package/dist/http/connectors.d.ts +1 -32
  32. package/dist/http/connectors.js +13 -68
  33. package/dist/http/delegated-tools.d.ts +8 -1
  34. package/dist/http/delegated-tools.js +30 -30
  35. package/dist/http/host-tools.d.ts +1 -0
  36. package/dist/http/host-tools.js +64 -59
  37. package/dist/http/index.d.ts +2 -2
  38. package/dist/http/index.js +1 -1
  39. package/dist/http/media.js +48 -69
  40. package/dist/http/memory.js +45 -36
  41. package/dist/http/model-proxy-fields.d.ts +23 -0
  42. package/dist/http/model-proxy-fields.js +29 -0
  43. package/dist/http/model-proxy.js +193 -395
  44. package/dist/http/research-tavily.adapter.js +72 -49
  45. package/dist/http/research.js +33 -23
  46. package/dist/http/resource-request.d.ts +37 -0
  47. package/dist/http/resource-request.js +59 -0
  48. package/dist/http/routines.js +49 -90
  49. package/dist/http/runs.d.ts +7 -5
  50. package/dist/http/runs.js +21 -7
  51. package/dist/http/terminals.js +23 -48
  52. package/dist/http/tool-catalog.d.ts +5 -0
  53. package/dist/http/xai.d.ts +11 -1
  54. package/dist/http/xai.js +125 -124
  55. package/dist/index.d.ts +2 -0
  56. package/dist/index.js +1 -0
  57. package/dist/legacy-data-migration.js +41 -24
  58. package/dist/run/core/retry.js +51 -35
  59. package/dist/run/diagnostics/diagnostics.js +5 -17
  60. package/dist/run-credentials.js +49 -27
  61. package/dist/run-lifecycle.d.ts +31 -2
  62. package/dist/run-lifecycle.js +49 -42
  63. package/dist/store/agent-sessions/sqlite.d.ts +6 -0
  64. package/dist/store/agent-sessions/sqlite.js +6 -4
  65. package/dist/surface-exchanges.js +2 -1
  66. package/dist/terminal-session.d.ts +1 -1
  67. package/dist/terminal-session.js +26 -33
  68. package/dist/tool-audit.d.ts +1 -0
  69. package/dist/tool-audit.js +1 -0
  70. package/dist/tool-executor.d.ts +24 -3
  71. package/dist/tool-executor.js +276 -139
  72. package/dist/tool-recovery/recovery.js +64 -35
  73. package/dist/tool-recovery/stack.d.ts +2 -1
  74. package/dist/tool-recovery/stack.js +1 -1
  75. package/dist/tool-result-diagnostics.d.ts +27 -0
  76. package/dist/tool-result-diagnostics.js +77 -0
  77. package/dist/tool-result-media.d.ts +27 -14
  78. package/dist/tool-result-media.js +51 -38
  79. package/package.json +14 -14
package/CHANGELOG.md CHANGED
@@ -1,3 +1,151 @@
1
+ ## 0.6.10
2
+
3
+ ### Patch Changes
4
+
5
+ - 4f52784: Stop advertising agents `AgentExecutor` cannot run.
6
+
7
+ The built-in `agents` feature scanned every runtime definition, but `AgentExecutor` rejects some of
8
+ them (`antigravity`, whose deferral is documented in that module). A consumer could therefore offer a
9
+ user an agent that failed the instant it was selected, with the mismatch visible nowhere.
10
+
11
+ - `@jini-ai/daemon` exports `isAgentExecutorSupported(def)` and `assessAgentExecutorCompatibility(def)`.
12
+ `run()` now consumes the latter instead of re-checking the conditions itself, so the discovery-time
13
+ answer and the run-time guards cannot drift apart. Error messages are unchanged.
14
+ - `@jini-ai/server`'s `agents` feature applies the predicate by default, and exports
15
+ `isExecutableDetectedAgent` for hosts that want it directly.
16
+
17
+ `antigravity`'s definition remains in `@jini-ai/agent-runtime` — the predicate is advisory, and a
18
+ non-`AgentExecutor` launch path can ignore it. An id with no registered def is kept rather than
19
+ dropped, so a host supplying its own `detector` is unaffected.
20
+
21
+ Note for anyone implementing something similar: the predicate deliberately takes the full
22
+ `RuntimeAgentDef`, not a projected `DetectedAgent`. That projection omits `maxPromptArgBytes`, and the
23
+ argv-bound defs (`aider`, `deepseek`) qualify solely through it — judging the projection would have
24
+ dropped two working agents while fixing one broken one.
25
+
26
+ - 8ff5653: Drive Antigravity (`agy`) — the 24th and last agent definition `AgentExecutor` rejected.
27
+
28
+ `AgentExecutor` drove 23 of 24 registered definitions. Antigravity was rejected by an explicit
29
+ `def.id === 'antigravity'` guard, for two reasons that are real but narrow:
30
+
31
+ - **`agy` can print an OAuth sign-in URL to stdout and still exit 0.** Streaming stdout live shows
32
+ that URL to the user as if it were the model's reply — a URL that carries the daemon operator's
33
+ `client_id`/`redirect_uri` into a chat transcript, and that is useless to click anyway (`agy -p`
34
+ print mode has no field to paste the resulting auth code into).
35
+ - **`agy` has no `--model` flag.** The model choice is written into one process-global
36
+ `~/.gemini/antigravity-cli/settings.json` that `agy` reads on its own startup, so two concurrent
37
+ runs race: run A writes model A, A spawns, B writes model B, and only _then_ does A's `agy` read
38
+ the file — so A silently executes on B's model.
39
+
40
+ Both are now met by **declarative `RuntimeAgentDef` fields the executor reads generically**, joining
41
+ the 14 optional behavior flags (`promptViaFile`, `authProbe`, …) that already work this way. There is
42
+ no agent-id branch anywhere in the dispatch path — a deliberate divergence from Open Design's own
43
+ daemon, which hardcodes the id twice.
44
+
45
+ New on `@jini-ai/agent-runtime`'s `RuntimeAgentDef`:
46
+
47
+ - `needsAgentLogFile?: boolean` — asks the caller to stage a temp path and pass it as
48
+ `RuntimeContext.agentLogFilePath` before `buildArgs` runs, mirroring `promptViaFile`. Staged by the
49
+ new `prepareAgentLogFile(def, label)` (the sibling of `preparePromptFileForAgent`; it creates only
50
+ the `0o700` containing directory, since the log file is output the CLI authors, not input we do).
51
+ - `stdoutPolicy?: RuntimeStdoutPolicy` — `{buffering: 'live'}` (the default, and every other
52
+ definition's behavior) or `{buffering: 'until-close', sanitize?}`. A discriminated union rather
53
+ than two flat fields on purpose: a sanitizer is only meaningful on the buffered path, because the
54
+ pattern to redact can straddle two `'data'` chunks. Two independent flags would let a definition
55
+ declare a sanitizer the caller could not honor — a confidentiality gap that _looks_ closed.
56
+ - `runtimeLock?: RuntimeLock` — a mutex around a process-global side effect `buildArgs` performs.
57
+ Acquired before `buildArgs`, released on whichever of `waitForHandoff` settling or process exit
58
+ comes first. Releasing on exit is load-bearing, not a fallback: a watcher that stops polling means
59
+ "I stopped watching", never "the child definitely didn't read the file".
60
+
61
+ All three are stripped from the `DetectedAgent` registry projection. They instruct whoever _spawns_
62
+ the CLI, not whoever lists agents — and two carry closures, which `JSON.stringify` would flatten into
63
+ a misleading `{"buffering":"until-close"}` / `{}` rather than omit.
64
+
65
+ In `@jini-ai/daemon`, `AgentExecutor` now stages a log file when `needsAgentLogFile` is set (released
66
+ on every pre-spawn, spawn-failure, and close path alongside the prompt file, through one composed
67
+ `cleanupStagedFiles` closure), calls `runtimeLock.acquire()` before `buildArgs`, and branches its
68
+ `streamFormat: 'plain'` stdout handling on `stdoutPolicy`. `assessAgentExecutorCompatibility` no
69
+ longer rejects anything by id, and `isAgentExecutorSupported` now accepts all 24 definitions.
70
+
71
+ **No behavior change for any other agent.** Antigravity is the only definition declaring any of the
72
+ three fields; the other four `streamFormat: 'plain'` definitions (`grok-build`, `aider`, `deepseek`,
73
+ `qwen`) keep streaming live, per chunk, which `aider`'s and `deepseek`'s own comments call out as
74
+ deliberate. That is pinned by an explicit regression test rather than left implied.
75
+
76
+ One thing worth knowing if you consume the run event stream: on the buffered path the raw `'stdout'`
77
+ echo is held back and sanitized too, not just the `'agent'`/`text_delta` copy. Emitting an
78
+ unsanitized raw echo while withholding the chat copy would leak the exact string the sanitizer exists
79
+ to remove to any subscriber. The opt-in byte journal still records raw bytes per chunk — that is its
80
+ documented contract, and it deliberately lives in a separate `EventLog` instance that is never
81
+ replayed to run-event subscribers.
82
+
83
+ - 3fe4d14: Store tool-result images once in media, preserve model-facing pixels, and provide opt-in digest-verified echo summaries and bounded image-safe diagnostics. Hosts with exact-byte journaling must inject a separate stdout blob/reference writer.
84
+
85
+ Preserve conflicting image representations during digest verification, and keep the required bridge executor authoritative when route dependencies supply optional settings.
86
+
87
+ - e16a907: Choose reported-cost semantics from agent definition metadata rather than a vendor id; absent metadata retains per-run behavior.
88
+ - 4f52784: Add `createRunScopedContextStore` and widen `ResolvedRunInput` with the executor's optional inputs.
89
+
90
+ **`createRunScopedContextStore<T>({ lifecycle })`** — bind/resolve/auto-evict keyed by run id, for
91
+ host-owned context that must survive the gap between starting a run and a later delegated call that
92
+ carries only that run's id. `resolve` fails closed (`RunContextNotBoundError`) rather than fabricating a
93
+ default, and bindings are evicted when the run reaches a terminal state.
94
+
95
+ `resolveRunInput` could already decode and stash such context, but it is handed no lifecycle and so
96
+ cannot register the eviction half — leaving a map that only grows and keeps stale authority resolvable.
97
+ Owning both halves together is the point. `T` is fully generic: this helper does not decode
98
+ `contextRef`, does not sign or verify anything, and has no notion of a principal. It is in-memory only,
99
+ and says so — after a restart every `resolve` fails, which is the correct posture rather than a gap.
100
+
101
+ **`ResolvedRunInput` now carries `permissionMode`, `model`, `reasoning`, and `credentialEnv`**, each
102
+ forwarded to `AgentExecutor.run()` only when present.
103
+
104
+ `permissionMode` is the load-bearing one and closes a real trap: every def with an auto-approve flag
105
+ (`bypassPermissions` / `--yolo` / `--dangerously-skip-permissions`) applies it by default when the field
106
+ is absent. A host that had been passing `'restricted'` to `AgentExecutor.run()` by hand and then adopted
107
+ `createDefaultRunStartHandler` would silently have started auto-approving every action — a security
108
+ regression wearing the clothes of a refactor. Existing behavior is unchanged: omitted fields are omitted
109
+ from the executor call, never passed as an explicit `undefined`.
110
+
111
+ - 4f52784: Add a `sidecar-strict` security mode and per-run MCP credential propagation.
112
+
113
+ For a daemon whose threat model is **another process running as the same OS user** rather than a
114
+ remote attacker, the existing `jini-local` mode is a no-op: `registerApiBearerAuthMiddleware`
115
+ short-circuits for any loopback peer before it reads the `Authorization` header, and a `127.0.0.1`
116
+ bind keeps remote hosts out while doing nothing about a co-resident process. A consumer that spawns a
117
+ Jini daemon holding real authority — starting agent runs, executing tools against a real database —
118
+ previously had to write its own middleware.
119
+
120
+ - `@jini-ai/http-kit` gains `requireStrictBearerToken`: fail-closed 503 when the named token env var
121
+ is unset, 401 on mismatch, **no loopback exemption and no disable flag**. Its `tokenEnvVar` is
122
+ required with no default, so this package never names a host's secret.
123
+ - `composeJiniKernel` gains `security: { mode: 'sidecar-strict', host, tokenEnvVar, exemptPaths? }`.
124
+ Purely additive — `host` and `jini-local` are unchanged by construction, since the modes are arms of
125
+ a discriminated union. The strict gate mounts ahead of the JSON body parser, so a caller it rejects
126
+ never has its body parsed.
127
+ - `@jini-ai/daemon`'s `McpJsonInjectionOptions` gains `credential?: (runId) => string | Promise<string>`
128
+ — a **resolver, not a string**, because injection options are built once before any run exists and a
129
+ boot-wide shared secret would defeat the point of scoping a credential to a run. It is delivered to
130
+ the child as `JINI_DAEMON_TOKEN`.
131
+ - `@jini-ai/mcp`'s `jini-mcp` reads that variable and attaches `Authorization` to every daemon call.
132
+ Optional throughout: with no credential, request headers and `.mcp.json` output are byte-identical
133
+ to before.
134
+
135
+ Also generalized: both existing bearer gates now compare tokens in constant time (`timingSafeEqual`)
136
+ and share one header-parsing helper, closing a timing side channel and removing a duplicated regex.
137
+
138
+ - Updated dependencies [22f0619]
139
+ - Updated dependencies [82ed2d3]
140
+ - Updated dependencies [3fe4d14]
141
+ - Updated dependencies [5a3de86]
142
+ - @jini-ai/core@0.6.10
143
+ - @jini-ai/protocol@0.6.10
144
+
145
+ ## 0.6.9 — 2026-10-22
146
+
147
+ - 2026-10-22: Memory fixes (bounded event log and tool-executor audit), chromeIntegration option, provider-plugin registration, coverage.
148
+
1
149
  ## 0.6.8 — 2026-10-16
2
150
 
3
151
  - An unknown tool on the read-only gateway points to discovery, never a mutation 409 (75d37c32).
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Persist one server's MCP grant for agy, which has no run-scoped settings override.
3
+ * Reuses the platform lock and atomic writer; keeps the first existing file as a backup.
4
+ * Invalid settings and I/O failures reject before spawn rather than widening permissions.
5
+ * @complexity O(settings bytes), including the allow-list scan; bounded by the settings file.
6
+ */
7
+ export declare function ensureAntigravityMcpPermission({ settingsPath, serverName }: {
8
+ settingsPath: string;
9
+ serverName: string;
10
+ }, _options?: Record<string, never>): Promise<void>;
11
+ //# sourceMappingURL=antigravity-permissions.d.ts.map
@@ -0,0 +1,64 @@
1
+ import { constants, promises as fs } from 'node:fs';
2
+ import { withFileLock, writeFileAtomicAsync } from '@jini-ai/platform/fs';
3
+ import { isRecord } from './values.js';
4
+ /** Read the current settings text; a missing file is undefined, every other failure rejects. */
5
+ async function readSettingsText(settingsPath) {
6
+ try {
7
+ return await fs.readFile(settingsPath, 'utf8');
8
+ }
9
+ catch (error) {
10
+ if (error.code !== 'ENOENT')
11
+ throw error;
12
+ return undefined;
13
+ }
14
+ }
15
+ /** Parse and validate the settings shape; anything unexpected rejects rather than widening permissions. */
16
+ function parseSettings(raw) {
17
+ const settings = raw === undefined ? {} : JSON.parse(raw);
18
+ if (!isRecord(settings))
19
+ throw new Error('Antigravity settings must be an object');
20
+ const permissions = settings.permissions === undefined ? {} : settings.permissions;
21
+ if (!isRecord(permissions) || (permissions.allow !== undefined && !Array.isArray(permissions.allow))) {
22
+ throw new Error('Antigravity permissions.allow must be an array');
23
+ }
24
+ return { settings, permissions, allow: permissions.allow ?? [] };
25
+ }
26
+ /** The allow list holding exactly one copy of rule (first occurrence kept, order otherwise
27
+ * unchanged), or null when it already holds exactly one and nothing needs writing. */
28
+ function grantedAllowList(allow, rule) {
29
+ const first = allow.indexOf(rule);
30
+ if (first < 0)
31
+ return [...allow, rule];
32
+ const deduped = allow.filter((entry, index) => entry !== rule || index === first);
33
+ return deduped.length === allow.length ? null : deduped;
34
+ }
35
+ async function backupOriginalOnce(settingsPath) {
36
+ // Never replace the first backup, including on subsequent launches after operator edits.
37
+ await fs.copyFile(settingsPath, `${settingsPath}.jini-backup`, constants.COPYFILE_EXCL)
38
+ .catch((error) => { if (error.code !== 'EEXIST')
39
+ throw error; });
40
+ }
41
+ /**
42
+ * Persist one server's MCP grant for agy, which has no run-scoped settings override.
43
+ * Reuses the platform lock and atomic writer; keeps the first existing file as a backup.
44
+ * Invalid settings and I/O failures reject before spawn rather than widening permissions.
45
+ * @complexity O(settings bytes), including the allow-list scan; bounded by the settings file.
46
+ */
47
+ export async function ensureAntigravityMcpPermission({ settingsPath, serverName }, _options = {}) {
48
+ if (!/^[\w.-]+$/.test(serverName))
49
+ throw new Error('Invalid MCP server name');
50
+ const rule = `mcp(${serverName}/*)`;
51
+ await withFileLock({ lockPath: `${settingsPath}.jini-lock`, run: async () => {
52
+ const raw = await readSettingsText(settingsPath);
53
+ const { settings, permissions, allow } = parseSettings(raw);
54
+ const nextAllow = grantedAllowList(allow, rule);
55
+ if (nextAllow === null)
56
+ return;
57
+ if (raw !== undefined)
58
+ await backupOriginalOnce(settingsPath);
59
+ await writeFileAtomicAsync({ filePath: settingsPath, content: `${JSON.stringify({
60
+ ...settings, permissions: { ...permissions, allow: nextAllow },
61
+ }, null, 2)}\n` }, { refuseSymlink: true });
62
+ } }, { createParent: true, timeoutMs: 5000 });
63
+ }
64
+ //# sourceMappingURL=antigravity-permissions.js.map
@@ -1,3 +1,5 @@
1
+ import type { DelegatedResultCorrelation } from '../delegated-result-correlation.js';
2
+ import type { StdoutJournalBlobPort } from '../tool-result-diagnostics.js';
1
3
  import type { MessageAttachmentImage } from '../attachment-content.js';
2
4
  import type { probeMcpBridge } from './mcp-bridge-probe.js';
3
5
  import type { spawn as nodeSpawn, ChildProcess } from 'node:child_process';
@@ -92,7 +94,7 @@ export type AgentRuntimeEventTranslation = {
92
94
  readonly kind: 'ignored';
93
95
  };
94
96
  /** Machine-readable failure reasons `run()` can reject with — every one is preceded by a `lifecycle.finish({status:'failed'})` call (see index.ts module doc's Invariant section). */
95
- export type AgentExecutorErrorCode = 'AGENT_MODEL_UNRESOLVED' | 'AGENT_PERMISSION_MODE_INVALID' | 'AGENT_NOT_FOUND' | 'AGENT_RUNTIME_UNSUPPORTED' | 'AGENT_BINARY_NOT_RESOLVED' | 'AGENT_SPAWN_FAILED' | 'AGENT_PROMPT_TOO_LARGE';
97
+ export type AgentExecutorErrorCode = 'NATIVE_TOOL_POLICY_UNSUPPORTED' | 'AGENT_MODEL_UNRESOLVED' | 'AGENT_PERMISSION_MODE_INVALID' | 'AGENT_NOT_FOUND' | 'AGENT_RUNTIME_UNSUPPORTED' | 'AGENT_BINARY_NOT_RESOLVED' | 'AGENT_SPAWN_FAILED' | 'AGENT_PROMPT_TOO_LARGE';
96
98
  /** Thrown by `AgentExecutor.run()` on every failure path — never a bare `Error`, so callers can branch on `.code` instead of parsing `.message`. */
97
99
  export declare class AgentExecutorError extends Error {
98
100
  readonly code: AgentExecutorErrorCode;
@@ -119,6 +121,12 @@ export interface AgentExecutorRunInput {
119
121
  * delegate auto-approval, and any unknown mode is rejected before launching a process.
120
122
  */
121
123
  readonly permissionMode?: 'bypass' | 'restricted';
124
+ /** Host-authorized native shell/edit grant; unsupported restricted providers fail before spawn. */
125
+ readonly nativeToolAccess?: boolean;
126
+ /** Independent host-verified permissions, snapshotted for this run. */
127
+ readonly nativeCapabilities?: Partial<import('@jini-ai/agent-runtime').NativeCapabilities>;
128
+ /** Trusted-host CLI parity; never derived from prompt/context fields. */
129
+ readonly cliParity?: boolean;
122
130
  /**
123
131
  * Host-validated image files. How each one actually reaches the model
124
132
  * depends on `def.imageDelivery` (see `@jini-ai/agent-runtime`'s `types.ts`):
@@ -184,6 +192,8 @@ export interface AgentExecutorRunInput {
184
192
  readonly settingSources?: readonly string[];
185
193
  /** Forwarded verbatim to `RuntimeBuildOptions.settings` (extra settings JSON or file path for this run). */
186
194
  readonly settings?: string;
195
+ /** Forwarded verbatim to `RuntimeBuildOptions.chromeIntegration`; `undefined` = no browser-integration flag. */
196
+ readonly chromeIntegration?: boolean;
187
197
  }
188
198
  export interface AgentExecutor {
189
199
  /**
@@ -195,16 +205,19 @@ export interface AgentExecutor {
195
205
  * underlying run is always already transitioned to `'failed'` via
196
206
  * `lifecycle.finish()` before this rejects (see index.ts module doc's Invariant).
197
207
  */
198
- run(requiredArgs: Pick<AgentExecutorRunInput, "runId" | "agentId" | "prompt" | "cwd">, optionalArgs?: Pick<AgentExecutorRunInput, "model" | "reasoning" | "permissionMode" | "imagePaths" | "imageContents" | "extraAllowedDirs" | "uploadRoot" | "credentialEnv" | "env" | "resumeSessionId" | "newSessionId" | "disallowedTools" | "allowedTools" | "settingSources" | "settings">): Promise<void>;
208
+ run(requiredArgs: Pick<AgentExecutorRunInput, "runId" | "agentId" | "prompt" | "cwd">, optionalArgs?: Pick<AgentExecutorRunInput, "model" | "reasoning" | "nativeToolAccess" | "nativeCapabilities" | "cliParity" | "permissionMode" | "imagePaths" | "imageContents" | "extraAllowedDirs" | "uploadRoot" | "credentialEnv" | "env" | "resumeSessionId" | "newSessionId" | "disallowedTools" | "allowedTools" | "settingSources" | "settings" | "chromeIntegration">): Promise<void>;
199
209
  /**
200
210
  * Delivers a message the human sent while `runId` is going straight into its live agent process
201
211
  * (see {@link UserMessageDelivery} for each outcome). A stream-json agent is interrupted first and
202
212
  * gets the message once the CLI acknowledges the interrupt, so this settles in milliseconds (at
203
213
  * most `INTERRUPT_ACK_TIMEOUT_MS`); it never waits on the agent's reply.
214
+ * Optional `imageContents` contains host-authorized pixels, encoded exactly as in an idle send.
204
215
  */
205
216
  sendUserMessage(requiredArgs: {
206
217
  readonly runId: string;
207
218
  readonly text: string;
219
+ }, optionalArgs?: {
220
+ readonly imageContents?: readonly MessageAttachmentImage[];
208
221
  }): Promise<UserMessageDelivery>;
209
222
  }
210
223
  /**
@@ -212,7 +225,8 @@ export interface AgentExecutor {
212
225
  * - `'delivered'` — the running turn was interrupted and the message written into the live
213
226
  * session; the agent answers it next, in the same process and session.
214
227
  * - `'unsupported'` — the run is live, but its agent has no way to take input mid-run (its prompt
215
- * is plain text or an RPC request). The host should interrupt and send it as the next turn.
228
+ * is plain text or an RPC request). Queue attachment turns intact after the reply; text-only
229
+ * turns may retain the interrupt-and-resume fallback.
216
230
  * - `'not-running'` — no live process will accept it: an unknown run, a finished one, or one whose
217
231
  * last turn already ended and closed stdin. The run is ending; the host should send it next.
218
232
  */
@@ -260,7 +274,7 @@ export interface StdinCloseHandle {
260
274
  /** Journals a sent-to-stdin byte chunk, queued through the same FIFO {@link wireChildLifecycle} already uses for emitted events. No-op when no journal was configured (see `CreateAgentExecutorOptions.journal`). */
261
275
  recordSentBytes(content: string): void;
262
276
  /** Interrupts this child's turn, then writes a mid-run user message into its open stdin — see {@link AgentExecutor.sendUserMessage}. */
263
- sendUserMessage(text: string): Promise<UserMessageDelivery>;
277
+ sendUserMessage(text: string, imageContents?: readonly MessageAttachmentImage[]): Promise<UserMessageDelivery>;
264
278
  }
265
279
  /**
266
280
  * Gap 3 (capability-routed continuation transport) — host-owned config for the
@@ -313,6 +327,17 @@ export interface ContinuationOptions {
313
327
  * interface that defaults to *nothing* rather than a real implementation).
314
328
  */
315
329
  export interface McpJsonInjectionOptions {
330
+ /** Read current host-registered tool IDs at launch, outside model-controlled input. */
331
+ readonly diagnosticToolIds?: () => readonly string[];
332
+ /**
333
+ * Visible registry IDs to resolve before the bridge's first tools/list, including for clients
334
+ * whose mid-run refresh is unverified. Independent of diagnostic/redaction IDs; this grants
335
+ * no execution permission. The bridge fetches authoritative descriptors using the run credential.
336
+ * @example startupToolIds: ({ runId }) => visibleToolsForRun(runId).map(tool => tool.id)
337
+ */
338
+ readonly startupToolIds?: (requiredArgs: {
339
+ readonly runId: string;
340
+ }, optionalArgs?: Record<string, never>) => readonly string[];
316
341
  /** Probe a reported bridge failure before emitting its error. @default true when injection is configured. */
317
342
  readonly probeOnFailure?: boolean;
318
343
  /** Absolute path (or PATH-resolvable name) to the `jini-mcp` bin entry (`packages/mcp/src/bin/serve.ts`) this driver tells the spawned CLI to launch as its own MCP server subprocess. */
@@ -401,12 +426,14 @@ export interface McpJsonInjectionOptions {
401
426
  readonly path: string;
402
427
  }) => Promise<void>;
403
428
  /** `'codex-toml'` only. Links the run home's sessions/archived_sessions to the operator's
404
- * persistent rollout store. Config and auth remain isolated and are removed after the run.
429
+ * persistent rollout store and authentication. Parity also links user home entries; config alone stays isolated.
405
430
  * @default Native mkdir (0700) and directory symlinks; errors fail staging before spawn. */
406
431
  readonly linkSessionStore?: (required: {
407
432
  runHome: string;
408
433
  sourceHome: string;
409
- }, optional?: {}) => Promise<void>;
434
+ }, optional?: {
435
+ inheritUserConfig?: boolean;
436
+ }) => Promise<void>;
410
437
  }
411
438
  /** One `.mcp.json` `mcpServers` entry — the shape Claude Code's own config schema expects. */
412
439
  export interface McpJsonServerEntry {
@@ -575,6 +602,10 @@ export interface FailureClassificationContext {
575
602
  */
576
603
  export type ClassifyFailure = (context: FailureClassificationContext) => boolean | Promise<boolean>;
577
604
  export interface CreateAgentExecutorOptions {
605
+ /** Negotiated v1 only; share with bridge/lifecycle. Absent preserves legacy diagnostics/echoes. */
606
+ readonly delegatedResultCorrelation?: DelegatedResultCorrelation;
607
+ /** Required with correlation + byte journal: exact stdout blobs and durable reference records. */
608
+ readonly stdoutJournalBlobs?: StdoutJournalBlobPort;
578
609
  /** Host environment snapshot; baseline spawn keys and source config paths are allowlisted. */
579
610
  readonly hostEnv?: NodeJS.ProcessEnv;
580
611
  readonly runtimeState?: AgentRuntimeState;
@@ -63,7 +63,12 @@ interface AgentTranslationSideEffectSink {
63
63
  readonly onSessionId: (args: {
64
64
  readonly sessionId: string;
65
65
  }) => void;
66
- readonly onToolCall: () => void;
66
+ /** Receives the already-narrowed `tool_use` payload, so a caller never re-checks its variant. */
67
+ readonly onToolCall: (args: {
68
+ readonly toolUse: Extract<RunAgentPayload, {
69
+ type: 'tool_use';
70
+ }>;
71
+ }) => void;
67
72
  readonly onUserVisibleOutput: () => void;
68
73
  }
69
74
  /**
@@ -101,6 +101,7 @@ function translateToolInputDeltaEvent(rawEvent) {
101
101
  };
102
102
  }
103
103
  export function translateToolResultEvent({ rawEvent }) {
104
+ const delegatedToolUseId = asOptionalString(rawEvent.delegatedToolUseId);
104
105
  const isError = typeof rawEvent.isError === 'boolean' ? rawEvent.isError : undefined;
105
106
  return {
106
107
  kind: 'agent',
@@ -108,6 +109,7 @@ export function translateToolResultEvent({ rawEvent }) {
108
109
  type: 'tool_result',
109
110
  toolUseId: asString(rawEvent.toolUseId),
110
111
  content: asString(rawEvent.content),
112
+ ...(delegatedToolUseId !== undefined ? { delegatedToolUseId } : {}),
111
113
  ...(isError !== undefined ? { isError } : {}),
112
114
  },
113
115
  };
@@ -224,7 +226,7 @@ export function applyAgentTranslationSideEffects({ payload, sessionId, sink }) {
224
226
  if (sessionId !== undefined)
225
227
  sink.onSessionId({ sessionId });
226
228
  if (payload.type === 'tool_use') {
227
- sink.onToolCall();
229
+ sink.onToolCall({ toolUse: payload });
228
230
  }
229
231
  else if ((payload.type === 'text_delta' || payload.type === 'thinking_delta') && payload.delta.length > 0) {
230
232
  sink.onUserVisibleOutput();
@@ -1,3 +1,5 @@
1
+ import type { DelegatedResultCorrelation } from '../delegated-result-correlation.js';
2
+ import type { StdoutJournalBlobPort } from '../tool-result-diagnostics.js';
1
3
  import type { probeMcpBridge } from './mcp-bridge-probe.js';
2
4
  import type { ModelCatalogSnapshot } from '@jini-ai/agent-runtime';
3
5
  import type { ChildProcess } from 'node:child_process';
@@ -24,6 +26,8 @@ export declare function defaultCleanupFailureSink(context: AgentCleanupFailureCo
24
26
  */
25
27
  export declare const DEFAULT_BUFFERED_STDOUT_MAX_BYTES: number;
26
28
  interface WireChildLifecycleContext extends TerminateChildTreeDeps {
29
+ readonly delegatedResultCorrelation?: DelegatedResultCorrelation;
30
+ readonly stdoutJournalBlobs?: StdoutJournalBlobPort;
27
31
  readonly startingModel?: string;
28
32
  readonly modelCatalog?: ModelCatalogSnapshot;
29
33
  readonly runId: string;
@@ -162,6 +166,7 @@ interface RunAcpDispatchInput {
162
166
  readonly prompt: string;
163
167
  readonly cwd: string;
164
168
  readonly permissionMode: 'bypass' | 'restricted' | undefined;
169
+ readonly cliParity?: boolean | undefined;
165
170
  readonly model: string | undefined;
166
171
  readonly modelCatalog?: ModelCatalogSnapshot;
167
172
  readonly imagePaths: readonly string[];