@narumitw/pi-subagents 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -4
- package/package.json +7 -7
- package/src/automation-registration.ts +137 -0
- package/src/automation-tool.ts +40 -0
- package/src/automation.ts +6 -156
- package/src/cached-module-loader.ts +18 -0
- package/src/config-registration.ts +114 -0
- package/src/config-ui.ts +2 -2
- package/src/consult-registration.ts +132 -0
- package/src/consult-render.ts +1 -1
- package/src/consult-tool.ts +95 -0
- package/src/consult.ts +37 -204
- package/src/create-stateful-transport.ts +106 -18
- package/src/inspect-registration.ts +64 -0
- package/src/inspect-tool.ts +43 -0
- package/src/inspect.ts +7 -69
- package/src/panel-planning.ts +2 -2
- package/src/panel-presets.ts +3 -0
- package/src/params.ts +2 -2
- package/src/pi-args.ts +41 -0
- package/src/render.ts +4 -47
- package/src/runner-outcome.ts +31 -0
- package/src/runner.ts +18 -79
- package/src/settings.ts +4 -1
- package/src/stateful.ts +254 -98
- package/src/subagents.ts +82 -29
- package/src/usage-format.ts +42 -0
- package/src/verified-execution-contract.ts +3 -31
- package/src/verified-execution-schema.ts +32 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ExtensionAPI, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { cachedModuleLoader, throwIfAborted } from "./cached-module-loader.js";
|
|
3
|
+
import type { SubagentInspectRuntime } from "./inspect.js";
|
|
4
|
+
import { renderInspectCall, renderInspectResult } from "./inspect-render.js";
|
|
5
|
+
import { SubagentInspectParams } from "./inspect-tool.js";
|
|
6
|
+
|
|
7
|
+
interface InspectExecutionModule {
|
|
8
|
+
executeSubagentInspect: typeof import("./inspect.js").executeSubagentInspect;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface InspectRegistrationDependencies {
|
|
12
|
+
loadExecution?: () => Promise<InspectExecutionModule>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function registerSubagentInspect(
|
|
16
|
+
pi: ExtensionAPI,
|
|
17
|
+
runtime: SubagentInspectRuntime,
|
|
18
|
+
dependencies: InspectRegistrationDependencies = {},
|
|
19
|
+
): void {
|
|
20
|
+
const loadExecution = cachedModuleLoader(
|
|
21
|
+
dependencies.loadExecution ?? (() => import("./inspect.js")),
|
|
22
|
+
);
|
|
23
|
+
let lifecycleGeneration = 0;
|
|
24
|
+
pi.on("session_start", () => {
|
|
25
|
+
lifecycleGeneration += 1;
|
|
26
|
+
});
|
|
27
|
+
pi.on("session_shutdown", () => {
|
|
28
|
+
lifecycleGeneration += 1;
|
|
29
|
+
});
|
|
30
|
+
const definition: ToolDefinition<typeof SubagentInspectParams, Record<string, unknown>> = {
|
|
31
|
+
name: "subagent_inspect",
|
|
32
|
+
label: "Inspect Subagents",
|
|
33
|
+
description:
|
|
34
|
+
"Inspect available subagent definitions, models, retained runs, persisted blocking workflows, runtime status, and diagnostics without changing subagent or workspace state. This tool never starts a child, sends or acknowledges messages, interrupts or closes runs, changes settings, or modifies files.",
|
|
35
|
+
promptSnippet: "Inspect subagent metadata and runtime state without changing it",
|
|
36
|
+
parameters: SubagentInspectParams,
|
|
37
|
+
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
38
|
+
const ownerGeneration = lifecycleGeneration;
|
|
39
|
+
throwIfAborted(signal, "Subagent inspection loading was cancelled");
|
|
40
|
+
let executionModule: InspectExecutionModule;
|
|
41
|
+
try {
|
|
42
|
+
executionModule = await loadExecution();
|
|
43
|
+
} catch (error) {
|
|
44
|
+
throwIfAborted(signal, "Subagent inspection loading was cancelled");
|
|
45
|
+
if (ownerGeneration !== lifecycleGeneration) {
|
|
46
|
+
throw new DOMException("Subagent inspection session was replaced", "AbortError");
|
|
47
|
+
}
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
throwIfAborted(signal, "Subagent inspection loading was cancelled");
|
|
51
|
+
if (ownerGeneration !== lifecycleGeneration) {
|
|
52
|
+
throw new DOMException("Subagent inspection session was replaced", "AbortError");
|
|
53
|
+
}
|
|
54
|
+
return executionModule.executeSubagentInspect(params, ctx, runtime);
|
|
55
|
+
},
|
|
56
|
+
renderCall(args, theme) {
|
|
57
|
+
return renderInspectCall(args, theme);
|
|
58
|
+
},
|
|
59
|
+
renderResult(result, options, theme, context) {
|
|
60
|
+
return renderInspectResult(result, options, theme, context);
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
pi.registerTool(definition);
|
|
64
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { StringEnum } from "@earendil-works/pi-ai";
|
|
2
|
+
import { type Static, Type } from "typebox";
|
|
3
|
+
|
|
4
|
+
const INSPECT_ACTIONS = [
|
|
5
|
+
"list_agents",
|
|
6
|
+
"get_agent",
|
|
7
|
+
"list_runs",
|
|
8
|
+
"get_run",
|
|
9
|
+
"list_workflows",
|
|
10
|
+
"get_workflow",
|
|
11
|
+
"list_models",
|
|
12
|
+
"preview_context",
|
|
13
|
+
"status",
|
|
14
|
+
"diagnose",
|
|
15
|
+
] as const;
|
|
16
|
+
|
|
17
|
+
const AgentScopeSchema = StringEnum(["user", "project", "both"] as const, {
|
|
18
|
+
default: "user",
|
|
19
|
+
description: "Agent definition scope. Project scopes require a trusted project.",
|
|
20
|
+
});
|
|
21
|
+
const LimitSchema = Type.Number({ minimum: 1, maximum: 100, multipleOf: 1 });
|
|
22
|
+
const ContextModeSchema = Type.Union([
|
|
23
|
+
StringEnum(["none", "all", "summary"] as const),
|
|
24
|
+
Type.Number({ minimum: 1, multipleOf: 1 }),
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
export const SubagentInspectParams = Type.Object(
|
|
28
|
+
{
|
|
29
|
+
action: StringEnum(INSPECT_ACTIONS),
|
|
30
|
+
agent: Type.Optional(Type.String({ minLength: 1 })),
|
|
31
|
+
agentId: Type.Optional(Type.String({ minLength: 1 })),
|
|
32
|
+
workflowId: Type.Optional(Type.String({ minLength: 1, maxLength: 256 })),
|
|
33
|
+
agentScope: Type.Optional(AgentScopeSchema),
|
|
34
|
+
limit: Type.Optional(LimitSchema),
|
|
35
|
+
includeClosed: Type.Optional(Type.Boolean({ default: false })),
|
|
36
|
+
context: Type.Optional(ContextModeSchema),
|
|
37
|
+
contextEntryIds: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
38
|
+
},
|
|
39
|
+
{ additionalProperties: false },
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
export type SubagentInspectParams = Static<typeof SubagentInspectParams>;
|
|
43
|
+
export { INSPECT_ACTIONS };
|
package/src/inspect.ts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
CONFIG_DIR_NAME,
|
|
5
|
-
type ExtensionAPI,
|
|
6
|
-
type ExtensionContext,
|
|
7
|
-
} from "@earendil-works/pi-coding-agent";
|
|
8
|
-
import { type Static, Type } from "typebox";
|
|
2
|
+
import { CONFIG_DIR_NAME, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
9
3
|
import { discoverAgents } from "./agents/discovery.js";
|
|
10
4
|
import type {
|
|
11
5
|
AgentConfig,
|
|
@@ -16,7 +10,7 @@ import type {
|
|
|
16
10
|
import { projectCapabilityManifest } from "./capabilities.js";
|
|
17
11
|
import { resolveConsultTools } from "./consult-policy.js";
|
|
18
12
|
import { buildContextSnapshot, type ContextMode } from "./context.js";
|
|
19
|
-
import {
|
|
13
|
+
import { INSPECT_ACTIONS } from "./inspect-tool.js";
|
|
20
14
|
import { DEFAULT_MAX_CONTEXT_BYTES } from "./limits.js";
|
|
21
15
|
import { resolvePiInvocation } from "./pi-invocation.js";
|
|
22
16
|
import type { AgentRunInspectionDetail, AgentRunInspectionSummary } from "./registry.js";
|
|
@@ -36,47 +30,10 @@ import type { StatefulSubagentRuntimeStatus } from "./stateful.js";
|
|
|
36
30
|
import type { WorkItemLedgerSnapshot } from "./work-item-ledger.js";
|
|
37
31
|
import { inspectSessionWorkflows } from "./work-item-persistence.js";
|
|
38
32
|
|
|
39
|
-
const INSPECT_ACTIONS = [
|
|
40
|
-
"list_agents",
|
|
41
|
-
"get_agent",
|
|
42
|
-
"list_runs",
|
|
43
|
-
"get_run",
|
|
44
|
-
"list_workflows",
|
|
45
|
-
"get_workflow",
|
|
46
|
-
"list_models",
|
|
47
|
-
"preview_context",
|
|
48
|
-
"status",
|
|
49
|
-
"diagnose",
|
|
50
|
-
] as const;
|
|
51
|
-
|
|
52
|
-
const AgentScopeSchema = StringEnum(["user", "project", "both"] as const, {
|
|
53
|
-
default: "user",
|
|
54
|
-
description: "Agent definition scope. Project scopes require a trusted project.",
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
const LimitSchema = Type.Number({ minimum: 1, maximum: 100, multipleOf: 1 });
|
|
58
|
-
const ContextModeSchema = Type.Union([
|
|
59
|
-
StringEnum(["none", "all", "summary"] as const),
|
|
60
|
-
Type.Number({ minimum: 1, multipleOf: 1 }),
|
|
61
|
-
]);
|
|
62
33
|
const MAX_DETAILS_LIST_BYTES = 40 * 1024;
|
|
63
34
|
|
|
64
|
-
export
|
|
65
|
-
|
|
66
|
-
action: StringEnum(INSPECT_ACTIONS),
|
|
67
|
-
agent: Type.Optional(Type.String({ minLength: 1 })),
|
|
68
|
-
agentId: Type.Optional(Type.String({ minLength: 1 })),
|
|
69
|
-
workflowId: Type.Optional(Type.String({ minLength: 1, maxLength: 256 })),
|
|
70
|
-
agentScope: Type.Optional(AgentScopeSchema),
|
|
71
|
-
limit: Type.Optional(LimitSchema),
|
|
72
|
-
includeClosed: Type.Optional(Type.Boolean({ default: false })),
|
|
73
|
-
context: Type.Optional(ContextModeSchema),
|
|
74
|
-
contextEntryIds: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
75
|
-
},
|
|
76
|
-
{ additionalProperties: false },
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
export type SubagentInspectParams = Static<typeof SubagentInspectParams>;
|
|
35
|
+
export { registerSubagentInspect } from "./inspect-registration.js";
|
|
36
|
+
export { SubagentInspectParams } from "./inspect-tool.js";
|
|
80
37
|
|
|
81
38
|
export interface SubagentInspectRuntime {
|
|
82
39
|
getBlockingEnabled(): boolean;
|
|
@@ -106,26 +63,6 @@ type ValidatedInspectOperation =
|
|
|
106
63
|
| { action: "status" }
|
|
107
64
|
| { action: "diagnose" };
|
|
108
65
|
|
|
109
|
-
export function registerSubagentInspect(pi: ExtensionAPI, runtime: SubagentInspectRuntime): void {
|
|
110
|
-
pi.registerTool({
|
|
111
|
-
name: "subagent_inspect",
|
|
112
|
-
label: "Inspect Subagents",
|
|
113
|
-
description:
|
|
114
|
-
"Inspect available subagent definitions, models, retained runs, persisted blocking workflows, runtime status, and diagnostics without changing subagent or workspace state. This tool never starts a child, sends or acknowledges messages, interrupts or closes runs, changes settings, or modifies files.",
|
|
115
|
-
promptSnippet: "Inspect subagent metadata and runtime state without changing it",
|
|
116
|
-
parameters: SubagentInspectParams,
|
|
117
|
-
async execute(_toolCallId, params, _signal, _onUpdate, ctx): Promise<InspectToolResult> {
|
|
118
|
-
return executeSubagentInspect(validateInspectParams(params), ctx, runtime);
|
|
119
|
-
},
|
|
120
|
-
renderCall(args, theme) {
|
|
121
|
-
return renderInspectCall(args, theme);
|
|
122
|
-
},
|
|
123
|
-
renderResult(result, options, theme, context) {
|
|
124
|
-
return renderInspectResult(result, options, theme, context);
|
|
125
|
-
},
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
66
|
export function validateInspectParams(params: unknown): ValidatedInspectOperation {
|
|
130
67
|
const values = parameterRecord(params);
|
|
131
68
|
const rawAction = values.action;
|
|
@@ -195,11 +132,12 @@ export function validateInspectParams(params: unknown): ValidatedInspectOperatio
|
|
|
195
132
|
return { action };
|
|
196
133
|
}
|
|
197
134
|
|
|
198
|
-
async function executeSubagentInspect(
|
|
199
|
-
|
|
135
|
+
export async function executeSubagentInspect(
|
|
136
|
+
params: unknown,
|
|
200
137
|
ctx: ExtensionContext,
|
|
201
138
|
runtime: SubagentInspectRuntime,
|
|
202
139
|
): Promise<InspectToolResult> {
|
|
140
|
+
const operation = validateInspectParams(params);
|
|
203
141
|
if (operation.action === "list_agents" || operation.action === "get_agent") {
|
|
204
142
|
assertTrustedScope(operation.agentScope, ctx);
|
|
205
143
|
const settings = inspectSubagentSettings().settings;
|
package/src/panel-planning.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { AgentConfig } from "./agents/types.js";
|
|
2
2
|
import { DEFAULT_MAX_CONTEXT_BYTES, truncateUtf8 } from "./limits.js";
|
|
3
|
+
import type { PanelPreset } from "./panel-presets.js";
|
|
3
4
|
import { type WorkItemDefinition, WorkItemLedger } from "./work-item-ledger.js";
|
|
4
5
|
|
|
5
|
-
export
|
|
6
|
-
export type PanelPreset = (typeof PANEL_PRESETS)[number];
|
|
6
|
+
export type { PanelPreset } from "./panel-presets.js";
|
|
7
7
|
|
|
8
8
|
export interface PanelReviewerRequest {
|
|
9
9
|
id: string;
|
package/src/params.ts
CHANGED
|
@@ -3,10 +3,10 @@ import { type Static, Type } from "typebox";
|
|
|
3
3
|
import { THINKING_LEVELS } from "./agents/types.js";
|
|
4
4
|
import { DelegationContractSchema } from "./delegation-contract.js";
|
|
5
5
|
import { MAX_CONFIGURABLE_PARALLEL_TASKS, MAX_SUBAGENT_TIMEOUT_MS } from "./limits.js";
|
|
6
|
-
import { PANEL_PRESETS } from "./panel-
|
|
6
|
+
import { PANEL_PRESETS } from "./panel-presets.js";
|
|
7
7
|
import { SUBAGENT_RESULT_FORMATS } from "./result-contract.js";
|
|
8
8
|
import { MAX_SUBAGENT_TOOL_CALLS, MAX_SUBAGENT_TURNS } from "./turn-budget.js";
|
|
9
|
-
import { VerifiedExecutionContractSchema } from "./verified-execution-
|
|
9
|
+
import { VerifiedExecutionContractSchema } from "./verified-execution-schema.js";
|
|
10
10
|
|
|
11
11
|
const TimeoutMs = Type.Number({
|
|
12
12
|
description:
|
package/src/pi-args.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { SubagentThinkingLevel } from "./agents/types.js";
|
|
2
|
+
|
|
3
|
+
export interface PiArgsOptions {
|
|
4
|
+
model?: string;
|
|
5
|
+
thinkingLevel?: SubagentThinkingLevel;
|
|
6
|
+
tools?: string[];
|
|
7
|
+
disableExtensions?: boolean;
|
|
8
|
+
disableSkills?: boolean;
|
|
9
|
+
disablePromptTemplates?: boolean;
|
|
10
|
+
disableContextFiles?: boolean;
|
|
11
|
+
projectTrust?: boolean;
|
|
12
|
+
baseSystemPromptPath?: string;
|
|
13
|
+
appendSystemPromptPaths?: string[];
|
|
14
|
+
/** Existing single append prompt path retained for compatibility. */
|
|
15
|
+
systemPromptPath?: string;
|
|
16
|
+
task: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function buildPiArgs(options: PiArgsOptions): string[] {
|
|
20
|
+
const args: string[] = ["--mode", "json", "-p", "--no-session"];
|
|
21
|
+
if (options.model) args.push("--model", options.model);
|
|
22
|
+
if (options.thinkingLevel) args.push("--thinking", options.thinkingLevel);
|
|
23
|
+
if (options.disableExtensions) args.push("--no-extensions");
|
|
24
|
+
if (options.disableSkills) args.push("--no-skills");
|
|
25
|
+
if (options.disablePromptTemplates) args.push("--no-prompt-templates");
|
|
26
|
+
if (options.disableContextFiles) args.push("--no-context-files");
|
|
27
|
+
if (options.projectTrust !== undefined) {
|
|
28
|
+
args.push(options.projectTrust ? "--approve" : "--no-approve");
|
|
29
|
+
}
|
|
30
|
+
if (Array.isArray(options.tools)) {
|
|
31
|
+
if (options.tools.length > 0) args.push("--tools", options.tools.join(","));
|
|
32
|
+
else args.push("--no-tools");
|
|
33
|
+
}
|
|
34
|
+
if (options.baseSystemPromptPath) args.push("--system-prompt", options.baseSystemPromptPath);
|
|
35
|
+
for (const promptPath of options.appendSystemPromptPaths ?? []) {
|
|
36
|
+
args.push("--append-system-prompt", promptPath);
|
|
37
|
+
}
|
|
38
|
+
if (options.systemPromptPath) args.push("--append-system-prompt", options.systemPromptPath);
|
|
39
|
+
args.push(`Task: ${options.task}`);
|
|
40
|
+
return args;
|
|
41
|
+
}
|
package/src/render.ts
CHANGED
|
@@ -7,16 +7,13 @@ import {
|
|
|
7
7
|
type ToolRenderResultOptions,
|
|
8
8
|
} from "@earendil-works/pi-coding-agent";
|
|
9
9
|
import { Container, Markdown, Spacer, Text } from "@earendil-works/pi-tui";
|
|
10
|
-
import type { AgentScope
|
|
10
|
+
import type { AgentScope } from "./agents/types.js";
|
|
11
11
|
import { renderPanelCall, renderPanelResult } from "./panel-render.js";
|
|
12
12
|
import { hasUsableAggregator, type SubagentParams } from "./params.js";
|
|
13
13
|
import { expansionHint, formatToolActivity, safeBlock, safeLine } from "./render-common.js";
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
type SingleResult,
|
|
18
|
-
type SubagentDetails,
|
|
19
|
-
} from "./runner.js";
|
|
14
|
+
import type { SingleResult, SubagentDetails } from "./runner.js";
|
|
15
|
+
import { getResultFinalOutput, isResultError } from "./runner-outcome.js";
|
|
16
|
+
import { formatUsageStats } from "./usage-format.js";
|
|
20
17
|
|
|
21
18
|
const COLLAPSED_ITEM_COUNT = 5;
|
|
22
19
|
|
|
@@ -29,46 +26,6 @@ function previewAgent(agent: unknown): string {
|
|
|
29
26
|
return safeLine(agent, "...", 256);
|
|
30
27
|
}
|
|
31
28
|
|
|
32
|
-
export function formatTokens(count: number): string {
|
|
33
|
-
if (count < 1000) return count.toString();
|
|
34
|
-
if (count < 10000) return `${(count / 1000).toFixed(1)}k`;
|
|
35
|
-
if (count < 1000000) return `${Math.round(count / 1000)}k`;
|
|
36
|
-
return `${(count / 1000000).toFixed(1)}M`;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function formatUsageStats(
|
|
40
|
-
usage: {
|
|
41
|
-
input: number;
|
|
42
|
-
output: number;
|
|
43
|
-
cacheRead: number;
|
|
44
|
-
cacheWrite: number;
|
|
45
|
-
cost: number;
|
|
46
|
-
contextTokens?: number;
|
|
47
|
-
turns?: number;
|
|
48
|
-
},
|
|
49
|
-
model?: string,
|
|
50
|
-
thinkingLevel?: SubagentThinkingLevel,
|
|
51
|
-
actualProvider?: string,
|
|
52
|
-
actualModel?: string,
|
|
53
|
-
): string {
|
|
54
|
-
const parts: string[] = [];
|
|
55
|
-
if (usage.turns) parts.push(`${usage.turns} turn${usage.turns > 1 ? "s" : ""}`);
|
|
56
|
-
if (usage.input) parts.push(`↑${formatTokens(usage.input)}`);
|
|
57
|
-
if (usage.output) parts.push(`↓${formatTokens(usage.output)}`);
|
|
58
|
-
if (usage.cacheRead) parts.push(`R${formatTokens(usage.cacheRead)}`);
|
|
59
|
-
if (usage.cacheWrite) parts.push(`W${formatTokens(usage.cacheWrite)}`);
|
|
60
|
-
if (usage.cost) parts.push(`$${usage.cost.toFixed(4)}`);
|
|
61
|
-
if (usage.contextTokens && usage.contextTokens > 0)
|
|
62
|
-
parts.push(`ctx:${formatTokens(usage.contextTokens)}`);
|
|
63
|
-
const safeProvider = actualProvider ? safeLine(actualProvider, "", 256) : undefined;
|
|
64
|
-
const safeModel = actualModel ? safeLine(actualModel, "", 256) : undefined;
|
|
65
|
-
const actual =
|
|
66
|
-
safeProvider && safeModel ? `${safeProvider}/${safeModel}` : (safeModel ?? safeProvider);
|
|
67
|
-
if (actual ?? model) parts.push(actual ?? safeLine(model, "", 256));
|
|
68
|
-
if (thinkingLevel) parts.push(`requested-thinking:${safeLine(thinkingLevel, "", 128)}`);
|
|
69
|
-
return parts.join(" ");
|
|
70
|
-
}
|
|
71
|
-
|
|
72
29
|
function formatResultUsageStats(result: SingleResult): string {
|
|
73
30
|
return formatUsageStats(
|
|
74
31
|
result.usage,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { SingleResult } from "./runner.js";
|
|
2
|
+
import {
|
|
3
|
+
formatResultFailure as formatBaseResultFailure,
|
|
4
|
+
getResultFinalOutput as getBaseResultFinalOutput,
|
|
5
|
+
isResultError as isBaseResultError,
|
|
6
|
+
} from "./runner-result.js";
|
|
7
|
+
|
|
8
|
+
export function getResultFinalOutput(result: SingleResult): string {
|
|
9
|
+
return getBaseResultFinalOutput(result);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function isResultError(result: SingleResult): boolean {
|
|
13
|
+
return (
|
|
14
|
+
isBaseResultError(result) ||
|
|
15
|
+
result.resultContractInvalid === true ||
|
|
16
|
+
(result.outcome !== undefined &&
|
|
17
|
+
result.outcome.status !== "completed" &&
|
|
18
|
+
result.outcome.status !== "partial")
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function formatResultFailure(result: SingleResult): string {
|
|
23
|
+
const contractError = result.resultContractInvalid
|
|
24
|
+
? `Subagent returned an invalid ${result.resultFormat ?? "structured"} result contract`
|
|
25
|
+
: result.outcome && !["completed", "partial"].includes(result.outcome.status)
|
|
26
|
+
? `Subagent outcome ${result.outcome.status}${result.outcome.reasonCode ? ` (${result.outcome.reasonCode})` : ""}; recovery: ${result.outcome.recoveryActions.join(", ") || "none"}`
|
|
27
|
+
: undefined;
|
|
28
|
+
return contractError
|
|
29
|
+
? formatBaseResultFailure({ ...result, errorMessage: contractError })
|
|
30
|
+
: formatBaseResultFailure(result);
|
|
31
|
+
}
|
package/src/runner.ts
CHANGED
|
@@ -31,6 +31,7 @@ import type { PanelSynthesis } from "./panel-contract.js";
|
|
|
31
31
|
import type { PanelEvidenceArtifact } from "./panel-evidence.js";
|
|
32
32
|
import type { PanelFailure } from "./panel-failure.js";
|
|
33
33
|
import type { PanelPhaseBudgets, PanelPreset } from "./panel-planning.js";
|
|
34
|
+
import { buildPiArgs } from "./pi-args.js";
|
|
34
35
|
import { resolvePiInvocation } from "./pi-invocation.js";
|
|
35
36
|
import { JsonLineDecoder } from "./protocol.js";
|
|
36
37
|
import {
|
|
@@ -38,12 +39,8 @@ import {
|
|
|
38
39
|
parseAnyStructuredSubagentResult,
|
|
39
40
|
type SubagentResultFormat,
|
|
40
41
|
} from "./result-contract.js";
|
|
41
|
-
import {
|
|
42
|
-
|
|
43
|
-
getResultFinalOutput as getBaseResultFinalOutput,
|
|
44
|
-
getFinalOutput,
|
|
45
|
-
isResultError as isBaseResultError,
|
|
46
|
-
} from "./runner-result.js";
|
|
42
|
+
import { formatResultFailure, getResultFinalOutput, isResultError } from "./runner-outcome.js";
|
|
43
|
+
import { getFinalOutput } from "./runner-result.js";
|
|
47
44
|
import {
|
|
48
45
|
addUsageValue,
|
|
49
46
|
mergeUsageStats,
|
|
@@ -68,6 +65,13 @@ import type { WorkItemLedgerSnapshot } from "./work-item-ledger.js";
|
|
|
68
65
|
|
|
69
66
|
export const KILL_GRACE_MS = 5000;
|
|
70
67
|
|
|
68
|
+
export type { PiArgsOptions } from "./pi-args.js";
|
|
69
|
+
export { buildPiArgs } from "./pi-args.js";
|
|
70
|
+
export {
|
|
71
|
+
formatResultFailure,
|
|
72
|
+
getResultFinalOutput,
|
|
73
|
+
isResultError,
|
|
74
|
+
} from "./runner-outcome.js";
|
|
71
75
|
export type { UsageStats } from "./runner-usage.js";
|
|
72
76
|
export type RecentActivityItem =
|
|
73
77
|
| { type: "text"; text: string }
|
|
@@ -154,31 +158,6 @@ export interface SubagentDetails {
|
|
|
154
158
|
isError?: boolean;
|
|
155
159
|
}
|
|
156
160
|
|
|
157
|
-
export function getResultFinalOutput(result: SingleResult): string {
|
|
158
|
-
return getBaseResultFinalOutput(result);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export function isResultError(result: SingleResult): boolean {
|
|
162
|
-
return (
|
|
163
|
-
isBaseResultError(result) ||
|
|
164
|
-
result.resultContractInvalid === true ||
|
|
165
|
-
(result.outcome !== undefined &&
|
|
166
|
-
result.outcome.status !== "completed" &&
|
|
167
|
-
result.outcome.status !== "partial")
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export function formatResultFailure(result: SingleResult): string {
|
|
172
|
-
const contractError = result.resultContractInvalid
|
|
173
|
-
? `Subagent returned an invalid ${result.resultFormat ?? "structured"} result contract`
|
|
174
|
-
: result.outcome && !["completed", "partial"].includes(result.outcome.status)
|
|
175
|
-
? `Subagent outcome ${result.outcome.status}${result.outcome.reasonCode ? ` (${result.outcome.reasonCode})` : ""}; recovery: ${result.outcome.recoveryActions.join(", ") || "none"}`
|
|
176
|
-
: undefined;
|
|
177
|
-
return contractError
|
|
178
|
-
? formatBaseResultFailure({ ...result, errorMessage: contractError })
|
|
179
|
-
: formatBaseResultFailure(result);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
161
|
function boundMessageText(
|
|
183
162
|
message: Message,
|
|
184
163
|
maxBytes: number,
|
|
@@ -359,46 +338,6 @@ async function writePromptToTempFile(
|
|
|
359
338
|
return { dir: tmpDir, filePath };
|
|
360
339
|
}
|
|
361
340
|
|
|
362
|
-
export interface PiArgsOptions {
|
|
363
|
-
model?: string;
|
|
364
|
-
thinkingLevel?: SubagentThinkingLevel;
|
|
365
|
-
tools?: string[];
|
|
366
|
-
disableExtensions?: boolean;
|
|
367
|
-
disableSkills?: boolean;
|
|
368
|
-
disablePromptTemplates?: boolean;
|
|
369
|
-
disableContextFiles?: boolean;
|
|
370
|
-
projectTrust?: boolean;
|
|
371
|
-
baseSystemPromptPath?: string;
|
|
372
|
-
appendSystemPromptPaths?: string[];
|
|
373
|
-
/** Existing single append prompt path retained for compatibility. */
|
|
374
|
-
systemPromptPath?: string;
|
|
375
|
-
task: string;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
export function buildPiArgs(options: PiArgsOptions): string[] {
|
|
379
|
-
const args: string[] = ["--mode", "json", "-p", "--no-session"];
|
|
380
|
-
if (options.model) args.push("--model", options.model);
|
|
381
|
-
if (options.thinkingLevel) args.push("--thinking", options.thinkingLevel);
|
|
382
|
-
if (options.disableExtensions) args.push("--no-extensions");
|
|
383
|
-
if (options.disableSkills) args.push("--no-skills");
|
|
384
|
-
if (options.disablePromptTemplates) args.push("--no-prompt-templates");
|
|
385
|
-
if (options.disableContextFiles) args.push("--no-context-files");
|
|
386
|
-
if (options.projectTrust !== undefined) {
|
|
387
|
-
args.push(options.projectTrust ? "--approve" : "--no-approve");
|
|
388
|
-
}
|
|
389
|
-
if (Array.isArray(options.tools)) {
|
|
390
|
-
if (options.tools.length > 0) args.push("--tools", options.tools.join(","));
|
|
391
|
-
else args.push("--no-tools");
|
|
392
|
-
}
|
|
393
|
-
if (options.baseSystemPromptPath) args.push("--system-prompt", options.baseSystemPromptPath);
|
|
394
|
-
for (const promptPath of options.appendSystemPromptPaths ?? []) {
|
|
395
|
-
args.push("--append-system-prompt", promptPath);
|
|
396
|
-
}
|
|
397
|
-
if (options.systemPromptPath) args.push("--append-system-prompt", options.systemPromptPath);
|
|
398
|
-
args.push(`Task: ${options.task}`);
|
|
399
|
-
return args;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
341
|
function signalProcess(proc: ReturnType<typeof spawn>, signal: NodeJS.Signals): void {
|
|
403
342
|
if (process.platform !== "win32" && proc.pid) {
|
|
404
343
|
try {
|
|
@@ -857,16 +796,16 @@ export async function runSingleAgent(
|
|
|
857
796
|
onExceeded: stopForBudget,
|
|
858
797
|
});
|
|
859
798
|
|
|
860
|
-
timeout = setTimeout(() => {
|
|
861
|
-
stopForBudget({
|
|
862
|
-
reason: launchPolicy?.workTimeoutReason ?? "work_timeout",
|
|
863
|
-
limit: launchPolicy?.workTimeoutReportLimit ?? timeoutMs,
|
|
864
|
-
});
|
|
865
|
-
}, timeoutMs);
|
|
866
|
-
timeout.unref();
|
|
867
|
-
|
|
868
799
|
proc.once("spawn", () => {
|
|
869
800
|
currentResult.processStarted = true;
|
|
801
|
+
if (settled || budgetStop || wasAborted) return;
|
|
802
|
+
timeout = setTimeout(() => {
|
|
803
|
+
stopForBudget({
|
|
804
|
+
reason: launchPolicy?.workTimeoutReason ?? "work_timeout",
|
|
805
|
+
limit: launchPolicy?.workTimeoutReportLimit ?? timeoutMs,
|
|
806
|
+
});
|
|
807
|
+
}, timeoutMs);
|
|
808
|
+
timeout.unref();
|
|
870
809
|
});
|
|
871
810
|
proc.stdout?.on("data", (data) => decoder.push(data));
|
|
872
811
|
proc.stderr?.on("data", (data) => {
|
package/src/settings.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
3
4
|
import * as path from "node:path";
|
|
4
5
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
5
|
-
import lockfile from "proper-lockfile";
|
|
6
6
|
import type {
|
|
7
7
|
AgentConfig,
|
|
8
8
|
CompletionDelivery,
|
|
@@ -76,6 +76,8 @@ export {
|
|
|
76
76
|
|
|
77
77
|
const SETTINGS_FILE = "pi-subagents.json";
|
|
78
78
|
const LEGACY_SETTINGS_FILE = "pi-subagents-config.json";
|
|
79
|
+
const require = createRequire(import.meta.url);
|
|
80
|
+
|
|
79
81
|
const SETTINGS_LOCK_FS_ADAPTER = {
|
|
80
82
|
mkdir: fs.mkdir,
|
|
81
83
|
mkdirSync: fs.mkdirSync,
|
|
@@ -511,6 +513,7 @@ function writeSettingsObjectUnlocked(settings: object, replaceCanonical?: boolea
|
|
|
511
513
|
}
|
|
512
514
|
|
|
513
515
|
function withSettingsMutationLock<T>(mutate: () => T): T {
|
|
516
|
+
const lockfile = require("proper-lockfile") as typeof import("proper-lockfile");
|
|
514
517
|
const agentDir = getAgentDir();
|
|
515
518
|
fs.mkdirSync(agentDir, { recursive: true });
|
|
516
519
|
const configPath = path.join(agentDir, SETTINGS_FILE);
|