@jini-ai/daemon 0.6.12 → 0.6.15-rc.2

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 (58) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/agent-executor/codex/home-config.d.ts +50 -0
  3. package/dist/agent-executor/codex/home-config.js +209 -0
  4. package/dist/agent-executor/codex/home-staging.d.ts +60 -0
  5. package/dist/agent-executor/codex/home-staging.js +195 -0
  6. package/dist/agent-executor/codex/isolation.d.ts +27 -0
  7. package/dist/agent-executor/codex/isolation.js +67 -0
  8. package/dist/agent-executor/contracts.d.ts +4 -1
  9. package/dist/agent-executor/event-translation.d.ts +7 -7
  10. package/dist/agent-executor/event-translation.js +17 -17
  11. package/dist/agent-executor/events.js +5 -2
  12. package/dist/agent-executor/index.d.ts +3 -1
  13. package/dist/agent-executor/index.js +3 -1
  14. package/dist/agent-executor/launch.d.ts +3 -2
  15. package/dist/agent-executor/launch.js +77 -21
  16. package/dist/agent-executor/live-session-state.js +4 -1
  17. package/dist/agent-executor/mcp-bridge-probe.d.ts +2 -1
  18. package/dist/agent-executor/mcp-bridge-probe.js +1 -1
  19. package/dist/agent-executor/mcp-bridge.d.ts +9 -86
  20. package/dist/agent-executor/mcp-bridge.js +12 -360
  21. package/dist/ai-trace-lifecycle.d.ts +48 -0
  22. package/dist/ai-trace-lifecycle.js +116 -0
  23. package/dist/core/read-only-tools.js +3 -1
  24. package/dist/delegated-tool-bridge.js +19 -1
  25. package/dist/http/attachment-lookup-routes.d.ts +44 -0
  26. package/dist/http/attachment-lookup-routes.js +71 -0
  27. package/dist/http/attachments.d.ts +112 -7
  28. package/dist/http/attachments.js +58 -17
  29. package/dist/http/connectors.js +23 -18
  30. package/dist/http/index.d.ts +6 -0
  31. package/dist/http/index.js +3 -0
  32. package/dist/http/message-relay.js +4 -1
  33. package/dist/http/tool-execution-routes.d.ts +69 -0
  34. package/dist/http/tool-execution-routes.js +181 -0
  35. package/dist/http/xai.js +19 -9
  36. package/dist/index.d.ts +3 -0
  37. package/dist/index.js +3 -0
  38. package/dist/remote-attachment-lookup.d.ts +29 -0
  39. package/dist/remote-attachment-lookup.js +71 -0
  40. package/dist/remote-tool-executor.d.ts +85 -0
  41. package/dist/remote-tool-executor.js +250 -0
  42. package/dist/routing-tool-executor.d.ts +29 -0
  43. package/dist/routing-tool-executor.js +16 -0
  44. package/dist/run-lifecycle.d.ts +4 -1
  45. package/dist/run-lifecycle.js +35 -5
  46. package/dist/store/event-log/sqlite-kernel.d.ts +18 -0
  47. package/dist/store/event-log/sqlite-kernel.js +135 -0
  48. package/dist/store/event-log/sqlite-policy.d.ts +93 -0
  49. package/dist/store/event-log/sqlite-policy.js +74 -0
  50. package/dist/store/event-log/sqlite.d.ts +7 -31
  51. package/dist/store/event-log/sqlite.js +32 -95
  52. package/dist/tool-recovery/analytics.d.ts +27 -0
  53. package/dist/tool-recovery/analytics.js +38 -0
  54. package/dist/tool-recovery/index.d.ts +2 -0
  55. package/dist/tool-recovery/index.js +2 -0
  56. package/dist/tool-recovery/observability.d.ts +82 -0
  57. package/dist/tool-recovery/observability.js +88 -0
  58. package/package.json +15 -14
@@ -144,6 +144,9 @@ export interface AgentExecutorRunInput {
144
144
  readonly imageContents?: readonly MessageAttachmentImage[];
145
145
  /** Additional host-validated directories the runtime may read. */
146
146
  readonly extraAllowedDirs?: readonly string[];
147
+ /** Owner-picked folders outside filesystemRoot: readable where the CLI can enforce read-only, never
148
+ * a writable grant. Refused before spawn when unsafe (see `validateReadOnlyExtraDirs`). */
149
+ readonly readOnlyExtraDirs?: readonly string[];
147
150
  /** Trusted root that must contain pi-rpc image paths after realpath resolution. */
148
151
  readonly uploadRoot?: string;
149
152
  /**
@@ -207,7 +210,7 @@ export interface AgentExecutor {
207
210
  * underlying run is always already transitioned to `'failed'` via
208
211
  * `lifecycle.finish()` before this rejects (see index.ts module doc's Invariant).
209
212
  */
210
- run(requiredArgs: Pick<AgentExecutorRunInput, "runId" | "agentId" | "prompt" | "cwd">, optionalArgs?: Pick<AgentExecutorRunInput, "filesystemRoot" | "model" | "reasoning" | "nativeToolAccess" | "nativeCapabilities" | "cliParity" | "permissionMode" | "imagePaths" | "imageContents" | "extraAllowedDirs" | "uploadRoot" | "credentialEnv" | "env" | "resumeSessionId" | "newSessionId" | "disallowedTools" | "allowedTools" | "settingSources" | "settings" | "chromeIntegration">): Promise<void>;
213
+ run(requiredArgs: Pick<AgentExecutorRunInput, "runId" | "agentId" | "prompt" | "cwd">, optionalArgs?: Pick<AgentExecutorRunInput, "filesystemRoot" | "model" | "reasoning" | "nativeToolAccess" | "nativeCapabilities" | "cliParity" | "permissionMode" | "imagePaths" | "imageContents" | "extraAllowedDirs" | "readOnlyExtraDirs" | "uploadRoot" | "credentialEnv" | "env" | "resumeSessionId" | "newSessionId" | "disallowedTools" | "allowedTools" | "settingSources" | "settings" | "chromeIntegration">): Promise<void>;
211
214
  /**
212
215
  * Delivers a message the human sent while `runId` is going straight into its live agent process
213
216
  * (see {@link UserMessageDelivery} for each outcome). A stream-json agent queues the message
@@ -1,20 +1,20 @@
1
1
  import type { RunAgentPayload, RunErrorPayload } from '@jini-ai/protocol';
2
2
  import type { AgentRuntimeEventTranslation } from './contracts.js';
3
3
  /**
4
- * Narrows a parsed `usage` event's `usage` sub-object (`{input_tokens?, output_tokens?}`) — the one
4
+ * Narrows a parsed `usage` event's `usage` sub-object — the one
5
5
  * piece of {@link translateUsagePayload} with real nested branching (an optional container holding
6
- * two optional numeric fields), extracted so that function reads as a flat field-by-field mapping.
6
+ * optional numeric fields), extracted so that function reads as a flat field-by-field mapping.
7
+ * Reuses the runtime's alias owner so cache/reasoning counters survive the canonical boundary.
7
8
  * @param rawUsage - `rawEvent.usage` once already narrowed to a record, or `undefined` when absent/malformed.
8
- * @returns `undefined` when neither token count is present — matching `translateUsagePayload`'s
9
+ * @returns `undefined` when no token count is present — matching `translateUsagePayload`'s
9
10
  * original "omit the whole `usage` field rather than emit an empty object" behavior.
10
11
  * @complexity O(1).
11
12
  */
12
13
  export declare function extractUsageTokens({ rawUsage }: {
13
14
  readonly rawUsage: Record<string, unknown> | undefined;
14
- }): {
15
- input_tokens?: number;
16
- output_tokens?: number;
17
- } | undefined;
15
+ }, _options?: Record<string, never>): Extract<RunAgentPayload, {
16
+ type: 'usage';
17
+ }>['usage'];
18
18
  export declare function translateStatusEvent({ rawEvent }: {
19
19
  readonly rawEvent: Record<string, unknown>;
20
20
  }): AgentRuntimeEventTranslation;
