@narumitw/pi-subagents 0.49.2 → 0.51.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.
Files changed (81) hide show
  1. package/README.md +313 -53
  2. package/package.json +11 -8
  3. package/src/adaptive-scheduler.ts +196 -0
  4. package/src/admission-benchmark.ts +95 -0
  5. package/src/admission-policy.ts +78 -0
  6. package/src/agent-projection.ts +53 -0
  7. package/src/agents.ts +58 -1
  8. package/src/auto-transport.ts +114 -0
  9. package/src/blocking-status.ts +63 -0
  10. package/src/capabilities.ts +145 -0
  11. package/src/capability-grant.ts +115 -0
  12. package/src/capability-router.ts +107 -0
  13. package/src/completion-delivery.ts +257 -0
  14. package/src/config-status.ts +221 -0
  15. package/src/config-ui.ts +215 -236
  16. package/src/consult-resources.ts +4 -27
  17. package/src/consult.ts +9 -1
  18. package/src/create-stateful-transport.ts +55 -0
  19. package/src/delegation-contract.ts +417 -0
  20. package/src/execution-plan.ts +322 -0
  21. package/src/execution-profiles.ts +95 -0
  22. package/src/execution-ui.ts +320 -0
  23. package/src/execution.ts +848 -158
  24. package/src/in-process-transport.ts +269 -25
  25. package/src/inspect-render.ts +101 -1
  26. package/src/inspect.ts +296 -3
  27. package/src/integration-controller.ts +98 -0
  28. package/src/limits.ts +3 -0
  29. package/src/orchestration-metrics.ts +78 -0
  30. package/src/outcome.ts +61 -0
  31. package/src/panel-child-group.ts +35 -0
  32. package/src/panel-contract.ts +343 -0
  33. package/src/panel-evidence.ts +59 -0
  34. package/src/panel-execution.ts +772 -0
  35. package/src/panel-failure.ts +56 -0
  36. package/src/panel-planning.ts +175 -0
  37. package/src/panel-prompts.ts +132 -0
  38. package/src/panel-reconciliation.ts +57 -0
  39. package/src/panel-render.ts +103 -0
  40. package/src/parallel-limit-ui.ts +112 -0
  41. package/src/params.ts +172 -3
  42. package/src/persistence.ts +182 -32
  43. package/src/prompt-resources.ts +38 -0
  44. package/src/registry-types.ts +175 -0
  45. package/src/registry.ts +466 -143
  46. package/src/render.ts +72 -6
  47. package/src/result-contract.ts +416 -0
  48. package/src/retained-semantic-state.ts +100 -0
  49. package/src/rpc-timeout-finalization.ts +207 -0
  50. package/src/rpc-transport-metadata.ts +65 -0
  51. package/src/rpc-transport.ts +990 -0
  52. package/src/rpc-turn-capture.ts +142 -0
  53. package/src/runner-result.ts +55 -0
  54. package/src/runner-usage.ts +48 -0
  55. package/src/runner.ts +325 -73
  56. package/src/semantic-snapshot.ts +214 -0
  57. package/src/settings.ts +254 -35
  58. package/src/spawn-idempotency.ts +61 -0
  59. package/src/stateful-config.ts +13 -0
  60. package/src/stateful-guidance.ts +1 -0
  61. package/src/stateful-lifecycle.ts +45 -2
  62. package/src/stateful-limit-ui.ts +246 -0
  63. package/src/stateful-limits.ts +96 -0
  64. package/src/stateful-prompt.ts +11 -2
  65. package/src/stateful-render.ts +48 -3
  66. package/src/stateful.ts +467 -357
  67. package/src/subagents.ts +114 -46
  68. package/src/subprocess-transport.ts +64 -5
  69. package/src/supervision.ts +103 -0
  70. package/src/timeout-checkpoint.ts +305 -0
  71. package/src/timeout-finalization.ts +75 -0
  72. package/src/transport-types.ts +68 -0
  73. package/src/transport-ui.ts +169 -0
  74. package/src/transport.ts +16 -4
  75. package/src/turn-budget.ts +109 -0
  76. package/src/verification-policy.ts +17 -0
  77. package/src/work-item-ledger.ts +682 -0
  78. package/src/work-item-persistence.ts +218 -0
  79. package/src/workflow-planning.ts +150 -0
  80. package/src/workflow-ui.ts +61 -0
  81. package/src/workspace.ts +69 -12
