@mono-agent/agent-runtime 0.4.0 → 0.4.1
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.
- package/ARCHITECTURE.md +37 -16
- package/MIGRATION.md +185 -7
- package/README.md +24 -14
- package/package.json +103 -10
- package/src/agent/allowlists.js +3 -0
- package/src/agent/approval.js +3 -0
- package/src/agent/compaction.js +244 -1
- package/src/agent/prompt/skill-index.js +6 -0
- package/src/agent/sandbox-seam.js +227 -0
- package/src/agent/tool-bloat.js +8 -2
- package/src/agent/tools/bash.js +16 -8
- package/src/agent/tools/edit.js +7 -3
- package/src/agent/tools/glob.js +11 -5
- package/src/agent/tools/grep.js +11 -5
- package/src/agent/tools/pi-bridge.js +227 -45
- package/src/agent/tools/read.js +28 -3
- package/src/agent/tools/shared/output-truncation.js +8 -3
- package/src/agent/tools/shared/path-resolver.js +24 -18
- package/src/agent/tools/shared/ripgrep.js +33 -6
- package/src/agent/tools/shared/runtime-context.js +60 -50
- package/src/agent/tools/shared/tool-context.js +157 -0
- package/src/agent/tools/web-fetch.js +21 -10
- package/src/agent/tools/web-search.js +11 -4
- package/src/agent/tools/write.js +7 -3
- package/src/agent/transcript.js +16 -1
- package/src/ai/cost.js +128 -3
- package/src/ai/failure.js +96 -4
- package/src/ai/live-input-prompt.js +11 -1
- package/src/ai/providers/claude-cli.js +2 -2
- package/src/ai/providers/claude-sdk.js +55 -12
- package/src/ai/providers/codex-app.js +36 -23
- package/src/ai/providers/opencode-app.js +2 -2
- package/src/ai/providers/opencode-discovery.js +2 -2
- package/src/ai/providers/pi-events.js +5 -0
- package/src/ai/providers/pi-models.js +21 -4
- package/src/ai/providers/pi-native/compaction-driver.js +394 -0
- package/src/ai/providers/pi-native/result-builder.js +312 -0
- package/src/ai/providers/pi-native/session-lifecycle.js +438 -0
- package/src/ai/providers/pi-native/stream-subscriber.js +148 -0
- package/src/ai/providers/pi-native/structured-output.js +130 -0
- package/src/ai/providers/pi-native/turn-runner.js +278 -0
- package/src/ai/providers/pi-native.js +415 -1106
- package/src/ai/runtime/model-refs.js +30 -0
- package/src/ai/runtime/registry.js +29 -0
- package/src/ai/runtime/router.js +96 -12
- package/src/ai/runtime/session-liveness.js +110 -0
- package/src/ai/runtime/sessions.js +16 -0
- package/src/ai/types.js +252 -19
- package/src/index.js +1 -1
- package/src/pi-auth.js +147 -16
- package/src/runtime-brand.js +21 -1
- package/src/runtime.js +72 -8
- package/types/agent/allowlists.d.ts +25 -0
- package/types/agent/approval.d.ts +30 -0
- package/types/agent/compaction.d.ts +97 -0
- package/types/agent/index.d.ts +5 -0
- package/types/agent/prompt/skill-index.d.ts +19 -0
- package/types/agent/sandbox-seam.d.ts +148 -0
- package/types/agent/tool-bloat.d.ts +22 -0
- package/types/agent/tools/bash.d.ts +16 -0
- package/types/agent/tools/edit.d.ts +14 -0
- package/types/agent/tools/glob.d.ts +16 -0
- package/types/agent/tools/grep.d.ts +22 -0
- package/types/agent/tools/index.d.ts +10 -0
- package/types/agent/tools/pi-bridge.d.ts +144 -0
- package/types/agent/tools/read.d.ts +19 -0
- package/types/agent/tools/shared/constants.d.ts +13 -0
- package/types/agent/tools/shared/dedup.d.ts +8 -0
- package/types/agent/tools/shared/output-truncation.d.ts +22 -0
- package/types/agent/tools/shared/path-resolver.d.ts +6 -0
- package/types/agent/tools/shared/ripgrep.d.ts +38 -0
- package/types/agent/tools/shared/runtime-context.d.ts +27 -0
- package/types/agent/tools/shared/tool-context.d.ts +48 -0
- package/types/agent/tools/web-fetch.d.ts +13 -0
- package/types/agent/tools/web-search.d.ts +11 -0
- package/types/agent/tools/write.d.ts +12 -0
- package/types/agent/transcript.d.ts +45 -0
- package/types/ai/backend.d.ts +41 -0
- package/types/ai/cost.d.ts +96 -0
- package/types/ai/failure.d.ts +117 -0
- package/types/ai/file-change-stats.d.ts +75 -0
- package/types/ai/index.d.ts +7 -0
- package/types/ai/live-input-prompt.d.ts +6 -0
- package/types/ai/observer.d.ts +68 -0
- package/types/ai/providers/claude-cli.d.ts +211 -0
- package/types/ai/providers/claude-sdk.d.ts +66 -0
- package/types/ai/providers/claude-subagents.d.ts +2 -0
- package/types/ai/providers/codex-app.d.ts +151 -0
- package/types/ai/providers/opencode-app.d.ts +95 -0
- package/types/ai/providers/opencode-discovery.d.ts +4 -0
- package/types/ai/providers/pi-errors.d.ts +3 -0
- package/types/ai/providers/pi-events.d.ts +24 -0
- package/types/ai/providers/pi-messages.d.ts +5 -0
- package/types/ai/providers/pi-models.d.ts +57 -0
- package/types/ai/providers/pi-native/compaction-driver.d.ts +86 -0
- package/types/ai/providers/pi-native/result-builder.d.ts +168 -0
- package/types/ai/providers/pi-native/session-lifecycle.d.ts +62 -0
- package/types/ai/providers/pi-native/stream-subscriber.d.ts +50 -0
- package/types/ai/providers/pi-native/structured-output.d.ts +69 -0
- package/types/ai/providers/pi-native/turn-runner.d.ts +60 -0
- package/types/ai/providers/pi-native.d.ts +18 -0
- package/types/ai/registry.d.ts +1 -0
- package/types/ai/runtime/capabilities-used.d.ts +21 -0
- package/types/ai/runtime/capabilities.d.ts +33 -0
- package/types/ai/runtime/context-windows.d.ts +8 -0
- package/types/ai/runtime/fast-mode.d.ts +2 -0
- package/types/ai/runtime/model-refs.d.ts +35 -0
- package/types/ai/runtime/registry.d.ts +38 -0
- package/types/ai/runtime/router.d.ts +56 -0
- package/types/ai/runtime/session-liveness.d.ts +55 -0
- package/types/ai/runtime/sessions.d.ts +38 -0
- package/types/ai/streaming/codex-events.d.ts +30 -0
- package/types/ai/streaming/opencode-events.d.ts +41 -0
- package/types/ai/types.d.ts +702 -0
- package/types/index.d.ts +7 -0
- package/types/pi-auth.d.ts +28 -0
- package/types/runtime-brand.d.ts +30 -0
- package/types/runtime.d.ts +10 -0
- package/src/ai/providers/pi-sdk.js +0 -35
package/src/ai/types.js
CHANGED
|
@@ -1,40 +1,155 @@
|
|
|
1
1
|
// Runtime contracts. These are JSDoc-only; the codebase is JS
|
|
2
2
|
// (no TypeScript), so the types document the bridge surface that active
|
|
3
3
|
// runtimes implement through src/ai/runtime/registry.js.
|
|
4
|
+
//
|
|
5
|
+
// `tsc -p tsconfig.types.json` (allowJs + declaration + emitDeclarationOnly)
|
|
6
|
+
// compiles these typedefs into packages/agent-runtime/types/ai/types.d.ts;
|
|
7
|
+
// downstream TS consumers (currently the runtime-adapter package) import them
|
|
8
|
+
// via `import('./types.js').X` JSDoc syntax from the other seam files, or
|
|
9
|
+
// transitively through the package root's generated declarations.
|
|
10
|
+
//
|
|
11
|
+
// A few fields below use the `"literal" | (string & {})` shape (a "branded
|
|
12
|
+
// string union"). This keeps the known literal values available for
|
|
13
|
+
// IDE/editor autocomplete while still accepting any plain `string`, since
|
|
14
|
+
// hosts validate the real vocabulary at runtime (parseRuntimeModelReference,
|
|
15
|
+
// resolveRuntimeBridge) rather than at the type level — the type only
|
|
16
|
+
// documents the values active runtimes currently produce.
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {"claude" | "pi" | "codex" | "opencode" | (string & {})} RuntimeSdkId
|
|
20
|
+
* Canonical active runtime id. See ACTIVE_RUNTIME_KINDS (model-refs.js) for
|
|
21
|
+
* the enforced-at-runtime vocabulary.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {"claude" | "claude-code" | "codex-app" | "opencode-app" | "pi" | (string & {})} RuntimeBridgeId
|
|
26
|
+
* Registry bridge id (distinct from RuntimeSdkId: a single sdk can be served
|
|
27
|
+
* by more than one bridge, e.g. sdk "claude" is served by both the "claude"
|
|
28
|
+
* SDK bridge and the "claude-code" CLI bridge). See
|
|
29
|
+
* src/ai/runtime/registry.js's builtinBridgeSpecs.
|
|
30
|
+
*/
|
|
4
31
|
|
|
5
32
|
/**
|
|
6
33
|
* @typedef {Object} RuntimeModelRef
|
|
7
|
-
* @property {
|
|
8
|
-
* @property {string} model
|
|
9
|
-
* @property {string} reference
|
|
10
|
-
*
|
|
34
|
+
* @property {RuntimeSdkId} sdk Canonical active runtime id.
|
|
35
|
+
* @property {string} model Provider model id.
|
|
36
|
+
* @property {string} [reference] Original canonical model reference; always set by
|
|
37
|
+
* parseRuntimeModelReference, but router.js's chain
|
|
38
|
+
* shorthand accepts bare {sdk, model} refs without one.
|
|
39
|
+
* @property {string} [provider] Pi/OpenCode provider id when sdk === "pi" | "opencode".
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @typedef {{type: string, [key: string]: *}} RuntimeEvent
|
|
44
|
+
* Structured runtime/telemetry event. `type` is the only required field;
|
|
45
|
+
* every event kind (tool_approval_pending, provider_failover_started,
|
|
46
|
+
* context_compaction_applied, ...) adds its own extra fields.
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @typedef {Object} RuntimeObserver
|
|
51
|
+
* Per-call or host-level observer merged by createObserverHub (ai/observer.js).
|
|
52
|
+
* Loose on purpose: observer.js is not a kernel seam file.
|
|
53
|
+
* @property {(event: RuntimeEvent) => (void|Promise<void>)} [onEvent]
|
|
54
|
+
* @property {() => (void|Promise<void>)} [flush]
|
|
11
55
|
*/
|
|
12
56
|
|
|
13
57
|
/**
|
|
14
|
-
* @typedef {Object}
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
58
|
+
* @typedef {Object} RuntimeToolLimits
|
|
59
|
+
* Typed per-run tool-output limits (the supported replacement for the
|
|
60
|
+
* `agent_tool_*` / `agent_mcp_*` keys of the deprecated `settings` bag). Every
|
|
61
|
+
* field is optional; an omitted field falls back to the kernel default (see
|
|
62
|
+
* resolveAgentCompactionPolicy, agent/compaction.js).
|
|
63
|
+
* @property {number} [toolTextLimitChars] Max chars of a builtin tool's text result.
|
|
64
|
+
* @property {number} [bashOutputLimitChars] Max chars of bash stdout/stderr.
|
|
65
|
+
* @property {number} [mcpTextLimitChars] Max chars of an MCP tool's text result.
|
|
66
|
+
* @property {number} [searchResultLimit] Max Grep/search hits returned.
|
|
67
|
+
* @property {number} [imageInlineMaxBytes] Max bytes of an inlined image result.
|
|
68
|
+
* @property {number} [toolPayloadMaxBytes] Hard cap on a single tool_result payload.
|
|
69
|
+
* @property {number} [mcpCallTimeoutMs] Per-MCP-call inactivity timeout.
|
|
70
|
+
* @property {number} [mcpCallMaxTotalTimeoutMs] Hard wall-clock cap for one MCP call.
|
|
71
|
+
* @property {number} [bashTimeoutMs] Documented for forward-compat; NOT wired to
|
|
72
|
+
* any tool today (no `agent_bash_*_timeout` mechanism exists — bash reads its per-call
|
|
73
|
+
* `timeout` argument), so setting it has no effect until a run-level default is introduced.
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @typedef {Object} RuntimeCompactionPolicy
|
|
78
|
+
* Typed per-run context-compaction policy (the supported replacement for the
|
|
79
|
+
* `agent_compaction_*` keys of the deprecated `settings` bag). Every field is
|
|
80
|
+
* optional; an omitted field falls back to the kernel default.
|
|
81
|
+
* @property {boolean} [enabled] Whether auto-compaction runs at all.
|
|
82
|
+
* @property {number} [triggerRatio] Fraction of the context window that arms the proactive trigger.
|
|
83
|
+
* @property {number} [keepRecentTokens] Recent-token budget preserved across a compaction.
|
|
84
|
+
* @property {number} [summaryMaxTokens] Max tokens of the generated summary.
|
|
85
|
+
* @property {number} [minSavingsTokens] Minimum token savings required to keep a compaction.
|
|
86
|
+
* @property {boolean} [fixedOverheadEnabled] Whether the system-prompt + tool-schema overhead correction is folded into the trigger.
|
|
87
|
+
* @property {number} [contextWindowOverride] Forces the compaction window instead of the live-model-recognized one (applied at resolveLiveCompactionPolicy; has no legacy settings equivalent).
|
|
88
|
+
*/
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @typedef {Object} RuntimePromptOverrides
|
|
92
|
+
* Optional overrides for the kernel's built-in prompt fragments. Precedence is
|
|
93
|
+
* run over host over the kernel default: an absent field leaves the built-in
|
|
94
|
+
* string in place. Supplied on both AgentRuntimeHostOptions (host default) and
|
|
95
|
+
* RuntimeRunOptions (per-run).
|
|
96
|
+
* @property {(systemPrompt: string) => string} [structuredOutputInstruction]
|
|
97
|
+
* Replaces the StructuredOutput system-prompt instruction (receives the raw
|
|
98
|
+
* system prompt, returns the augmented one). Only applied when an outputSchema is active.
|
|
99
|
+
* @property {() => string} [structuredOutputFinalization]
|
|
100
|
+
* Replaces the structured-output finalization re-prompt.
|
|
101
|
+
* @property {(body: string) => string} [liveInputGuidance]
|
|
102
|
+
* Replaces the live-input steering wrapper (receives the raw guidance body).
|
|
103
|
+
*/
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @typedef {Object} RuntimeRunOptions
|
|
107
|
+
* The options object a host passes to `createRuntime(host).run(systemPrompt, options)`.
|
|
108
|
+
* @property {RuntimeModelRef} model Resolved model reference; see parseRuntimeModelReference.
|
|
109
|
+
* @property {string} [executionMode] "sdk" (default) or "cli"; selects which bridge variant handles the model.
|
|
110
|
+
* @property {boolean} [liveInput] Whether this run expects a live/streaming input channel.
|
|
111
|
+
* @property {ReadonlyArray<*>} [observers] Per-call observers (see RuntimeObserver) merged with host-level (createRuntime) observers.
|
|
112
|
+
* @property {(event: RuntimeEvent) => void} [onEvent]
|
|
113
|
+
* @property {ReadonlyArray<Object>} [messages]
|
|
18
114
|
* @property {string} [effort]
|
|
19
115
|
* @property {boolean} [fastMode]
|
|
20
116
|
* @property {string} [cwd]
|
|
21
117
|
* @property {Object<string, Object>} [mcpServers]
|
|
22
|
-
* @property {
|
|
23
|
-
* @property {
|
|
118
|
+
* @property {ReadonlyArray<string>} [allowedTools]
|
|
119
|
+
* @property {ReadonlyArray<string>} [disallowedTools]
|
|
24
120
|
* @property {string} [permissionMode]
|
|
25
121
|
* @property {number} [maxTurns]
|
|
26
122
|
* @property {Object} [outputSchema]
|
|
27
123
|
* @property {string} [runArtifactDir]
|
|
28
124
|
* @property {AbortSignal} [abortSignal]
|
|
29
|
-
* @property {
|
|
30
|
-
* @property {
|
|
125
|
+
* @property {import('../agent/sandbox-seam.js').SandboxPolicy} [sandboxPolicy] Per-run sandbox policy; merged monotonically with the host policy (see resolveSandboxPolicy, agent/tools/shared/tool-context.js).
|
|
126
|
+
* @property {import('../agent/sandbox-seam.js').RuntimeSandboxEngine} [sandboxEngine] Per-run concrete sandbox engine handed to the active sandbox implementation.
|
|
127
|
+
* @property {import('../agent/sandbox-seam.js').RuntimeSandbox} [sandbox] Per-run sandbox IMPLEMENTATION override; when set it enforces this run's tools instead of the host/ToolContext impl (precedence run > host > passthrough). Policy DATA still merges monotonically (I13); this overrides only the enforcing code.
|
|
128
|
+
* @property {RuntimeToolLimits} [toolLimits] Typed per-run tool-output limits (supported replacement for the deprecated `settings` tool keys).
|
|
129
|
+
* @property {RuntimeCompactionPolicy} [compaction] Typed per-run compaction policy (supported replacement for the deprecated `settings` compaction keys).
|
|
130
|
+
* @property {RuntimePromptOverrides} [prompts] Per-run prompt-fragment overrides (run wins over the host default).
|
|
131
|
+
* @property {Object} [settings] DEPRECATED. Legacy flat settings bag; consumed only as a per-group FALLBACK when the corresponding typed object (`toolLimits` / `compaction`) is absent. Consuming any key emits one `deprecated_settings_option` runtime_warning per run. Migrate via resolveRuntimePolicies (@mono-agent/runtime-adapter).
|
|
31
132
|
* @property {Object} [nativeSubagents] Same-runtime teammate helpers exposed through native provider subagent surfaces.
|
|
32
|
-
* @property {(
|
|
133
|
+
* @property {Object} [diagnosticsSeed] Set by createRouterRuntime (ai/runtime/router.js) with a `resume_snapshot` when
|
|
134
|
+
* failing over mid-chain; a host-level coordinator may relay it forward (see agent/transcript.js), not read by any
|
|
135
|
+
* bridge in this package today.
|
|
136
|
+
* @property {string} [systemPromptPrefix] Set by createRouterRuntime alongside diagnosticsSeed; the router also
|
|
137
|
+
* prepends the same text to the systemPrompt argument directly, so bridges that ignore this field still continue
|
|
138
|
+
* correctly.
|
|
33
139
|
*/
|
|
34
140
|
|
|
35
141
|
/**
|
|
36
|
-
* @typedef {
|
|
37
|
-
*
|
|
142
|
+
* @typedef {RuntimeRunOptions
|
|
143
|
+
* & Pick<AgentRuntimeHostOptions, "resolveCustomPricing" | "resolvePiApiKey" | "persistArtifact" | "onCompactionRecorded" | "onToolApprovalRequest" | "toolRiskTiers" | "approvalDefaultRiskTier" | "approvalTimeoutMs" | "approvalAlwaysAllowTools">
|
|
144
|
+
* & {runtimeBrand: import('../runtime-brand.js').RuntimeBrand, toolContext?: import('../agent/tools/shared/tool-context.js').ToolContext, observerHub: {emit: (event: RuntimeEvent) => void, flush: () => Promise<void>}}
|
|
145
|
+
* } RuntimeRequest
|
|
146
|
+
* The request shape a bridge's `execute(systemPrompt, req)` receives as its
|
|
147
|
+
* second (options) argument: the host-supplied RuntimeRunOptions, merged by
|
|
148
|
+
* createRuntime (runtime.js) with the bound HOST_KEYS host-integration
|
|
149
|
+
* callbacks, the resolved runtimeBrand, the per-instance toolContext (read by
|
|
150
|
+
* internal tool helpers; absent when a host drives a bridge directly without
|
|
151
|
+
* createRuntime), and the per-run observerHub (onEvent is overridden to the
|
|
152
|
+
* hub's emit). `systemPrompt` is passed positionally, not folded into this object.
|
|
38
153
|
*/
|
|
39
154
|
|
|
40
155
|
/**
|
|
@@ -48,22 +163,140 @@
|
|
|
48
163
|
* @property {number} [numTurns]
|
|
49
164
|
* @property {string} [model]
|
|
50
165
|
* @property {string} [effort]
|
|
51
|
-
* @property {
|
|
166
|
+
* @property {RuntimeSdkId} [sdk]
|
|
52
167
|
* @property {boolean} [cancelled]
|
|
53
168
|
* @property {string|null} [error]
|
|
54
169
|
* @property {Object|null} [errorDetails]
|
|
55
170
|
* @property {string|null} [failureKind]
|
|
56
171
|
* @property {string|null} [providerSessionId]
|
|
172
|
+
* @property {string|null} [stderrTail] Bounded stderr tail from a CLI-backed bridge; see createStderrTail (ai/failure.js).
|
|
57
173
|
* @property {Array<Object>} [runtimeWarnings]
|
|
58
174
|
* @property {Object} [diagnostics]
|
|
175
|
+
* @property {Object} [capabilitiesUsed]
|
|
176
|
+
* @property {Array<{model: RuntimeModelRef, failureKind: (string|null), requestId?: (string|null), retryableSubkind?: (string|null), requirements?: Object}>} [failoverHistory] Set by createRouterRuntime (ai/runtime/router.js) on every attempt after the first.
|
|
177
|
+
*/
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* @typedef {Object} RuntimeCapabilities
|
|
181
|
+
* Capability flags a bridge reports for a given model reference. Structural,
|
|
182
|
+
* not sealed: bridges may report extra provider-specific flags (e.g.
|
|
183
|
+
* supports_fast_mode) alongside the common ones from COMMON_CAPABILITIES
|
|
184
|
+
* (ai/runtime/capabilities.js).
|
|
185
|
+
* @property {string} [kind]
|
|
186
|
+
* @property {string} [runtime]
|
|
187
|
+
* @property {boolean} [streaming]
|
|
188
|
+
* @property {boolean} [structured_output]
|
|
189
|
+
* @property {boolean} [supports_session_resume]
|
|
190
|
+
* @property {*} [native_runtime_config]
|
|
191
|
+
* @property {boolean} [supports_mcp]
|
|
192
|
+
* @property {boolean} [supports_skills]
|
|
193
|
+
* @property {boolean} [supports_builtin_tools]
|
|
194
|
+
* @property {boolean} [supports_live_input]
|
|
195
|
+
* @property {boolean} [supports_native_subagents]
|
|
196
|
+
* @property {boolean} [supports_fast_mode]
|
|
59
197
|
*/
|
|
60
198
|
|
|
61
199
|
/**
|
|
62
200
|
* @typedef {Object} RuntimeBridge
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
201
|
+
* A fully loaded bridge (registry.js's resolveRuntimeBridge result): the
|
|
202
|
+
* executable provider implementation. Unlike RuntimeBridgeDescriptor,
|
|
203
|
+
* `capabilities` here is the bridge module's own static value (computed once
|
|
204
|
+
* at module load, e.g. `runtimeCapabilities("pi")`), not a callable.
|
|
205
|
+
* @property {RuntimeBridgeId} id
|
|
206
|
+
* @property {(ref: RuntimeModelRef, options?: Object) => boolean} supports
|
|
207
|
+
* @property {RuntimeCapabilities} capabilities
|
|
66
208
|
* @property {(systemPrompt: string, req: RuntimeRequest) => Promise<RuntimeResult>} execute
|
|
67
209
|
*/
|
|
68
210
|
|
|
211
|
+
/**
|
|
212
|
+
* @typedef {Object} RuntimeBridgeDescriptor
|
|
213
|
+
* The introspection-only projection of a RuntimeBridge (registry.js's
|
|
214
|
+
* listRuntimeBridges result): id/supports/capabilities without execute, so
|
|
215
|
+
* hosts can describe backend support without loading provider modules.
|
|
216
|
+
* @property {RuntimeBridgeId} id
|
|
217
|
+
* @property {(ref: RuntimeModelRef, options?: Object) => boolean} supports
|
|
218
|
+
* @property {(ref?: RuntimeModelRef) => RuntimeCapabilities} capabilities
|
|
219
|
+
*/
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* @typedef {Object} ApprovalRequestPayload
|
|
223
|
+
* Payload passed to a host's `onToolApprovalRequest` callback (agent/approval.js).
|
|
224
|
+
* @property {string} requestId
|
|
225
|
+
* @property {string} toolName
|
|
226
|
+
* @property {string|null} toolUseId
|
|
227
|
+
* @property {string} argumentsSummary Secret-redacted, length-capped JSON summary of the tool call arguments.
|
|
228
|
+
* @property {"low"|"medium"|"high"} riskTier
|
|
229
|
+
* @property {string|null} model
|
|
230
|
+
*/
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* @typedef {Object} ApprovalDecision
|
|
234
|
+
* A host's response to an ApprovalRequestPayload.
|
|
235
|
+
* @property {"approve"|"deny"|"always"} decision
|
|
236
|
+
* @property {string} [reason]
|
|
237
|
+
*/
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* @typedef {Object} CompactionRecordedPayload
|
|
241
|
+
* Payload passed to a host's `onCompactionRecorded` callback (ai/providers/pi-native.js)
|
|
242
|
+
* after a successful context compaction, so the host can persist the row.
|
|
243
|
+
* @property {string|null} task_run_id
|
|
244
|
+
* @property {string} trigger
|
|
245
|
+
* @property {string} provider_kind
|
|
246
|
+
* @property {string|null} model
|
|
247
|
+
* @property {number|null} tokens_before
|
|
248
|
+
* @property {string} summary
|
|
249
|
+
* @property {string|null} first_kept_entry_id
|
|
250
|
+
* @property {"succeeded"} status
|
|
251
|
+
* @property {number} created_at
|
|
252
|
+
*/
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* @typedef {Object} AgentRuntimeHostOptions
|
|
256
|
+
* The `host` object passed to `createRuntime(host)` / `createRouterRuntime({host, chain})`.
|
|
257
|
+
* Combines the tool-runtime keys (forwarded to configureToolRuntime) and the
|
|
258
|
+
* host-integration callbacks (bound once, applied to every run via hostDefaults).
|
|
259
|
+
* @property {string} [workspace]
|
|
260
|
+
* @property {string} [repoRoot]
|
|
261
|
+
* @property {string} [ripgrepPath]
|
|
262
|
+
* @property {string} [qaOutputDir]
|
|
263
|
+
* @property {import('../agent/sandbox-seam.js').SandboxPolicy} [sandboxPolicy]
|
|
264
|
+
* @property {import('../agent/sandbox-seam.js').RuntimeSandboxEngine} [sandboxEngine]
|
|
265
|
+
* @property {import('../agent/sandbox-seam.js').RuntimeSandbox} [sandbox] Sandbox seam implementation (see agent/sandbox-seam.js); defaults to the zero-dependency passthroughSandbox. Real hosts inject runtime-adapter's implementation from packages/runtime-adapter/src/sandbox.ts.
|
|
266
|
+
* @property {RuntimePromptOverrides} [prompts] Host-level prompt-fragment override defaults; a per-run `options.prompts` field wins over these (see resolvePrompts, runtime.js).
|
|
267
|
+
* @property {ReadonlyArray<*>} [observers] Observer instances (see RuntimeObserver); loose because observer.js is not a kernel seam file.
|
|
268
|
+
* @property {*} [runtimeBrand] See resolveRuntimeBrand (runtime-brand.js); accepts a partial RuntimeBrand.
|
|
269
|
+
* @property {(parsed: {sdk: (string|null), provider?: string, model: string}) => (import('./cost.js').NormalizedPricing|null)} [resolveCustomPricing] See resolvePricing (ai/cost.js).
|
|
270
|
+
* @property {import('../pi-auth.js').PiApiKeyResolver} [resolvePiApiKey] See createPiOAuthApiKeyResolver (pi-auth.js) for a ready-made implementation.
|
|
271
|
+
* @property {(artifact: {filename: string, buffer: Buffer, toolName: string, toolUseId: (string|null)}) => (string|null)} [persistArtifact]
|
|
272
|
+
* @property {(record: CompactionRecordedPayload) => void} [onCompactionRecorded]
|
|
273
|
+
* @property {(payload: ApprovalRequestPayload) => Promise<ApprovalDecision>} [onToolApprovalRequest]
|
|
274
|
+
* @property {Object<string, ("low"|"medium"|"high")>} [toolRiskTiers]
|
|
275
|
+
* @property {"low"|"medium"|"high"} [approvalDefaultRiskTier]
|
|
276
|
+
* @property {number} [approvalTimeoutMs]
|
|
277
|
+
* @property {ReadonlyArray<string>} [approvalAlwaysAllowTools]
|
|
278
|
+
*/
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* @typedef {Object} AgentRuntimeToolOptions
|
|
282
|
+
* The subset of AgentRuntimeHostOptions accepted by `runtime.configureTools(next)`
|
|
283
|
+
* (createRuntime's TOOL_RUNTIME_KEYS pick).
|
|
284
|
+
* @property {string} [workspace]
|
|
285
|
+
* @property {string} [repoRoot]
|
|
286
|
+
* @property {string} [ripgrepPath]
|
|
287
|
+
* @property {string} [qaOutputDir]
|
|
288
|
+
* @property {import('../agent/sandbox-seam.js').SandboxPolicy} [sandboxPolicy]
|
|
289
|
+
* @property {import('../agent/sandbox-seam.js').RuntimeSandboxEngine} [sandboxEngine]
|
|
290
|
+
* @property {import('../agent/sandbox-seam.js').RuntimeSandbox} [sandbox]
|
|
291
|
+
*/
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* @typedef {Object} AgentRuntimeInstance
|
|
295
|
+
* The object `createRuntime`/`createRouterRuntime` return.
|
|
296
|
+
* @property {(systemPrompt: string, options: RuntimeRunOptions) => Promise<RuntimeResult>} run
|
|
297
|
+
* @property {(next?: AgentRuntimeToolOptions) => void} configureTools
|
|
298
|
+
* @property {(providerSessionId: string) => Promise<boolean|void>} disposeSession
|
|
299
|
+
* @property {() => Promise<void>} disposeAllSessions
|
|
300
|
+
*/
|
|
301
|
+
|
|
69
302
|
export const PROVIDER_KIND_VALUES = ["claude", "pi", "codex"];
|
package/src/index.js
CHANGED
package/src/pi-auth.js
CHANGED
|
@@ -1,48 +1,174 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { realpathSync } from "node:fs";
|
|
1
4
|
import { chmod, mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
|
-
import { dirname } from "node:path";
|
|
5
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
3
6
|
|
|
4
7
|
import { getOAuthApiKey } from "@earendil-works/pi-ai/oauth";
|
|
5
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {{type: "api_key", key?: string, env?: Object<string, *>}} PiApiKeyCredential
|
|
11
|
+
* @typedef {{type: "oauth", access?: string, refresh?: string, expires: number, [key: string]: *}} PiOAuthCredential
|
|
12
|
+
* @typedef {PiApiKeyCredential|PiOAuthCredential} PiCredential
|
|
13
|
+
* @typedef {((provider: string) => Promise<string|undefined>) & {
|
|
14
|
+
* readCredential?: (provider: string) => Promise<PiCredential|undefined>,
|
|
15
|
+
* modifyCredential?: (provider: string, fn: (current: PiCredential|undefined) => Promise<PiCredential|undefined>) => Promise<PiCredential|undefined>,
|
|
16
|
+
* deleteCredential?: (provider: string) => Promise<void>
|
|
17
|
+
* }} PiApiKeyResolver
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const authFileChains = new Map();
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {Object} [options]
|
|
24
|
+
* @param {string} [options.path] Path to the pi auth.json credentials file.
|
|
25
|
+
* @returns {PiApiKeyResolver}
|
|
26
|
+
*/
|
|
6
27
|
export function createPiOAuthApiKeyResolver(options = {}) {
|
|
7
|
-
const
|
|
28
|
+
const configuredAuthPath = typeof options.path === "string" && options.path.trim().length > 0
|
|
8
29
|
? options.path
|
|
9
30
|
: undefined;
|
|
31
|
+
const authPath = configuredAuthPath ? canonicalizeAuthPath(configuredAuthPath) : undefined;
|
|
10
32
|
|
|
11
|
-
|
|
33
|
+
async function resolvePiOAuthApiKey(provider) {
|
|
12
34
|
if (!authPath || typeof provider !== "string" || provider.trim().length === 0) {
|
|
13
35
|
return undefined;
|
|
14
36
|
}
|
|
15
37
|
|
|
16
|
-
|
|
17
|
-
|
|
38
|
+
return enqueueAuthFile(authPath, async () => {
|
|
39
|
+
const auth = await readAuthFile(authPath);
|
|
40
|
+
if (auth === undefined || auth[provider] === undefined) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const result = await getOAuthApiKey(provider, cloneAuth(auth));
|
|
45
|
+
if (result === null || result === undefined || typeof result.apiKey !== "string" || result.apiKey.length === 0) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
auth[provider] = {
|
|
50
|
+
type: "oauth",
|
|
51
|
+
...result.newCredentials,
|
|
52
|
+
};
|
|
53
|
+
await writeAuthFile(authPath, auth);
|
|
54
|
+
return result.apiKey;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
resolvePiOAuthApiKey.readCredential = async function readCredential(provider) {
|
|
59
|
+
if (!authPath || typeof provider !== "string" || provider.trim().length === 0) {
|
|
18
60
|
return undefined;
|
|
19
61
|
}
|
|
62
|
+
const auth = await readAuthFile(authPath);
|
|
63
|
+
return cloneCredential(auth?.[provider]);
|
|
64
|
+
};
|
|
20
65
|
|
|
21
|
-
|
|
22
|
-
if (
|
|
66
|
+
resolvePiOAuthApiKey.modifyCredential = async function modifyCredential(provider, fn) {
|
|
67
|
+
if (!authPath || typeof provider !== "string" || provider.trim().length === 0) {
|
|
23
68
|
return undefined;
|
|
24
69
|
}
|
|
70
|
+
if (typeof fn !== "function") {
|
|
71
|
+
throw new TypeError("modifyCredential requires a function");
|
|
72
|
+
}
|
|
73
|
+
return enqueueAuthFile(authPath, async () => {
|
|
74
|
+
const auth = (await readAuthFile(authPath)) || {};
|
|
75
|
+
const current = cloneCredential(auth[provider]);
|
|
76
|
+
const next = await fn(current);
|
|
77
|
+
if (next !== undefined) {
|
|
78
|
+
auth[provider] = cloneCredential(next) || next;
|
|
79
|
+
await writeAuthFile(authPath, auth);
|
|
80
|
+
return cloneCredential(auth[provider]);
|
|
81
|
+
}
|
|
82
|
+
return current;
|
|
83
|
+
});
|
|
84
|
+
};
|
|
25
85
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
await
|
|
31
|
-
|
|
86
|
+
resolvePiOAuthApiKey.deleteCredential = async function deleteCredential(provider) {
|
|
87
|
+
if (!authPath || typeof provider !== "string" || provider.trim().length === 0) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
await enqueueAuthFile(authPath, async () => {
|
|
91
|
+
const auth = await readAuthFile(authPath);
|
|
92
|
+
if (!auth || !Object.hasOwn(auth, provider)) return;
|
|
93
|
+
delete auth[provider];
|
|
94
|
+
await writeAuthFile(authPath, auth);
|
|
95
|
+
});
|
|
32
96
|
};
|
|
97
|
+
|
|
98
|
+
return resolvePiOAuthApiKey;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @param {string} path
|
|
103
|
+
* @param {() => Promise<*>} task
|
|
104
|
+
* @returns {Promise<*>}
|
|
105
|
+
*/
|
|
106
|
+
function enqueueAuthFile(path, task) {
|
|
107
|
+
const previous = authFileChains.get(path) ?? Promise.resolve();
|
|
108
|
+
const next = (async () => {
|
|
109
|
+
await previous.catch(() => {});
|
|
110
|
+
return task();
|
|
111
|
+
})();
|
|
112
|
+
authFileChains.set(path, next.catch(() => {}));
|
|
113
|
+
return next;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @param {string} path
|
|
118
|
+
* @returns {string}
|
|
119
|
+
*/
|
|
120
|
+
function canonicalizeAuthPath(path) {
|
|
121
|
+
const resolved = resolve(path);
|
|
122
|
+
try {
|
|
123
|
+
return realpathSync.native(resolved);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
if (!isMissingPathError(error)) {
|
|
126
|
+
return resolved;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
return join(realpathSync.native(dirname(resolved)), basename(resolved));
|
|
132
|
+
} catch {
|
|
133
|
+
return resolved;
|
|
134
|
+
}
|
|
33
135
|
}
|
|
34
136
|
|
|
137
|
+
/**
|
|
138
|
+
* @param {*} error
|
|
139
|
+
* @returns {boolean}
|
|
140
|
+
*/
|
|
141
|
+
function isMissingPathError(error) {
|
|
142
|
+
return error && (error.code === "ENOENT" || error.code === "ENOTDIR");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @param {Object<string, *>} auth
|
|
147
|
+
* @returns {Object<string, *>}
|
|
148
|
+
*/
|
|
35
149
|
function cloneAuth(auth) {
|
|
36
150
|
return Object.fromEntries(
|
|
37
151
|
Object.entries(auth).map(([provider, credentials]) => [
|
|
38
152
|
provider,
|
|
39
|
-
|
|
40
|
-
? { ...credentials }
|
|
41
|
-
: credentials,
|
|
153
|
+
cloneCredential(credentials),
|
|
42
154
|
]),
|
|
43
155
|
);
|
|
44
156
|
}
|
|
45
157
|
|
|
158
|
+
/**
|
|
159
|
+
* @param {*} credential
|
|
160
|
+
* @returns {*}
|
|
161
|
+
*/
|
|
162
|
+
function cloneCredential(credential) {
|
|
163
|
+
return credential && typeof credential === "object" && !Array.isArray(credential)
|
|
164
|
+
? { ...credential }
|
|
165
|
+
: credential;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @param {string} path
|
|
170
|
+
* @returns {Promise<Object<string, *>|undefined>}
|
|
171
|
+
*/
|
|
46
172
|
async function readAuthFile(path) {
|
|
47
173
|
let raw;
|
|
48
174
|
try {
|
|
@@ -69,6 +195,11 @@ async function readAuthFile(path) {
|
|
|
69
195
|
// writers in the same millisecond never collide on the temp path.
|
|
70
196
|
let atomicWriteSequence = 0;
|
|
71
197
|
|
|
198
|
+
/**
|
|
199
|
+
* @param {string} path
|
|
200
|
+
* @param {Object<string, *>} auth
|
|
201
|
+
* @returns {Promise<void>}
|
|
202
|
+
*/
|
|
72
203
|
async function writeAuthFile(path, auth) {
|
|
73
204
|
const dir = dirname(path);
|
|
74
205
|
await mkdir(dir, { recursive: true, mode: 0o700 });
|
package/src/runtime-brand.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
1
3
|
// Host-customisable identity strings used by the runtime when it has to
|
|
2
4
|
// stamp a name onto something that leaves the process: MCP client name,
|
|
3
5
|
// transcript-snapshot schema id, temp-directory prefix, the doctor command
|
|
@@ -7,6 +9,20 @@
|
|
|
7
9
|
// to `createRuntime` to make the package look like theirs without forking
|
|
8
10
|
// string-by-string.
|
|
9
11
|
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {Object} RuntimeBrand
|
|
14
|
+
* @property {string} schemaPrefix
|
|
15
|
+
* @property {string} mcpClientName
|
|
16
|
+
* @property {string} mcpClientVersion
|
|
17
|
+
* @property {string} tempdirPrefix
|
|
18
|
+
* @property {string} providerModelPrefix
|
|
19
|
+
* @property {string} doctorCommand
|
|
20
|
+
* @property {string} serviceName
|
|
21
|
+
* @property {string} clientInfoName
|
|
22
|
+
* @property {string} clientInfoTitle
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** @type {RuntimeBrand} */
|
|
10
26
|
export const DEFAULT_RUNTIME_BRAND = Object.freeze({
|
|
11
27
|
schemaPrefix: "agent_runtime",
|
|
12
28
|
mcpClientName: "agent-runtime",
|
|
@@ -21,10 +37,14 @@ export const DEFAULT_RUNTIME_BRAND = Object.freeze({
|
|
|
21
37
|
clientInfoTitle: "Agent Runtime",
|
|
22
38
|
});
|
|
23
39
|
|
|
40
|
+
/**
|
|
41
|
+
* @param {Partial<RuntimeBrand>} [input]
|
|
42
|
+
* @returns {RuntimeBrand}
|
|
43
|
+
*/
|
|
24
44
|
export function resolveRuntimeBrand(input) {
|
|
25
45
|
if (!input || typeof input !== "object") return { ...DEFAULT_RUNTIME_BRAND };
|
|
26
46
|
const out = { ...DEFAULT_RUNTIME_BRAND };
|
|
27
|
-
for (const key of Object.keys(DEFAULT_RUNTIME_BRAND)) {
|
|
47
|
+
for (const key of /** @type {Array<keyof RuntimeBrand>} */ (Object.keys(DEFAULT_RUNTIME_BRAND))) {
|
|
28
48
|
const value = input[key];
|
|
29
49
|
if (typeof value === "string" && value.trim()) out[key] = value.trim();
|
|
30
50
|
}
|