@@ -1,47 +1,46 @@
1
+ import { normalizeAiUsage } from '@jini-ai/agent-runtime';
1
2
  import { isRecord, asString, asOptionalString, } from './values.js';
2
3
  function asOptionalNumber(value) {
3
4
  return typeof value === 'number' ? value : undefined;
4
5
  }
5
6
  /**
6
- * Narrows a parsed `usage` event's `usage` sub-object (`{input_tokens?, output_tokens?}`) — the one
7
+ * Narrows a parsed `usage` event's `usage` sub-object — the one
7
8
  * piece of {@link translateUsagePayload} with real nested branching (an optional container holding
8
- * two optional numeric fields), extracted so that function reads as a flat field-by-field mapping.
9
+ * optional numeric fields), extracted so that function reads as a flat field-by-field mapping.
10
+ * Reuses the runtime's alias owner so cache/reasoning counters survive the canonical boundary.
9
11
  * @param rawUsage - `rawEvent.usage` once already narrowed to a record, or `undefined` when absent/malformed.
10
- * @returns `undefined` when neither token count is present — matching `translateUsagePayload`'s
12
+ * @returns `undefined` when no token count is present — matching `translateUsagePayload`'s
11
13
  * original "omit the whole `usage` field rather than emit an empty object" behavior.
12
14
  * @complexity O(1).
13
15
  */
14
- export function extractUsageTokens({ rawUsage }) {
15
- if (!rawUsage)
16
+ export function extractUsageTokens({ rawUsage }, _options = {}) {
17
+ const normalized = normalizeAiUsage({ usage: rawUsage });
18
+ if (!normalized)
16
19
  return undefined;
17
- const inputTokens = asOptionalNumber(rawUsage.input_tokens);
18
- const outputTokens = asOptionalNumber(rawUsage.output_tokens);
19
- if (inputTokens === undefined && outputTokens === undefined)
20
- return undefined;
21
- return {
22
- ...(inputTokens !== undefined ? { input_tokens: inputTokens } : {}),
23
- ...(outputTokens !== undefined ? { output_tokens: outputTokens } : {}),
24
- };
20
+ const fields = { input_tokens: normalized.inputTokens, output_tokens: normalized.outputTokens, cache_read_input_tokens: normalized.cacheReadTokens, cache_creation_input_tokens: normalized.cacheWriteTokens, reasoning_tokens: normalized.reasoningTokens };
21
+ return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== null));
25
22
  }
26
23
  /**
27
24
  * Narrows one parsed `usage` event's loosely-typed fields into
28
25
  * `RunAgentPayload`'s `usage` variant. The 4 source parsers attach extra
29
- * fields this narrow payload has no room for — `thought_tokens`,
26
+ * fields beyond the basic counts — `thought_tokens`,
30
27
  * `cached_read_tokens`/`cached_write_tokens` (opencode/gemini/codex),
31
28
  * `modelUsage`/`stopReason`/`isError` (qoder), a top-level `stopReason`
32
- * (claude/copilot) — all intentionally dropped here, not carried through.
29
+ * (claude/copilot). Cache/reasoning counters and stop reasons now survive tracing; vendor-only
30
+ * `modelUsage` and `isError` remain outside this neutral usage contract.
33
31
  * @param rawEvent - The raw `{type:'usage', ...}` record from a stream parser.
34
32
  * @returns The narrowed `RunAgentPayload` `usage` variant.
35
33
  * @complexity O(1).
36
34
  * @overallScore 100/100
37
35
  */
