@mono-agent/agent-runtime 0.15.4 → 0.17.0
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/MIGRATION.md +50 -14
- package/package.json +3 -3
- package/src/agent/tools/agent-tool.js +118 -8
- package/src/agent/tools/bash.js +2 -2
- package/src/agent/tools/exec.js +3 -1
- package/src/agent/tools/shared/tool-context.js +22 -0
- package/src/ai/pi-interop.js +7 -5
- package/src/ai/pi-oauth-compat.js +193 -0
- package/src/ai/providers/pi-native/result-builder.js +32 -0
- package/src/ai/providers/pi-native/turn-runner.js +14 -3
- package/src/ai/providers/pi-native.js +33 -7
- package/src/ai/runtime/capabilities.js +2 -0
- package/src/ai/runtime/router.js +14 -3
- package/src/ai/types.js +4 -1
- package/src/pi-auth.js +2 -2
- package/src/runtime.js +15 -1
- package/types/agent/tools/agent-tool.d.ts +43 -1
- package/types/agent/tools/shared/tool-context.d.ts +15 -0
- package/types/ai/pi-oauth-compat.d.ts +57 -0
- package/types/ai/providers/pi-native/result-builder.d.ts +41 -0
- package/types/ai/providers/pi-native/turn-runner.d.ts +2 -3
- package/types/ai/runtime/capabilities.d.ts +3 -0
- package/types/ai/types.d.ts +22 -3
- package/types/ai/backend.d.ts +0 -57
- package/types/ai/registry.d.ts +0 -1
|
@@ -32,6 +32,38 @@ export function usageFromMessages(messages = []) {
|
|
|
32
32
|
return usage;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Add what this run's subagents spent to the run's own usage.
|
|
37
|
+
*
|
|
38
|
+
* A delegation is work the run asked for and is billed to the same account, but
|
|
39
|
+
* `usageFromMessages` only ever sees this agent's transcript — a subagent keeps
|
|
40
|
+
* its own — so every consumer of the run's usage was short by the whole cost of
|
|
41
|
+
* every subagent. Folding it in here rather than publishing a separate event
|
|
42
|
+
* makes the console's cost, the TUI status bar and the exported metrics correct
|
|
43
|
+
* with no change at any of them. The trade is attribution: a subagent on
|
|
44
|
+
* another model has its spend reported under this run's model, which is the
|
|
45
|
+
* right answer for a run total and the wrong one for a per-model breakdown.
|
|
46
|
+
*
|
|
47
|
+
* `estimatedCost` is this agent's own fallback price, used when the provider
|
|
48
|
+
* priced nothing (subscription auth). Adding to the raw `cost` alone in that
|
|
49
|
+
* case would have replaced this agent's cost with the subagent's rather than
|
|
50
|
+
* summing them.
|
|
51
|
+
*
|
|
52
|
+
* @param {{input: number, output: number, cacheRead: number, cacheWrite: number, cost: number}} usage
|
|
53
|
+
* @param {{costUsd: number, input: number, output: number, cacheRead: number, cacheWrite: number}} delegated
|
|
54
|
+
* @param {number} estimatedCost
|
|
55
|
+
* @returns {{input: number, output: number, cacheRead: number, cacheWrite: number, cost: number}}
|
|
56
|
+
*/
|
|
57
|
+
export function withSubagentUsage(usage, delegated, estimatedCost) {
|
|
58
|
+
return {
|
|
59
|
+
input: usage.input + delegated.input,
|
|
60
|
+
output: usage.output + delegated.output,
|
|
61
|
+
cacheRead: usage.cacheRead + delegated.cacheRead,
|
|
62
|
+
cacheWrite: usage.cacheWrite + delegated.cacheWrite,
|
|
63
|
+
cost: delegated.costUsd > 0 ? (usage.cost || estimatedCost) + delegated.costUsd : usage.cost,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
35
67
|
/**
|
|
36
68
|
* Normalize one provider request's usage into an exact context snapshot.
|
|
37
69
|
* Unlike usageFromMessages(), this deliberately does not aggregate earlier
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
// caller-owned runState.
|
|
9
9
|
|
|
10
10
|
import { AgentHarness } from "@earendil-works/pi-agent-core";
|
|
11
|
-
import { NodeExecutionEnv } from "@earendil-works/pi-agent-core/node";
|
|
12
11
|
import {
|
|
13
12
|
createStructuredOutputTool,
|
|
14
13
|
getPiBuiltinTools,
|
|
@@ -134,6 +133,15 @@ export async function buildTurnTools(runState, {
|
|
|
134
133
|
// child is never less sandboxed than the parent that spawned it.
|
|
135
134
|
sandboxPolicy: options.sandboxPolicy,
|
|
136
135
|
sandboxEngine,
|
|
136
|
+
// The same skills this turn was disclosed. A child runs OUTSIDE the
|
|
137
|
+
// harness, so without this it gets no index and — because ReadSkill is
|
|
138
|
+
// only built when `skills` is non-empty — no way to read one either. It
|
|
139
|
+
// then rediscovers by trial and error whatever its parent could simply
|
|
140
|
+
// have looked up. Pass-through only: whether the child actually receives
|
|
141
|
+
// them is the host's decision, not this layer's.
|
|
142
|
+
skills: options.skills,
|
|
143
|
+
skillsRoot: options.skillsRoot,
|
|
144
|
+
toolEnvironment: options.toolEnvironment,
|
|
137
145
|
},
|
|
138
146
|
ctx: runCtx,
|
|
139
147
|
}));
|
|
@@ -241,7 +249,6 @@ export function toolResultErrorOverride(details) {
|
|
|
241
249
|
}
|
|
242
250
|
|
|
243
251
|
export function buildTurnHarness(runState, {
|
|
244
|
-
cwd,
|
|
245
252
|
session,
|
|
246
253
|
piModels,
|
|
247
254
|
model,
|
|
@@ -259,8 +266,12 @@ export function buildTurnHarness(runState, {
|
|
|
259
266
|
sdk,
|
|
260
267
|
reference,
|
|
261
268
|
}) {
|
|
269
|
+
// pi-agent-core 0.83.0 removed `env` from AgentHarnessOptions: an
|
|
270
|
+
// ExecutionEnv now reaches tools through the generic per-turn `toolContext`
|
|
271
|
+
// instead. mono-agent needs neither — it uses none of pi's built-in
|
|
272
|
+
// file/shell tools, and its own tools close over what they need — so the
|
|
273
|
+
// option is dropped rather than migrated.
|
|
262
274
|
const harness = new AgentHarness({
|
|
263
|
-
env: new NodeExecutionEnv({ cwd: cwd || process.cwd() }),
|
|
264
275
|
session,
|
|
265
276
|
models: piModels,
|
|
266
277
|
model,
|
|
@@ -31,7 +31,9 @@ import {
|
|
|
31
31
|
resolveAgentCompactionPolicy,
|
|
32
32
|
resolveRuntimePolicyInputs,
|
|
33
33
|
} from "../../agent/compaction.js";
|
|
34
|
+
import { subagentInvocationCount, subagentUsageForRun } from "../../agent/tools/agent-tool.js";
|
|
34
35
|
import { closePiMcpClients } from "../../agent/tools/pi-bridge.js";
|
|
36
|
+
import { readToolRuntime } from "../../agent/tools/shared/runtime-context.js";
|
|
35
37
|
import { createApprovalManager } from "../../agent/approval.js";
|
|
36
38
|
import { buildCapabilitiesUsed, toolCompactionAppliedFromWarnings } from "../runtime/capabilities-used.js";
|
|
37
39
|
import { reasoningLevelsForPiModel, resolvePiRuntimeModel } from "./pi-models.js";
|
|
@@ -55,6 +57,7 @@ import {
|
|
|
55
57
|
emitCapabilitiesResolved,
|
|
56
58
|
emitUsageCostEvents,
|
|
57
59
|
usageFromMessages,
|
|
60
|
+
withSubagentUsage,
|
|
58
61
|
} from "./pi-native/result-builder.js";
|
|
59
62
|
import {
|
|
60
63
|
cleanupSessionOnThrow,
|
|
@@ -481,7 +484,6 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
481
484
|
// external abort handler (which sets runState.externalAbort and aborts the
|
|
482
485
|
// harness). Sets runState.harness + runState.removeAbortHandler.
|
|
483
486
|
harness = buildTurnHarness(runState, {
|
|
484
|
-
cwd: options.cwd,
|
|
485
487
|
session: runState.session,
|
|
486
488
|
piModels,
|
|
487
489
|
model: runtime.model,
|
|
@@ -645,15 +647,24 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
645
647
|
const { runTranscript, lastAssistant, stopReason, finalText, finalThinking } = state;
|
|
646
648
|
const runAssistantCount = state.assistantMessages.length;
|
|
647
649
|
|
|
648
|
-
const
|
|
650
|
+
const ownUsage = usageFromMessages(runTranscript);
|
|
651
|
+
// Priced from this agent's own tokens: it is the fallback for a run the
|
|
652
|
+
// provider did not price, and only these tokens are this model's.
|
|
649
653
|
const estimatedCost = estimateCost({
|
|
650
654
|
resolveCustomPricing: options.resolveCustomPricing,
|
|
651
655
|
model: reference,
|
|
652
|
-
inputTokens:
|
|
653
|
-
outputTokens:
|
|
654
|
-
cachedTokens:
|
|
655
|
-
cacheWriteTokens:
|
|
656
|
+
inputTokens: ownUsage.input,
|
|
657
|
+
outputTokens: ownUsage.output,
|
|
658
|
+
cachedTokens: ownUsage.cacheRead,
|
|
659
|
+
cacheWriteTokens: ownUsage.cacheWrite,
|
|
656
660
|
});
|
|
661
|
+
// Keyed by the same parentRunId the Agent tool stamps its budget under, the
|
|
662
|
+
// way `subagentInvoked` below reads its count.
|
|
663
|
+
const usage = withSubagentUsage(
|
|
664
|
+
ownUsage,
|
|
665
|
+
subagentUsageForRun(options.subagents, (options.toolContext ?? readToolRuntime())?.runId),
|
|
666
|
+
estimatedCost,
|
|
667
|
+
);
|
|
657
668
|
emitUsageCostEvents({
|
|
658
669
|
onEvent,
|
|
659
670
|
resolved,
|
|
@@ -709,8 +720,23 @@ export async function generatePiNativeResponse(systemPrompt, options = {}) {
|
|
|
709
720
|
promptCacheActive: usage.cacheRead > 0 || usage.cacheWrite > 0,
|
|
710
721
|
thinkingEnabled: effectiveThinkingLevel !== "off" && effectiveThinkingLevel !== "low",
|
|
711
722
|
structuredOutputEnforced: !!options.outputSchema,
|
|
712
|
-
|
|
723
|
+
// What actually happened, not what was configured. This used to be a
|
|
724
|
+
// hardcoded `false`, which reported "no subagent ran" for runs that had
|
|
725
|
+
// just spawned several — the one signal that would have shown delegation
|
|
726
|
+
// working said it never happened.
|
|
727
|
+
//
|
|
728
|
+
// The count is keyed by the same parentRunId the Agent tool stamps its
|
|
729
|
+
// budget under (turn-runner threads `runCtx?.runId`); `runId` survives the
|
|
730
|
+
// sandbox-branch spread there, so reading it off the tool context directly
|
|
731
|
+
// yields the same key.
|
|
732
|
+
subagentInvoked: subagentInvocationCount(
|
|
733
|
+
options.subagents,
|
|
734
|
+
(options.toolContext ?? readToolRuntime())?.runId,
|
|
735
|
+
) > 0,
|
|
713
736
|
mcpServersUsed: mcpClients.map((entry) => entry?.name).filter(Boolean),
|
|
737
|
+
// Empty by contract, not by omission: "native" means provider-native
|
|
738
|
+
// subagents (Claude's Task tool). mono-agent's `Agent` is its own, so pi
|
|
739
|
+
// has none — `subagent_invoked` above is where pi delegation is reported.
|
|
714
740
|
nativeSubagentsUsed: [],
|
|
715
741
|
toolCompactionApplied: toolCompactionAppliedFromWarnings(runtimeWarnings),
|
|
716
742
|
// Tristate: true = a compaction fired this run (proactive or reactive),
|
|
@@ -13,6 +13,7 @@ export const COMMON_CAPABILITIES = {
|
|
|
13
13
|
supports_builtin_tools: true,
|
|
14
14
|
supports_live_input: true,
|
|
15
15
|
supports_native_subagents: true,
|
|
16
|
+
supports_request_tool_environment: false,
|
|
16
17
|
supports_fast_mode: false,
|
|
17
18
|
tool_policy: TOOL_POLICY_PROJECTED,
|
|
18
19
|
};
|
|
@@ -34,6 +35,7 @@ export const RUNTIME_CAPABILITIES = {
|
|
|
34
35
|
// The pi-native bridge does not (yet) wire native subagents / an AskAgent
|
|
35
36
|
// tool, so advertise no support rather than letting callers expect it.
|
|
36
37
|
supports_native_subagents: false,
|
|
38
|
+
supports_request_tool_environment: true,
|
|
37
39
|
},
|
|
38
40
|
codex: {
|
|
39
41
|
runtime: "cli",
|
package/src/ai/runtime/router.js
CHANGED
|
@@ -98,7 +98,7 @@ const RESOLVER_PROTECTED_OPTION_KEYS = new Set([
|
|
|
98
98
|
"sessionId", "providerSessionId", "sessionKeepAlive", "sessionIdleTimeoutMs",
|
|
99
99
|
"diagnosticsSeed", "systemPromptPrefix", "sandboxPolicy", "sandboxEngine", "sandbox",
|
|
100
100
|
"allowedTools", "disallowedTools", "permissionMode", "mcpServers", "skills",
|
|
101
|
-
"outputSchema", "nativeSubagents", "liveInput", "fastMode",
|
|
101
|
+
"outputSchema", "nativeSubagents", "liveInput", "fastMode", "toolEnvironment",
|
|
102
102
|
]);
|
|
103
103
|
|
|
104
104
|
/**
|
|
@@ -335,11 +335,16 @@ export function createRouterRuntime({ host = {}, chain = [], routeSafety = "unif
|
|
|
335
335
|
// A same-model retry is not a failover: only the first attempt of a new
|
|
336
336
|
// route announces a transition.
|
|
337
337
|
if (retryIndex === 0 && failoverHistory.length > 0) {
|
|
338
|
+
const previous = failoverHistory[failoverHistory.length - 1];
|
|
338
339
|
emit(callOptions, {
|
|
339
340
|
type: "provider_failover_started",
|
|
340
|
-
from: modelKey(
|
|
341
|
+
from: modelKey(previous?.model),
|
|
341
342
|
to: modelKey(entry.model),
|
|
342
343
|
attemptIndex: i,
|
|
344
|
+
// Why the route changed, in the same vocabulary provider_retry_started
|
|
345
|
+
// uses. Operators reading a transcript need the cause next to the
|
|
346
|
+
// transition, not only in the run artifact's failoverHistory.
|
|
347
|
+
reason: previous?.retryableSubkind || previous?.failureKind || null,
|
|
343
348
|
});
|
|
344
349
|
}
|
|
345
350
|
|
|
@@ -378,9 +383,12 @@ export function createRouterRuntime({ host = {}, chain = [], routeSafety = "unif
|
|
|
378
383
|
// (failover)" when X is still the route the operator asked for.
|
|
379
384
|
if (failoverHistory.some((attempt) => modelKey(attempt.model) !== modelKey(entry.model))) {
|
|
380
385
|
emit(callOptions, {
|
|
386
|
+
// modelKey, not the ModelRef: every consumer of this event reads
|
|
387
|
+
// `model` as a string (responder.ts's stringField is string-only),
|
|
388
|
+
// so an object here is dropped silently rather than rendered.
|
|
381
389
|
type: "provider_failover_completed",
|
|
382
390
|
attemptIndex: i,
|
|
383
|
-
model: entry.model,
|
|
391
|
+
model: modelKey(entry.model),
|
|
384
392
|
});
|
|
385
393
|
}
|
|
386
394
|
return { ...result, failoverHistory, routeSafetyHistory };
|
|
@@ -1038,6 +1046,9 @@ function entrySatisfiesRequirements(entry, options) {
|
|
|
1038
1046
|
if (options.liveInput) {
|
|
1039
1047
|
effectiveRequires.supports_live_input = true;
|
|
1040
1048
|
}
|
|
1049
|
+
if (options.toolEnvironment !== undefined) {
|
|
1050
|
+
effectiveRequires.supports_request_tool_environment = true;
|
|
1051
|
+
}
|
|
1041
1052
|
if (options.fastMode === true) {
|
|
1042
1053
|
effectiveRequires.supports_fast_mode = true;
|
|
1043
1054
|
}
|
package/src/ai/types.js
CHANGED
|
@@ -147,7 +147,8 @@
|
|
|
147
147
|
* @property {boolean} [fastMode]
|
|
148
148
|
* @property {string} [cwd]
|
|
149
149
|
* @property {Object<string, Object>} [mcpServers]
|
|
150
|
-
* @property {ReadonlyArray<
|
|
150
|
+
* @property {ReadonlyArray<{name: string, description?: string}>} [skills] Skills disclosed to this run, as `{name, description}`. Non-empty makes `supports_skills` a routing requirement (see router.js), so a chain entry that lacks it is skipped.
|
|
151
|
+
* @property {string} [skillsRoot] Directory holding `<name>/SKILL.md`. Required alongside `skills` for `ReadSkill` to be built.
|
|
151
152
|
* @property {ReadonlyArray<string>} [allowedTools]
|
|
152
153
|
* @property {ReadonlyArray<string>} [disallowedTools]
|
|
153
154
|
* @property {string} [permissionMode]
|
|
@@ -155,6 +156,7 @@
|
|
|
155
156
|
* @property {Object} [outputSchema]
|
|
156
157
|
* @property {string} [runArtifactDir]
|
|
157
158
|
* @property {AbortSignal} [abortSignal]
|
|
159
|
+
* @property {{schema: 1, values: Readonly<Record<string, string>>, pathPrepend?: readonly string[]}} [toolEnvironment] Host-only environment for Bash, Exec, and nested subagents in this run.
|
|
158
160
|
* @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).
|
|
159
161
|
* @property {import('../agent/sandbox-seam.js').RuntimeSandboxEngine} [sandboxEngine] Per-run concrete sandbox engine handed to the active sandbox implementation.
|
|
160
162
|
* @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.
|
|
@@ -278,6 +280,7 @@
|
|
|
278
280
|
* @property {boolean} [supports_builtin_tools]
|
|
279
281
|
* @property {boolean} [supports_live_input]
|
|
280
282
|
* @property {boolean} [supports_native_subagents]
|
|
283
|
+
* @property {boolean} [supports_request_tool_environment]
|
|
281
284
|
* @property {boolean} [supports_fast_mode]
|
|
282
285
|
* @property {"projected"|"allow_all_only"} [tool_policy] Whether the bridge can
|
|
283
286
|
* project named allow/deny policies or accepts only a semantically unrestricted
|
package/src/pi-auth.js
CHANGED
|
@@ -4,7 +4,7 @@ import { realpathSync } from "node:fs";
|
|
|
4
4
|
import { chmod, mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
5
5
|
import { basename, dirname, join, resolve } from "node:path";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { resolveOAuthApiKey } from "./ai/pi-oauth-compat.js";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @typedef {{type: "api_key", key?: string, env?: Object<string, *>}} PiApiKeyCredential
|
|
@@ -41,7 +41,7 @@ export function createPiOAuthApiKeyResolver(options = {}) {
|
|
|
41
41
|
return undefined;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
const result = await
|
|
44
|
+
const result = await resolveOAuthApiKey(provider, cloneAuth(auth));
|
|
45
45
|
if (result === null || result === undefined || typeof result.apiKey !== "string" || result.apiKey.length === 0) {
|
|
46
46
|
return undefined;
|
|
47
47
|
}
|
package/src/runtime.js
CHANGED
|
@@ -165,6 +165,14 @@ export function createRuntime(host = {}) {
|
|
|
165
165
|
// WebSearch, so even a read-only profile could bypass network policy.
|
|
166
166
|
...(request.sandboxPolicy === undefined ? {} : { sandboxPolicy: request.sandboxPolicy }),
|
|
167
167
|
...(request.sandboxEngine === undefined ? {} : { sandboxEngine: request.sandboxEngine }),
|
|
168
|
+
// The parent's disclosed skills, for the same reason: they are a per-run
|
|
169
|
+
// option, so a child that does not receive them has no ReadSkill tool and no
|
|
170
|
+
// index, and must rediscover by trial and error what its parent could look
|
|
171
|
+
// up. A host-supplied `run` may gate this; the default has no route or deny
|
|
172
|
+
// list of its own to consult, so it forwards what it was given.
|
|
173
|
+
...(request.skills === undefined ? {} : { skills: request.skills }),
|
|
174
|
+
...(request.skillsRoot === undefined ? {} : { skillsRoot: request.skillsRoot }),
|
|
175
|
+
...(request.toolEnvironment === undefined ? {} : { toolEnvironment: request.toolEnvironment }),
|
|
168
176
|
...(request.executionMode === undefined ? {} : { executionMode: request.executionMode }),
|
|
169
177
|
...(request.cwd === undefined ? {} : { cwd: request.cwd }),
|
|
170
178
|
// A profile that pins effort — declared or authored at call time — means it
|
|
@@ -203,6 +211,12 @@ export function createRuntime(host = {}) {
|
|
|
203
211
|
});
|
|
204
212
|
const liveInput = instrumentLiveInputAppliedEvents(options.liveInput, hub.emit);
|
|
205
213
|
const prompts = resolvePrompts(host.prompts, options.prompts);
|
|
214
|
+
// A request-scoped environment must never mutate the long-lived runtime's
|
|
215
|
+
// shared ToolContext. Clone only for this call, preserving configureTools
|
|
216
|
+
// updates while keeping credentials isolated between concurrent turns.
|
|
217
|
+
const runToolContext = options.toolEnvironment === undefined
|
|
218
|
+
? toolContext
|
|
219
|
+
: { ...toolContext, toolEnvironment: options.toolEnvironment };
|
|
206
220
|
// Default the nested-run callback so the Agent built-in is usable without
|
|
207
221
|
// host wiring; the depth field is left exactly as the caller set it, since
|
|
208
222
|
// defaultSubagentRun is what increments it for the child.
|
|
@@ -219,7 +233,7 @@ export function createRuntime(host = {}) {
|
|
|
219
233
|
model: options.model,
|
|
220
234
|
executionMode,
|
|
221
235
|
runtimeBrand,
|
|
222
|
-
toolContext,
|
|
236
|
+
toolContext: runToolContext,
|
|
223
237
|
observerHub: hub,
|
|
224
238
|
onEvent: hub.emit,
|
|
225
239
|
...(liveInput === undefined ? {} : { liveInput }),
|
|
@@ -1,8 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How many subagents this logical run actually spawned.
|
|
3
|
+
*
|
|
4
|
+
* A read-only accessor so a provider can report `subagent_invoked` truthfully
|
|
5
|
+
* without reaching into `__budgets`, which is a deliberately private,
|
|
6
|
+
* non-enumerable implementation detail. Returns 0 when nothing was ever
|
|
7
|
+
* registered — a run with no `Agent` tool never creates a budget entry, and that
|
|
8
|
+
* is indistinguishable from one that had the tool and never used it, which is
|
|
9
|
+
* exactly what "no subagent was invoked" means for this signal.
|
|
10
|
+
*
|
|
11
|
+
* @param {*} subagents The run-scoped options object, or undefined.
|
|
12
|
+
* @param {string|undefined} parentRunId
|
|
13
|
+
* @returns {number}
|
|
14
|
+
*/
|
|
15
|
+
export function subagentInvocationCount(subagents: any, parentRunId: string | undefined): number;
|
|
16
|
+
/**
|
|
17
|
+
* What this logical run's subagents spent, summed across every delegation.
|
|
18
|
+
*
|
|
19
|
+
* A delegation is work the run asked for, so its cost belongs to the run's
|
|
20
|
+
* total — a provider folds this into its own usage before reporting, which is
|
|
21
|
+
* what makes the console's cost, the TUI status bar and the exported metrics
|
|
22
|
+
* agree with the bill. Same read-only-accessor contract as
|
|
23
|
+
* `subagentInvocationCount`: `__budgets` stays private. All zeroes when nothing
|
|
24
|
+
* delegated, which is the truthful answer for a run that never used the tool.
|
|
25
|
+
*
|
|
26
|
+
* @param {*} subagents The run-scoped options object, or undefined.
|
|
27
|
+
* @param {string|undefined} parentRunId
|
|
28
|
+
* @returns {{costUsd: number, input: number, output: number, cacheRead: number, cacheWrite: number}}
|
|
29
|
+
*/
|
|
30
|
+
export function subagentUsageForRun(subagents: any, parentRunId: string | undefined): {
|
|
31
|
+
costUsd: number;
|
|
32
|
+
input: number;
|
|
33
|
+
output: number;
|
|
34
|
+
cacheRead: number;
|
|
35
|
+
cacheWrite: number;
|
|
36
|
+
};
|
|
1
37
|
/**
|
|
2
38
|
* Build the `Agent` tool, or null when subagents are unavailable for this run.
|
|
3
39
|
*
|
|
4
40
|
* @param {RuntimeSubagentsOptions|null|undefined} subagents
|
|
5
|
-
* @param {{model?: *, executionMode?: string, cwd?: string, parentRunId?: string, sandboxPolicy?: *, sandboxEngine?: *, onEvent?: (event: *) => void}} [context]
|
|
41
|
+
* @param {{model?: *, executionMode?: string, cwd?: string, parentRunId?: string, sandboxPolicy?: *, sandboxEngine?: *, skills?: {name: string, description?: string}[], skillsRoot?: string, toolEnvironment?: *, onEvent?: (event: *) => void}} [context]
|
|
6
42
|
* @returns {*|null}
|
|
7
43
|
*/
|
|
8
44
|
export function createAgentTool(subagents: RuntimeSubagentsOptions | null | undefined, context?: {
|
|
@@ -12,6 +48,12 @@ export function createAgentTool(subagents: RuntimeSubagentsOptions | null | unde
|
|
|
12
48
|
parentRunId?: string;
|
|
13
49
|
sandboxPolicy?: any;
|
|
14
50
|
sandboxEngine?: any;
|
|
51
|
+
skills?: {
|
|
52
|
+
name: string;
|
|
53
|
+
description?: string;
|
|
54
|
+
}[];
|
|
55
|
+
skillsRoot?: string;
|
|
56
|
+
toolEnvironment?: any;
|
|
15
57
|
onEvent?: (event: any) => void;
|
|
16
58
|
}): any | null;
|
|
17
59
|
/**
|
|
@@ -30,6 +30,16 @@ export function resetToolContext(ctx: ToolContext): ToolContext;
|
|
|
30
30
|
* @returns {SandboxPolicy|undefined}
|
|
31
31
|
*/
|
|
32
32
|
export function resolveSandboxPolicy(ctx: ToolContext | undefined, requestPolicy?: SandboxPolicy): SandboxPolicy | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Compose the process environment overlay for Bash/Exec. The caller-supplied
|
|
35
|
+
* base carries each tool's own hardening values; request values are applied
|
|
36
|
+
* only at this final process boundary. No global environment is mutated.
|
|
37
|
+
*
|
|
38
|
+
* @param {ToolContext|undefined} ctx
|
|
39
|
+
* @param {Record<string, string|undefined>} [base]
|
|
40
|
+
* @returns {Record<string, string|undefined>|undefined}
|
|
41
|
+
*/
|
|
42
|
+
export function requestToolProcessEnvironment(ctx: ToolContext | undefined, base?: Record<string, string | undefined>): Record<string, string | undefined> | undefined;
|
|
33
43
|
export type RuntimeBrand = import("../../../runtime-brand.js").RuntimeBrand;
|
|
34
44
|
export type SandboxPolicy = import("../../sandbox-seam.js").SandboxPolicy;
|
|
35
45
|
export type RuntimeSandboxEngine = import("../../sandbox-seam.js").RuntimeSandboxEngine;
|
|
@@ -45,4 +55,9 @@ export type ToolContext = {
|
|
|
45
55
|
sandboxEngine?: RuntimeSandboxEngine;
|
|
46
56
|
sandbox: RuntimeSandbox;
|
|
47
57
|
runtimeBrand: RuntimeBrand;
|
|
58
|
+
toolEnvironment?: {
|
|
59
|
+
schema: 1;
|
|
60
|
+
values: Readonly<Record<string, string>>;
|
|
61
|
+
pathPrepend?: readonly string[];
|
|
62
|
+
};
|
|
48
63
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** @internal Exported only so tests can force a rebuild of the memoized index. */
|
|
2
|
+
export function resetPiProviderIndexForTests(): void;
|
|
3
|
+
/**
|
|
4
|
+
* The OAuth implementation for a Pi provider id, or undefined when the provider
|
|
5
|
+
* is unknown or supports only API-key auth (e.g. `opencode-go`).
|
|
6
|
+
*
|
|
7
|
+
* @param {string} providerId
|
|
8
|
+
* @returns {OAuthAuth|undefined}
|
|
9
|
+
*/
|
|
10
|
+
export function getPiOAuthAuth(providerId: string): OAuthAuth | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Every Pi provider id that supports OAuth. Replaces
|
|
13
|
+
* `getOAuthProviders().map((provider) => provider.id)`.
|
|
14
|
+
*
|
|
15
|
+
* @returns {string[]}
|
|
16
|
+
*/
|
|
17
|
+
export function getPiOAuthProviderIds(): string[];
|
|
18
|
+
/**
|
|
19
|
+
* Resolve an API key from stored OAuth credentials, refreshing first when the
|
|
20
|
+
* token has expired.
|
|
21
|
+
*
|
|
22
|
+
* Reproduces pi-ai 0.80.6's `getOAuthApiKey(providerId, credentials)` contract
|
|
23
|
+
* so its call sites keep their shape: takes the whole provider-keyed credential
|
|
24
|
+
* map, returns `null` when this provider has no stored credential, and is
|
|
25
|
+
* *pure* — the refreshed credential comes back as `newCredentials` for the
|
|
26
|
+
* caller to persist rather than being written here.
|
|
27
|
+
*
|
|
28
|
+
* The refresh trigger is deliberately the old exact-expiry check. pi's own
|
|
29
|
+
* `Models.getAuth()` refreshes five minutes ahead of expiry; matching that would
|
|
30
|
+
* change live token rotation timing, which this migration does not intend.
|
|
31
|
+
*
|
|
32
|
+
* @param {string} providerId
|
|
33
|
+
* @param {Record<string, *>|undefined} credentials Provider-keyed credential map.
|
|
34
|
+
* @returns {Promise<{newCredentials: OAuthCredential, apiKey: string|undefined}|null>}
|
|
35
|
+
*/
|
|
36
|
+
export function resolveOAuthApiKey(providerId: string, credentials: Record<string, any> | undefined): Promise<{
|
|
37
|
+
newCredentials: OAuthCredential;
|
|
38
|
+
apiKey: string | undefined;
|
|
39
|
+
} | null>;
|
|
40
|
+
/**
|
|
41
|
+
* Bridge the legacy six-callback OAuth surface onto 0.83.0's single
|
|
42
|
+
* `prompt`/`notify` pair.
|
|
43
|
+
*
|
|
44
|
+
* `manual_code` must stay wired to `onManualCodeInput`: Anthropic races its
|
|
45
|
+
* localhost callback against a pasted redirect URL, and that path is the reason
|
|
46
|
+
* `agent-app`'s `runPiOAuthLogin` exists at all.
|
|
47
|
+
*
|
|
48
|
+
* @param {OAuthLoginCallbacks} callbacks
|
|
49
|
+
* @returns {AuthInteraction}
|
|
50
|
+
*/
|
|
51
|
+
export function toAuthInteraction(callbacks: OAuthLoginCallbacks): AuthInteraction;
|
|
52
|
+
export type OAuthAuth = import("@earendil-works/pi-ai").OAuthAuth;
|
|
53
|
+
export type OAuthCredential = import("@earendil-works/pi-ai").OAuthCredential;
|
|
54
|
+
export type AuthInteraction = import("@earendil-works/pi-ai").AuthInteraction;
|
|
55
|
+
export type AuthPrompt = import("@earendil-works/pi-ai").AuthPrompt;
|
|
56
|
+
export type AuthEvent = import("@earendil-works/pi-ai").AuthEvent;
|
|
57
|
+
export type OAuthLoginCallbacks = import("@earendil-works/pi-ai/oauth").OAuthLoginCallbacks;
|
|
@@ -10,6 +10,47 @@ export function usageFromMessages(messages?: Array<any>): {
|
|
|
10
10
|
cacheWrite: number;
|
|
11
11
|
cost: number;
|
|
12
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* Add what this run's subagents spent to the run's own usage.
|
|
15
|
+
*
|
|
16
|
+
* A delegation is work the run asked for and is billed to the same account, but
|
|
17
|
+
* `usageFromMessages` only ever sees this agent's transcript — a subagent keeps
|
|
18
|
+
* its own — so every consumer of the run's usage was short by the whole cost of
|
|
19
|
+
* every subagent. Folding it in here rather than publishing a separate event
|
|
20
|
+
* makes the console's cost, the TUI status bar and the exported metrics correct
|
|
21
|
+
* with no change at any of them. The trade is attribution: a subagent on
|
|
22
|
+
* another model has its spend reported under this run's model, which is the
|
|
23
|
+
* right answer for a run total and the wrong one for a per-model breakdown.
|
|
24
|
+
*
|
|
25
|
+
* `estimatedCost` is this agent's own fallback price, used when the provider
|
|
26
|
+
* priced nothing (subscription auth). Adding to the raw `cost` alone in that
|
|
27
|
+
* case would have replaced this agent's cost with the subagent's rather than
|
|
28
|
+
* summing them.
|
|
29
|
+
*
|
|
30
|
+
* @param {{input: number, output: number, cacheRead: number, cacheWrite: number, cost: number}} usage
|
|
31
|
+
* @param {{costUsd: number, input: number, output: number, cacheRead: number, cacheWrite: number}} delegated
|
|
32
|
+
* @param {number} estimatedCost
|
|
33
|
+
* @returns {{input: number, output: number, cacheRead: number, cacheWrite: number, cost: number}}
|
|
34
|
+
*/
|
|
35
|
+
export function withSubagentUsage(usage: {
|
|
36
|
+
input: number;
|
|
37
|
+
output: number;
|
|
38
|
+
cacheRead: number;
|
|
39
|
+
cacheWrite: number;
|
|
40
|
+
cost: number;
|
|
41
|
+
}, delegated: {
|
|
42
|
+
costUsd: number;
|
|
43
|
+
input: number;
|
|
44
|
+
output: number;
|
|
45
|
+
cacheRead: number;
|
|
46
|
+
cacheWrite: number;
|
|
47
|
+
}, estimatedCost: number): {
|
|
48
|
+
input: number;
|
|
49
|
+
output: number;
|
|
50
|
+
cacheRead: number;
|
|
51
|
+
cacheWrite: number;
|
|
52
|
+
cost: number;
|
|
53
|
+
};
|
|
13
54
|
/**
|
|
14
55
|
* Normalize one provider request's usage into an exact context snapshot.
|
|
15
56
|
* Unlike usageFromMessages(), this deliberately does not aggregate earlier
|
|
@@ -45,8 +45,7 @@ export function thinkingLevelForEffort(effort: string, capabilities: any): strin
|
|
|
45
45
|
export function toolResultErrorOverride(details: any): {
|
|
46
46
|
isError: true;
|
|
47
47
|
} | undefined;
|
|
48
|
-
export function buildTurnHarness(runState: any, {
|
|
49
|
-
cwd: any;
|
|
48
|
+
export function buildTurnHarness(runState: any, { session, piModels, model, thinkingLevel, systemPrompt, outputSchema, tools, transport, maxRetries, maxRetryDelayMs, steeringMode, onEvent, options, toolLimits, sdk, reference, }: {
|
|
50
49
|
session: any;
|
|
51
50
|
piModels: any;
|
|
52
51
|
model: any;
|
|
@@ -63,7 +62,7 @@ export function buildTurnHarness(runState: any, { cwd, session, piModels, model,
|
|
|
63
62
|
toolLimits: any;
|
|
64
63
|
sdk: any;
|
|
65
64
|
reference: any;
|
|
66
|
-
}): AgentHarness<import("@earendil-works/pi-agent-core").Skill, import("@earendil-works/pi-agent-core").PromptTemplate, import("@earendil-works/pi-agent-core").
|
|
65
|
+
}): AgentHarness<undefined, import("@earendil-works/pi-agent-core").Skill, import("@earendil-works/pi-agent-core").PromptTemplate, import("@earendil-works/pi-agent-core").AgentHarnessTool<undefined>>;
|
|
67
66
|
/**
|
|
68
67
|
* Start the live-input steering consumer. Consumes follow-up messages and steers
|
|
69
68
|
* the harness mid-run; the consumer is tied to run completion (an internal
|
|
@@ -9,6 +9,7 @@ export namespace COMMON_CAPABILITIES {
|
|
|
9
9
|
export let supports_builtin_tools: boolean;
|
|
10
10
|
export let supports_live_input: boolean;
|
|
11
11
|
export let supports_native_subagents: boolean;
|
|
12
|
+
export let supports_request_tool_environment: boolean;
|
|
12
13
|
export let supports_fast_mode: boolean;
|
|
13
14
|
export { TOOL_POLICY_PROJECTED as tool_policy };
|
|
14
15
|
}
|
|
@@ -23,6 +24,8 @@ export namespace RUNTIME_CAPABILITIES {
|
|
|
23
24
|
export { supports_session_resume_2 as supports_session_resume };
|
|
24
25
|
let supports_native_subagents_1: boolean;
|
|
25
26
|
export { supports_native_subagents_1 as supports_native_subagents };
|
|
27
|
+
let supports_request_tool_environment_1: boolean;
|
|
28
|
+
export { supports_request_tool_environment_1 as supports_request_tool_environment };
|
|
26
29
|
let runtime_1: string;
|
|
27
30
|
export { runtime_1 as runtime };
|
|
28
31
|
}
|
package/types/ai/types.d.ts
CHANGED
|
@@ -118,7 +118,8 @@
|
|
|
118
118
|
* @property {boolean} [fastMode]
|
|
119
119
|
* @property {string} [cwd]
|
|
120
120
|
* @property {Object<string, Object>} [mcpServers]
|
|
121
|
-
* @property {ReadonlyArray<
|
|
121
|
+
* @property {ReadonlyArray<{name: string, description?: string}>} [skills] Skills disclosed to this run, as `{name, description}`. Non-empty makes `supports_skills` a routing requirement (see router.js), so a chain entry that lacks it is skipped.
|
|
122
|
+
* @property {string} [skillsRoot] Directory holding `<name>/SKILL.md`. Required alongside `skills` for `ReadSkill` to be built.
|
|
122
123
|
* @property {ReadonlyArray<string>} [allowedTools]
|
|
123
124
|
* @property {ReadonlyArray<string>} [disallowedTools]
|
|
124
125
|
* @property {string} [permissionMode]
|
|
@@ -126,6 +127,7 @@
|
|
|
126
127
|
* @property {Object} [outputSchema]
|
|
127
128
|
* @property {string} [runArtifactDir]
|
|
128
129
|
* @property {AbortSignal} [abortSignal]
|
|
130
|
+
* @property {{schema: 1, values: Readonly<Record<string, string>>, pathPrepend?: readonly string[]}} [toolEnvironment] Host-only environment for Bash, Exec, and nested subagents in this run.
|
|
129
131
|
* @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).
|
|
130
132
|
* @property {import('../agent/sandbox-seam.js').RuntimeSandboxEngine} [sandboxEngine] Per-run concrete sandbox engine handed to the active sandbox implementation.
|
|
131
133
|
* @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.
|
|
@@ -242,6 +244,7 @@
|
|
|
242
244
|
* @property {boolean} [supports_builtin_tools]
|
|
243
245
|
* @property {boolean} [supports_live_input]
|
|
244
246
|
* @property {boolean} [supports_native_subagents]
|
|
247
|
+
* @property {boolean} [supports_request_tool_environment]
|
|
245
248
|
* @property {boolean} [supports_fast_mode]
|
|
246
249
|
* @property {"projected"|"allow_all_only"} [tool_policy] Whether the bridge can
|
|
247
250
|
* project named allow/deny policies or accepts only a semantically unrestricted
|
|
@@ -578,9 +581,16 @@ export type RuntimeRunOptions = {
|
|
|
578
581
|
[x: string]: any;
|
|
579
582
|
};
|
|
580
583
|
/**
|
|
581
|
-
*
|
|
584
|
+
* Skills disclosed to this run, as `{name, description}`. Non-empty makes `supports_skills` a routing requirement (see router.js), so a chain entry that lacks it is skipped.
|
|
582
585
|
*/
|
|
583
|
-
skills?: ReadonlyArray<
|
|
586
|
+
skills?: ReadonlyArray<{
|
|
587
|
+
name: string;
|
|
588
|
+
description?: string;
|
|
589
|
+
}>;
|
|
590
|
+
/**
|
|
591
|
+
* Directory holding `<name>/SKILL.md`. Required alongside `skills` for `ReadSkill` to be built.
|
|
592
|
+
*/
|
|
593
|
+
skillsRoot?: string;
|
|
584
594
|
allowedTools?: ReadonlyArray<string>;
|
|
585
595
|
disallowedTools?: ReadonlyArray<string>;
|
|
586
596
|
permissionMode?: string;
|
|
@@ -588,6 +598,14 @@ export type RuntimeRunOptions = {
|
|
|
588
598
|
outputSchema?: any;
|
|
589
599
|
runArtifactDir?: string;
|
|
590
600
|
abortSignal?: AbortSignal;
|
|
601
|
+
/**
|
|
602
|
+
* Host-only environment for Bash, Exec, and nested subagents in this run.
|
|
603
|
+
*/
|
|
604
|
+
toolEnvironment?: {
|
|
605
|
+
schema: 1;
|
|
606
|
+
values: Readonly<Record<string, string>>;
|
|
607
|
+
pathPrepend?: readonly string[];
|
|
608
|
+
};
|
|
591
609
|
/**
|
|
592
610
|
* Per-run sandbox policy; merged monotonically with the host policy (see resolveSandboxPolicy, agent/tools/shared/tool-context.js).
|
|
593
611
|
*/
|
|
@@ -829,6 +847,7 @@ export type RuntimeCapabilities = {
|
|
|
829
847
|
supports_builtin_tools?: boolean;
|
|
830
848
|
supports_live_input?: boolean;
|
|
831
849
|
supports_native_subagents?: boolean;
|
|
850
|
+
supports_request_tool_environment?: boolean;
|
|
832
851
|
supports_fast_mode?: boolean;
|
|
833
852
|
/**
|
|
834
853
|
* Whether the bridge can
|