pi-subagents 0.40.0 → 0.41.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 +76 -0
- package/README.md +246 -525
- package/agents/oracle.md +1 -0
- package/package.json +12 -4
- package/prompts/parallel-context-build.md +1 -1
- package/prompts/parallel-handoff-plan.md +1 -1
- package/prompts/review-loop.md +1 -1
- package/skills/pi-subagents/SKILL.md +6 -6
- package/skills/pi-subagents/references/constraints-and-recipes.md +19 -26
- package/skills/pi-subagents/references/execution-controls.md +98 -97
- package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/skills/pi-subagents/references/prompting-and-roles.md +18 -27
- package/src/agents/agent-management.ts +155 -65
- package/src/agents/agent-serializer.ts +19 -0
- package/src/agents/agents.ts +154 -71
- package/src/agents/chain-serializer.ts +10 -7
- package/src/agents/frontmatter.ts +5 -3
- package/src/agents/identity.ts +1 -1
- package/src/agents/proactive-skills.ts +13 -10
- package/src/agents/skills.ts +23 -6
- package/src/api/control-channel.ts +4 -0
- package/src/api/delegation.ts +26 -194
- package/src/api/external-runs.ts +129 -0
- package/src/api/intercom-bridge.ts +3 -0
- package/src/api/pi-args.ts +5 -0
- package/src/api/preflight.ts +3 -3
- package/src/api/shared-types.ts +19 -0
- package/src/extension/config.ts +10 -0
- package/src/extension/control-notices.ts +5 -39
- package/src/extension/doctor.ts +10 -9
- package/src/extension/fanout-child.ts +7 -4
- package/src/extension/index.ts +232 -68
- package/src/extension/rpc.ts +18 -12
- package/src/extension/schemas.ts +48 -37
- package/src/extension/tool-description.ts +36 -86
- package/src/inspectors/herdr/actions.ts +229 -0
- package/src/inspectors/herdr/client.ts +130 -0
- package/src/inspectors/herdr/inspector-runner.ts +141 -0
- package/src/inspectors/herdr/project-panes.ts +154 -0
- package/src/integrations/herdr-status.ts +330 -0
- package/src/intercom/intercom-bridge.ts +3 -2
- package/src/intercom/result-intercom.ts +5 -1
- package/src/missions/actions.ts +372 -0
- package/src/missions/lifecycle.ts +314 -0
- package/src/missions/store.ts +442 -0
- package/src/missions/types.ts +135 -0
- package/src/policy/authority.ts +46 -0
- package/src/profiles/profiles.ts +29 -3
- package/src/runs/background/async-execution.ts +98 -49
- package/src/runs/background/async-job-tracker.ts +10 -2
- package/src/runs/background/async-resume.ts +6 -6
- package/src/runs/background/async-status.ts +29 -1
- package/src/runs/background/auto-drain.ts +3 -3
- package/src/runs/background/chain-append.ts +3 -2
- package/src/runs/background/control-channel.ts +9 -7
- package/src/runs/background/fleet-view.ts +3 -4
- package/src/runs/background/notify.ts +2 -1
- package/src/runs/background/process-terminal.ts +5 -5
- package/src/runs/background/result-watcher.ts +13 -5
- package/src/runs/background/run-id-resolver.ts +3 -3
- package/src/runs/background/run-status.ts +35 -8
- package/src/runs/background/scheduled-runs.ts +602 -375
- package/src/runs/background/stale-run-reconciler.ts +3 -3
- package/src/runs/background/subagent-runner.ts +608 -445
- package/src/runs/background/subagent-wait.ts +50 -9
- package/src/runs/background/wait-subscriptions.ts +253 -0
- package/src/runs/background/wait-tool.ts +12 -4
- package/src/runs/foreground/async-steering-action.ts +3 -3
- package/src/runs/foreground/chain-clarify.ts +8 -4
- package/src/runs/foreground/chain-execution.ts +56 -30
- package/src/runs/foreground/execution.ts +15 -2
- package/src/runs/foreground/subagent-executor.ts +1023 -273
- package/src/runs/shared/acceptance.ts +28 -6
- package/src/runs/shared/child-protocol.ts +302 -22
- package/src/runs/shared/dynamic-fanout.ts +1 -1
- package/src/runs/shared/external-cli-runner.ts +130 -0
- package/src/runs/shared/long-running-guard.ts +42 -1
- package/src/runs/shared/nested-events.ts +59 -5
- package/src/runs/shared/nested-render.ts +9 -4
- package/src/runs/shared/parallel-handoff.ts +86 -2
- package/src/runs/shared/parallel-utils.ts +11 -2
- package/src/runs/shared/permissions.ts +95 -0
- package/src/runs/shared/pi-args.ts +11 -1
- package/src/runs/shared/pi-spawn.ts +11 -1
- package/src/runs/shared/run-history.ts +1 -1
- package/src/runs/shared/subagent-prompt-runtime.ts +36 -5
- package/src/runs/shared/subagent-startup-retry.ts +5 -2
- package/src/runs/shared/turn-budget.ts +6 -6
- package/src/runs/shared/worktree.ts +122 -12
- package/src/shared/accessible-dir.ts +29 -7
- package/src/shared/artifacts.ts +18 -1
- package/src/shared/fork-context.ts +3 -2
- package/src/shared/launch-contract.ts +1 -0
- package/src/shared/settings.ts +10 -0
- package/src/shared/types.ts +156 -14
- package/src/shared/utils.ts +8 -6
- package/src/slash/delegation-adapters.ts +32 -194
- package/src/slash/delegation-request.ts +43 -126
- package/src/slash/prompt-template-bridge.ts +158 -205
- package/src/slash/prompt-workflows.ts +21 -57
- package/src/slash/slash-bridge.ts +14 -0
- package/src/slash/slash-commands.ts +31 -632
- package/src/slash/subagents-admin.ts +18 -14
- package/src/tui/fleet-status.ts +156 -21
- package/src/tui/fleet-transcript.ts +110 -5
- package/src/tui/fleet.ts +56 -24
- package/src/tui/render.ts +291 -109
- package/src/types/pi-runtime-compat.d.ts +14 -0
- package/src/watchdog/lsp-diagnostics.ts +12 -7
- package/src/watchdog/model-selection.ts +2 -2
- package/src/watchdog/permission-arbiter.ts +145 -0
- package/src/watchdog/register-child.ts +1 -1
- package/src/watchdog/register-main.ts +1 -1
- package/src/watchdog/review.ts +4 -1
- package/src/watchdog/runtime.ts +3 -2
- package/src/workflows/chat-progress.ts +140 -0
- package/src/workflows/scripted-workflow.ts +415 -0
- package/agents/advisor.md +0 -73
- package/src/extension/chain-validation.ts +0 -181
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Usage } from "../../shared/types.ts";
|
|
2
|
+
import { formatProcessSignalError } from "./process-signal.ts";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Retry delays for child processes that exit before any model or tool activity.
|
|
@@ -10,6 +11,8 @@ export const SUBAGENT_STARTUP_RETRY_DELAYS_MS = [250, 750, 1500] as const;
|
|
|
10
11
|
/** A genuine startup race should fail well before a model request can complete. */
|
|
11
12
|
export const MAX_SUBAGENT_STARTUP_FAILURE_DURATION_MS = 2000;
|
|
12
13
|
|
|
14
|
+
const SIGKILL_PROCESS_SIGNAL_ERROR = formatProcessSignalError("SIGKILL");
|
|
15
|
+
|
|
13
16
|
export interface SubagentStartupFailureEvidence {
|
|
14
17
|
exitCode: number | null | undefined;
|
|
15
18
|
error?: string;
|
|
@@ -46,7 +49,7 @@ export function isRetryableSubagentStartupFailure(evidence: SubagentStartupFailu
|
|
|
46
49
|
return evidence.exitCode !== undefined
|
|
47
50
|
&& evidence.exitCode !== null
|
|
48
51
|
&& evidence.exitCode !== 0
|
|
49
|
-
&& !evidence.error?.trim()
|
|
52
|
+
&& (!evidence.error?.trim() || evidence.error === SIGKILL_PROCESS_SIGNAL_ERROR)
|
|
50
53
|
&& !evidence.finalOutput?.trim()
|
|
51
54
|
&& evidence.messageCount === 0
|
|
52
55
|
&& evidence.toolCount === 0
|
|
@@ -54,7 +57,7 @@ export function isRetryableSubagentStartupFailure(evidence: SubagentStartupFailu
|
|
|
54
57
|
&& evidence.durationMs >= 0
|
|
55
58
|
&& evidence.durationMs <= MAX_SUBAGENT_STARTUP_FAILURE_DURATION_MS
|
|
56
59
|
&& evidence.protocolError === undefined
|
|
57
|
-
&& (evidence.processSignal === undefined || evidence.processSignal === null)
|
|
60
|
+
&& (evidence.processSignal === undefined || evidence.processSignal === null || evidence.processSignal === "SIGKILL")
|
|
58
61
|
&& evidence.observedMutationAttempt !== true
|
|
59
62
|
&& evidence.detached !== true
|
|
60
63
|
&& evidence.interrupted !== true
|
|
@@ -23,9 +23,10 @@ export function resolveTurnBudgetConfig(
|
|
|
23
23
|
return { turnBudget: { maxTurns: budget.maxTurns, graceTurns } };
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export function appendTurnBudgetSystemPrompt(systemPrompt: string, budget:
|
|
26
|
+
export function appendTurnBudgetSystemPrompt(systemPrompt: string, budget: { maxTurns: number; graceTurns?: number } | undefined): string {
|
|
27
27
|
if (!budget) return systemPrompt;
|
|
28
|
-
const
|
|
28
|
+
const graceTurns = budget.graceTurns ?? DEFAULT_TURN_BUDGET_GRACE_TURNS;
|
|
29
|
+
const grace = graceTurns === 1 ? "1 additional assistant turn" : `${graceTurns} additional assistant turns`;
|
|
29
30
|
const block = [
|
|
30
31
|
"## Turn budget",
|
|
31
32
|
`This child run has a soft budget of ${budget.maxTurns} assistant turn${budget.maxTurns === 1 ? "" : "s"}.`,
|
|
@@ -55,8 +56,8 @@ export function formatTurnBudgetOutput(message: string, output: string): string
|
|
|
55
56
|
: message;
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
export function initialTurnBudgetState(budget:
|
|
59
|
-
return {
|
|
59
|
+
export function initialTurnBudgetState(budget: { maxTurns: number; graceTurns?: number }): TurnBudgetState {
|
|
60
|
+
return { maxTurns: budget.maxTurns, graceTurns: budget.graceTurns ?? DEFAULT_TURN_BUDGET_GRACE_TURNS, outcome: "within-budget", turnCount: 0 };
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
export function turnBudgetState(budget: ResolvedTurnBudget, turnCount: number, exceeded: boolean): TurnBudgetState {
|
|
@@ -91,8 +92,7 @@ export function turnBudgetDecision(
|
|
|
91
92
|
enforceHardLimit = false,
|
|
92
93
|
): "continue" | "defer" | "abort" {
|
|
93
94
|
const hardLimit = budget.maxTurns + budget.graceTurns;
|
|
94
|
-
if (turnCount < hardLimit) return "continue";
|
|
95
|
+
if (terminalAssistantStop || turnCount < hardLimit) return "continue";
|
|
95
96
|
if (toolWorkActiveOrStarting && !enforceHardLimit) return "defer";
|
|
96
|
-
if (turnCount === hardLimit && terminalAssistantStop) return "continue";
|
|
97
97
|
return "abort";
|
|
98
98
|
}
|
|
@@ -2,14 +2,16 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as os from "node:os";
|
|
4
4
|
import * as path from "node:path";
|
|
5
|
+
import { resolveAuthorityDecision, type AuthorityPolicyConfig } from "../../policy/authority.ts";
|
|
5
6
|
|
|
6
7
|
export interface WorktreeSetup {
|
|
7
8
|
cwd: string;
|
|
8
9
|
worktrees: WorktreeInfo[];
|
|
9
10
|
baseCommit: string;
|
|
11
|
+
capturedDiffs?: WorktreeDiff[];
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
interface WorktreeInfo {
|
|
14
|
+
export interface WorktreeInfo {
|
|
13
15
|
path: string;
|
|
14
16
|
agentCwd: string;
|
|
15
17
|
branch: string;
|
|
@@ -36,9 +38,21 @@ export interface WorktreeCleanupTask {
|
|
|
36
38
|
branch: string;
|
|
37
39
|
worktreeRemoved: boolean;
|
|
38
40
|
branchRemoved: boolean;
|
|
41
|
+
preserved?: boolean;
|
|
42
|
+
reason?: string;
|
|
39
43
|
errors?: string[];
|
|
40
44
|
}
|
|
41
45
|
|
|
46
|
+
export type WorktreeCleanupIntent =
|
|
47
|
+
| { kind: "preserve"; capturedDiffs?: WorktreeDiff[]; handoffManifestPath?: string }
|
|
48
|
+
| {
|
|
49
|
+
kind: "discard";
|
|
50
|
+
authorization:
|
|
51
|
+
| { kind: "policy"; policy?: AuthorityPolicyConfig }
|
|
52
|
+
| { kind: "confirmed"; policy?: AuthorityPolicyConfig };
|
|
53
|
+
}
|
|
54
|
+
| { kind: "setup-rollback" };
|
|
55
|
+
|
|
42
56
|
export interface WorktreeCleanupReport {
|
|
43
57
|
state: "complete" | "partial";
|
|
44
58
|
tasks: WorktreeCleanupTask[];
|
|
@@ -121,7 +135,9 @@ function resolveRepoState(cwd: string): RepoState {
|
|
|
121
135
|
const cwdRelative = resolveRepoCwdRelative(cwd);
|
|
122
136
|
const toplevel = runGitChecked(cwd, ["rev-parse", "--show-toplevel"]).trim();
|
|
123
137
|
|
|
124
|
-
|
|
138
|
+
// pi-subagents writes durable runtime state under .pi-subagents/ by default;
|
|
139
|
+
// that state must not make managed isolation unusable for later runs.
|
|
140
|
+
const status = runGitChecked(toplevel, ["status", "--porcelain", "--", ":!.pi-subagents"]);
|
|
125
141
|
if (status.trim().length > 0) {
|
|
126
142
|
throw new Error("worktree isolation requires a clean git working tree. Commit or stash changes first.");
|
|
127
143
|
}
|
|
@@ -515,21 +531,111 @@ function writeEmptyPatch(patchPath: string): void {
|
|
|
515
531
|
}
|
|
516
532
|
}
|
|
517
533
|
|
|
518
|
-
function
|
|
534
|
+
function handoffRecordsPatch(manifestPath: string | undefined, patchPath: string): boolean {
|
|
535
|
+
if (!manifestPath || !fs.existsSync(manifestPath)) return false;
|
|
536
|
+
try {
|
|
537
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8")) as {
|
|
538
|
+
version?: unknown;
|
|
539
|
+
groups?: Array<{ children?: Array<{ patch?: { path?: unknown; error?: unknown } }> }>;
|
|
540
|
+
};
|
|
541
|
+
if (manifest.version !== 1 || !Array.isArray(manifest.groups)) return false;
|
|
542
|
+
const resolvedPatchPath = path.resolve(patchPath);
|
|
543
|
+
return manifest.groups.some((group) => Array.isArray(group.children) && group.children.some((child) =>
|
|
544
|
+
child.patch?.error === undefined
|
|
545
|
+
&& typeof child.patch?.path === "string"
|
|
546
|
+
&& path.resolve(child.patch.path) === resolvedPatchPath,
|
|
547
|
+
));
|
|
548
|
+
} catch {
|
|
549
|
+
return false;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function cleanupSingleWorktree(
|
|
554
|
+
setup: WorktreeSetup,
|
|
555
|
+
worktree: WorktreeInfo,
|
|
556
|
+
intent: WorktreeCleanupIntent,
|
|
557
|
+
): WorktreeCleanupTask {
|
|
519
558
|
const errors: string[] = [];
|
|
520
559
|
let worktreeRemoved = false;
|
|
521
560
|
let branchRemoved = false;
|
|
561
|
+
if (intent.kind !== "setup-rollback") {
|
|
562
|
+
try {
|
|
563
|
+
removeSyntheticPathsBeforeDiff(worktree);
|
|
564
|
+
} catch (error) {
|
|
565
|
+
errors.push(`synthetic path cleanup failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
566
|
+
}
|
|
567
|
+
const status = runGit(worktree.path, ["status", "--porcelain"]);
|
|
568
|
+
const baseDiff = runGit(worktree.path, ["diff", "--quiet", setup.baseCommit, "--"]);
|
|
569
|
+
if (status.status !== 0 || (baseDiff.status !== 0 && baseDiff.status !== 1)) {
|
|
570
|
+
const reason = status.status !== 0
|
|
571
|
+
? status.stderr.trim() || status.stdout.trim() || "git status failed"
|
|
572
|
+
: baseDiff.stderr.trim() || baseDiff.stdout.trim() || "git diff check failed";
|
|
573
|
+
return {
|
|
574
|
+
index: worktree.index,
|
|
575
|
+
path: worktree.path,
|
|
576
|
+
branch: worktree.branch,
|
|
577
|
+
worktreeRemoved: false,
|
|
578
|
+
branchRemoved: false,
|
|
579
|
+
preserved: true,
|
|
580
|
+
reason: "cleanup safety check failed",
|
|
581
|
+
errors: [...errors, `cleanup refused: ${reason}`],
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
const hasWork = status.stdout.trim().length > 0 || baseDiff.status === 1;
|
|
585
|
+
if (hasWork && intent.kind === "preserve") {
|
|
586
|
+
const captured = (intent.capturedDiffs ?? setup.capturedDiffs)?.find((diff) => diff.index === worktree.index);
|
|
587
|
+
const patchCaptured = captured !== undefined
|
|
588
|
+
&& captured.error === undefined
|
|
589
|
+
&& fs.existsSync(captured.patchPath)
|
|
590
|
+
&& fs.statSync(captured.patchPath).size > 0
|
|
591
|
+
&& handoffRecordsPatch(intent.handoffManifestPath, captured.patchPath);
|
|
592
|
+
if (!patchCaptured) {
|
|
593
|
+
const reason = "worktree contains changes that are not represented by a captured handoff patch";
|
|
594
|
+
return {
|
|
595
|
+
index: worktree.index,
|
|
596
|
+
path: worktree.path,
|
|
597
|
+
branch: worktree.branch,
|
|
598
|
+
worktreeRemoved: false,
|
|
599
|
+
branchRemoved: false,
|
|
600
|
+
preserved: true,
|
|
601
|
+
reason,
|
|
602
|
+
errors: [...errors, `cleanup refused: ${reason}; preserved ${worktree.path}`],
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
if (hasWork && intent.kind === "discard") {
|
|
607
|
+
const decision = resolveAuthorityDecision({ action: "discardWorktree", policy: intent.authorization.policy });
|
|
608
|
+
const authorized = decision === "auto" || (decision === "confirm" && intent.authorization.kind === "confirmed");
|
|
609
|
+
if (!authorized) {
|
|
610
|
+
const reason = decision === "forbid"
|
|
611
|
+
? "authority policy forbids worktree discard"
|
|
612
|
+
: "worktree discard requires explicit user confirmation";
|
|
613
|
+
return {
|
|
614
|
+
index: worktree.index,
|
|
615
|
+
path: worktree.path,
|
|
616
|
+
branch: worktree.branch,
|
|
617
|
+
worktreeRemoved: false,
|
|
618
|
+
branchRemoved: false,
|
|
619
|
+
preserved: true,
|
|
620
|
+
reason,
|
|
621
|
+
errors: [...errors, `cleanup refused: ${reason}; preserved ${worktree.path}`],
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
522
626
|
try {
|
|
523
|
-
runGitChecked(
|
|
627
|
+
runGitChecked(setup.cwd, ["worktree", "remove", "--force", worktree.path]);
|
|
524
628
|
worktreeRemoved = true;
|
|
525
629
|
} catch (error) {
|
|
526
630
|
errors.push(`worktree removal failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
527
631
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
632
|
+
if (worktreeRemoved) {
|
|
633
|
+
try {
|
|
634
|
+
runGitChecked(setup.cwd, ["branch", "-D", worktree.branch]);
|
|
635
|
+
branchRemoved = true;
|
|
636
|
+
} catch (error) {
|
|
637
|
+
errors.push(`branch removal failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
638
|
+
}
|
|
533
639
|
}
|
|
534
640
|
return {
|
|
535
641
|
index: worktree.index,
|
|
@@ -569,7 +675,7 @@ export function createWorktrees(cwd: string, runId: string, count: number, optio
|
|
|
569
675
|
cwd: repo.toplevel,
|
|
570
676
|
worktrees,
|
|
571
677
|
baseCommit: repo.baseCommit,
|
|
572
|
-
});
|
|
678
|
+
}, { kind: "setup-rollback" });
|
|
573
679
|
throw error;
|
|
574
680
|
}
|
|
575
681
|
|
|
@@ -602,13 +708,17 @@ export function diffWorktrees(setup: WorktreeSetup, agents: string[], diffsDir:
|
|
|
602
708
|
}
|
|
603
709
|
}
|
|
604
710
|
|
|
711
|
+
setup.capturedDiffs = diffs;
|
|
605
712
|
return diffs;
|
|
606
713
|
}
|
|
607
714
|
|
|
608
|
-
export function cleanupWorktrees(
|
|
715
|
+
export function cleanupWorktrees(
|
|
716
|
+
setup: WorktreeSetup,
|
|
717
|
+
intent: WorktreeCleanupIntent = { kind: "preserve", ...(setup.capturedDiffs ? { capturedDiffs: setup.capturedDiffs } : {}) },
|
|
718
|
+
): WorktreeCleanupReport {
|
|
609
719
|
const tasks: WorktreeCleanupTask[] = [];
|
|
610
720
|
for (let index = setup.worktrees.length - 1; index >= 0; index--) {
|
|
611
|
-
tasks.push(cleanupSingleWorktree(setup
|
|
721
|
+
tasks.push(cleanupSingleWorktree(setup, setup.worktrees[index]!, intent));
|
|
612
722
|
}
|
|
613
723
|
tasks.sort((left, right) => left.index - right.index);
|
|
614
724
|
const errors: string[] = [];
|
|
@@ -8,18 +8,40 @@ type AccessibleDirOptions = {
|
|
|
8
8
|
retryDirectoryErrors?: boolean;
|
|
9
9
|
retryDelaysMs?: readonly number[];
|
|
10
10
|
wait?: (delayMs: number) => void;
|
|
11
|
+
pid?: number;
|
|
11
12
|
};
|
|
12
13
|
|
|
13
|
-
export function ensureAccessibleDir(dirPath: string, options: AccessibleDirOptions = {}):
|
|
14
|
+
export function ensureAccessibleDir(dirPath: string, options: AccessibleDirOptions = {}): string {
|
|
14
15
|
const fsImpl = options.fs ?? fs;
|
|
16
|
+
const pid = options.pid ?? process.pid;
|
|
15
17
|
const retryDirectoryErrors = options.retryDirectoryErrors ?? process.platform === "win32";
|
|
16
18
|
const retryDelaysMs = retryDirectoryErrors ? options.retryDelaysMs ?? DEFAULT_FILE_SYSTEM_RETRY_DELAYS_MS : [];
|
|
17
19
|
const wait = options.wait ?? waitForFileSystemRetry;
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
const mkdirWithRetry = (target: string): void => {
|
|
22
|
+
runFileSystemOperationWithRetry(() => {
|
|
23
|
+
fsImpl.mkdirSync(target, { recursive: true });
|
|
24
|
+
}, { retryDelaysMs, wait });
|
|
25
|
+
};
|
|
26
|
+
const accessWithRetry = (target: string): void => {
|
|
27
|
+
runFileSystemOperationWithRetry(() => {
|
|
28
|
+
fsImpl.accessSync(target, fs.constants.R_OK | fs.constants.W_OK);
|
|
29
|
+
}, { retryDelaysMs, wait });
|
|
30
|
+
};
|
|
31
|
+
const fallbackPath = (): string => {
|
|
32
|
+
const fallback = `${dirPath}-${pid}`;
|
|
33
|
+
mkdirWithRetry(fallback);
|
|
34
|
+
accessWithRetry(fallback);
|
|
35
|
+
return fallback;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
mkdirWithRetry(dirPath);
|
|
40
|
+
accessWithRetry(dirPath);
|
|
41
|
+
return dirPath;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
const code = (error as NodeJS.ErrnoException | undefined)?.code;
|
|
44
|
+
if (code !== "EPERM" && code !== "EACCES") throw error;
|
|
45
|
+
return fallbackPath();
|
|
46
|
+
}
|
|
25
47
|
}
|
package/src/shared/artifacts.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import { TEMP_ARTIFACTS_DIR, type ArtifactPaths, type ArtifactDirPreference } from "./types.ts";
|
|
3
|
+
import { CHAIN_RUNS_DIR, TEMP_ARTIFACTS_DIR, type ArtifactPaths, type ArtifactDirPreference } from "./types.ts";
|
|
4
4
|
import { getAgentDir } from "./utils.ts";
|
|
5
5
|
const CLEANUP_MARKER_FILE = ".last-cleanup";
|
|
6
6
|
const PROJECT_ARTIFACT_ROOT = ".pi-subagents";
|
|
@@ -17,6 +17,21 @@ export function getProjectChainRunsDir(cwd: string): string {
|
|
|
17
17
|
return path.join(getProjectSubagentsDir(cwd), "chain-runs");
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
export function getChainRunsDir(
|
|
21
|
+
projectCwd: string,
|
|
22
|
+
dirPreference: ArtifactDirPreference = "project",
|
|
23
|
+
): string {
|
|
24
|
+
switch (dirPreference) {
|
|
25
|
+
case "project":
|
|
26
|
+
return getProjectChainRunsDir(projectCwd);
|
|
27
|
+
case "session":
|
|
28
|
+
case "temp":
|
|
29
|
+
return CHAIN_RUNS_DIR;
|
|
30
|
+
default:
|
|
31
|
+
throw new Error(`Unsupported artifactDir ${JSON.stringify(dirPreference)}; expected "project", "session", or "temp".`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
20
35
|
export function getArtifactsDir(
|
|
21
36
|
sessionFile: string | null,
|
|
22
37
|
projectCwd?: string,
|
|
@@ -61,6 +76,7 @@ export function ensureArtifactsDir(dir: string): void {
|
|
|
61
76
|
}
|
|
62
77
|
|
|
63
78
|
export function writeArtifact(filePath: string, content: string): void {
|
|
79
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
64
80
|
fs.writeFileSync(filePath, content, "utf-8");
|
|
65
81
|
}
|
|
66
82
|
|
|
@@ -78,6 +94,7 @@ export function formatOutputArtifactContent(input: {
|
|
|
78
94
|
}
|
|
79
95
|
|
|
80
96
|
export function writeMetadata(filePath: string, metadata: object): void {
|
|
97
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
81
98
|
fs.writeFileSync(filePath, JSON.stringify(metadata, null, 2), "utf-8");
|
|
82
99
|
}
|
|
83
100
|
|
|
@@ -77,8 +77,9 @@ function isUnsafeAnthropicThinkingBlock(message: BranchSessionEntry["message"],
|
|
|
77
77
|
const isAnthropic = provider === "anthropic" || api === "anthropic-messages" || model.startsWith("anthropic/");
|
|
78
78
|
if (block.type === "redacted_thinking") return true;
|
|
79
79
|
if (block.type !== "thinking" || !isAnthropic) return false;
|
|
80
|
-
const
|
|
81
|
-
|
|
80
|
+
const record = block as Record<string, unknown>;
|
|
81
|
+
const signature = "thinkingSignature" in record ? record.thinkingSignature : "signature" in record ? record.signature : undefined;
|
|
82
|
+
return record.redacted === true || (typeof signature === "string" && signature.length > 0);
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
function createEntryId(entries: BranchSessionEntry[]): string {
|
|
@@ -38,6 +38,7 @@ export function projectAgentDefinition(agent: AgentConfig): Record<string, unkno
|
|
|
38
38
|
packageName: agent.packageName,
|
|
39
39
|
filePath: agent.filePath,
|
|
40
40
|
fileContentDigest: fileDigest(agent.filePath),
|
|
41
|
+
runner: agent.runner,
|
|
41
42
|
systemPrompt: agent.systemPrompt,
|
|
42
43
|
systemPromptMode: agent.systemPromptMode,
|
|
43
44
|
inheritProjectContext: agent.inheritProjectContext,
|
package/src/shared/settings.ts
CHANGED
|
@@ -120,6 +120,16 @@ export interface CheckpointStep {
|
|
|
120
120
|
message?: string;
|
|
121
121
|
phase?: string;
|
|
122
122
|
label?: string;
|
|
123
|
+
agent?: string;
|
|
124
|
+
task?: string;
|
|
125
|
+
as?: string;
|
|
126
|
+
output?: string | false;
|
|
127
|
+
outputMode?: OutputMode;
|
|
128
|
+
reads?: string[] | false;
|
|
129
|
+
progress?: boolean;
|
|
130
|
+
skill?: string | string[] | false;
|
|
131
|
+
skills?: string[] | false;
|
|
132
|
+
model?: string;
|
|
123
133
|
}
|
|
124
134
|
|
|
125
135
|
/** Parallel step: multiple agents running concurrently */
|