38
- function translateUsagePayload(rawEvent) {
36
+ function translateUsagePayload({ rawEvent }) {
39
37
  const rawUsage = isRecord(rawEvent.usage) ? rawEvent.usage : undefined;
40
38
  const usage = extractUsageTokens({ rawUsage: rawUsage });
41
39
  const costUsd = asOptionalNumber(rawEvent.costUsd);
42
40
  const cumulativeCostUsd = asOptionalNumber(rawEvent.cumulativeCostUsd);
43
41
  const costSessionId = asOptionalString(rawEvent.costSessionId);
44
42
  const durationMs = asOptionalNumber(rawEvent.durationMs);
43
+ const stopReason = asOptionalString(rawEvent.stopReason);
45
44
  return {
46
45
  type: 'usage',
47
46
  ...(usage !== undefined ? { usage } : {}),
@@ -49,6 +48,7 @@ function translateUsagePayload(rawEvent) {
49
48
  ...(cumulativeCostUsd !== undefined ? { cumulativeCostUsd } : {}),
50
49
  ...(costSessionId !== undefined ? { costSessionId } : {}),
51
50
  ...(durationMs !== undefined ? { durationMs } : {}),
51
+ ...(stopReason !== undefined ? { stopReason } : {}),
52
52
  };
53
53
  }
54
54
  export function translateStatusEvent({ rawEvent }) {
@@ -115,7 +115,7 @@ export function translateToolResultEvent({ rawEvent }) {
115
115
  };
116
116
  }
117
117
  function translateUsageEvent(rawEvent) {
118
- return { kind: 'agent', payload: translateUsagePayload(rawEvent) };
118
+ return { kind: 'agent', payload: translateUsagePayload({ rawEvent }) };
119
119
  }
120
120
  function translateRawEvent(rawEvent) {
121
121
  return { kind: 'agent', payload: { type: 'raw', line: asString(rawEvent.line) } };
@@ -14,7 +14,7 @@ import { resolveContinuationTransport, } from '../continuation/continuation-tran
14
14
  import { resultContent, } from '../delegated-tool-bridge.js';
15
15
  import { projectToolResultEvent, } from '../tool-result-media.js';
16
16
  import { translateAgentRuntimeEvent, translateAcpError, applyAgentTranslationSideEffects, } from './event-translation.js';
17
- import { errorMessage, isRecord, } from './values.js';
17
+ import { errorMessage, } from './values.js';
18
18
  import { MCP_BRIDGE_UNAVAILABLE, unavailableJiniBridgeStatus, bridgeUnavailableMessage, } from './mcp-bridge.js';
19
19
  /** Carry PID only on structured status so the host can record its OS start identity early.
20
20
  * A PID alone is never proof of death; hosts verify PID reuse before native continuation. */
@@ -741,7 +741,7 @@ export function wireChildLifecycle(ctx) {
741
741
  * confirms the write; UUID-attributed CLI receipts separately confirm consumption. */
742
742
  async function sendUserMessage({ text, id = randomUUID(), attachmentIds }, { imageContents: images = [] } = {}) {
743
743
  if (!acceptsMidRunUserMessages({ def }))
744
- return relay?.enqueue({ id, text, hasAttachments: images.length > 0 || (attachmentIds?.length ?? 0) > 0 }) ?? 'unsupported';
744
+ return relayUserMessage({ id, text, images, attachmentIds });
745
745
  const stdin = child.stdin;
746
746
  if (stdinClosed || !stdin)
747
747
  return 'not-running';
@@ -749,6 +749,9 @@ export function wireChildLifecycle(ctx) {
749
749
  writeUserMessage(stdin, text, images, id);
750
750
  return 'delivered';
751
751
  }
752
+ function relayUserMessage({ id, text, images, attachmentIds }) {
753
+ return relay?.enqueue({ id, text, hasAttachments: images.length > 0 || (attachmentIds?.length ?? 0) > 0 }) ?? 'unsupported';
754
+ }
752
755
  /** Writes a complete mid-run turn using the idle image encoder; journals text without base64.
753
756
  * The same UUID identifies stdin, queue acceptance and subsequent consumption receipts. */
754
757
  function writeUserMessage(stdin, text, images, id) {
@@ -92,7 +92,9 @@
92
92
  export { type SupportedStreamFormat, type AgentExecutorCompatibility, type AgentRuntimeEventTranslation, type AgentExecutorErrorCode, AgentExecutorError, type AgentExecutorRunInput, type AgentExecutor, type UserMessageDelivery, type CollectProcessTreePidsPort, type StopProcessesPort, type AgentCleanupFailurePhase, type AgentCleanupFailureContext, type ContinuationOptions, type McpJsonInjectionOptions, type McpBridgeDelivery, type PreparedCodexHome, type PreparedClaudeConfigDir, type ClaudeConfigDirSeams, type ClaudeConfigDirIsolationOptions, type FailureClassificationContext, type ClassifyFailure, type CreateAgentExecutorOptions, type SystemPromptOverlayDelivery, type PreparedSystemPromptOverlayFile, type SpawnAgentChildProcessResult, } from './contracts.js';
93
93
  export { isSupportedStreamFormat, assessAgentExecutorCompatibility, isAgentExecutorSupported, resolveDefAndStreamFormat, resolveImageDeliveryAndArgvBudget, resolveRunEnv, selectAgentExecutorHostEnv, resolveLaunch, stagePromptFile, stageLogFile, computeChildEnv, computeRuntimeContext, buildAgentBuildArgsOptions, resolveSystemPromptOverlayDelivery, buildRunArgs, prepareSystemPromptOverlayFileIfNeeded, guardWindowsCommandLineBudget, spawnAgentChildProcess, isStdinDrivenFormat, confirmChildSpawned, createAgentExecutor, } from './launch.js';
94
94
  export { extractUsageTokens, translateStatusEvent, translateToolResultEvent, translateErrorEvent, translateTurnEndEvent, translateAgentRuntimeEvent, applyAgentTranslationSideEffects, } from './event-translation.js';
95
- export { MCP_BRIDGE_UNAVAILABLE, unavailableJiniBridgeStatus, buildMcpJsonServerEntry, mergeMcpJsonContent, buildAcpMcpBridgeServers, mergeEnvContentMcpConfig, mergeEnvContentInstructions, buildCodexMcpServerToml, buildCodexHomeConfigToml, resolveSourceCodexHomeDir, buildMcpBridgeDelivery, resolveSourceClaudeConfigDir, resolveMcpBridgeForRun, writeMcpJsonIfNeeded, prepareCodexHomeIfNeeded, prepareClaudeConfigDirIfNeeded, } from './mcp-bridge.js';
95
+ export { MCP_BRIDGE_UNAVAILABLE, unavailableJiniBridgeStatus, buildMcpJsonServerEntry, mergeMcpJsonContent, buildAcpMcpBridgeServers, mergeEnvContentMcpConfig, mergeEnvContentInstructions, buildMcpBridgeDelivery, resolveSourceClaudeConfigDir, resolveMcpBridgeForRun, writeMcpJsonIfNeeded, prepareClaudeConfigDirIfNeeded, } from './mcp-bridge.js';
96
+ export { buildCodexMcpServerToml, buildCodexHomeConfigToml } from './codex/home-config.js';
97
+ export { resolveSourceCodexHomeDir, prepareCodexHomeIfNeeded } from './codex/home-staging.js';
96
98
  export { probeMcpBridge, type McpBridgeProbeResult } from './mcp-bridge-probe.js';
97
99
  export { DEFAULT_BUFFERED_STDOUT_MAX_BYTES, runAcpDispatch, runPiRpcDispatch, } from './events.js';
98
100
  export { armHandoffWatcher, } from './handoff.js';
@@ -92,7 +92,9 @@
92
92
  export { AgentExecutorError, } from './contracts.js';
93
93
  export { isSupportedStreamFormat, assessAgentExecutorCompatibility, isAgentExecutorSupported, resolveDefAndStreamFormat, resolveImageDeliveryAndArgvBudget, resolveRunEnv, selectAgentExecutorHostEnv, resolveLaunch, stagePromptFile, stageLogFile, computeChildEnv, computeRuntimeContext, buildAgentBuildArgsOptions, resolveSystemPromptOverlayDelivery, buildRunArgs, prepareSystemPromptOverlayFileIfNeeded, guardWindowsCommandLineBudget, spawnAgentChildProcess, isStdinDrivenFormat, confirmChildSpawned, createAgentExecutor, } from './launch.js';
94
94
  export { extractUsageTokens, translateStatusEvent, translateToolResultEvent, translateErrorEvent, translateTurnEndEvent, translateAgentRuntimeEvent, applyAgentTranslationSideEffects, } from './event-translation.js';
95
- export { MCP_BRIDGE_UNAVAILABLE, unavailableJiniBridgeStatus, buildMcpJsonServerEntry, mergeMcpJsonContent, buildAcpMcpBridgeServers, mergeEnvContentMcpConfig, mergeEnvContentInstructions, buildCodexMcpServerToml, buildCodexHomeConfigToml, resolveSourceCodexHomeDir, buildMcpBridgeDelivery, resolveSourceClaudeConfigDir, resolveMcpBridgeForRun, writeMcpJsonIfNeeded, prepareCodexHomeIfNeeded, prepareClaudeConfigDirIfNeeded, } from './mcp-bridge.js';
95
+ export { MCP_BRIDGE_UNAVAILABLE, unavailableJiniBridgeStatus, buildMcpJsonServerEntry, mergeMcpJsonContent, buildAcpMcpBridgeServers, mergeEnvContentMcpConfig, mergeEnvContentInstructions, buildMcpBridgeDelivery, resolveSourceClaudeConfigDir, resolveMcpBridgeForRun, writeMcpJsonIfNeeded, prepareClaudeConfigDirIfNeeded, } from './mcp-bridge.js';
96
+ export { buildCodexMcpServerToml, buildCodexHomeConfigToml } from './codex/home-config.js';
97
+ export { resolveSourceCodexHomeDir, prepareCodexHomeIfNeeded } from './codex/home-staging.js';
96
98
  export { probeMcpBridge } from './mcp-bridge-probe.js';
97
99
  export { DEFAULT_BUFFERED_STDOUT_MAX_BYTES, runAcpDispatch, runPiRpcDispatch, } from './events.js';
98
100
  export { armHandoffWatcher, } from './handoff.js';
@@ -190,14 +190,15 @@ type AgentBuildInput = Parameters<typeof buildAgentBuildArgsOptions>[0]['input']
190
190
  /** Phase 9a: the def's `buildArgs` 4th argument. Historically omitted when every override was absent;
191
191
  * that let defs silently enable auto-approval. Always supply the validated restricted default now. Pure. */
192
192
  export declare function buildAgentBuildArgsOptions({ input, systemPromptOverlay }: {
193
- readonly input: Pick<AgentExecutorRunInput, 'filesystemRoot' | 'model' | 'reasoning' | 'permissionMode' | 'nativeToolAccess' | 'nativeCapabilities' | 'cliParity' | 'disallowedTools' | 'allowedTools' | 'settingSources' | 'settings' | 'chromeIntegration'> & {
193
+ readonly input: Pick<AgentExecutorRunInput, 'filesystemRoot' | 'readOnlyExtraDirs' | 'model' | 'reasoning' | 'permissionMode' | 'nativeToolAccess' | 'nativeCapabilities' | 'cliParity' | 'disallowedTools' | 'allowedTools' | 'settingSources' | 'settings' | 'chromeIntegration'> & {
194
194
  filesystemRuntimeReadPaths?: readonly string[];
195
195
  filesystemTempDir?: string;
196
196
  filesystemSharedTempRoots?: readonly string[];
197
+ filesystemRuntimeTempBase?: string;
197
198
  filesystemNotice?: string;
198
199
  };
199
200
  readonly systemPromptOverlay: string | null | undefined;
200
- }): RuntimeBuildOptions;
201
+ }, _options?: {}): RuntimeBuildOptions;
201
202
  /**
202
203
  * **The single dispatch point from a computed system-prompt overlay to its delivery mechanism** —
203
204
  * see `RuntimeAgentDef.systemPromptDelivery`'s own doc for the declared shape. Pure and
@@ -6,14 +6,15 @@ import { spawn as nodeSpawn, } from 'node:child_process';
6
6
  import { promises as fsPromises, } from 'node:fs';
7
7
  import { tmpdir, } from 'node:os';
8
8
  import { join, } from 'node:path';
9
- import { agentCapabilities, assertClaudeFilesystemSupport, claudeFilesystemRuntimePaths, claudeFilesystemSettings, validateFilesystemLaunch, bindRuntimeAgentDef, defaultRuntimeState, getRuntimeModelDiscoveryDeps, userToolchainBinDirs, applyAgentLaunchEnv, ensureAgentCapabilities, resolveModelForLaunch, getAgentDef, resolveAgentLaunch, attachAcpSession, attachPiRpcSession, checkPromptArgvBudget, checkWindowsCmdShimCommandLineBudget, checkWindowsDirectExeCommandLineBudget, prepareAgentLogFile, preparePromptFileForAgent, } from '@jini-ai/agent-runtime';
9
+ import { agentCapabilities, createClaudeFilesystemSupportProbe, claudeFilesystemRuntimePaths, claudeFilesystemSettings, validateFilesystemLaunch, validateReadOnlyExtraDirs, bindRuntimeAgentDef, defaultRuntimeState, getRuntimeModelDiscoveryDeps, userToolchainBinDirs, applyAgentLaunchEnv, ensureAgentCapabilities, resolveModelForLaunch, getAgentDef, resolveAgentLaunch, attachAcpSession, attachPiRpcSession, checkPromptArgvBudget, checkWindowsCmdShimCommandLineBudget, checkWindowsDirectExeCommandLineBudget, prepareAgentLogFile, preparePromptFileForAgent, } from '@jini-ai/agent-runtime';
10
10
  import { createCommandInvocation } from '@jini-ai/platform/command';
11
11
  import { collectProcessTreePids, listProcessSnapshots, stopProcesses, } from '@jini-ai/platform/process';
12
12
  import { applyImagePromptDelivery, } from '../image-prompt-delivery.js';
13
13
  import { SUPPORTED_STREAM_FORMATS, AgentExecutorError, } from './contracts.js';
14
14
  import { errorMessage, } from './values.js';
15
15
  import { defaultCleanupFailureSink, DEFAULT_BUFFERED_STDOUT_MAX_BYTES, wireChildLifecycle, writePromptToStdin, runAcpDispatch, runPiRpcDispatch, } from './events.js';
16
- import { mergeEnvContentMcpConfig, mergeEnvContentInstructions, defaultRemoveMcpJsonFile, resolveMcpBridgeForRun, writeMcpJsonIfNeeded, prepareCodexHomeIfNeeded, prepareClaudeConfigDirIfNeeded, } from './mcp-bridge.js';
16
+ import { mergeEnvContentMcpConfig, mergeEnvContentInstructions, defaultRemoveMcpJsonFile, resolveMcpBridgeForRun, writeMcpJsonIfNeeded, prepareClaudeConfigDirIfNeeded, } from './mcp-bridge.js';
17
+ import { prepareCodexHomeIfNeeded, withStagedCodexHome } from './codex/home-staging.js';
17
18
  import { acquireRuntimeLockIfConfigured, armHandoffWatcher, } from './handoff.js';
18
19
  /**
19
20
  * Narrows a `RuntimeAgentDef.streamFormat` string to the supported
@@ -279,7 +280,7 @@ export function computeChildEnv({ spawnEnv, mcpBridge }, { codexHomeDir, systemP
279
280
  ...envPassthroughApplied,
280
281
  [stagedInstructionsFile.varName]: mergeEnvContentInstructions({ existingRaw: envPassthroughApplied[stagedInstructionsFile.varName], instructionsFilePath: stagedInstructionsFile.path }),
281
282
  };
282
- const codexHomeApplied = codexHomeDir === undefined ? instructionsApplied : { ...instructionsApplied, CODEX_HOME: codexHomeDir };
283
+ const codexHomeApplied = codexHomeDir === undefined ? instructionsApplied : withStagedCodexHome({ env: instructionsApplied, codexHomeDir });
283
284
  // Finding 1 of SEC-assistant-env-isolation-2026-09-07: same "only set when staged" shape as
284
285
  // codexHomeApplied above — mutually exclusive with it in practice (codexHomeDir is only ever set
285
286
  // for a 'codex-toml' bridge, claudeConfigDir only ever for a `claude`-id run), so both existing
@@ -336,13 +337,24 @@ function nativeBuildOptions({ input, permissionMode }, _options = {}) {
336
337
  ...(input.cliParity === true && permissionMode === 'bypass' && input.nativeToolAccess !== false ? { cliParity: true } : {}),
337
338
  };
338
339
  }
339
- /** Preserve explicit empty restrictions and settings: they have meaning to the native CLI. */
340
- function settingsBuildOptions({ input }, _options = {}) {
340
+ /** Forward only supplied confinement paths; empty restriction arrays remain meaningful.
341
+ * Keeps executor-resolved filesystem facts through the definition's second settings merge. Pure, O(1).
342
+ */
343
+ function filesystemBuildOptions({ input }, _options = {}) {
341
344
  return {
342
345
  ...(input.filesystemRoot !== undefined ? { filesystemRoot: input.filesystemRoot } : {}),
343
346
  ...(input.filesystemSharedTempRoots ? { filesystemSharedTempRoots: input.filesystemSharedTempRoots } : {}),
344
347
  ...(input.filesystemTempDir ? { filesystemTempDir: input.filesystemTempDir } : {}),
345
348
  ...(input.filesystemRuntimeReadPaths ? { filesystemRuntimeReadPaths: input.filesystemRuntimeReadPaths } : {}),
349
+ ...(input.filesystemRuntimeTempBase !== undefined ? { filesystemRuntimeTempBase: input.filesystemRuntimeTempBase } : {}),
350
+ // Only while a folder limit applies: an unconfined CLI gets no extra grant, read-only or otherwise.
351
+ ...(input.filesystemRoot !== undefined && input.readOnlyExtraDirs?.length ? { filesystemReadOnlyDirs: input.readOnlyExtraDirs } : {}),
352
+ };
353
+ }
354
+ /** Preserve explicit empty restrictions and settings: they have meaning to the native CLI. */
355
+ function settingsBuildOptions({ input }, _options = {}) {
356
+ return {
357
+ ...filesystemBuildOptions({ input }),
346
358
  ...(input.disallowedTools !== undefined ? { disallowedTools: input.disallowedTools } : {}),
347
359
  ...(input.allowedTools !== undefined ? { allowedTools: input.allowedTools } : {}),
348
360
  ...(input.settingSources !== undefined ? { settingSources: input.settingSources } : {}),
@@ -350,9 +362,13 @@ function settingsBuildOptions({ input }, _options = {}) {
350
362
  ...(input.chromeIntegration !== undefined ? { chromeIntegration: input.chromeIntegration } : {}),
351
363
  };
352
364
  }
365
+ /** A relay needs both a delivered bridge and a runtime that understands it. Pure, O(1). */
366
+ function canRegisterRunMessageRelay({ def, mcpBridge }, _options = {}) {
367
+ return mcpBridge !== null && supportsMessageRelay({ def });
368
+ }
353
369
  /** Phase 9a: the def's `buildArgs` 4th argument. Historically omitted when every override was absent;
354
370
  * that let defs silently enable auto-approval. Always supply the validated restricted default now. Pure. */
355
- export function buildAgentBuildArgsOptions({ input, systemPromptOverlay }) {
371
+ export function buildAgentBuildArgsOptions({ input, systemPromptOverlay }, _options = {}) {
356
372
  const permissionMode = input.permissionMode ?? 'restricted';
357
373
  if (permissionMode !== 'restricted' && permissionMode !== 'bypass') {
358
374
  throw new AgentExecutorError({ code: 'AGENT_PERMISSION_MODE_INVALID', message: 'AgentExecutor: permissionMode must be restricted or bypass' });
@@ -622,6 +638,22 @@ export async function confirmChildSpawned({ input, deps }) {
622
638
  function nativeCapabilityInput({ input }, _options = {}) {
623
639
  return { ...(input.nativeCapabilities !== undefined ? { nativeCapabilities: input.nativeCapabilities } : {}) };
624
640
  }
641
+ /** Switch names a person sees for each native capability (the host's settings use the same words). */
642
+ const NATIVE_CAPABILITY_LABELS = {
643
+ shell: 'Bash/shell', fileWrite: 'File edit/write', userMcp: 'Your MCP servers', subagents: 'Subagents/Task',
644
+ userSettings: 'Your skills and settings', web: 'Web fetch/search', workflows: 'Workflows', loginEnvironment: 'Your shell environment',
645
+ };
646
+ /** The refusal a person reads when a CLI without a native-tool policy cannot honor this run's switches:
647
+ * it names the CLI and the switch to turn on. Owner 2026-10-27: the old "no native-tool policy and
648
+ * audit adapter" told a Gemini user nothing. Pure, O(c) for c OFF switches.
649
+ * @example nativeToolPolicyRefusal({ cliName: 'Antigravity', offSwitches: ['userMcp'] }, {});
650
+ */
651
+ function nativeToolPolicyRefusal({ cliName, offSwitches }, _options = {}) {
652
+ const switches = offSwitches.map(key => `'${NATIVE_CAPABILITY_LABELS[key] ?? key}'`);
653
+ const blocked = switches.length > 0 ? `with ${new Intl.ListFormat('en', { type: 'conjunction' }).format(switches)} off` : `with this chat's CLI tool limits`;
654
+ const action = switches.length === 0 ? `Turn on 'Full CLI access'` : `Turn ${switches.length === 1 ? 'it' : 'them'} on`;
655
+ return `${cliName} can't run ${blocked}. ${action} in the assistant settings, or choose Claude or Codex.`;
656
+ }
625
657
  /** Correlation projects retained stdout, so exact-byte journals need a separate blob writer. */
626
658
  function validateStdoutJournalOptions(options, _options = {}) {
627
659
  if (options.delegatedResultCorrelation && options.journal && !options.stdoutJournalBlobs) {
@@ -685,12 +717,16 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
685
717
  const claudeConfigDirIsolationEnabled = options.claudeConfigDirIsolationEnabled ?? false;
686
718
  const promptAugmenter = options.promptAugmenter;
687
719
  const filesystemTemps = new Map();
688
- /** Remove only this run's private temp directory, on startup failure or child cleanup. */
720
+ const verifyClaudeFilesystem = options.assertClaudeFilesystemSupport ?? createClaudeFilesystemSupportProbe({});
721
+ /** Remove this run's private temp directories on startup failure, exit, crash or cancellation.
722
+ * O(d) removals for d staged directories; each failure is reported without skipping the others.
723
+ */
689
724
  async function cleanupFilesystemTemp({ runId }, _optional = {}) {
690
- const path = filesystemTemps.get(runId);
691
- if (!path)
725
+ const paths = filesystemTemps.get(runId);
726
+ if (!paths)
692
727
  return;
693
- await fsPromises.rm(path, { recursive: true, force: true }).catch(() => console.warn('[agent-executor] private temp cleanup failed'));
728
+ await Promise.all(paths.map(path => fsPromises.rm(path, { recursive: true, force: true })
729
+ .catch(() => console.warn('[agent-executor] private temp cleanup failed'))));
694
730
  filesystemTemps.delete(runId);
695
731
  }
696
732
  /**
@@ -711,7 +747,7 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
711
747
  // the user got a bare "Run failed". Best-effort: a stream that will not take the event must not
712
748
  // stop the run from finishing.
713
749
  await lifecycle
714
- .emit({ runId: runId, input: { event: 'error', data: { message: `The assistant could not start: ${message.replace(/^AgentExecutor:\s*/, '')}` } } })
750
+ .emit({ runId: runId, input: { event: 'error', data: { error: { code, message }, message: `The assistant could not start: ${message.replace(/^AgentExecutor:\s*/, '')}` } } })
715
751
  .catch(() => undefined);
716
752
  await cleanupFilesystemTemp({ runId }, {});
717
753
  await lifecycle.finish({ runId, status: 'failed', code: null, signal: null, resumable: false });
@@ -745,8 +781,11 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
745
781
  // Never silently ignore a host's locked native-tool policy on another CLI.
746
782
  // Parity cannot override an explicit lock or a restricted run.
747
783
  input.cliParity = input.cliParity === true && input.permissionMode === 'bypass' && input.nativeToolAccess !== false;
748
- if ((input.nativeToolAccess !== undefined && !input.cliParity || Object.values(input.nativeCapabilities ?? {}).some(value => value === false)) && def.supportsNativeToolAccess !== true) {
749
- return failBeforeSpawn({ runId: input.runId, code: 'NATIVE_TOOL_POLICY_UNSUPPORTED', message: 'This CLI has no native-tool policy and audit adapter. Choose a CLI with native-tool policy support.' });
784
+ if (def.supportsNativeToolAccess === true)
785
+ return;
786
+ const offSwitches = Object.entries(input.nativeCapabilities ?? {}).filter(([, value]) => value === false).map(([key]) => key);
787
+ if (input.nativeToolAccess !== undefined && !input.cliParity || offSwitches.length > 0) {
788
+ return failBeforeSpawn({ runId: input.runId, code: 'NATIVE_TOOL_POLICY_UNSUPPORTED', message: nativeToolPolicyRefusal({ cliName: def.name, offSwitches }, {}) });
750
789
  }
751
790
  }
752
791
  /** Validate every directory after image augmentation, before model probes or resource staging.
@@ -760,6 +799,8 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
760
799
  ...(imageDelivery.extraAllowedDirs ? { extraAllowedDirs: imageDelivery.extraAllowedDirs } : {}),
761
800
  ...(input.imagePaths ? { imagePaths: input.imagePaths } : {}),
762
801
  }, {});
802
+ if (input.readOnlyExtraDirs)
803
+ input.readOnlyExtraDirs = validateReadOnlyExtraDirs({ filesystemRoot: input.filesystemRoot, dirs: input.readOnlyExtraDirs }, {});
763
804
  input.cwd = input.filesystemRoot;
764
805
  }
765
806
  catch {
@@ -774,20 +815,27 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
774
815
  if (input.filesystemRoot === undefined)
775
816
  return;
776
817
  try {
777
- const verify = options.assertClaudeFilesystemSupport ?? assertClaudeFilesystemSupport;
778
- const supported = await verify({ agentId: def.id, launchPath: launch.launchPath, env: spawnEnv }, {});
818
+ const supported = await verifyClaudeFilesystem({ agentId: def.id, launchPath: launch.launchPath, env: spawnEnv }, {});
779
819
  const runtimeDir = join(input.filesystemRoot, '.agent-runtime');
780
820
  await fsPromises.mkdir(runtimeDir, { recursive: true, mode: 0o700 });
781
821
  // Refuse a runtime-folder symlink escaping the site before creating anything beneath it.
782
822
  validateFilesystemLaunch({ filesystemRoot: input.filesystemRoot, cwd: input.cwd, extraAllowedDirs: [runtimeDir] }, {});
783
823
  input.filesystemTempDir = await fsPromises.realpath(await fsPromises.mkdtemp(join(runtimeDir, 'tmp-')));
784
- filesystemTemps.set(input.runId, input.filesystemTempDir);
824
+ filesystemTemps.set(input.runId, [input.filesystemTempDir]);
785
825
  // Recheck after creation too: swapping the runtime symlink must never grant outside temp access.
786
826
  validateFilesystemLaunch({ filesystemRoot: input.filesystemRoot, cwd: input.cwd, extraAllowedDirs: [input.filesystemTempDir] }, {});
787
827
  input.filesystemSharedTempRoots = [spawnEnv.TMPDIR, spawnEnv.TMP, spawnEnv.TEMP].filter((path) => Boolean(path));
788
828
  spawnEnv.TMPDIR = input.filesystemTempDir;
789
829
  spawnEnv.TMP = input.filesystemTempDir;
790
830
  spawnEnv.TEMP = input.filesystemTempDir;
831
+ if (def.id === 'claude' && process.platform !== 'win32') {
832
+ // Claude shortens long Bash socket paths to shared scratch. Keep even the /private alias
833
+ // plus /claude-<uid> below 44 bytes, and isolate it with mkdtemp's private 0700 directory.
834
+ const runtimeTemp = await fsPromises.mkdtemp('/tmp/jini-');
835
+ filesystemTemps.get(input.runId).push(runtimeTemp);
836
+ input.filesystemRuntimeTempBase = runtimeTemp;
837
+ spawnEnv.CLAUDE_CODE_TMPDIR = runtimeTemp;
838
+ }
791
839
  if (supported === false) {
792
840
  input.filesystemNotice = 'files and shell are not folder-limited for this agent';
793
841
  console.warn('[agent-executor] files and shell are not folder-limited for this agent');
@@ -842,6 +890,9 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
842
890
  }, options.runtimeState ? { state: options.runtimeState } : {});
843
891
  return { systemPromptOverlay, overlayDelivery };
844
892
  }
893
+ /** Resolve the launch environment before staging the run's private temp overrides.
894
+ * Mutates only this run's input; O(e) for e environment entries plus executable resolution I/O.
895
+ */
845
896
  async function resolveRunLaunch({ input, def }, _options = {}) {
846
897
  // The def's own declared env (e.g. claude's `ENABLE_TOOL_SEARCH=false`) is a default the run's
847
898
  // env can override. Detection already spawns with it (`detection.ts`); without this the run
@@ -884,9 +935,8 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
884
935
  const formatForStdin = { streamFormat };
885
936
  const stdinHandle = isStdinDrivenFormat(formatForStdin)
886
937
  ? wireChildLifecycle({
887
- ...(mcpBridge && supportsMessageRelay({ def }) ? { registerMessageRelay: (relay) => liveRunRelays.set(input.runId, relay) } : {}),
888
- ...(input.model ? { startingModel: input.model } : {}),
889
- ...(modelCatalog ? { modelCatalog } : {}),
938
+ ...(canRegisterRunMessageRelay({ def, mcpBridge }) ? { registerMessageRelay: (relay) => liveRunRelays.set(input.runId, relay) } : {}),
939
+ ...childLifecycleOptions({ input, modelCatalog }),
890
940
  runId: input.runId,
891
941
  def,
892
942
  streamFormat: formatForStdin.streamFormat,
@@ -898,8 +948,6 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
898
948
  onCleanupFailure: onCleanupFailureFn,
899
949
  cleanupStagedFiles,
900
950
  journal,
901
- ...(options.delegatedResultCorrelation ? { delegatedResultCorrelation: options.delegatedResultCorrelation } : {}),
902
- ...(options.stdoutJournalBlobs ? { stdoutJournalBlobs: options.stdoutJournalBlobs } : {}),
903
951
  continuation,
904
952
  classifyFailure,
905
953
  bufferedStdoutMaxBytes,
@@ -917,6 +965,14 @@ export function createAgentExecutor(requiredArgs, optionalArgs = {}) {
917
965
  child.once('close', () => { liveRunInputs.delete(runId); liveRunRelays.delete(runId); });
918
966
  return stdinHandle;
919
967
  }
968
+ function childLifecycleOptions({ input, modelCatalog }) {
969
+ return {
970
+ ...(input.model ? { startingModel: input.model } : {}),
971
+ ...(modelCatalog ? { modelCatalog } : {}),
972
+ ...(options.delegatedResultCorrelation ? { delegatedResultCorrelation: options.delegatedResultCorrelation } : {}),
973
+ ...(options.stdoutJournalBlobs ? { stdoutJournalBlobs: options.stdoutJournalBlobs } : {}),
974
+ };
975
+ }
920
976
  async function dispatchAcpPrompt({ input, def, child, modelCatalog, cleanupStagedFiles, releaseStagedResources, mcpBridge, overlayDelivery }, _options = {}) {
921
977
  await runAcpDispatch({ input: {
922
978
  runId: input.runId,
@@ -46,9 +46,12 @@ export function createLiveSessionState({ emit }, _options = {}) {
46
46
  const patch = isRecord(frame.patch) ? frame.patch : frame;
47
47
  const description = typeof patch.description === 'string' ? patch.description : previous?.description;
48
48
  const detail = typeof frame.summary === 'string' ? frame.summary : previous?.detail;
49
- const local = frame.task_type === 'local_agent' || typeof frame.subagent_type === 'string' || previous?.local === true;
49
+ const local = isLocalTask({ frame, previous });
50
50
  return { local, ...(description === undefined ? {} : { description }), ...(detail === undefined ? {} : { detail }) };
51
51
  }
52
+ function isLocalTask({ frame, previous }) {
53
+ return frame.task_type === 'local_agent' || typeof frame.subagent_type === 'string' || previous?.local === true;
54
+ }
52
55
  function nextTaskStatus(frame, previous) {
53
56
  if (frame.subtype === 'task_started')
54
57
  return 'running';
@@ -16,8 +16,9 @@ export declare function probeMcpBridge({ entry }: {
16
16
  readonly args: readonly string[];
17
17
  readonly env?: Readonly<Record<string, string>>;
18
18
  };
19
- }, { spawn, baseEnv, timeoutMs, now }?: {
19
+ }, { spawn, hostEnv, baseEnv, timeoutMs, now }?: {
20
20
  readonly spawn?: typeof nodeSpawn;
21
+ readonly hostEnv?: Readonly<Record<string, string | undefined>>;
21
22
  readonly baseEnv?: Readonly<Record<string, string>>;
22
23
  readonly timeoutMs?: number;
23
24
  readonly now?: () => number;
@@ -44,7 +44,7 @@ function reapProbe({ timer, lines, child }) {
44
44
  * Spawn/errors/exits/timeouts return diagnostics rather than rejecting. Stderr retains 2000 bytes.
45
45
  * @complexity O(n) in received bytes; stderr storage is bounded independently of child output.
46
46
  */
47
- export async function probeMcpBridge({ entry }, { spawn = nodeSpawn, baseEnv = buildAgentEnv({ hostEnv: process.env }), timeoutMs = 15_000, now = Date.now } = {}) {
47
+ export async function probeMcpBridge({ entry }, { spawn = nodeSpawn, hostEnv = process.env, baseEnv = buildAgentEnv({ hostEnv }), timeoutMs = 15_000, now = Date.now } = {}) {
48
48
  const startedAt = now();
49
49
  const env = { ...baseEnv, ...entry.env, JINI_RUN_ID: 'bridge-probe' };
50
50
  // Initialize needs no callback: never delegate a real run's authority to this diagnostic child.
@@ -1,7 +1,8 @@
1
1
  import type { McpBridgeProbeResult } from './mcp-bridge-probe.js';
2
2
  import { ensureAntigravityMcpPermission } from './antigravity-permissions.js';
3
3
  import type { AcpMcpServerInput, RuntimeAgentDef } from '@jini-ai/agent-runtime';
4
- import type { McpJsonInjectionOptions, McpJsonServerEntry, McpBridgeDelivery, PreparedCodexHome, PreparedClaudeConfigDir, ClaudeConfigDirIsolationOptions, FailBeforeSpawn } from './contracts.js';
4
+ import type { McpJsonInjectionOptions, McpJsonServerEntry, McpBridgeDelivery, PreparedClaudeConfigDir, ClaudeConfigDirIsolationOptions, FailBeforeSpawn } from './contracts.js';
5
+ export declare const JINI_MCP_SERVER_KEY = "jini";
5
6
  /** The run-error code a run gets when its CLI reports the injected `jini` bridge as anything but connected. */
6
7
  export declare const MCP_BRIDGE_UNAVAILABLE = "MCP_BRIDGE_UNAVAILABLE";
7
8
  /**
@@ -149,66 +150,6 @@ export declare function mergeEnvContentInstructions({ existingRaw, instructionsF
149
150
  readonly existingRaw: string | undefined;
150
151
  readonly instructionsFilePath: string;
151
152
  }): string;
152
- /**
153
- * Mechanism 5 of 5 — `'codex-toml'`'s serialization step. Builds the `[mcp_servers.jini]` TOML
154
- * table (plus, when the entry carries any env vars, a separate `[mcp_servers.jini.env]` table)
155
- * Codex's own config schema expects.
156
- *
157
- * Confirmed against a real installed Codex CLI (0.151.0), not assumed from docs: round-tripping
158
- * `codex mcp add <name> --env K=V -- <cmd> <args>` against a scratch `CODEX_HOME` and reading back
159
- * `config.toml` produced exactly this shape (`command`/`args` as TOML strings/array in the main
160
- * table, env vars in a nested `.env` table) — see `archived provenance ledger` for the transcript.
161
- * @param entry - The shared bridge entry from {@link buildMcpJsonServerEntry}.
162
- * @returns A TOML fragment with no leading/trailing blank-line padding — {@link buildCodexHomeConfigToml} owns spacing when combining it with existing content.
163
- * @complexity O(n) in the number of argv/env entries.
164
- * @overallScore 100/100
165
- */
166
- export declare function buildCodexMcpServerToml({ entry }: {
167
- readonly entry: McpJsonServerEntry;
168
- }): string;
169
- /**
170
- * Builds the full `config.toml` a run's scratch `CODEX_HOME` gets: the real Codex home's own
171
- * config, with any pre-existing `[mcp_servers.jini]` table removed (see
172
- * {@link stripExistingJiniMcpServerTable}), then this run's own table appended.
173
- *
174
- * **Append-only by design, not a parse-and-merge, for everything but the jini table itself.**
175
- * `mergeMcpJsonContent`/`mergeEnvContentMcpConfig` above can safely parse-merge-reserialize because
176
- * their formats have a JS-native parser (`JSON.parse`); this driver has no general TOML parser in
177
- * its dependency graph (see `buildCodexMcpServerToml`'s doc), and every other setting a real Codex
178
- * install carries — model choice, sandbox policy, the trusted-project list, the operator's own
179
- * other MCP servers — must survive a spawn byte-for-byte. Only the one table this driver itself
180
- * owns (`jini` is this integration's own reserved server name — see {@link JINI_MCP_SERVER_KEY}) is
181
- * removed by default, via the narrow line-oriented scan above, never a full TOML parse. Explicit
182
- * per-run OFF choices additionally omit personal MCP tables or retain only those tables when
183
- * user settings are off. Inline root MCP declarations fail closed when isolation is requested.
184
- * @param existingRaw - The real Codex home's `config.toml` content, or `undefined` when it does not
185
- * exist (a fresh Codex install — degrades to "start from just this run's block", matching
186
- * {@link mergeMcpJsonContent}'s own "missing file" handling).
187
- * @param entry - The shared bridge entry.
188
- * @returns The full text to write to the scratch `CODEX_HOME`'s `config.toml`.
189
- * @complexity O(n) in the existing config's length.
190
- */
191
- export declare function buildCodexHomeConfigToml({ existingRaw, entry, inheritUserMcp, inheritUserSettings }: {
192
- readonly existingRaw: string | undefined;
193
- readonly entry: McpJsonServerEntry;
194
- readonly inheritUserMcp?: boolean;
195
- readonly inheritUserSettings?: boolean;
196
- }, _optional?: Record<string, never>): string;
197
- /**
198
- * Where `'codex-toml'` reads the operator's REAL Codex config from, to seed a run's scratch copy —
199
- * never where it writes. Resolved against the daemon HOST process's own environment (`hostEnv`,
200
- * `process.env` at the real call site), not a run's sandboxed spawn env: `CODEX_HOME` is not in
201
- * `BASELINE_AGENT_ENV_KEYS`, so a spawned child never inherits it anyway, and the whole point here
202
- * is finding wherever the *operator's actual* Codex install lives, which is a host-machine fact.
203
- * @param hostEnv - The daemon process's own environment.
204
- * @returns `hostEnv.CODEX_HOME` when set to a non-blank value (matching Codex's own resolution
205
- * order), else the CLI's documented default, `~/.codex`.
206
- * @complexity O(1).
207
- * @overallScore 100/100
208
- */
209
- export declare function resolveSourceCodexHomeDir({ hostEnv }: {
210
- readonly hostEnv: NodeJS.ProcessEnv;
211
- }): string;
212
153
  /**
213
154
  * **The single dispatch point from an `externalMcpInjection` strategy to its delivery mechanism.**
214
155
  * Pure and synchronous — the one effectful input (the per-run bearer credential) arrives already
@@ -238,6 +179,13 @@ export declare function buildMcpBridgeDelivery(input: {
238
179
  readonly credential: string | undefined;
239
180
  readonly messageRelayNonce?: string;
240
181
  }): McpBridgeDelivery | null;
182
+ export declare function defaultReadMcpJsonFile({ path }: {
183
+ readonly path: string;
184
+ }): Promise<string>;
185
+ export declare function defaultWriteMcpJsonFile({ path, content }: {
186
+ readonly path: string;
187
+ readonly content: string;
188
+ }): Promise<void>;
241
189
  export declare function defaultRemoveMcpJsonFile({ path }: {
242
190
  readonly path: string;
243
191
  }): Promise<void>;
@@ -287,31 +235,6 @@ export declare function writeMcpJsonIfNeeded({ input, deps }: {
287
235
  readonly failBeforeSpawn: FailBeforeSpawn;
288
236
  };
289
237
  }): Promise<string | undefined>;
290
- /**
291
- * Phase 10b: mechanism 5 of 5's one effect — stages this run's scratch `CODEX_HOME` directory,
292
- * returning the prepared handle `cleanupStagedFiles` should later release (`null` for every other
293
- * mechanism, or for an unconfigured host — matching {@link writeMcpJsonIfNeeded}'s identical gate).
294
- * @param input.def - Only used for its `id`, in the failure message.
295
- * @param input.mcpBridge - This run's resolved bridge delivery — a no-op unless its `kind` is `'codex-toml'`.
296
- * @param deps.hostEnv - The daemon's own environment, threaded through to {@link resolveSourceCodexHomeDir} rather than read from a module-level `process.env` so this phase stays testable with an injected env.
297
- * @complexity O(1) plus {@link prepareCodexHomeForRun}'s own cost.
298
- * @overallScore 100/100
299
- */
300
- export declare function prepareCodexHomeIfNeeded({ input, deps }: {
301
- readonly input: {
302
- readonly runId: string;
303
- readonly nativeToolAccess?: boolean | undefined;
304
- readonly nativeCapabilities?: Partial<import('@jini-ai/agent-runtime').NativeCapabilities>;
305
- readonly def: RuntimeAgentDef;
306
- readonly mcpBridge: McpBridgeDelivery | null;
307
- };
308
- readonly deps: {
309
- readonly mcpJsonInjection: McpJsonInjectionOptions | undefined;
310
- readonly hostEnv: NodeJS.ProcessEnv;
311
- readonly releaseStagedResources: () => Promise<void>;
312
- readonly failBeforeSpawn: FailBeforeSpawn;
313
- };
314
- }): Promise<PreparedCodexHome | null>;
315
238
  /**
316
239
  * Finding 1 of SEC-assistant-env-isolation-2026-09-07's one effect — stages this run's scratch
317
240
  * `CLAUDE_CONFIG_DIR` directory, returning the prepared handle `cleanupStagedFiles` should later