@@ -0,0 +1,207 @@
1
+ import { DEFAULT_MAX_OUTPUT_BYTES, truncateUtf8 } from "./limits.js";
2
+ import type { SubagentResultFormat } from "./result-contract.js";
3
+ import type { TimeoutCheckpoint, TurnTerminationReason } from "./timeout-checkpoint.js";
4
+ import {
5
+ buildTimeoutFinalizationPrompt,
6
+ resolveTimeoutFinalizationMs,
7
+ } from "./timeout-finalization.js";
8
+
9
+ interface RpcTimeoutFinalizationClient {
10
+ prompt(message: string, timeoutMs?: number): Promise<void>;
11
+ abort(): Promise<void>;
12
+ onEvent(listener: (event: unknown) => void): () => void;
13
+ onClose(listener: (error: Error) => void): () => void;
14
+ }
15
+
16
+ interface RpcSummaryCapture {
17
+ output: string;
18
+ partial: string;
19
+ stopReason?: string;
20
+ error?: string;
21
+ }
22
+
23
+ export interface RpcTimeoutFinalizationOptions {
24
+ client: RpcTimeoutFinalizationClient;
25
+ task: string;
26
+ partialOutput: string;
27
+ checkpoint?: TimeoutCheckpoint;
28
+ terminationReason?: TurnTerminationReason;
29
+ resultFormat?: SubagentResultFormat;
30
+ signal: AbortSignal;
31
+ workTimeoutMs: number;
32
+ finalizationTimeoutMs?: number;
33
+ abortGraceMs: number;
34
+ resetCapture(): void;
35
+ getCapture(): RpcSummaryCapture;
36
+ release(): Promise<void>;
37
+ }
38
+
39
+ export interface RpcTimeoutFinalizationResult {
40
+ output: string;
41
+ truncated: boolean;
42
+ status: "completed" | "failed" | "timed_out";
43
+ error?: string;
44
+ }
45
+
46
+ export async function finalizeTimedOutRpcTurn(
47
+ options: RpcTimeoutFinalizationOptions,
48
+ ): Promise<RpcTimeoutFinalizationResult> {
49
+ options.resetCapture();
50
+ let settleResolve!: () => void;
51
+ let settleReject!: (error: Error) => void;
52
+ const settled = new Promise<void>((resolve, reject) => {
53
+ settleResolve = resolve;
54
+ settleReject = reject;
55
+ });
56
+ void settled.catch(() => undefined);
57
+ const unsubscribeEvent = options.client.onEvent((event) => {
58
+ if (eventType(event) === "agent_settled") settleResolve();
59
+ });
60
+ const unsubscribeClose = options.client.onClose(settleReject);
61
+ let error: string | undefined;
62
+ try {
63
+ const finalizationMs = resolveTimeoutFinalizationMs(
64
+ options.workTimeoutMs,
65
+ options.finalizationTimeoutMs,
66
+ );
67
+ const deadline = Date.now() + finalizationMs;
68
+ await raceWithAbort(
69
+ () =>
70
+ options.client.prompt(
71
+ buildTimeoutFinalizationPrompt({
72
+ task: options.task,
73
+ partialOutput: options.partialOutput,
74
+ checkpoint: options.checkpoint,
75
+ terminationReason: options.terminationReason,
76
+ resultFormat: options.resultFormat,
77
+ }),
78
+ remainingMs(deadline),
79
+ ),
80
+ options.signal,
81
+ remainingMs(deadline),
82
+ );
83
+ const settlement = await waitForSettlement(settled, options.signal, remainingMs(deadline));
84
+ if (settlement !== "settled") {
85
+ const [abortCompleted, stopped] = await Promise.all([
86
+ settlesWithin(options.client.abort(), options.abortGraceMs),
87
+ settlesWithin(settled, options.abortGraceMs),
88
+ ]);
89
+ if (!stopped) await boundedRelease(options);
90
+ error = [
91
+ `timeout summary ${settlement}`,
92
+ abortCompleted ? undefined : "summary abort command did not settle",
93
+ ]
94
+ .filter(Boolean)
95
+ .join("; ");
96
+ }
97
+ } catch (caught) {
98
+ error = boundedError(caught);
99
+ await boundedRelease(options);
100
+ } finally {
101
+ unsubscribeEvent();
102
+ unsubscribeClose();
103
+ }
104
+ const capture = options.getCapture();
105
+ const output = truncateUtf8(capture.output || capture.partial, DEFAULT_MAX_OUTPUT_BYTES);
106
+ if (!error && (capture.stopReason === "error" || !output.text.trim())) {
107
+ error = capture.error || "Timeout summary produced no final text";
108
+ }
109
+ return {
110
+ output: output.text,
111
+ truncated: output.truncated,
112
+ status: error ? (/timed out|timeout/iu.test(error) ? "timed_out" : "failed") : "completed",
113
+ error,
114
+ };
115
+ }
116
+
117
+ function eventType(value: unknown): string | undefined {
118
+ return value &&
119
+ typeof value === "object" &&
120
+ typeof (value as { type?: unknown }).type === "string"
121
+ ? ((value as { type: string }).type ?? undefined)
122
+ : undefined;
123
+ }
124
+
125
+ function remainingMs(deadline: number): number {
126
+ return Math.max(1, deadline - Date.now());
127
+ }
128
+
129
+ async function raceWithAbort<T>(
130
+ start: () => Promise<T>,
131
+ signal: AbortSignal,
132
+ timeoutMs: number,
133
+ ): Promise<T> {
134
+ if (signal.aborted) throw abortError();
135
+ let onAbort: (() => void) | undefined;
136
+ let timer: NodeJS.Timeout | undefined;
137
+ const aborted = new Promise<never>((_resolve, reject) => {
138
+ onAbort = () => reject(abortError());
139
+ signal.addEventListener("abort", onAbort, { once: true });
140
+ if (signal.aborted) onAbort();
141
+ });
142
+ const timedOut = new Promise<never>((_resolve, reject) => {
143
+ timer = setTimeout(() => reject(new Error("RPC timeout summary prompt timed out")), timeoutMs);
144
+ });
145
+ try {
146
+ return await Promise.race([start(), aborted, timedOut]);
147
+ } finally {
148
+ if (timer) clearTimeout(timer);
149
+ if (onAbort) signal.removeEventListener("abort", onAbort);
150
+ }
151
+ }
152
+
153
+ async function waitForSettlement(
154
+ settled: Promise<void>,
155
+ signal: AbortSignal,
156
+ timeoutMs: number,
157
+ ): Promise<"settled" | "aborted" | "timeout"> {
158
+ if (signal.aborted) return "aborted";
159
+ let timer: NodeJS.Timeout | undefined;
160
+ let onAbort: (() => void) | undefined;
161
+ try {
162
+ return await Promise.race([
163
+ settled.then(() => "settled" as const),
164
+ new Promise<"aborted">((resolve) => {
165
+ onAbort = () => resolve("aborted");
166
+ signal.addEventListener("abort", onAbort, { once: true });
167
+ }),
168
+ new Promise<"timeout">((resolve) => {
169
+ timer = setTimeout(() => resolve("timeout"), timeoutMs);
170
+ }),
171
+ ]);
172
+ } finally {
173
+ if (timer) clearTimeout(timer);
174
+ if (onAbort) signal.removeEventListener("abort", onAbort);
175
+ }
176
+ }
177
+
178
+ async function boundedRelease(options: RpcTimeoutFinalizationOptions): Promise<boolean> {
179
+ return settlesWithin(options.release(), options.abortGraceMs + 1_000);
180
+ }
181
+
182
+ async function settlesWithin(settled: Promise<void>, timeoutMs: number): Promise<boolean> {
183
+ let timer: NodeJS.Timeout | undefined;
184
+ try {
185
+ return await Promise.race([
186
+ settled.then(
187
+ () => true,
188
+ () => true,
189
+ ),
190
+ new Promise<boolean>((resolve) => {
191
+ timer = setTimeout(() => resolve(false), timeoutMs);
192
+ }),
193
+ ]);
194
+ } finally {
195
+ if (timer) clearTimeout(timer);
196
+ }
197
+ }
198
+
199
+ function abortError(): Error {
200
+ const error = new Error("RPC timeout summary aborted");
201
+ error.name = "AbortError";
202
+ return error;
203
+ }
204
+
205
+ function boundedError(error: unknown): string {
206
+ return truncateUtf8(error instanceof Error ? error.message : String(error), 16 * 1024).text;
207
+ }
@@ -0,0 +1,65 @@
1
+ import type { AgentConfig, SubagentThinkingLevel } from "./agents.js";
2
+ import { DEFAULT_MAX_OUTPUT_BYTES } from "./limits.js";
3
+ import type { ManagedAgent, TurnOutcome } from "./registry.js";
4
+ import { boundedPrivateText } from "./safe-text.js";
5
+ import type { TransportTelemetry } from "./transport-types.js";
6
+
7
+ export function modelIdentity(value: unknown): { provider?: string; model?: string } {
8
+ if (!value || typeof value !== "object") return {};
9
+ const model = value as Record<string, unknown>;
10
+ return {
11
+ provider:
12
+ typeof model.provider === "string" ? boundedPrivateText(model.provider, 256) : undefined,
13
+ model: typeof model.id === "string" ? boundedPrivateText(model.id, 256) : undefined,
14
+ };
15
+ }
16
+
17
+ export function normalizeThinking(value: unknown): SubagentThinkingLevel | undefined {
18
+ return typeof value === "string" &&
19
+ ["off", "minimal", "low", "medium", "high", "xhigh", "max"].includes(value)
20
+ ? (value as SubagentThinkingLevel)
21
+ : undefined;
22
+ }
23
+
24
+ export function rpcPolicy(
25
+ config: AgentConfig,
26
+ agent: ManagedAgent,
27
+ ): NonNullable<TurnOutcome["policy"]> {
28
+ return {
29
+ inherited: ["environment", "cwdResources"],
30
+ overridden: [
31
+ "cwd",
32
+ "extensions",
33
+ ...(config.model ? ["model"] : []),
34
+ ...(agent.thinkingLevel || config.thinkingLevel ? ["thinkingLevel"] : []),
35
+ ...(config.tools ? ["tools"] : []),
36
+ ],
37
+ unsupported: ["approvalPolicy", "sandboxProfile", "providerHeaders", "extensionState"],
38
+ };
39
+ }
40
+
41
+ export function interruptedRpcOutcome(
42
+ output: string,
43
+ telemetry: TransportTelemetry,
44
+ failurePhase: TransportTelemetry["phase"],
45
+ ): TurnOutcome {
46
+ return {
47
+ output,
48
+ exitCode: 130,
49
+ aborted: true,
50
+ error: "RPC subagent was aborted",
51
+ telemetry: {
52
+ ...telemetry,
53
+ phase: "interrupted",
54
+ failurePhase,
55
+ updatedAt: Date.now(),
56
+ },
57
+ };
58
+ }
59
+
60
+ export function boundedError(error: unknown): string {
61
+ return boundedPrivateText(
62
+ error instanceof Error ? error.message : String(error),
63
+ DEFAULT_MAX_OUTPUT_BYTES,
64
+ );
65
+ }