pi-subagents 0.58.0 → 0.60.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/CHANGELOG.md +102 -0
- package/docs/agents.md +5 -3
- package/docs/configuration.md +4 -4
- package/docs/extension-api.md +1 -1
- package/docs/models.md +24 -1
- package/docs/observability.md +1 -1
- package/docs/tool-reference.md +93 -5
- package/docs/workflows.md +94 -3
- package/package.json +3 -1
- package/prompts/review-loop.md +2 -2
- package/skills/council-mode/SKILL.md +48 -243
- package/skills/council-mode/references/pass-contracts.md +150 -0
- package/skills/pi-subagents/SKILL.md +87 -37
- package/skills/pi-subagents/references/constraints-and-recipes.md +29 -233
- package/skills/pi-subagents/references/execution-controls.md +52 -7
- package/skills/pi-subagents/references/management-authoring-rpc.md +3 -4
- package/skills/pi-subagents/references/multi-lane-orchestration.md +13 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +54 -26
- package/skills/pi-subagents/references/review-and-validation.md +73 -0
- package/src/agents/agent-management.ts +159 -42
- package/src/agents/agent-serializer.ts +4 -2
- package/src/agents/agents.ts +67 -26
- package/src/agents/runtime-agent-registry.ts +9 -16
- package/src/api/background-work.ts +5 -1
- package/src/api/delegation.ts +0 -7
- package/src/api/preflight.ts +6 -9
- package/src/api/shared-types.ts +2 -0
- package/src/extension/fanout-child.ts +5 -3
- package/src/extension/index.ts +51 -23
- package/src/extension/public-execution.ts +16 -2
- package/src/extension/schemas.ts +36 -10
- package/src/extension/tool-description.ts +24 -5
- package/src/intercom/result-intercom.ts +2 -0
- package/src/profiles/profiles.ts +5 -6
- package/src/runs/background/active-async-capacity.ts +2 -2
- package/src/runs/background/async-execution.ts +52 -37
- package/src/runs/background/async-job-tracker.ts +17 -12
- package/src/runs/background/async-resume.ts +23 -25
- package/src/runs/background/async-status-snapshot.ts +23 -261
- package/src/runs/background/async-status.ts +110 -8
- package/src/runs/background/chain-append.ts +6 -3
- package/src/runs/background/chain-root-attachment.ts +60 -8
- package/src/runs/background/control-channel.ts +3 -2
- package/src/runs/background/fleet-view.ts +21 -11
- package/src/runs/background/notify.ts +158 -6
- package/src/runs/background/result-files.ts +2 -1
- package/src/runs/background/result-watcher.ts +2 -0
- package/src/runs/background/resume-guidance.ts +1 -1
- package/src/runs/background/retained-children.ts +1 -1
- package/src/runs/background/run-status.ts +30 -9
- package/src/runs/background/scheduled-runs.ts +86 -7
- package/src/runs/background/stale-run-reconciler.ts +10 -4
- package/src/runs/background/steering.ts +4 -14
- package/src/runs/background/subagent-runner.ts +351 -358
- package/src/runs/background/subagent-wait.ts +68 -12
- package/src/runs/background/terminal-run-index.ts +1 -1
- package/src/runs/background/wait-completions.ts +25 -1
- package/src/runs/background/wait-config.ts +23 -9
- package/src/runs/background/wait-tool.ts +9 -2
- package/src/runs/foreground/async-steering-action.ts +2 -2
- package/src/runs/foreground/execution.ts +125 -122
- package/src/runs/foreground/foreground-control.ts +3 -0
- package/src/runs/foreground/foreground-history.ts +1 -0
- package/src/runs/foreground/subagent-executor.ts +576 -240
- package/src/runs/foreground/workflow-detach-reconcile.ts +99 -200
- package/src/runs/shared/abort-recovery.ts +119 -0
- package/src/runs/shared/async-status-projection.ts +597 -0
- package/src/runs/shared/background-process-options.ts +9 -0
- package/src/runs/shared/child-identity.ts +19 -4
- package/src/runs/shared/child-launch-plan.ts +151 -0
- package/src/runs/shared/completion-evidence.ts +89 -0
- package/src/runs/shared/completion-guard.ts +1 -1
- package/src/runs/shared/dynamic-fanout.ts +2 -2
- package/src/runs/shared/host-step-status.ts +230 -0
- package/src/runs/shared/lane-metadata.ts +105 -0
- package/src/runs/shared/mcp-config-sources.ts +42 -6
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +93 -143
- package/src/runs/shared/mcp-direct-tool-grant.ts +194 -0
- package/src/runs/shared/model-fallback.ts +9 -2
- package/src/runs/shared/mutation-evidence.ts +52 -3
- package/src/runs/shared/nested-events.ts +6 -2
- package/src/runs/shared/nested-render.ts +7 -3
- package/src/runs/shared/parallel-handoff.ts +419 -7
- package/src/runs/shared/parallel-utils.ts +7 -0
- package/src/runs/shared/pi-args.ts +67 -3
- package/src/runs/shared/single-output.ts +72 -22
- package/src/runs/shared/subagent-prompt-runtime.ts +33 -6
- package/src/runs/shared/workflow-graph.ts +15 -0
- package/src/runs/shared/worktree-cleanup-plan.ts +847 -0
- package/src/runs/shared/worktree.ts +18 -0
- package/src/shared/child-session-name.ts +46 -0
- package/src/shared/extension-context.ts +24 -0
- package/src/shared/formatters.ts +5 -2
- package/src/shared/launch-contract.ts +1 -1
- package/src/shared/settings.ts +9 -103
- package/src/shared/types.ts +232 -30
- package/src/shared/utils.ts +35 -55
- package/src/slash/delegation-adapters.ts +1 -8
- package/src/slash/delegation-request.ts +0 -4
- package/src/slash/slash-bridge.ts +1 -2
- package/src/slash/slash-commands.ts +369 -90
- package/src/slash/slash-live-state.ts +22 -11
- package/src/tui/fleet-status.ts +134 -75
- package/src/tui/fleet.ts +11 -5
- package/src/tui/render-helpers.ts +31 -0
- package/src/tui/render.ts +895 -121
- package/src/watchdog/change-signature.ts +40 -1
- package/src/watchdog/turn-delta.ts +1 -1
- package/src/workflows/chat-progress.ts +6 -3
- package/src/workflows/host-command.ts +235 -0
- package/src/workflows/scripted-workflow.ts +503 -38
- package/src/workflows/workflow-child-summary.ts +9 -5
- package/src/workflows/workflow-preflight.ts +270 -0
- package/src/workflows/workflow-receipt.ts +43 -4
- package/src/workflows/workflow-settlement.ts +246 -0
- package/src/runs/shared/turn-budget.ts +0 -98
|
@@ -77,6 +77,8 @@ interface CreateWorktreesOptions {
|
|
|
77
77
|
agents?: string[];
|
|
78
78
|
setupHook?: WorktreeSetupHookConfig;
|
|
79
79
|
baseDir?: string;
|
|
80
|
+
/** Called with deterministic ownership metadata before any worktree is created. */
|
|
81
|
+
beforeCreate?: (setup: WorktreeSetup) => void;
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
interface ResolvedWorktreeSetupHook {
|
|
@@ -680,6 +682,22 @@ export function createWorktrees(cwd: string, runId: string, count: number, optio
|
|
|
680
682
|
const repo = resolveRepoState(cwd);
|
|
681
683
|
const setupHook = resolveWorktreeSetupHook(repo.toplevel, options?.setupHook);
|
|
682
684
|
const baseDir = resolveWorktreeBaseDir(options?.baseDir, repo.toplevel);
|
|
685
|
+
const plannedSetup: WorktreeSetup = {
|
|
686
|
+
cwd: repo.toplevel,
|
|
687
|
+
baseCommit: repo.baseCommit,
|
|
688
|
+
worktrees: Array.from({ length: count }, (_, index) => {
|
|
689
|
+
const worktreePath = buildWorktreePath(baseDir, runId, index);
|
|
690
|
+
return {
|
|
691
|
+
path: worktreePath,
|
|
692
|
+
agentCwd: repo.cwdRelative ? path.join(worktreePath, repo.cwdRelative) : worktreePath,
|
|
693
|
+
branch: buildWorktreeBranch(runId, index),
|
|
694
|
+
index,
|
|
695
|
+
nodeModulesLinked: false,
|
|
696
|
+
syntheticPaths: [],
|
|
697
|
+
};
|
|
698
|
+
}),
|
|
699
|
+
};
|
|
700
|
+
options?.beforeCreate?.(plannedSetup);
|
|
683
701
|
const worktrees: WorktreeInfo[] = [];
|
|
684
702
|
|
|
685
703
|
try {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { previewDisplayText } from "./display-text.ts";
|
|
2
|
+
import { PROMPT_REDACTED } from "./utils.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Human-readable display name for a child session, derived at launch time from
|
|
6
|
+
* the agent name and its task (or workflow node label). The parent computes it
|
|
7
|
+
* once and threads it two ways:
|
|
8
|
+
*
|
|
9
|
+
* 1. Into the child process via `PI_SUBAGENT_SESSION_NAME`, where the prompt
|
|
10
|
+
* runtime calls `pi.setSessionName(...)` so the child's own session file
|
|
11
|
+
* is identifiable in `pi --resume` and host session browsers.
|
|
12
|
+
* 2. Into the `sessionName` field of result/progress payloads, so hosts
|
|
13
|
+
* rendering the parent stream can label each child row.
|
|
14
|
+
*
|
|
15
|
+
* The name is display-only metadata. When the intercom bridge is active the
|
|
16
|
+
* child keeps its machine intercom target as the session name instead — that
|
|
17
|
+
* name is a routing address and must win (see subagent-prompt-runtime).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** Longest task excerpt kept in the name; the full string is capped below. */
|
|
21
|
+
const TASK_EXCERPT_MAX_CHARS = 60;
|
|
22
|
+
/** Hard cap on the final name so host UI rows stay one line. */
|
|
23
|
+
export const CHILD_SESSION_NAME_MAX_CHARS = 80;
|
|
24
|
+
|
|
25
|
+
export function deriveChildSessionName(input: {
|
|
26
|
+
agent?: string;
|
|
27
|
+
task?: string;
|
|
28
|
+
/** Workflow node label; preferred over the task excerpt when present. */
|
|
29
|
+
label?: string;
|
|
30
|
+
}): string | undefined {
|
|
31
|
+
const agent = input.agent?.trim() ?? "";
|
|
32
|
+
const rawLabel = input.label?.trim() ?? "";
|
|
33
|
+
const rawTask = input.task?.trim() ?? "";
|
|
34
|
+
// Never build a name from redacted text — the excerpt would be meaningless
|
|
35
|
+
// and the redaction marker itself carries no information.
|
|
36
|
+
const excerptSource =
|
|
37
|
+
rawLabel && rawLabel !== PROMPT_REDACTED
|
|
38
|
+
? rawLabel
|
|
39
|
+
: rawTask && rawTask !== PROMPT_REDACTED
|
|
40
|
+
? rawTask
|
|
41
|
+
: "";
|
|
42
|
+
const excerpt = excerptSource ? previewDisplayText(excerptSource, TASK_EXCERPT_MAX_CHARS) : "";
|
|
43
|
+
const base = agent && excerpt ? `${agent}: ${excerpt}` : agent || excerpt;
|
|
44
|
+
if (!base) return undefined;
|
|
45
|
+
return previewDisplayText(base, CHILD_SESSION_NAME_MAX_CHARS);
|
|
46
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
/** Pi exposes replaced extension contexts as ordinary Errors without a stable code. */
|
|
4
|
+
export function isStaleExtensionContextError(error: unknown): boolean {
|
|
5
|
+
return error instanceof Error
|
|
6
|
+
&& /extension ctx is stale|extension context no longer active|stale after session replacement or reload/i.test(error.message);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Run a synchronous operation against a cached UI context without leaking replacement errors. */
|
|
10
|
+
export function withCachedUiContext<T>(
|
|
11
|
+
ctx: ExtensionContext | null | undefined,
|
|
12
|
+
onStale: () => void,
|
|
13
|
+
run: (ctx: ExtensionContext) => T,
|
|
14
|
+
): T | undefined {
|
|
15
|
+
if (!ctx) return undefined;
|
|
16
|
+
try {
|
|
17
|
+
if (!ctx.hasUI) return undefined;
|
|
18
|
+
return run(ctx);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
if (!isStaleExtensionContextError(error)) throw error;
|
|
21
|
+
onStale();
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/shared/formatters.ts
CHANGED
|
@@ -11,10 +11,13 @@ import { previewDisplayText, sanitizeDisplayText } from "./display-text.ts";
|
|
|
11
11
|
import { splitKnownThinkingSuffix, THINKING_LEVELS } from "./model-info.ts";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Format token count
|
|
14
|
+
* Format token count for compact display.
|
|
15
15
|
*/
|
|
16
16
|
export function formatTokens(n: number): string {
|
|
17
|
-
|
|
17
|
+
if (n < 1000) return String(n);
|
|
18
|
+
if (n < 10000) return `${(n / 1000).toFixed(1)}k`;
|
|
19
|
+
if (n < 999_500) return `${Math.round(n / 1000)}k`;
|
|
20
|
+
return `${Number((n / 1_000_000).toFixed(1))}M`;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
export function formatTokenUsage(usage: TokenUsage, legacyLabel = "tok"): string {
|
|
@@ -51,6 +51,7 @@ export function projectAgentDefinition(agent: AgentConfig): Record<string, unkno
|
|
|
51
51
|
fast: agent.fast,
|
|
52
52
|
thinking: agent.thinking,
|
|
53
53
|
tools: agent.tools,
|
|
54
|
+
allowNestedSubagents: agent.allowNestedSubagents,
|
|
54
55
|
mcpDirectTools: agent.mcpDirectTools,
|
|
55
56
|
extensions: agent.extensions,
|
|
56
57
|
subagentOnlyExtensions: agent.subagentOnlyExtensions,
|
|
@@ -63,7 +64,6 @@ export function projectAgentDefinition(agent: AgentConfig): Record<string, unkno
|
|
|
63
64
|
defaultContext: agent.defaultContext,
|
|
64
65
|
defaultAsync: agent.defaultAsync,
|
|
65
66
|
defaultTimeoutMs: agent.defaultTimeoutMs,
|
|
66
|
-
defaultTurnBudget: agent.defaultTurnBudget,
|
|
67
67
|
defaultAcceptance: agent.defaultAcceptance,
|
|
68
68
|
acceptanceRole: agent.acceptanceRole,
|
|
69
69
|
interactive: agent.interactive,
|
package/src/shared/settings.ts
CHANGED
|
@@ -7,41 +7,19 @@ import * as os from "node:os";
|
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import { discoverAgents, formatUnknownAgentError, unknownAgentDiagnosticContext, type AgentConfig, type AgentScope, type UnknownAgentDiagnosticContext } from "../agents/agents.ts";
|
|
9
9
|
import { normalizeSkillInput } from "../agents/skills.ts";
|
|
10
|
+
import { normalizeOutputOverride, type OutputOverrideInput, type ResolvedStepBehavior } from "../runs/shared/child-launch-plan.ts";
|
|
10
11
|
import { CHAIN_RUNS_DIR, type AcceptanceInput, type AgentContract, type ChainGateLayer, type JsonSchemaObject, type OutputMode, type ToolBudgetConfig } from "./types.ts";
|
|
11
12
|
const CHAIN_DIR_MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
12
13
|
const INITIAL_PROGRESS_CONTENT = "# Progress\n\n## Status\nIn Progress\n\n## Tasks\n\n## Files Changed\n\n## Notes\n";
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
progress: boolean;
|
|
23
|
-
skills: string[] | false;
|
|
24
|
-
model?: string;
|
|
25
|
-
fast?: boolean;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export type OutputOverrideInput = string | boolean;
|
|
29
|
-
|
|
30
|
-
export interface StepOverrides {
|
|
31
|
-
output?: OutputOverrideInput;
|
|
32
|
-
outputMode?: OutputMode;
|
|
33
|
-
reads?: string[] | false;
|
|
34
|
-
progress?: boolean;
|
|
35
|
-
skills?: string[] | false;
|
|
36
|
-
model?: string;
|
|
37
|
-
fast?: boolean;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function normalizeOutputOverride(output: unknown): string | false | undefined {
|
|
41
|
-
if (output === false || output === "false") return false;
|
|
42
|
-
if (output === true || output === "true") return undefined;
|
|
43
|
-
return typeof output === "string" && output.length > 0 ? output : undefined;
|
|
44
|
-
}
|
|
15
|
+
export {
|
|
16
|
+
planChildLaunch,
|
|
17
|
+
resolveStepBehavior,
|
|
18
|
+
resolveTaskTextForFileUpdatePolicy,
|
|
19
|
+
suppressProgressForReadOnlyTask,
|
|
20
|
+
taskDisallowsFileUpdates,
|
|
21
|
+
} from "../runs/shared/child-launch-plan.ts";
|
|
22
|
+
export type { ChildLaunchPlan, ChildLaunchPlanInput, OutputOverrideInput, ResolvedStepBehavior, StepOverrides } from "../runs/shared/child-launch-plan.ts";
|
|
45
23
|
|
|
46
24
|
// =============================================================================
|
|
47
25
|
// Chain Step Types
|
|
@@ -239,78 +217,6 @@ export function resolveChainTemplates(
|
|
|
239
217
|
});
|
|
240
218
|
}
|
|
241
219
|
|
|
242
|
-
// =============================================================================
|
|
243
|
-
// Behavior Resolution
|
|
244
|
-
// =============================================================================
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Resolve effective chain behavior per step.
|
|
248
|
-
* Priority: step override > agent frontmatter > false (disabled)
|
|
249
|
-
*/
|
|
250
|
-
export function resolveStepBehavior(
|
|
251
|
-
agentConfig: AgentConfig,
|
|
252
|
-
stepOverrides: StepOverrides,
|
|
253
|
-
chainSkills?: string[],
|
|
254
|
-
): ResolvedStepBehavior {
|
|
255
|
-
// Output: step override > frontmatter > false (no output)
|
|
256
|
-
const stepOutput = normalizeOutputOverride(stepOverrides.output);
|
|
257
|
-
const output =
|
|
258
|
-
stepOutput !== undefined
|
|
259
|
-
? stepOutput
|
|
260
|
-
: normalizeOutputOverride(agentConfig.output) ?? false;
|
|
261
|
-
|
|
262
|
-
// Reads: step override > frontmatter defaultReads > false (no reads)
|
|
263
|
-
const reads =
|
|
264
|
-
stepOverrides.reads !== undefined
|
|
265
|
-
? stepOverrides.reads
|
|
266
|
-
: agentConfig.defaultReads ?? false;
|
|
267
|
-
|
|
268
|
-
// Progress: step override > frontmatter defaultProgress > false
|
|
269
|
-
const progress =
|
|
270
|
-
stepOverrides.progress !== undefined
|
|
271
|
-
? stepOverrides.progress
|
|
272
|
-
: agentConfig.defaultProgress ?? false;
|
|
273
|
-
|
|
274
|
-
let skills: string[] | false;
|
|
275
|
-
if (stepOverrides.skills === false) {
|
|
276
|
-
skills = false;
|
|
277
|
-
} else if (stepOverrides.skills !== undefined) {
|
|
278
|
-
skills = [...stepOverrides.skills];
|
|
279
|
-
if (chainSkills && chainSkills.length > 0) {
|
|
280
|
-
skills = [...new Set([...skills, ...chainSkills])];
|
|
281
|
-
}
|
|
282
|
-
} else {
|
|
283
|
-
skills = agentConfig.skills ? [...agentConfig.skills] : [];
|
|
284
|
-
if (chainSkills && chainSkills.length > 0) {
|
|
285
|
-
skills = [...new Set([...skills, ...chainSkills])];
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const outputMode = stepOverrides.outputMode ?? agentConfig.outputMode ?? "inline";
|
|
290
|
-
const model = stepOverrides.model ?? agentConfig.model;
|
|
291
|
-
const fast = stepOverrides.fast ?? agentConfig.fast;
|
|
292
|
-
return { output, outputMode, reads, progress, skills, model, fast };
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
export function resolveTaskTextForFileUpdatePolicy(task: string | undefined, originalTask?: string): string | undefined {
|
|
296
|
-
if (!task) return originalTask;
|
|
297
|
-
return originalTask ? task.replaceAll("{task}", originalTask) : task;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
export function taskDisallowsFileUpdates(task: string | undefined): boolean {
|
|
301
|
-
if (!task) return false;
|
|
302
|
-
return /\breview[- ]only\b/i.test(task)
|
|
303
|
-
|| /\bread[- ]only\s+(?:review|audit|inspection|pass)\b/i.test(task)
|
|
304
|
-
|| /\b(?:no|without)\s+(?:file\s+)?edits?\b/i.test(task)
|
|
305
|
-
|| /\b(?:do not|don't|must not)\s+(?:edit|modify|write|touch)\b/i.test(task)
|
|
306
|
-
|| /\bleave\s+files?\s+unchanged\b/i.test(task);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
export function suppressProgressForReadOnlyTask(behavior: ResolvedStepBehavior, task: string | undefined, originalTask?: string): ResolvedStepBehavior {
|
|
310
|
-
const policyTask = resolveTaskTextForFileUpdatePolicy(task, originalTask);
|
|
311
|
-
return behavior.progress && taskDisallowsFileUpdates(policyTask) ? { ...behavior, progress: false } : behavior;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
220
|
// =============================================================================
|
|
315
221
|
// Chain Instruction Injection
|
|
316
222
|
// =============================================================================
|