@narumitw/pi-subagents 2.1.4 → 3.0.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/README.md +187 -1228
- package/dist/child-communication-bridge.ts +136 -0
- package/dist/child-communication-bridge.ts.map +7 -0
- package/dist/chunks/chunk-UKTWUQRM.js +667 -0
- package/dist/chunks/chunk-UKTWUQRM.js.map +7 -0
- package/dist/index.ts +1448 -1614
- package/dist/index.ts.map +4 -4
- package/docs/design-principles.md +31 -0
- package/docs/ipc-transport-idea.md +103 -0
- package/docs/tools.md +109 -0
- package/package.json +11 -19
- package/src/broker-credentials.ts +71 -0
- package/src/child-communication-bridge.ts +153 -0
- package/src/child-communication-tools.ts +147 -0
- package/src/completion-renderer.ts +60 -0
- package/src/index.ts +1 -1
- package/src/message-broker.ts +628 -0
- package/src/model-output.ts +42 -0
- package/src/process.ts +688 -0
- package/src/runtime.ts +538 -0
- package/src/subagents.ts +33 -34
- package/src/tools.ts +416 -0
- package/src/types.ts +85 -0
- package/src/widget.ts +130 -0
- package/dist/chunks/auto-transport-FUUKFDIG.ts +0 -109
- package/dist/chunks/auto-transport-FUUKFDIG.ts.map +0 -7
- package/dist/chunks/capability-grant-PR72SWWS.ts +0 -19
- package/dist/chunks/capability-grant-PR72SWWS.ts.map +0 -7
- package/dist/chunks/child-peer-bridge.ts +0 -108
- package/dist/chunks/child-peer-bridge.ts.map +0 -7
- package/dist/chunks/chunk-2LMJU25E.ts +0 -1641
- package/dist/chunks/chunk-2LMJU25E.ts.map +0 -7
- package/dist/chunks/chunk-47SK2QYC.ts +0 -56
- package/dist/chunks/chunk-47SK2QYC.ts.map +0 -7
- package/dist/chunks/chunk-4AQSF7AS.ts +0 -29
- package/dist/chunks/chunk-4AQSF7AS.ts.map +0 -7
- package/dist/chunks/chunk-5DGRMKNV.ts +0 -126
- package/dist/chunks/chunk-5DGRMKNV.ts.map +0 -7
- package/dist/chunks/chunk-6H6TBBED.ts +0 -108
- package/dist/chunks/chunk-6H6TBBED.ts.map +0 -7
- package/dist/chunks/chunk-6KJ34M6S.ts +0 -159
- package/dist/chunks/chunk-6KJ34M6S.ts.map +0 -7
- package/dist/chunks/chunk-6NSJVPXX.ts +0 -480
- package/dist/chunks/chunk-6NSJVPXX.ts.map +0 -7
- package/dist/chunks/chunk-7AAJEUSL.ts +0 -238
- package/dist/chunks/chunk-7AAJEUSL.ts.map +0 -7
- package/dist/chunks/chunk-BFK2Z4ZF.ts +0 -189
- package/dist/chunks/chunk-BFK2Z4ZF.ts.map +0 -7
- package/dist/chunks/chunk-BOAXY55Y.ts +0 -153
- package/dist/chunks/chunk-BOAXY55Y.ts.map +0 -7
- package/dist/chunks/chunk-D4CR7T73.ts +0 -353
- package/dist/chunks/chunk-D4CR7T73.ts.map +0 -7
- package/dist/chunks/chunk-FEVPWRMU.ts +0 -300
- package/dist/chunks/chunk-FEVPWRMU.ts.map +0 -7
- package/dist/chunks/chunk-G6VZTCFH.ts +0 -70
- package/dist/chunks/chunk-G6VZTCFH.ts.map +0 -7
- package/dist/chunks/chunk-H3FP6DLR.ts +0 -160
- package/dist/chunks/chunk-H3FP6DLR.ts.map +0 -7
- package/dist/chunks/chunk-HA36LPCO.ts +0 -130
- package/dist/chunks/chunk-HA36LPCO.ts.map +0 -7
- package/dist/chunks/chunk-HOP5FXDT.ts +0 -27
- package/dist/chunks/chunk-HOP5FXDT.ts.map +0 -7
- package/dist/chunks/chunk-IGAMVWFN.ts +0 -139
- package/dist/chunks/chunk-IGAMVWFN.ts.map +0 -7
- package/dist/chunks/chunk-ILEQ27AL.ts +0 -45
- package/dist/chunks/chunk-ILEQ27AL.ts.map +0 -7
- package/dist/chunks/chunk-ITVWPNU4.ts +0 -819
- package/dist/chunks/chunk-ITVWPNU4.ts.map +0 -7
- package/dist/chunks/chunk-IWC32VPY.ts +0 -172
- package/dist/chunks/chunk-IWC32VPY.ts.map +0 -7
- package/dist/chunks/chunk-JSZIP73U.ts +0 -362
- package/dist/chunks/chunk-JSZIP73U.ts.map +0 -7
- package/dist/chunks/chunk-JU6LUNLP.ts +0 -380
- package/dist/chunks/chunk-JU6LUNLP.ts.map +0 -7
- package/dist/chunks/chunk-KMGKCEO4.ts +0 -38
- package/dist/chunks/chunk-KMGKCEO4.ts.map +0 -7
- package/dist/chunks/chunk-LASD73CM.ts +0 -339
- package/dist/chunks/chunk-LASD73CM.ts.map +0 -7
- package/dist/chunks/chunk-LEOYDZI3.ts +0 -22
- package/dist/chunks/chunk-LEOYDZI3.ts.map +0 -7
- package/dist/chunks/chunk-LL4LP2T7.ts +0 -737
- package/dist/chunks/chunk-LL4LP2T7.ts.map +0 -7
- package/dist/chunks/chunk-N2T5IN4X.ts +0 -18
- package/dist/chunks/chunk-N2T5IN4X.ts.map +0 -7
- package/dist/chunks/chunk-N7BLVXKK.ts +0 -73
- package/dist/chunks/chunk-N7BLVXKK.ts.map +0 -7
- package/dist/chunks/chunk-NLT67IZS.ts +0 -322
- package/dist/chunks/chunk-NLT67IZS.ts.map +0 -7
- package/dist/chunks/chunk-NTRPLF46.ts +0 -63
- package/dist/chunks/chunk-NTRPLF46.ts.map +0 -7
- package/dist/chunks/chunk-ONDTY4EL.ts +0 -91
- package/dist/chunks/chunk-ONDTY4EL.ts.map +0 -7
- package/dist/chunks/chunk-OVPHFGKO.ts +0 -1769
- package/dist/chunks/chunk-OVPHFGKO.ts.map +0 -7
- package/dist/chunks/chunk-P7OH4XMF.ts +0 -60
- package/dist/chunks/chunk-P7OH4XMF.ts.map +0 -7
- package/dist/chunks/chunk-PBZMBTNJ.ts +0 -422
- package/dist/chunks/chunk-PBZMBTNJ.ts.map +0 -7
- package/dist/chunks/chunk-PGLSFLYW.ts +0 -52
- package/dist/chunks/chunk-PGLSFLYW.ts.map +0 -7
- package/dist/chunks/chunk-RRR66UWR.ts +0 -28
- package/dist/chunks/chunk-RRR66UWR.ts.map +0 -7
- package/dist/chunks/chunk-RSUXZD6S.ts +0 -275
- package/dist/chunks/chunk-RSUXZD6S.ts.map +0 -7
- package/dist/chunks/chunk-RTCVYIZA.ts +0 -221
- package/dist/chunks/chunk-RTCVYIZA.ts.map +0 -7
- package/dist/chunks/chunk-RY2AEGMZ.ts +0 -34
- package/dist/chunks/chunk-RY2AEGMZ.ts.map +0 -7
- package/dist/chunks/chunk-SWGQLFSD.ts +0 -60
- package/dist/chunks/chunk-SWGQLFSD.ts.map +0 -7
- package/dist/chunks/chunk-TM2R67J3.ts +0 -52
- package/dist/chunks/chunk-TM2R67J3.ts.map +0 -7
- package/dist/chunks/chunk-TMZRHIIK.ts +0 -497
- package/dist/chunks/chunk-TMZRHIIK.ts.map +0 -7
- package/dist/chunks/chunk-TZ34IQ3M.ts +0 -59
- package/dist/chunks/chunk-TZ34IQ3M.ts.map +0 -7
- package/dist/chunks/chunk-UMBPMVJW.ts +0 -22
- package/dist/chunks/chunk-UMBPMVJW.ts.map +0 -7
- package/dist/chunks/chunk-VDG7LTYE.ts +0 -80
- package/dist/chunks/chunk-VDG7LTYE.ts.map +0 -7
- package/dist/chunks/chunk-X4NMONPE.ts +0 -73
- package/dist/chunks/chunk-X4NMONPE.ts.map +0 -7
- package/dist/chunks/chunk-YPJEN6NU.ts +0 -65
- package/dist/chunks/chunk-YPJEN6NU.ts.map +0 -7
- package/dist/chunks/chunk-YU53SHA7.ts +0 -328
- package/dist/chunks/chunk-YU53SHA7.ts.map +0 -7
- package/dist/chunks/completion-delivery-RSJU6BXL.ts +0 -17
- package/dist/chunks/completion-delivery-RSJU6BXL.ts.map +0 -7
- package/dist/chunks/config-status-FKGDECZ3.ts +0 -35
- package/dist/chunks/config-status-FKGDECZ3.ts.map +0 -7
- package/dist/chunks/config-ui-ABHYNGQ7.ts +0 -1212
- package/dist/chunks/config-ui-ABHYNGQ7.ts.map +0 -7
- package/dist/chunks/consult-LJU3IQY5.ts +0 -519
- package/dist/chunks/consult-LJU3IQY5.ts.map +0 -7
- package/dist/chunks/context-QDKLVQXE.ts +0 -11
- package/dist/chunks/context-QDKLVQXE.ts.map +0 -7
- package/dist/chunks/create-stateful-transport-JWC2EFYL.ts +0 -130
- package/dist/chunks/create-stateful-transport-JWC2EFYL.ts.map +0 -7
- package/dist/chunks/cwd-policy-NB6XZ5GF.ts +0 -17
- package/dist/chunks/cwd-policy-NB6XZ5GF.ts.map +0 -7
- package/dist/chunks/delegation-contract-LA56I5DT.ts +0 -24
- package/dist/chunks/delegation-contract-LA56I5DT.ts.map +0 -7
- package/dist/chunks/discovery-MIFB2U4Y.ts +0 -12
- package/dist/chunks/discovery-MIFB2U4Y.ts.map +0 -7
- package/dist/chunks/execution-Q2JZLKJA.ts +0 -4037
- package/dist/chunks/execution-Q2JZLKJA.ts.map +0 -7
- package/dist/chunks/in-process-transport-HJ6TZXC3.ts +0 -39
- package/dist/chunks/in-process-transport-HJ6TZXC3.ts.map +0 -7
- package/dist/chunks/inspect-UH2TKH6E.ts +0 -634
- package/dist/chunks/inspect-UH2TKH6E.ts.map +0 -7
- package/dist/chunks/peer-communication-KL36Z6CO.ts +0 -294
- package/dist/chunks/peer-communication-KL36Z6CO.ts.map +0 -7
- package/dist/chunks/persistence-UY3PY6E5.ts +0 -348
- package/dist/chunks/persistence-UY3PY6E5.ts.map +0 -7
- package/dist/chunks/registry-BT54L6CY.ts +0 -1445
- package/dist/chunks/registry-BT54L6CY.ts.map +0 -7
- package/dist/chunks/retained-semantic-state-GM75FZGE.ts +0 -81
- package/dist/chunks/retained-semantic-state-GM75FZGE.ts.map +0 -7
- package/dist/chunks/rpc-transport-7R7DVCEB.ts +0 -1222
- package/dist/chunks/rpc-transport-7R7DVCEB.ts.map +0 -7
- package/dist/chunks/runtime-policy-5YELCOVC.ts +0 -15
- package/dist/chunks/runtime-policy-5YELCOVC.ts.map +0 -7
- package/dist/chunks/semantic-snapshot-WPJ3UAFW.ts +0 -19
- package/dist/chunks/semantic-snapshot-WPJ3UAFW.ts.map +0 -7
- package/dist/chunks/spawn-idempotency-BNHOSMZW.ts +0 -13
- package/dist/chunks/spawn-idempotency-BNHOSMZW.ts.map +0 -7
- package/dist/chunks/stateful-lifecycle-JAR6K55H.ts +0 -15
- package/dist/chunks/stateful-lifecycle-JAR6K55H.ts.map +0 -7
- package/dist/chunks/subprocess-transport-VHZJRBTW.ts +0 -160
- package/dist/chunks/subprocess-transport-VHZJRBTW.ts.map +0 -7
- package/dist/chunks/usage-recording-store-CW3EH2SZ.ts +0 -164
- package/dist/chunks/usage-recording-store-CW3EH2SZ.ts.map +0 -7
- package/dist/chunks/workspace-WDKLN72J.ts +0 -11
- package/dist/chunks/workspace-WDKLN72J.ts.map +0 -7
- package/docs/async-runtime-protocol.md +0 -84
- package/docs/implementation-notes/pi-subagents-capability-matrix.md +0 -62
- package/docs/implementation-notes/pi-subagents-current-direction.md +0 -99
- package/docs/implementation-notes/pi-subagents-rpc-v1.md +0 -139
- package/docs/pi-subagents-diagrams.md +0 -183
- package/src/adaptive-scheduler.ts +0 -224
- package/src/admission-policy.ts +0 -78
- package/src/agent-projection.ts +0 -53
- package/src/agents/built-ins.ts +0 -71
- package/src/agents/catalog.ts +0 -241
- package/src/agents/discovery.ts +0 -265
- package/src/agents/types.ts +0 -100
- package/src/agents.ts +0 -51
- package/src/auto-transport.ts +0 -121
- package/src/blocking-status.ts +0 -63
- package/src/cached-module-loader.ts +0 -18
- package/src/capabilities.ts +0 -145
- package/src/capability-grant.ts +0 -115
- package/src/capability-router.ts +0 -107
- package/src/child-peer-bridge.ts +0 -124
- package/src/child-peer-tools.ts +0 -132
- package/src/completion-delivery.ts +0 -409
- package/src/completion-render.ts +0 -190
- package/src/completion-requirement.ts +0 -479
- package/src/completion-routing.ts +0 -24
- package/src/config-registration.ts +0 -114
- package/src/config-status.ts +0 -217
- package/src/config-ui.ts +0 -983
- package/src/consult-policy.ts +0 -15
- package/src/consult-registration.ts +0 -125
- package/src/consult-render.ts +0 -194
- package/src/consult-resources.ts +0 -51
- package/src/consult-tool.ts +0 -95
- package/src/consult.ts +0 -566
- package/src/context.ts +0 -126
- package/src/create-stateful-transport.ts +0 -157
- package/src/cwd-policy.ts +0 -183
- package/src/delegation-contract.ts +0 -463
- package/src/execution/budget.ts +0 -56
- package/src/execution/runtime-policy.ts +0 -19
- package/src/execution-plan.ts +0 -322
- package/src/execution-ui.ts +0 -259
- package/src/execution.ts +0 -1679
- package/src/in-process-transport.ts +0 -941
- package/src/inspect-registration.ts +0 -64
- package/src/inspect-render.ts +0 -334
- package/src/inspect-tool.ts +0 -45
- package/src/inspect.ts +0 -769
- package/src/integration-controller.ts +0 -98
- package/src/limits.ts +0 -75
- package/src/orchestration-metrics.ts +0 -116
- package/src/outcome.ts +0 -61
- package/src/panel-child-group.ts +0 -35
- package/src/panel-contract.ts +0 -343
- package/src/panel-evidence.ts +0 -59
- package/src/panel-execution.ts +0 -769
- package/src/panel-failure.ts +0 -56
- package/src/panel-planning.ts +0 -175
- package/src/panel-presets.ts +0 -3
- package/src/panel-prompts.ts +0 -132
- package/src/panel-reconciliation.ts +0 -57
- package/src/panel-render.ts +0 -103
- package/src/parallel-limit-ui.ts +0 -113
- package/src/params.ts +0 -278
- package/src/peer-communication.ts +0 -352
- package/src/peer-transport.ts +0 -49
- package/src/persistence.ts +0 -522
- package/src/pi-args.ts +0 -43
- package/src/pi-invocation.ts +0 -168
- package/src/process-control.ts +0 -43
- package/src/prompt-resources.ts +0 -38
- package/src/prompt-source-safety.ts +0 -48
- package/src/protocol.ts +0 -76
- package/src/registry-types.ts +0 -204
- package/src/registry.ts +0 -1698
- package/src/render-common.ts +0 -252
- package/src/render.ts +0 -704
- package/src/result-contract.ts +0 -431
- package/src/retained-semantic-state.ts +0 -100
- package/src/rpc-timeout-finalization.ts +0 -207
- package/src/rpc-transport-metadata.ts +0 -65
- package/src/rpc-transport.ts +0 -1035
- package/src/rpc-turn-capture.ts +0 -217
- package/src/runner-outcome.ts +0 -31
- package/src/runner-result.ts +0 -55
- package/src/runner-types.ts +0 -102
- package/src/runner-usage.ts +0 -48
- package/src/runner.ts +0 -866
- package/src/safe-text.ts +0 -67
- package/src/semantic-snapshot.ts +0 -214
- package/src/session-guidance-contract.ts +0 -399
- package/src/settings/inspection.ts +0 -302
- package/src/settings/schema.ts +0 -195
- package/src/settings-reader.ts +0 -215
- package/src/settings.ts +0 -452
- package/src/spawn-idempotency.ts +0 -68
- package/src/stateful-agent-view.ts +0 -85
- package/src/stateful-config.ts +0 -13
- package/src/stateful-guidance.ts +0 -29
- package/src/stateful-lifecycle.ts +0 -74
- package/src/stateful-limit-ui.ts +0 -249
- package/src/stateful-limits.ts +0 -96
- package/src/stateful-prompt.ts +0 -49
- package/src/stateful-registration.ts +0 -1320
- package/src/stateful-render.ts +0 -318
- package/src/stateful-safety.ts +0 -47
- package/src/stateful-tool-params.ts +0 -215
- package/src/stateful.ts +0 -12
- package/src/subagent-details.ts +0 -43
- package/src/subagents-extension.ts +0 -372
- package/src/subprocess-transport.ts +0 -145
- package/src/supervision.ts +0 -104
- package/src/task-path.ts +0 -65
- package/src/timeout-checkpoint.ts +0 -305
- package/src/timeout-finalization.ts +0 -75
- package/src/tool-schema-compatibility.ts +0 -73
- package/src/transport-types.ts +0 -74
- package/src/transport-ui.ts +0 -135
- package/src/transport.ts +0 -43
- package/src/turn-budget.ts +0 -109
- package/src/usage-format.ts +0 -42
- package/src/usage-recording-config.ts +0 -13
- package/src/usage-recording-store.ts +0 -183
- package/src/usage-recording.ts +0 -478
- package/src/verification-harness.ts +0 -516
- package/src/verification-policy.ts +0 -67
- package/src/verification-receipt.ts +0 -275
- package/src/verified-execution-benchmark.ts +0 -86
- package/src/verified-execution-contract.ts +0 -191
- package/src/verified-execution-schema.ts +0 -32
- package/src/work-item-ledger.ts +0 -1404
- package/src/work-item-persistence.ts +0 -254
- package/src/workflow-completion-controller.ts +0 -397
- package/src/workflow-planning.ts +0 -172
- package/src/workflow-tree-identity.ts +0 -289
- package/src/workflow-ui.ts +0 -69
- package/src/workflow-verification.ts +0 -296
- package/src/workspace.ts +0 -174
package/src/consult.ts
DELETED
|
@@ -1,566 +0,0 @@
|
|
|
1
|
-
import * as path from "node:path";
|
|
2
|
-
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
3
|
-
import type { Usage } from "@earendil-works/pi-ai";
|
|
4
|
-
import { CONFIG_DIR_NAME, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
5
|
-
import { DEFAULT_AGENT_CATALOG_MAX_ITEMS } from "./agents/catalog.js";
|
|
6
|
-
import { type AgentDiscoveryResult, discoverAgents } from "./agents/discovery.js";
|
|
7
|
-
import {
|
|
8
|
-
type AgentConfig,
|
|
9
|
-
type AgentScope,
|
|
10
|
-
type ConsultResourcePolicy,
|
|
11
|
-
isThinkingLevel,
|
|
12
|
-
type SubagentSettings,
|
|
13
|
-
type THINKING_LEVELS,
|
|
14
|
-
} from "./agents/types.js";
|
|
15
|
-
import { resolveConsultTools } from "./consult-policy.js";
|
|
16
|
-
import { resolveConsultResourceLaunchPolicy } from "./consult-resources.js";
|
|
17
|
-
import type {
|
|
18
|
-
ConsultDetails,
|
|
19
|
-
ConsultProgress,
|
|
20
|
-
ConsultProgressActivity,
|
|
21
|
-
SubagentConsultParams,
|
|
22
|
-
} from "./consult-tool.js";
|
|
23
|
-
import {
|
|
24
|
-
assertConsultationTargetAllowed,
|
|
25
|
-
type ResolvedSubagentTarget,
|
|
26
|
-
resolveSubagentTarget,
|
|
27
|
-
} from "./cwd-policy.js";
|
|
28
|
-
import {
|
|
29
|
-
assertSubagentDepthAllowed,
|
|
30
|
-
resolveDefaultSubagentTimeoutMs,
|
|
31
|
-
} from "./execution/runtime-policy.js";
|
|
32
|
-
import {
|
|
33
|
-
DEFAULT_MAX_CONTEXT_BYTES,
|
|
34
|
-
DEFAULT_MAX_STDERR_BYTES,
|
|
35
|
-
MAX_SUBAGENT_TIMEOUT_MS,
|
|
36
|
-
} from "./limits.js";
|
|
37
|
-
import { getResultFinalOutput, isResultError, runSingleAgent } from "./runner.js";
|
|
38
|
-
import type { ChildLaunchPolicy, SingleResult } from "./runner-types.js";
|
|
39
|
-
import { boundedPrivateText, boundText, safeDisplayPath, safeTerminalLine } from "./safe-text.js";
|
|
40
|
-
import {
|
|
41
|
-
DEFAULT_CONSULT_RESOURCE_POLICY,
|
|
42
|
-
DEFAULT_CONSULTATION_CWD_POLICY,
|
|
43
|
-
} from "./settings/inspection.js";
|
|
44
|
-
import { resolveSubagentThinkingLevel } from "./settings.js";
|
|
45
|
-
import type { SubagentDetails } from "./subagent-details.js";
|
|
46
|
-
|
|
47
|
-
export { registerSubagentConsult } from "./consult-registration.js";
|
|
48
|
-
export type {
|
|
49
|
-
ConsultDetails,
|
|
50
|
-
ConsultProgress,
|
|
51
|
-
ConsultProgressActivity,
|
|
52
|
-
} from "./consult-tool.js";
|
|
53
|
-
export { SubagentConsultParams } from "./consult-tool.js";
|
|
54
|
-
|
|
55
|
-
export interface ConsultChildRequest {
|
|
56
|
-
agent: AgentConfig;
|
|
57
|
-
task: string;
|
|
58
|
-
cwd: string;
|
|
59
|
-
agentScope: AgentScope;
|
|
60
|
-
thinkingLevel?: (typeof THINKING_LEVELS)[number];
|
|
61
|
-
timeoutMs: number;
|
|
62
|
-
effectiveTools: string[];
|
|
63
|
-
resourcePolicy: ConsultResourcePolicy;
|
|
64
|
-
launchPolicy: ChildLaunchPolicy;
|
|
65
|
-
signal: AbortSignal;
|
|
66
|
-
onUpdate?: (result: SingleResult) => void;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export interface RegisterSubagentConsultOptions {
|
|
70
|
-
getSettings(): SubagentSettings | undefined;
|
|
71
|
-
runChild?: (request: ConsultChildRequest) => Promise<SingleResult>;
|
|
72
|
-
invocationOverride?: { command: string; argsPrefix?: string[] };
|
|
73
|
-
resolveResourceLaunchPolicy?: typeof resolveConsultResourceLaunchPolicy;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const READ_ONLY_INSTRUCTION = [
|
|
77
|
-
"This is a read-only consultation.",
|
|
78
|
-
"Use only the tools made available by the executor to inspect and reason about existing content.",
|
|
79
|
-
"Do not claim to edit files, run shell commands, mutate state, or persist a session.",
|
|
80
|
-
"If the task asks for implementation, return analysis or instructions instead of claiming changes.",
|
|
81
|
-
].join("\n");
|
|
82
|
-
|
|
83
|
-
const MAX_UNKNOWN_AGENT_NAME_BYTES = 128;
|
|
84
|
-
|
|
85
|
-
export async function executeSubagentConsult(
|
|
86
|
-
params: SubagentConsultParams,
|
|
87
|
-
signal: AbortSignal,
|
|
88
|
-
onUpdate: ((partial: AgentToolResult<ConsultDetails>) => void) | undefined,
|
|
89
|
-
ctx: ExtensionContext,
|
|
90
|
-
options: RegisterSubagentConsultOptions,
|
|
91
|
-
isCurrent: () => boolean = () => true,
|
|
92
|
-
): Promise<AgentToolResult<ConsultDetails>> {
|
|
93
|
-
const operation = validateConsultParams(params);
|
|
94
|
-
assertSubagentDepthAllowed();
|
|
95
|
-
if (signal.aborted) throw abortError("Subagent consultation was aborted before start");
|
|
96
|
-
return executeConsult(
|
|
97
|
-
operation,
|
|
98
|
-
ctx,
|
|
99
|
-
signal,
|
|
100
|
-
options,
|
|
101
|
-
(partial) => {
|
|
102
|
-
if (signal.aborted || !isCurrent()) return;
|
|
103
|
-
onUpdate?.(partial);
|
|
104
|
-
},
|
|
105
|
-
isCurrent,
|
|
106
|
-
() => undefined,
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function formatAvailableConsultAgents(discovery: AgentDiscoveryResult): string {
|
|
111
|
-
const listed = discovery.agents.slice(0, DEFAULT_AGENT_CATALOG_MAX_ITEMS);
|
|
112
|
-
const labels = listed.map(
|
|
113
|
-
(agent) => `${safeTerminalLine(agent.name, MAX_UNKNOWN_AGENT_NAME_BYTES)} (${agent.source})`,
|
|
114
|
-
);
|
|
115
|
-
const omitted =
|
|
116
|
-
discovery.agents.length - listed.length + (discovery.omittedAgentDefinitions ?? 0);
|
|
117
|
-
const parts = [labels.join(", ") || "none"];
|
|
118
|
-
if (omitted > 0) {
|
|
119
|
-
parts.push(`[${omitted} additional agent definition${omitted === 1 ? "" : "s"} omitted.]`);
|
|
120
|
-
}
|
|
121
|
-
if (discovery.metadataDiscoveryIncomplete) {
|
|
122
|
-
parts.push("[Agent metadata discovery was incomplete; some definitions may be unavailable.]");
|
|
123
|
-
}
|
|
124
|
-
return parts.join(" ");
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function validateConsultParams(
|
|
128
|
-
params: unknown,
|
|
129
|
-
): Required<Pick<SubagentConsultParams, "agent" | "task" | "agentScope" | "confirmProjectAgents">> &
|
|
130
|
-
Pick<SubagentConsultParams, "cwd" | "timeoutMs" | "thinkingLevel"> {
|
|
131
|
-
if (!params || typeof params !== "object" || Array.isArray(params)) {
|
|
132
|
-
throw new Error("subagent_consult parameters must be an object");
|
|
133
|
-
}
|
|
134
|
-
const values = params as Record<string, unknown>;
|
|
135
|
-
const allowed = [
|
|
136
|
-
"agent",
|
|
137
|
-
"task",
|
|
138
|
-
"agentScope",
|
|
139
|
-
"confirmProjectAgents",
|
|
140
|
-
"cwd",
|
|
141
|
-
"timeoutMs",
|
|
142
|
-
"thinkingLevel",
|
|
143
|
-
];
|
|
144
|
-
const unexpected = Object.keys(values).find(
|
|
145
|
-
(key) => values[key] !== undefined && !allowed.includes(key),
|
|
146
|
-
);
|
|
147
|
-
if (unexpected) throw new Error(`subagent_consult does not accept ${unexpected}`);
|
|
148
|
-
const agent = requiredString(values.agent, "agent");
|
|
149
|
-
const task = requiredString(values.task, "task");
|
|
150
|
-
if (task.includes("\0")) throw new Error("subagent_consult task must not contain NUL bytes");
|
|
151
|
-
if (Buffer.byteLength(task, "utf8") > DEFAULT_MAX_CONTEXT_BYTES) {
|
|
152
|
-
throw new Error(
|
|
153
|
-
`subagent_consult task must be at most ${DEFAULT_MAX_CONTEXT_BYTES} UTF-8 bytes`,
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
const agentScope = optionalScope(values.agentScope);
|
|
157
|
-
if (
|
|
158
|
-
values.confirmProjectAgents !== undefined &&
|
|
159
|
-
typeof values.confirmProjectAgents !== "boolean"
|
|
160
|
-
) {
|
|
161
|
-
throw new Error("subagent_consult confirmProjectAgents must be boolean");
|
|
162
|
-
}
|
|
163
|
-
if (values.cwd !== undefined && (typeof values.cwd !== "string" || !values.cwd.trim())) {
|
|
164
|
-
throw new Error("subagent_consult cwd must be a non-empty string");
|
|
165
|
-
}
|
|
166
|
-
if (
|
|
167
|
-
values.timeoutMs !== undefined &&
|
|
168
|
-
(typeof values.timeoutMs !== "number" ||
|
|
169
|
-
!Number.isFinite(values.timeoutMs) ||
|
|
170
|
-
values.timeoutMs < 1 ||
|
|
171
|
-
values.timeoutMs > MAX_SUBAGENT_TIMEOUT_MS)
|
|
172
|
-
) {
|
|
173
|
-
throw new Error(`subagent_consult timeoutMs must be between 1 and ${MAX_SUBAGENT_TIMEOUT_MS}`);
|
|
174
|
-
}
|
|
175
|
-
if (values.thinkingLevel !== undefined && !isThinkingLevel(values.thinkingLevel)) {
|
|
176
|
-
throw new Error("subagent_consult thinkingLevel is invalid");
|
|
177
|
-
}
|
|
178
|
-
return {
|
|
179
|
-
agent,
|
|
180
|
-
task,
|
|
181
|
-
agentScope,
|
|
182
|
-
confirmProjectAgents: values.confirmProjectAgents !== false,
|
|
183
|
-
cwd: values.cwd as string | undefined,
|
|
184
|
-
timeoutMs: values.timeoutMs as number | undefined,
|
|
185
|
-
thinkingLevel: values.thinkingLevel as (typeof THINKING_LEVELS)[number] | undefined,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
async function executeConsult(
|
|
190
|
-
operation: ReturnType<typeof validateConsultParams>,
|
|
191
|
-
ctx: ExtensionContext,
|
|
192
|
-
signal: AbortSignal,
|
|
193
|
-
options: RegisterSubagentConsultOptions,
|
|
194
|
-
emitUpdate: (partial: AgentToolResult<ConsultDetails>) => void,
|
|
195
|
-
isCurrent: () => boolean,
|
|
196
|
-
trackWork: (work: Promise<unknown>) => void,
|
|
197
|
-
): Promise<AgentToolResult<ConsultDetails>> {
|
|
198
|
-
if (
|
|
199
|
-
(operation.agentScope === "project" || operation.agentScope === "both") &&
|
|
200
|
-
!ctx.isProjectTrusted()
|
|
201
|
-
) {
|
|
202
|
-
throw new Error("Project-local subagent definitions require a trusted project");
|
|
203
|
-
}
|
|
204
|
-
const settings = options.getSettings();
|
|
205
|
-
const target = resolveSubagentTarget({
|
|
206
|
-
workspace: ctx.cwd,
|
|
207
|
-
requestedCwd: operation.cwd,
|
|
208
|
-
currentProjectTrusted: ctx.isProjectTrusted(),
|
|
209
|
-
});
|
|
210
|
-
assertConsultationTargetAllowed(
|
|
211
|
-
target,
|
|
212
|
-
settings?.cwdPolicy?.consultation ?? DEFAULT_CONSULTATION_CWD_POLICY,
|
|
213
|
-
);
|
|
214
|
-
const discovery = discoverAgents(ctx.cwd, operation.agentScope, settings);
|
|
215
|
-
const agent = discovery.agents.find((candidate) => candidate.name === operation.agent);
|
|
216
|
-
if (!agent) {
|
|
217
|
-
throw new Error(
|
|
218
|
-
`Unknown subagent definition: ${boundedPrivateText(operation.agent, 256)}. ` +
|
|
219
|
-
`Available agents for agentScope "${operation.agentScope}": ${formatAvailableConsultAgents(discovery)}`,
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
const setupWork = resolveConsultSetup(operation, agent, settings, target, options);
|
|
223
|
-
trackWork(setupWork);
|
|
224
|
-
const setup = await setupWork;
|
|
225
|
-
assertCurrentRequest(signal, isCurrent);
|
|
226
|
-
|
|
227
|
-
if (agent.source === "project" && operation.confirmProjectAgents) {
|
|
228
|
-
if (!ctx.hasUI) {
|
|
229
|
-
throw new Error(
|
|
230
|
-
"Project-local subagent confirmation requires UI; pass confirmProjectAgents: false explicitly in a trusted project",
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
const approved = await ctx.ui.confirm(
|
|
234
|
-
"Run project-local read-only agent?",
|
|
235
|
-
`Agent: ${safeTerminalLine(agent.name, 256)}\nSource: ${safeTerminalLine(path.posix.join(CONFIG_DIR_NAME, "agents", path.basename(agent.filePath)))}`,
|
|
236
|
-
);
|
|
237
|
-
assertCurrentRequest(signal, isCurrent);
|
|
238
|
-
if (!approved) {
|
|
239
|
-
return {
|
|
240
|
-
content: [{ type: "text", text: "Read-only subagent consultation cancelled." }],
|
|
241
|
-
details: { ...setup.details, cancelled: true },
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
assertCurrentRequest(signal, isCurrent);
|
|
246
|
-
emitUpdate(consultStartingUpdate(setup.details));
|
|
247
|
-
assertCurrentRequest(signal, isCurrent);
|
|
248
|
-
const runChild = options.runChild ?? ((request) => runConsultChild(request, options));
|
|
249
|
-
const child = runChild({
|
|
250
|
-
agent: setup.agent,
|
|
251
|
-
task: operation.task,
|
|
252
|
-
cwd: setup.cwd,
|
|
253
|
-
agentScope: operation.agentScope,
|
|
254
|
-
thinkingLevel: setup.thinkingLevel,
|
|
255
|
-
timeoutMs: setup.timeoutMs,
|
|
256
|
-
effectiveTools: setup.effectiveTools,
|
|
257
|
-
resourcePolicy: setup.resourcePolicy,
|
|
258
|
-
launchPolicy: setup.launchPolicy,
|
|
259
|
-
signal,
|
|
260
|
-
onUpdate: (result) => {
|
|
261
|
-
if (signal.aborted || !isCurrent()) return;
|
|
262
|
-
emitUpdate(consultUpdate(result, setup.details));
|
|
263
|
-
},
|
|
264
|
-
});
|
|
265
|
-
trackWork(child);
|
|
266
|
-
const result = await child;
|
|
267
|
-
if (!isCurrent()) throw abortError("Subagent consultation owner was replaced");
|
|
268
|
-
if (result.aborted && !result.processStarted) {
|
|
269
|
-
throw abortError(result.errorMessage || "Subagent consultation was aborted before launch");
|
|
270
|
-
}
|
|
271
|
-
if (result.launchFailed) {
|
|
272
|
-
throw new Error(
|
|
273
|
-
boundedPrivateText(
|
|
274
|
-
result.errorMessage || result.stderr.trim() || "Subagent consultation failed to launch",
|
|
275
|
-
2 * 1024,
|
|
276
|
-
),
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
const error = isResultError(result);
|
|
280
|
-
const output = error
|
|
281
|
-
? formatConsultFailure(result)
|
|
282
|
-
: getResultFinalOutput(result) || "(no output)";
|
|
283
|
-
const bounded = boundText(output);
|
|
284
|
-
const details: ConsultDetails = {
|
|
285
|
-
...setup.details,
|
|
286
|
-
child: projectChildResult(result),
|
|
287
|
-
...(error ? { isError: true } : {}),
|
|
288
|
-
...(bounded.truncated ? { truncated: true } : {}),
|
|
289
|
-
};
|
|
290
|
-
return {
|
|
291
|
-
content: [{ type: "text", text: bounded.text }],
|
|
292
|
-
details,
|
|
293
|
-
usage: usageFromResult(result),
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
async function resolveConsultSetup(
|
|
298
|
-
operation: ReturnType<typeof validateConsultParams>,
|
|
299
|
-
agent: AgentConfig,
|
|
300
|
-
settings: SubagentSettings | undefined,
|
|
301
|
-
target: ResolvedSubagentTarget,
|
|
302
|
-
options: RegisterSubagentConsultOptions,
|
|
303
|
-
) {
|
|
304
|
-
const requestedResourcePolicy = settings?.consult?.resources ?? DEFAULT_CONSULT_RESOURCE_POLICY;
|
|
305
|
-
const resourcePolicy = target.trust.projectTrusted ? requestedResourcePolicy : "none";
|
|
306
|
-
const effectiveTools = resolveConsultTools(agent.tools);
|
|
307
|
-
const projectTrusted = target.trust.projectTrusted;
|
|
308
|
-
const thinkingLevel = resolveSubagentThinkingLevel([agent], agent.name, operation.thinkingLevel);
|
|
309
|
-
const timeoutMs = operation.timeoutMs ?? agent.timeoutMs ?? resolveDefaultSubagentTimeoutMs();
|
|
310
|
-
if (!Number.isFinite(timeoutMs) || timeoutMs < 1 || timeoutMs > MAX_SUBAGENT_TIMEOUT_MS) {
|
|
311
|
-
throw new Error(
|
|
312
|
-
`Subagent consultation timeout must be between 1 and ${MAX_SUBAGENT_TIMEOUT_MS}ms`,
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
|
-
const resolveResources =
|
|
316
|
-
options.resolveResourceLaunchPolicy ?? resolveConsultResourceLaunchPolicy;
|
|
317
|
-
const launchPolicy = await resolveResources(resourcePolicy, projectTrusted, target.cwd);
|
|
318
|
-
launchPolicy.tools = effectiveTools;
|
|
319
|
-
const childAgent: AgentConfig = {
|
|
320
|
-
...agent,
|
|
321
|
-
tools: effectiveTools,
|
|
322
|
-
systemPrompt: [agent.systemPrompt, READ_ONLY_INSTRUCTION].filter(Boolean).join("\n\n"),
|
|
323
|
-
};
|
|
324
|
-
const effectiveResources = {
|
|
325
|
-
policy: resourcePolicy,
|
|
326
|
-
projectResources: projectTrusted && resourcePolicy !== "none",
|
|
327
|
-
contextFiles: !launchPolicy.disableContextFiles,
|
|
328
|
-
skills: !launchPolicy.disableSkills,
|
|
329
|
-
promptTemplates: !launchPolicy.disablePromptTemplates,
|
|
330
|
-
};
|
|
331
|
-
const details: ConsultDetails = {
|
|
332
|
-
agent: boundedPrivateText(agent.name, 256),
|
|
333
|
-
agentSource: agent.source,
|
|
334
|
-
agentScope: operation.agentScope,
|
|
335
|
-
cwd: safeDisplayPath(target.cwd, target.workspace),
|
|
336
|
-
model: agent.model ? boundedPrivateText(agent.model, 256) : undefined,
|
|
337
|
-
thinkingLevel,
|
|
338
|
-
timeoutMs,
|
|
339
|
-
policy: {
|
|
340
|
-
requestedTools:
|
|
341
|
-
agent.tools === undefined
|
|
342
|
-
? null
|
|
343
|
-
: agent.tools.slice(0, 100).map((tool) => boundedPrivateText(tool, 256)),
|
|
344
|
-
effectiveTools,
|
|
345
|
-
cwdBoundary: target.boundary,
|
|
346
|
-
targetTrust: {
|
|
347
|
-
kind: target.trust.kind,
|
|
348
|
-
projectTrusted,
|
|
349
|
-
sourcePath: target.trust.sourcePath
|
|
350
|
-
? safeDisplayPath(target.trust.sourcePath, target.workspace)
|
|
351
|
-
: undefined,
|
|
352
|
-
warning: target.trust.warning,
|
|
353
|
-
},
|
|
354
|
-
requestedResources: requestedResourcePolicy,
|
|
355
|
-
effectiveResources,
|
|
356
|
-
...(resourcePolicy !== requestedResourcePolicy
|
|
357
|
-
? { resourceDowngradeReason: `Target trust is ${target.trust.kind}` }
|
|
358
|
-
: {}),
|
|
359
|
-
extensions: "disabled",
|
|
360
|
-
sessionPersistence: "disabled",
|
|
361
|
-
retainedAgent: false,
|
|
362
|
-
},
|
|
363
|
-
};
|
|
364
|
-
return {
|
|
365
|
-
agent: childAgent,
|
|
366
|
-
cwd: target.cwd,
|
|
367
|
-
resourcePolicy,
|
|
368
|
-
effectiveTools,
|
|
369
|
-
thinkingLevel,
|
|
370
|
-
timeoutMs,
|
|
371
|
-
launchPolicy,
|
|
372
|
-
details,
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
async function runConsultChild(
|
|
377
|
-
request: ConsultChildRequest,
|
|
378
|
-
options: RegisterSubagentConsultOptions,
|
|
379
|
-
): Promise<SingleResult> {
|
|
380
|
-
return runSingleAgent(
|
|
381
|
-
request.cwd,
|
|
382
|
-
[request.agent],
|
|
383
|
-
request.agent.name,
|
|
384
|
-
request.task,
|
|
385
|
-
request.cwd,
|
|
386
|
-
undefined,
|
|
387
|
-
request.signal,
|
|
388
|
-
request.thinkingLevel,
|
|
389
|
-
request.timeoutMs,
|
|
390
|
-
(partial) => {
|
|
391
|
-
const result = partial.details.results[0];
|
|
392
|
-
if (result) request.onUpdate?.(result);
|
|
393
|
-
},
|
|
394
|
-
(results): SubagentDetails => ({
|
|
395
|
-
mode: "single",
|
|
396
|
-
agentScope: request.agentScope,
|
|
397
|
-
projectAgentsDir: null,
|
|
398
|
-
results,
|
|
399
|
-
}),
|
|
400
|
-
options.invocationOverride,
|
|
401
|
-
request.launchPolicy,
|
|
402
|
-
);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
function consultStartingUpdate(details: ConsultDetails): AgentToolResult<ConsultDetails> {
|
|
406
|
-
return {
|
|
407
|
-
content: [{ type: "text", text: "Read-only subagent consultation starting." }],
|
|
408
|
-
details: {
|
|
409
|
-
...details,
|
|
410
|
-
progress: {
|
|
411
|
-
phase: "starting",
|
|
412
|
-
recentActivity: [],
|
|
413
|
-
recentActivityTotal: 0,
|
|
414
|
-
usage: emptyProgressUsage(),
|
|
415
|
-
},
|
|
416
|
-
},
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
function consultUpdate(
|
|
421
|
-
result: SingleResult,
|
|
422
|
-
details: ConsultDetails,
|
|
423
|
-
): AgentToolResult<ConsultDetails> {
|
|
424
|
-
const output = boundText(getResultFinalOutput(result) || "(running...)");
|
|
425
|
-
return {
|
|
426
|
-
content: [{ type: "text", text: output.text }],
|
|
427
|
-
details: {
|
|
428
|
-
...details,
|
|
429
|
-
child: projectChildResult(result),
|
|
430
|
-
progress: projectConsultProgress(result),
|
|
431
|
-
},
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
const CONSULT_ACTIVITY_ARGUMENTS: Record<"read" | "grep" | "find" | "ls", readonly string[]> = {
|
|
436
|
-
read: ["path", "file_path", "offset", "limit"],
|
|
437
|
-
grep: ["pattern", "path", "glob", "limit"],
|
|
438
|
-
find: ["pattern", "path", "limit"],
|
|
439
|
-
ls: ["path", "limit"],
|
|
440
|
-
};
|
|
441
|
-
|
|
442
|
-
function projectConsultProgress(result: SingleResult): ConsultProgress {
|
|
443
|
-
const recentActivity: ConsultProgressActivity[] = [];
|
|
444
|
-
for (const item of result.recentActivity ?? []) {
|
|
445
|
-
if (item.type === "text") {
|
|
446
|
-
const text = boundedPrivateText(item.text, 1024).trim();
|
|
447
|
-
if (text) recentActivity.push({ type: "text", text });
|
|
448
|
-
continue;
|
|
449
|
-
}
|
|
450
|
-
if (!Object.hasOwn(CONSULT_ACTIVITY_ARGUMENTS, item.name)) continue;
|
|
451
|
-
const name = item.name as keyof typeof CONSULT_ACTIVITY_ARGUMENTS;
|
|
452
|
-
const args: Record<string, string | number | boolean> = {};
|
|
453
|
-
for (const key of CONSULT_ACTIVITY_ARGUMENTS[name]) {
|
|
454
|
-
const value = item.args[key];
|
|
455
|
-
if (typeof value === "string") args[key] = safeTerminalLine(value, 512);
|
|
456
|
-
else if (typeof value === "number" && Number.isFinite(value)) args[key] = value;
|
|
457
|
-
else if (typeof value === "boolean") args[key] = value;
|
|
458
|
-
}
|
|
459
|
-
recentActivity.push({ type: "toolCall", name, args });
|
|
460
|
-
}
|
|
461
|
-
return {
|
|
462
|
-
phase: "running",
|
|
463
|
-
recentActivity,
|
|
464
|
-
recentActivityTotal: Math.max(recentActivity.length, result.recentActivityTotal ?? 0),
|
|
465
|
-
actualProvider: result.actualProvider
|
|
466
|
-
? boundedPrivateText(result.actualProvider, 256)
|
|
467
|
-
: undefined,
|
|
468
|
-
actualModel: result.actualModel ? boundedPrivateText(result.actualModel, 256) : undefined,
|
|
469
|
-
usage: {
|
|
470
|
-
input: result.usage.input,
|
|
471
|
-
output: result.usage.output,
|
|
472
|
-
cacheRead: result.usage.cacheRead,
|
|
473
|
-
cacheWrite: result.usage.cacheWrite,
|
|
474
|
-
cost: result.usage.cost,
|
|
475
|
-
contextTokens: result.usage.contextTokens,
|
|
476
|
-
turns: result.usage.turns,
|
|
477
|
-
},
|
|
478
|
-
};
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
function emptyProgressUsage(): ConsultProgress["usage"] {
|
|
482
|
-
return {
|
|
483
|
-
input: 0,
|
|
484
|
-
output: 0,
|
|
485
|
-
cacheRead: 0,
|
|
486
|
-
cacheWrite: 0,
|
|
487
|
-
cost: 0,
|
|
488
|
-
contextTokens: 0,
|
|
489
|
-
turns: 0,
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
function projectChildResult(result: SingleResult): Record<string, unknown> {
|
|
494
|
-
return {
|
|
495
|
-
exitCode: result.exitCode,
|
|
496
|
-
stopReason:
|
|
497
|
-
typeof result.stopReason === "string"
|
|
498
|
-
? boundedPrivateText(result.stopReason, 256)
|
|
499
|
-
: undefined,
|
|
500
|
-
timedOut: result.timedOut,
|
|
501
|
-
aborted: result.aborted,
|
|
502
|
-
truncated: result.truncated,
|
|
503
|
-
malformedEvents: result.malformedEvents,
|
|
504
|
-
processStarted: result.processStarted,
|
|
505
|
-
actualProvider: result.actualProvider
|
|
506
|
-
? boundedPrivateText(result.actualProvider, 256)
|
|
507
|
-
: undefined,
|
|
508
|
-
actualModel: result.actualModel ? boundedPrivateText(result.actualModel, 256) : undefined,
|
|
509
|
-
partialOutput: result.finalOutput
|
|
510
|
-
? boundedPrivateText(result.finalOutput, 8 * 1024)
|
|
511
|
-
: undefined,
|
|
512
|
-
error: result.errorMessage ? boundedPrivateText(result.errorMessage, 2 * 1024) : undefined,
|
|
513
|
-
usage: { ...result.usage },
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
function formatConsultFailure(result: SingleResult): string {
|
|
518
|
-
const rawError = result.errorMessage || result.stderr.trim();
|
|
519
|
-
const error = rawError ? boundedPrivateText(rawError, DEFAULT_MAX_STDERR_BYTES) : "";
|
|
520
|
-
const output = getResultFinalOutput(result);
|
|
521
|
-
return error && output
|
|
522
|
-
? `${error}\n\nPartial output:\n${output}`
|
|
523
|
-
: error || output || "(no output)";
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
function usageFromResult(result: SingleResult): Usage {
|
|
527
|
-
return {
|
|
528
|
-
input: result.usage.input,
|
|
529
|
-
output: result.usage.output,
|
|
530
|
-
cacheRead: result.usage.cacheRead,
|
|
531
|
-
cacheWrite: result.usage.cacheWrite,
|
|
532
|
-
totalTokens:
|
|
533
|
-
result.usage.totalTokens ??
|
|
534
|
-
result.usage.input + result.usage.output + result.usage.cacheRead + result.usage.cacheWrite,
|
|
535
|
-
cost: {
|
|
536
|
-
input: result.usage.costInput ?? 0,
|
|
537
|
-
output: result.usage.costOutput ?? 0,
|
|
538
|
-
cacheRead: result.usage.costCacheRead ?? 0,
|
|
539
|
-
cacheWrite: result.usage.costCacheWrite ?? 0,
|
|
540
|
-
total: result.usage.cost,
|
|
541
|
-
},
|
|
542
|
-
};
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
function assertCurrentRequest(signal: AbortSignal, isCurrent: () => boolean): void {
|
|
546
|
-
if (signal.aborted || !isCurrent()) throw abortError("Subagent consultation owner was replaced");
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
function abortError(message: string): Error {
|
|
550
|
-
const error = new Error(message);
|
|
551
|
-
error.name = "AbortError";
|
|
552
|
-
return error;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
function requiredString(value: unknown, name: string): string {
|
|
556
|
-
if (typeof value !== "string" || !value.trim()) {
|
|
557
|
-
throw new Error(`subagent_consult requires ${name}`);
|
|
558
|
-
}
|
|
559
|
-
return value;
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
function optionalScope(value: unknown): AgentScope {
|
|
563
|
-
if (value === undefined) return "user";
|
|
564
|
-
if (value === "user" || value === "project" || value === "both") return value;
|
|
565
|
-
throw new Error("subagent_consult agentScope must be user, project, or both");
|
|
566
|
-
}
|
package/src/context.ts
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { DEFAULT_MAX_CONTEXT_BYTES, truncateUtf8, truncateUtf8Tail } from "./limits.js";
|
|
3
|
-
|
|
4
|
-
export type ContextMode = "none" | "all" | "summary" | number;
|
|
5
|
-
|
|
6
|
-
export interface ContextSnapshot {
|
|
7
|
-
text: string;
|
|
8
|
-
turns: number;
|
|
9
|
-
truncated: boolean;
|
|
10
|
-
sourceIds: string[];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function textParts(content: unknown): string {
|
|
14
|
-
if (typeof content === "string") return content;
|
|
15
|
-
if (!Array.isArray(content)) return "";
|
|
16
|
-
return content
|
|
17
|
-
.filter((part): part is { type: "text"; text: string } =>
|
|
18
|
-
Boolean(
|
|
19
|
-
part &&
|
|
20
|
-
typeof part === "object" &&
|
|
21
|
-
(part as { type?: unknown }).type === "text" &&
|
|
22
|
-
typeof (part as { text?: unknown }).text === "string",
|
|
23
|
-
),
|
|
24
|
-
)
|
|
25
|
-
.map((part) => part.text)
|
|
26
|
-
.join("\n");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function redactPrivateText(text: string): string {
|
|
30
|
-
const tagPattern = /<\/?private>/gi;
|
|
31
|
-
let redacted = "";
|
|
32
|
-
let cursor = 0;
|
|
33
|
-
let depth = 0;
|
|
34
|
-
for (const match of text.matchAll(tagPattern)) {
|
|
35
|
-
const tag = match[0].toLowerCase();
|
|
36
|
-
const index = match.index ?? cursor;
|
|
37
|
-
if (depth === 0) redacted += text.slice(cursor, index);
|
|
38
|
-
if (tag === "<private>") {
|
|
39
|
-
if (depth === 0) redacted += "[private content omitted]";
|
|
40
|
-
depth++;
|
|
41
|
-
} else if (depth > 0) {
|
|
42
|
-
depth--;
|
|
43
|
-
} else {
|
|
44
|
-
redacted += match[0];
|
|
45
|
-
}
|
|
46
|
-
cursor = index + match[0].length;
|
|
47
|
-
}
|
|
48
|
-
if (depth === 0) redacted += text.slice(cursor);
|
|
49
|
-
return redacted
|
|
50
|
-
.split("\n")
|
|
51
|
-
.filter((line) => !line.includes("[subagent-private]"))
|
|
52
|
-
.join("\n");
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function buildContextSnapshot(
|
|
56
|
-
entries: readonly unknown[],
|
|
57
|
-
mode: ContextMode,
|
|
58
|
-
maxBytes = DEFAULT_MAX_CONTEXT_BYTES,
|
|
59
|
-
selectedSourceIds?: readonly string[],
|
|
60
|
-
): ContextSnapshot {
|
|
61
|
-
if (mode === "none") return { text: "", turns: 0, truncated: false, sourceIds: [] };
|
|
62
|
-
const messages: Array<{ role: "user" | "assistant"; text: string; sourceId: string }> = [];
|
|
63
|
-
for (const entry of entries) {
|
|
64
|
-
if (!entry || typeof entry !== "object") continue;
|
|
65
|
-
const candidate = entry as {
|
|
66
|
-
id?: string;
|
|
67
|
-
type?: string;
|
|
68
|
-
role?: string;
|
|
69
|
-
content?: unknown;
|
|
70
|
-
message?: { role?: string; content?: unknown };
|
|
71
|
-
};
|
|
72
|
-
const message: { role?: string; content?: unknown } | undefined =
|
|
73
|
-
candidate.type === "message" ? candidate.message : candidate;
|
|
74
|
-
if (message?.role !== "user" && message?.role !== "assistant") continue;
|
|
75
|
-
const text = redactPrivateText(textParts(message.content));
|
|
76
|
-
if (text.trim()) {
|
|
77
|
-
const sourceId =
|
|
78
|
-
candidate.id ??
|
|
79
|
-
createHash("sha256").update(`${message.role}\0${text}`).digest("hex").slice(0, 16);
|
|
80
|
-
messages.push({ role: message.role, text, sourceId });
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
const seenSourceIds = new Set<string>();
|
|
84
|
-
const uniqueMessages = messages.filter((message) => {
|
|
85
|
-
if (seenSourceIds.has(message.sourceId)) return false;
|
|
86
|
-
seenSourceIds.add(message.sourceId);
|
|
87
|
-
return true;
|
|
88
|
-
});
|
|
89
|
-
const selectedSet = selectedSourceIds ? new Set(selectedSourceIds) : undefined;
|
|
90
|
-
const eligible = selectedSet
|
|
91
|
-
? uniqueMessages.filter((message) => selectedSet.has(message.sourceId))
|
|
92
|
-
: uniqueMessages;
|
|
93
|
-
const turnLimit =
|
|
94
|
-
typeof mode === "number" ? Math.max(1, Math.floor(mode)) : Number.POSITIVE_INFINITY;
|
|
95
|
-
let userTurns = 0;
|
|
96
|
-
let start = eligible.length;
|
|
97
|
-
for (let index = eligible.length - 1; index >= 0; index--) {
|
|
98
|
-
if (eligible[index].role === "user") userTurns++;
|
|
99
|
-
if (userTurns > turnLimit) break;
|
|
100
|
-
start = index;
|
|
101
|
-
}
|
|
102
|
-
const selected = eligible.slice(start);
|
|
103
|
-
const raw =
|
|
104
|
-
mode === "summary" && selected.length > 4
|
|
105
|
-
? [
|
|
106
|
-
`## Earlier context checkpoint\n${
|
|
107
|
-
truncateUtf8(
|
|
108
|
-
selected
|
|
109
|
-
.slice(0, -4)
|
|
110
|
-
.map((message) => `${message.role}: ${message.text}`)
|
|
111
|
-
.join("\n"),
|
|
112
|
-
Math.floor(maxBytes / 3),
|
|
113
|
-
).text
|
|
114
|
-
}`,
|
|
115
|
-
...selected.slice(-4).map((message) => `## ${message.role}\n${message.text}`),
|
|
116
|
-
].join("\n\n")
|
|
117
|
-
: selected.map((message) => `## ${message.role}\n${message.text}`).join("\n\n");
|
|
118
|
-
const bounded =
|
|
119
|
-
mode === "summary" ? truncateUtf8Tail(raw, maxBytes) : truncateUtf8(raw, maxBytes);
|
|
120
|
-
return {
|
|
121
|
-
text: bounded.text,
|
|
122
|
-
turns: selected.filter((message) => message.role === "user").length,
|
|
123
|
-
truncated: bounded.truncated,
|
|
124
|
-
sourceIds: selected.map((message) => message.sourceId),
|
|
125
|
-
};
|
|
126
|
-
}
|