pi-crew 0.1.46 → 0.1.49
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 +97 -0
- package/agents/analyst.md +11 -11
- package/agents/critic.md +11 -11
- package/agents/executor.md +11 -11
- package/agents/explorer.md +11 -11
- package/agents/planner.md +11 -11
- package/agents/reviewer.md +11 -11
- package/agents/security-reviewer.md +11 -11
- package/agents/test-engineer.md +11 -11
- package/agents/verifier.md +11 -11
- package/agents/writer.md +11 -11
- package/docs/next-upgrade-roadmap.md +117 -42
- package/docs/refactor-tasks-phase3.md +394 -394
- package/docs/refactor-tasks-phase4.md +564 -564
- package/docs/refactor-tasks-phase5.md +402 -402
- package/docs/refactor-tasks-phase6.md +662 -662
- package/docs/research/AGENT-EXECUTION-ARCHITECTURE.md +261 -0
- package/docs/research/AGENT-LIFECYCLE-COMPARISON.md +111 -0
- package/docs/research/AUDIT_OH_MY_PI.md +261 -0
- package/docs/research/AUDIT_PI_CREW.md +457 -0
- package/docs/research/CAVEMAN-DEEP-RESEARCH.md +281 -0
- package/docs/research/COMPARISON_OH_MY_PI_VS_PI_CREW.md +264 -0
- package/docs/research/DEEP-RESEARCH-PI-POWERBAR.md +343 -0
- package/docs/research/DEEP_RESEARCH_SUBAGENT_ARCHITECTURE.md +480 -0
- package/docs/research/GAP_CLOSURE_IMPLEMENTATION_PLAN.md +354 -0
- package/docs/research/IMPLEMENTATION_PLAN.md +385 -0
- package/docs/research/LIVE-SESSION-PRODUCTION-READY-PLAN.md +502 -0
- package/docs/research/OH-MY-PI-DEEP-RESEARCH-v14.7.6.md +266 -0
- package/docs/research/REMAINING-GAPS-PLAN.md +363 -0
- package/docs/research/SESSION-SUMMARY-2026-05-08.md +146 -0
- package/docs/research/UI-RESPONSIVENESS-AUDIT.md +173 -0
- package/docs/research-awesome-agent-skills-distillation.md +100 -100
- package/docs/research-extension-examples.md +297 -297
- package/docs/research-extension-system.md +324 -324
- package/docs/research-oh-my-pi-distillation.md +56 -9
- package/docs/research-optimization-plan.md +548 -548
- package/docs/research-phase10-distillation.md +198 -198
- package/docs/research-phase11-distillation.md +201 -201
- package/docs/research-pi-coding-agent.md +357 -357
- package/docs/research-source-pi-crew-reference.md +174 -174
- package/docs/runtime-flow.md +148 -148
- package/docs/source-runtime-refactor-map.md +107 -107
- package/index.ts +6 -6
- package/package.json +99 -98
- package/schema.json +8 -0
- package/skills/async-worker-recovery/SKILL.md +42 -42
- package/skills/context-artifact-hygiene/SKILL.md +52 -52
- package/skills/delegation-patterns/SKILL.md +54 -54
- package/skills/mailbox-interactive/SKILL.md +40 -40
- package/skills/model-routing-context/SKILL.md +39 -39
- package/skills/multi-perspective-review/SKILL.md +58 -58
- package/skills/observability-reliability/SKILL.md +41 -41
- package/skills/orchestration/SKILL.md +157 -0
- package/skills/ownership-session-security/SKILL.md +41 -41
- package/skills/pi-extension-lifecycle/SKILL.md +39 -39
- package/skills/requirements-to-task-packet/SKILL.md +63 -63
- package/skills/resource-discovery-config/SKILL.md +41 -41
- package/skills/runtime-state-reader/SKILL.md +44 -44
- package/skills/secure-agent-orchestration-review/SKILL.md +45 -45
- package/skills/state-mutation-locking/SKILL.md +42 -42
- package/skills/systematic-debugging/SKILL.md +67 -67
- package/skills/ui-render-performance/SKILL.md +39 -39
- package/skills/verification-before-done/SKILL.md +57 -57
- package/skills/worktree-isolation/SKILL.md +39 -39
- package/src/agents/agent-config.ts +6 -0
- package/src/agents/agent-search.ts +98 -0
- package/src/agents/agent-serializer.ts +4 -0
- package/src/agents/discover-agents.ts +17 -4
- package/src/config/config.ts +24 -0
- package/src/config/defaults.ts +11 -0
- package/src/extension/autonomous-policy.ts +26 -33
- package/src/extension/cross-extension-rpc.ts +82 -82
- package/src/extension/help.ts +1 -0
- package/src/extension/management.ts +5 -0
- package/src/extension/register.ts +58 -13
- package/src/extension/registration/commands.ts +33 -1
- package/src/extension/registration/compaction-guard.ts +125 -125
- package/src/extension/registration/team-tool.ts +6 -4
- package/src/extension/run-bundle-schema.ts +89 -89
- package/src/extension/run-index.ts +24 -18
- package/src/extension/run-maintenance.ts +68 -62
- package/src/extension/team-tool/api.ts +23 -2
- package/src/extension/team-tool/cancel.ts +86 -11
- package/src/extension/team-tool/context.ts +3 -0
- package/src/extension/team-tool/handle-settings.ts +188 -188
- package/src/extension/team-tool/inspect.ts +41 -41
- package/src/extension/team-tool/intent-policy.ts +42 -0
- package/src/extension/team-tool/lifecycle-actions.ts +47 -18
- package/src/extension/team-tool/parallel-dispatch.ts +156 -0
- package/src/extension/team-tool/plan.ts +19 -19
- package/src/extension/team-tool/respond.ts +10 -2
- package/src/extension/team-tool/run.ts +3 -2
- package/src/extension/team-tool/status.ts +1 -1
- package/src/extension/team-tool-types.ts +1 -0
- package/src/extension/team-tool.ts +13 -3
- package/src/hooks/registry.ts +61 -0
- package/src/hooks/types.ts +41 -0
- package/src/i18n.ts +184 -184
- package/src/observability/exporters/otlp-exporter.ts +77 -77
- package/src/prompt/prompt-runtime.ts +72 -72
- package/src/runtime/agent-control.ts +108 -2
- package/src/runtime/agent-memory.ts +72 -72
- package/src/runtime/agent-observability.ts +114 -114
- package/src/runtime/async-marker.ts +26 -26
- package/src/runtime/async-runner.ts +3 -1
- package/src/runtime/attention-events.ts +28 -28
- package/src/runtime/background-runner.ts +19 -0
- package/src/runtime/cancellation-token.ts +89 -0
- package/src/runtime/cancellation.ts +61 -51
- package/src/runtime/capability-inventory.ts +116 -0
- package/src/runtime/child-pi.ts +2 -1
- package/src/runtime/code-summary.ts +247 -0
- package/src/runtime/completion-guard.ts +190 -190
- package/src/runtime/crash-recovery.ts +181 -0
- package/src/runtime/crew-agent-records.ts +35 -7
- package/src/runtime/crew-agent-runtime.ts +1 -0
- package/src/runtime/custom-tools/irc-tool.ts +201 -0
- package/src/runtime/custom-tools/submit-result-tool.ts +90 -0
- package/src/runtime/delivery-coordinator.ts +3 -1
- package/src/runtime/direct-run.ts +35 -35
- package/src/runtime/effectiveness.ts +81 -76
- package/src/runtime/event-stream-bridge.ts +90 -0
- package/src/runtime/foreground-control.ts +82 -82
- package/src/runtime/green-contract.ts +46 -46
- package/src/runtime/group-join.ts +106 -106
- package/src/runtime/heartbeat-gradient.ts +28 -28
- package/src/runtime/heartbeat-watcher.ts +124 -124
- package/src/runtime/live-agent-control.ts +88 -88
- package/src/runtime/live-agent-manager.ts +78 -2
- package/src/runtime/live-control-realtime.ts +36 -36
- package/src/runtime/live-extension-bridge.ts +150 -0
- package/src/runtime/live-irc.ts +92 -0
- package/src/runtime/live-session-health.ts +100 -0
- package/src/runtime/live-session-runtime.ts +297 -7
- package/src/runtime/mcp-proxy.ts +113 -0
- package/src/runtime/notebook-helpers.ts +90 -0
- package/src/runtime/orphan-sentinel.ts +7 -0
- package/src/runtime/output-validator.ts +187 -0
- package/src/runtime/parallel-research.ts +44 -44
- package/src/runtime/parallel-utils.ts +57 -0
- package/src/runtime/parent-guard.ts +80 -0
- package/src/runtime/pi-json-output.ts +111 -111
- package/src/runtime/policy-engine.ts +79 -79
- package/src/runtime/progress-event-coalescer.ts +43 -43
- package/src/runtime/prose-compressor.ts +164 -0
- package/src/runtime/recovery-recipes.ts +74 -74
- package/src/runtime/result-extractor.ts +121 -0
- package/src/runtime/role-permission.ts +39 -39
- package/src/runtime/runtime-resolver.ts +1 -4
- package/src/runtime/semaphore.ts +131 -0
- package/src/runtime/sensitive-paths.ts +92 -0
- package/src/runtime/session-resources.ts +25 -25
- package/src/runtime/session-snapshot.ts +59 -59
- package/src/runtime/session-usage.ts +79 -79
- package/src/runtime/sidechain-output.ts +29 -29
- package/src/runtime/stream-preview.ts +177 -0
- package/src/runtime/subagent-manager.ts +3 -2
- package/src/runtime/subprocess-tool-registry.ts +67 -0
- package/src/runtime/supervisor-contact.ts +59 -59
- package/src/runtime/task-display.ts +38 -38
- package/src/runtime/task-output-context.ts +59 -9
- package/src/runtime/task-runner/capabilities.ts +78 -78
- package/src/runtime/task-runner/live-executor.ts +2 -0
- package/src/runtime/task-runner/progress.ts +119 -119
- package/src/runtime/task-runner/prompt-builder.ts +70 -8
- package/src/runtime/task-runner/prompt-pipeline.ts +64 -64
- package/src/runtime/task-runner/result-utils.ts +14 -14
- package/src/runtime/task-runner/run-projection.ts +104 -0
- package/src/runtime/task-runner/state-helpers.ts +22 -22
- package/src/runtime/task-runner.ts +75 -4
- package/src/runtime/team-runner.ts +60 -8
- package/src/runtime/worker-heartbeat.ts +21 -21
- package/src/runtime/worker-startup.ts +57 -57
- package/src/runtime/workspace-tree.ts +298 -0
- package/src/runtime/yield-handler.ts +189 -0
- package/src/schema/config-schema.ts +6 -0
- package/src/schema/team-tool-schema.ts +11 -1
- package/src/skills/discover-skills.ts +67 -0
- package/src/state/active-run-registry.ts +4 -2
- package/src/state/artifact-store.ts +4 -1
- package/src/state/atomic-write.ts +50 -1
- package/src/state/blob-store.ts +117 -0
- package/src/state/contracts.ts +1 -0
- package/src/state/event-log-rotation.ts +158 -0
- package/src/state/event-log.ts +52 -2
- package/src/state/mailbox.ts +87 -7
- package/src/state/state-store.ts +24 -4
- package/src/state/task-claims.ts +44 -44
- package/src/state/types.ts +20 -0
- package/src/state/usage.ts +29 -29
- package/src/subagents/async-entry.ts +1 -1
- package/src/subagents/index.ts +3 -3
- package/src/subagents/live/control.ts +1 -1
- package/src/subagents/live/manager.ts +1 -1
- package/src/subagents/live/realtime.ts +1 -1
- package/src/subagents/live/session-runtime.ts +1 -1
- package/src/subagents/manager.ts +1 -1
- package/src/subagents/spawn.ts +1 -1
- package/src/teams/team-serializer.ts +38 -38
- package/src/types/diff.d.ts +18 -18
- package/src/ui/agent-management-overlay.ts +144 -0
- package/src/ui/crew-footer.ts +101 -101
- package/src/ui/crew-select-list.ts +111 -111
- package/src/ui/crew-widget.ts +11 -2
- package/src/ui/dashboard-panes/cancellation-pane.ts +43 -0
- package/src/ui/dashboard-panes/capability-pane.ts +60 -0
- package/src/ui/dashboard-panes/mailbox-pane.ts +35 -11
- package/src/ui/dashboard-panes/metrics-pane.ts +34 -34
- package/src/ui/dynamic-border.ts +25 -25
- package/src/ui/layout-primitives.ts +106 -106
- package/src/ui/live-run-sidebar.ts +4 -0
- package/src/ui/loaders.ts +158 -158
- package/src/ui/powerbar-publisher.ts +77 -15
- package/src/ui/render-coalescer.ts +51 -0
- package/src/ui/render-diff.ts +119 -119
- package/src/ui/render-scheduler.ts +143 -143
- package/src/ui/run-dashboard.ts +4 -0
- package/src/ui/run-event-bus.ts +209 -0
- package/src/ui/run-snapshot-cache.ts +68 -16
- package/src/ui/snapshot-types.ts +8 -0
- package/src/ui/spinner.ts +17 -17
- package/src/ui/status-colors.ts +58 -58
- package/src/ui/syntax-highlight.ts +116 -116
- package/src/ui/transcript-entries.ts +258 -0
- package/src/utils/atomic-write.ts +33 -33
- package/src/utils/completion-dedupe.ts +63 -63
- package/src/utils/frontmatter.ts +68 -68
- package/src/utils/git.ts +262 -262
- package/src/utils/ids.ts +17 -12
- package/src/utils/incremental-reader.ts +104 -0
- package/src/utils/names.ts +27 -27
- package/src/utils/redaction.ts +44 -44
- package/src/utils/safe-paths.ts +47 -47
- package/src/utils/scan-cache.ts +137 -0
- package/src/utils/sleep.ts +32 -32
- package/src/utils/sse-parser.ts +134 -0
- package/src/utils/task-name-generator.ts +337 -0
- package/src/utils/visual.ts +33 -2
- package/src/workflows/validate-workflow.ts +40 -40
- package/src/worktree/branch-freshness.ts +45 -45
- package/src/worktree/cleanup.ts +2 -1
- package/teams/default.team.md +12 -12
- package/teams/fast-fix.team.md +11 -11
- package/teams/implementation.team.md +18 -18
- package/teams/parallel-research.team.md +14 -14
- package/teams/research.team.md +11 -11
- package/teams/review.team.md +12 -12
- package/workflows/default.workflow.md +29 -29
- package/workflows/fast-fix.workflow.md +22 -22
- package/workflows/implementation.workflow.md +38 -38
- package/workflows/parallel-research.workflow.md +46 -46
- package/workflows/research.workflow.md +22 -22
- package/workflows/review.workflow.md +30 -30
|
@@ -1,62 +1,68 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import type { TeamRunManifest } from "../state/types.ts";
|
|
4
|
-
import { resolveRealContainedPath } from "../utils/safe-paths.ts";
|
|
5
|
-
import { projectCrewRoot } from "../utils/paths.ts";
|
|
6
|
-
import { listRuns } from "./run-index.ts";
|
|
7
|
-
import { logInternalError } from "../utils/internal-error.ts";
|
|
8
|
-
import { redactSecrets } from "../utils/redaction.ts";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import type { TeamRunManifest } from "../state/types.ts";
|
|
4
|
+
import { resolveRealContainedPath } from "../utils/safe-paths.ts";
|
|
5
|
+
import { projectCrewRoot } from "../utils/paths.ts";
|
|
6
|
+
import { listRuns } from "./run-index.ts";
|
|
7
|
+
import { logInternalError } from "../utils/internal-error.ts";
|
|
8
|
+
import { redactSecrets } from "../utils/redaction.ts";
|
|
9
|
+
import { createCancellationToken } from "../runtime/cancellation-token.ts";
|
|
10
|
+
|
|
11
|
+
export interface PruneRunsResult {
|
|
12
|
+
kept: string[];
|
|
13
|
+
removed: string[];
|
|
14
|
+
auditPath?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface PruneRunsOptions {
|
|
18
|
+
intent?: string;
|
|
19
|
+
signal?: AbortSignal;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function isFinished(run: TeamRunManifest): boolean {
|
|
23
|
+
return run.status === "completed" || run.status === "failed" || run.status === "cancelled" || run.status === "blocked";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isSafeToPrune(cwd: string, run: TeamRunManifest): boolean {
|
|
27
|
+
try {
|
|
28
|
+
const crewRoot = projectCrewRoot(cwd);
|
|
29
|
+
resolveRealContainedPath(crewRoot, run.stateRoot);
|
|
30
|
+
resolveRealContainedPath(crewRoot, run.artifactsRoot);
|
|
31
|
+
return true;
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function appendPruneAudit(cwd: string, payload: Record<string, unknown>): string | undefined {
|
|
38
|
+
try {
|
|
39
|
+
const filePath = path.join(projectCrewRoot(cwd), "audit", "prune.jsonl");
|
|
40
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
41
|
+
fs.appendFileSync(filePath, `${JSON.stringify(redactSecrets({ ...payload, auditedAt: new Date().toISOString() }))}\n`, "utf-8");
|
|
42
|
+
return filePath;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
logInternalError("prune.audit-write", error, `cwd=${cwd}`);
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function pruneFinishedRuns(cwd: string, keep: number, options: PruneRunsOptions = {}): PruneRunsResult {
|
|
50
|
+
const token = createCancellationToken({ signal: options.signal });
|
|
51
|
+
const finished = listRuns(cwd, options.signal).filter((run) => run.cwd === cwd && isFinished(run)).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
52
|
+
const kept = finished.slice(0, keep).map((run) => run.runId);
|
|
53
|
+
const removed: string[] = [];
|
|
54
|
+
const toRemove = finished.slice(keep);
|
|
55
|
+
for (let i = 0; i < toRemove.length; i++) {
|
|
56
|
+
if (i % 5 === 0) token.heartbeat(`prune:${i}/${toRemove.length}`);
|
|
57
|
+
const run = toRemove[i];
|
|
58
|
+
if (!isSafeToPrune(cwd, run)) {
|
|
59
|
+
logInternalError("prune.path-unsafe", new Error(`Skipping unsafe prune: stateRoot=${run.stateRoot}, artifactsRoot=${run.artifactsRoot}`), `runId=${run.runId}`);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
fs.rmSync(run.stateRoot, { recursive: true, force: true });
|
|
63
|
+
fs.rmSync(run.artifactsRoot, { recursive: true, force: true });
|
|
64
|
+
removed.push(run.runId);
|
|
65
|
+
}
|
|
66
|
+
const auditPath = appendPruneAudit(cwd, { action: "prune", keep, intent: options.intent, kept, removed });
|
|
67
|
+
return { kept, removed, auditPath };
|
|
68
|
+
}
|
|
@@ -5,7 +5,7 @@ import { loadRunManifestById, saveRunManifest, saveRunTasks, updateRunStatus } f
|
|
|
5
5
|
import { withRunLockSync } from "../../state/locks.ts";
|
|
6
6
|
import { canTransitionTaskStatus, isTeamTaskStatus } from "../../state/contracts.ts";
|
|
7
7
|
import { claimTask, releaseTaskClaim, transitionClaimedTaskStatus } from "../../state/task-claims.ts";
|
|
8
|
-
import { acknowledgeMailboxMessage, appendFollowUpMessage, appendMailboxMessage, appendSteeringMessage, readDeliveryState, readMailbox, readMailboxMessage, validateMailbox, type MailboxDirection } from "../../state/mailbox.ts";
|
|
8
|
+
import { acknowledgeMailboxMessage, appendFollowUpMessage, appendMailboxMessage, appendSteeringMessage, readDeliveryState, readMailbox, readMailboxMessage, validateMailbox, type MailboxDirection, type MailboxMessageKind } from "../../state/mailbox.ts";
|
|
9
9
|
import { appendEvent, readEvents, readEventsCursor } from "../../state/event-log.ts";
|
|
10
10
|
import { resolveCrewRuntime } from "../../runtime/runtime-resolver.ts";
|
|
11
11
|
import { probeLiveSessionRuntime } from "../../subagents/live/session-runtime.ts";
|
|
@@ -17,6 +17,7 @@ import { readForegroundControlStatus, writeForegroundInterruptRequest } from "..
|
|
|
17
17
|
import { followUpLiveAgent, getLiveAgent, listLiveAgents, resumeLiveAgent, steerLiveAgent, stopLiveAgent } from "../../subagents/live/manager.ts";
|
|
18
18
|
import { appendLiveAgentControlRequest } from "../../subagents/live/control.ts";
|
|
19
19
|
import { liveControlRealtimeMessage, publishLiveControlRealtime } from "../../subagents/live/realtime.ts";
|
|
20
|
+
import { buildCapabilityInventory } from "../../runtime/capability-inventory.ts";
|
|
20
21
|
import { resolveRealContainedPath } from "../../utils/safe-paths.ts";
|
|
21
22
|
import type { PiTeamsToolResult } from "../tool-result.ts";
|
|
22
23
|
import { configRecord, result, type TeamContext } from "./context.ts";
|
|
@@ -76,6 +77,10 @@ export async function handleApi(params: TeamToolParamsValue, ctx: TeamContext):
|
|
|
76
77
|
});
|
|
77
78
|
return result(JSON.stringify(filtered, null, 2), { action: "api", status: "ok", ...(runIdFilter ? { runId: runIdFilter } : {}) });
|
|
78
79
|
}
|
|
80
|
+
if (operation === "inventory") {
|
|
81
|
+
const inventory = buildCapabilityInventory(ctx.cwd, ctx.config);
|
|
82
|
+
return result(JSON.stringify(inventory, null, 2), { action: "api", status: "ok" });
|
|
83
|
+
}
|
|
79
84
|
if (!params.runId) return result("API requires runId.", { action: "api", status: "error" }, true);
|
|
80
85
|
const loaded = loadRunManifestById(ctx.cwd, params.runId);
|
|
81
86
|
if (!loaded) return result(`Run '${params.runId}' not found.`, { action: "api", status: "error" }, true);
|
|
@@ -275,9 +280,10 @@ export async function handleApi(params: TeamToolParamsValue, ctx: TeamContext):
|
|
|
275
280
|
if (operation === "read-mailbox") {
|
|
276
281
|
const direction = cfg.direction === "inbox" || cfg.direction === "outbox" ? cfg.direction as MailboxDirection : undefined;
|
|
277
282
|
const taskId = typeof cfg.taskId === "string" ? cfg.taskId : undefined;
|
|
283
|
+
const kind = typeof cfg.kind === "string" && ["message", "steer", "follow-up", "response", "group_join"].includes(cfg.kind) ? cfg.kind as MailboxMessageKind : undefined;
|
|
278
284
|
if (taskId && !loaded.tasks.some((task) => task.id === taskId)) return result(`API read-mailbox taskId '${taskId}' does not match a run task.`, { action: "api", status: "error", runId: loaded.manifest.runId }, true);
|
|
279
285
|
try {
|
|
280
|
-
return result(JSON.stringify(readMailbox(loaded.manifest, direction, taskId), null, 2), { action: "api", status: "ok", runId: loaded.manifest.runId, artifactsRoot: loaded.manifest.artifactsRoot });
|
|
286
|
+
return result(JSON.stringify(readMailbox(loaded.manifest, direction, taskId, kind), null, 2), { action: "api", status: "ok", runId: loaded.manifest.runId, artifactsRoot: loaded.manifest.artifactsRoot });
|
|
281
287
|
} catch (error) {
|
|
282
288
|
const message = error instanceof Error ? error.message : String(error);
|
|
283
289
|
return result(message, { action: "api", status: "error", runId: loaded.manifest.runId }, true);
|
|
@@ -416,5 +422,20 @@ export async function handleApi(params: TeamToolParamsValue, ctx: TeamContext):
|
|
|
416
422
|
return result(message, { action: "api", status: "error", runId: loaded.manifest.runId }, true);
|
|
417
423
|
}
|
|
418
424
|
}
|
|
425
|
+
if (operation === "diff") {
|
|
426
|
+
const diffArtifacts = loaded.manifest.artifacts.filter(a => a.kind === "diff" || a.kind === "patch");
|
|
427
|
+
if (diffArtifacts.length === 0) {
|
|
428
|
+
return result(`No diff artifacts found for run ${loaded.manifest.runId}. Diffs are captured in worktree mode.`, { action: "api", status: "ok", runId: loaded.manifest.runId, intent: `diff ${loaded.manifest.runId}: no diffs` });
|
|
429
|
+
}
|
|
430
|
+
const parts: string[] = [`Diff artifacts for run ${loaded.manifest.runId}:`];
|
|
431
|
+
for (const artifact of diffArtifacts) {
|
|
432
|
+
const content = safeReadContainedFile(loaded.manifest.artifactsRoot, artifact.path);
|
|
433
|
+
if (content) {
|
|
434
|
+
const display = content.length > 4000 ? content.slice(0, 4000) + "\n... (truncated)" : content;
|
|
435
|
+
parts.push(`\n--- ${artifact.path} ---\n${display}`);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return result(parts.join("\n"), { action: "api", status: "ok", runId: loaded.manifest.runId, artifactsRoot: loaded.manifest.artifactsRoot, intent: `diff ${loaded.manifest.runId}` });
|
|
439
|
+
}
|
|
419
440
|
return result(`Unknown API operation: ${operation}`, { action: "api", status: "error", runId: loaded.manifest.runId }, true);
|
|
420
441
|
}
|
|
@@ -3,11 +3,13 @@ import { withRunLockSync } from "../../state/locks.ts";
|
|
|
3
3
|
import { loadRunManifestById, saveRunTasks, updateRunStatus } from "../../state/state-store.ts";
|
|
4
4
|
import { saveCrewAgents, recordFromTask } from "../../runtime/crew-agent-records.ts";
|
|
5
5
|
import { writeForegroundInterruptRequest } from "../../runtime/foreground-control.ts";
|
|
6
|
-
import { cancellationReasonFromUnknown } from "../../runtime/cancellation.ts";
|
|
6
|
+
import { cancellationReasonFromUnknown, buildSyntheticTerminalEvidence, type CancellationReason } from "../../runtime/cancellation.ts";
|
|
7
7
|
import { appendEvent } from "../../state/event-log.ts";
|
|
8
8
|
import { logInternalError } from "../../utils/internal-error.ts";
|
|
9
|
+
import { executeHook, appendHookEvent } from "../../hooks/registry.ts";
|
|
9
10
|
import type { PiTeamsToolResult } from "../tool-result.ts";
|
|
10
11
|
import { result, type TeamContext } from "./context.ts";
|
|
12
|
+
import { enforceDestructiveIntent, intentFromConfig } from "./intent-policy.ts";
|
|
11
13
|
|
|
12
14
|
export interface AbortOwnedResult {
|
|
13
15
|
abortedIds: string[];
|
|
@@ -61,25 +63,93 @@ function configFromParams(params: TeamToolParamsValue): Record<string, unknown>
|
|
|
61
63
|
return params.config && typeof params.config === "object" && !Array.isArray(params.config) ? params.config : undefined;
|
|
62
64
|
}
|
|
63
65
|
|
|
64
|
-
function cancelReasonFromParams(params: TeamToolParamsValue):
|
|
66
|
+
function cancelReasonFromParams(params: TeamToolParamsValue): CancellationReason {
|
|
65
67
|
const config = configFromParams(params);
|
|
66
68
|
const rawReason = config?.reason ?? config?.cancelReason;
|
|
67
69
|
const reason = rawReason === undefined ? { code: "caller_cancelled" as const, message: "Run cancelled by user request." } : cancellationReasonFromUnknown(rawReason);
|
|
68
70
|
return { code: reason.code, message: reason.message };
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
function
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
export async function handleRetry(params: TeamToolParamsValue, ctx: TeamContext): Promise<PiTeamsToolResult> {
|
|
74
|
+
if (!params.runId) return result("Retry requires runId.", { action: "retry", status: "error" }, true);
|
|
75
|
+
const loaded = loadRunManifestById(ctx.cwd, params.runId);
|
|
76
|
+
if (!loaded) return result(`Run '${params.runId}' not found.`, { action: "retry", status: "error" }, true);
|
|
77
|
+
|
|
78
|
+
// Pre-lock ownership check: reject foreign-owned runs
|
|
79
|
+
const foreignRun = typeof loaded.manifest.ownerSessionId === "string" && loaded.manifest.ownerSessionId !== ctx.sessionId;
|
|
80
|
+
if (foreignRun) {
|
|
81
|
+
return result(`Run ${loaded.manifest.runId} belongs to another session; not retried.`, { action: "retry", status: "error", runId: loaded.manifest.runId }, true);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Execute before_retry hook after ownership confirmed, before mutation lock
|
|
85
|
+
const hookReport = await executeHook("before_retry", { runId: loaded.manifest.runId, cwd: ctx.cwd });
|
|
86
|
+
appendHookEvent(loaded.manifest, hookReport);
|
|
87
|
+
if (hookReport.outcome === "block") {
|
|
88
|
+
return result(`Retry blocked by hook: ${hookReport.reason ?? "before_retry hook blocked the operation."}`, { action: "retry", status: "error", runId: loaded.manifest.runId }, true);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const targetTaskId = typeof params.taskId === "string" ? params.taskId : undefined;
|
|
92
|
+
|
|
93
|
+
return withRunLockSync(loaded.manifest, () => {
|
|
94
|
+
const retryableStatuses: ReadonlySet<string> = new Set(["failed", "cancelled"]);
|
|
95
|
+
|
|
96
|
+
const matchingTasks = loaded.tasks.filter((task) => {
|
|
97
|
+
if (targetTaskId && task.id !== targetTaskId) return false;
|
|
98
|
+
return retryableStatuses.has(task.status);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (matchingTasks.length === 0) {
|
|
102
|
+
return result(targetTaskId ? `Task '${targetTaskId}' is not failed/cancelled; nothing to retry.` : "No failed/cancelled tasks to retry.", { action: "retry", status: "error", runId: loaded.manifest.runId }, true);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const retriedIds = new Set(matchingTasks.map((t) => t.id));
|
|
106
|
+
const tasks = loaded.tasks.map((task) => {
|
|
107
|
+
if (!retriedIds.has(task.id)) return task;
|
|
108
|
+
const { error: _error, finishedAt: _finishedAt, terminalEvidence: _terminalEvidence, ...rest } = task;
|
|
109
|
+
return { ...rest, status: "queued" as const };
|
|
110
|
+
});
|
|
111
|
+
saveRunTasks(loaded.manifest, tasks);
|
|
112
|
+
try {
|
|
113
|
+
saveCrewAgents(loaded.manifest, tasks.map((task) => recordFromTask(loaded.manifest, task, "child-process")));
|
|
114
|
+
} catch (error) {
|
|
115
|
+
logInternalError("team-tool.handleRetry.crewAgents", error, `runId=${loaded.manifest.runId}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const retriedTaskIds = [...retriedIds];
|
|
119
|
+
for (const taskId of retriedTaskIds) {
|
|
120
|
+
appendEvent(loaded.manifest.eventsPath, { type: "task.retried", runId: loaded.manifest.runId, taskId, message: `Task ${taskId} queued for retry.` });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return result(`Retried ${retriedTaskIds.length} task(s) in run ${loaded.manifest.runId}.`, {
|
|
124
|
+
action: "retry",
|
|
125
|
+
status: "ok",
|
|
126
|
+
runId: loaded.manifest.runId,
|
|
127
|
+
retriedTaskIds: retriedTaskIds,
|
|
128
|
+
intent: `retrying ${retriedTaskIds.length} task(s) in ${loaded.manifest.runId}`,
|
|
129
|
+
});
|
|
130
|
+
});
|
|
77
131
|
}
|
|
78
132
|
|
|
79
|
-
export function handleCancel(params: TeamToolParamsValue, ctx: TeamContext): PiTeamsToolResult {
|
|
133
|
+
export async function handleCancel(params: TeamToolParamsValue, ctx: TeamContext): Promise<PiTeamsToolResult> {
|
|
134
|
+
const intentError = enforceDestructiveIntent("cancel", params, ctx.config);
|
|
135
|
+
if (intentError) return intentError;
|
|
80
136
|
if (!params.runId) return result("Cancel requires runId.", { action: "cancel", status: "error" }, true);
|
|
81
137
|
const loaded = loadRunManifestById(ctx.cwd, params.runId);
|
|
82
138
|
if (!loaded) return result(`Run '${params.runId}' not found.`, { action: "cancel", status: "error" }, true);
|
|
139
|
+
|
|
140
|
+
// Pre-lock ownership check: reject foreign-owned runs before executing hooks
|
|
141
|
+
const preCheck = abortOwned(loaded.manifest.runId, undefined, ctx);
|
|
142
|
+
if (preCheck.abortedIds.length === 0 && preCheck.foreignIds.length > 0) {
|
|
143
|
+
return result(`Run ${loaded.manifest.runId} belongs to another session; not cancelled.`, { action: "cancel", status: "error", runId: loaded.manifest.runId, foreignIds: preCheck.foreignIds }, true);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Execute before_cancel hook after ownership confirmed, before mutation lock
|
|
147
|
+
const hookReport = await executeHook("before_cancel", { runId: loaded.manifest.runId, cwd: ctx.cwd });
|
|
148
|
+
appendHookEvent(loaded.manifest, hookReport);
|
|
149
|
+
if (hookReport.outcome === "block") {
|
|
150
|
+
return result(`Cancel blocked by hook: ${hookReport.reason ?? "before_cancel hook blocked the operation."}`, { action: "cancel", status: "error", runId: loaded.manifest.runId }, true);
|
|
151
|
+
}
|
|
152
|
+
|
|
83
153
|
return withRunLockSync(loaded.manifest, () => {
|
|
84
154
|
if ((loaded.manifest.status === "completed" || loaded.manifest.status === "cancelled") && !params.force) return result(`Run ${loaded.manifest.runId} is already ${loaded.manifest.status}; nothing to cancel. Use force: true to mark it cancelled anyway.`, { action: "cancel", status: "ok", runId: loaded.manifest.runId, artifactsRoot: loaded.manifest.artifactsRoot });
|
|
85
155
|
|
|
@@ -90,13 +160,17 @@ export function handleCancel(params: TeamToolParamsValue, ctx: TeamContext): PiT
|
|
|
90
160
|
}
|
|
91
161
|
const cancellableIds = new Set(abortResult.abortedIds);
|
|
92
162
|
const cancelReason = cancelReasonFromParams(params);
|
|
93
|
-
const cancelIntent =
|
|
163
|
+
const cancelIntent = intentFromConfig(params.config);
|
|
94
164
|
const cancelData = cancelIntent ? { reason: cancelReason.code, intent: cancelIntent } : { reason: cancelReason.code };
|
|
95
165
|
const cancelMessage = `${cancelReason.message} (${cancelReason.code})`;
|
|
96
166
|
|
|
97
167
|
const tasks = loaded.tasks.map((task) => {
|
|
98
168
|
if (cancellableIds.has(task.id) && (task.status === "queued" || task.status === "running" || task.status === "waiting")) {
|
|
99
|
-
|
|
169
|
+
const base = { ...task, status: "cancelled" as const, finishedAt: new Date().toISOString(), error: cancelMessage };
|
|
170
|
+
if (task.status === "running") {
|
|
171
|
+
return { ...base, terminalEvidence: [...(task.terminalEvidence ?? []), buildSyntheticTerminalEvidence("worker", cancelReason, task.startedAt)] };
|
|
172
|
+
}
|
|
173
|
+
return base;
|
|
100
174
|
}
|
|
101
175
|
return task;
|
|
102
176
|
});
|
|
@@ -111,6 +185,7 @@ export function handleCancel(params: TeamToolParamsValue, ctx: TeamContext): PiT
|
|
|
111
185
|
} catch (error) {
|
|
112
186
|
logInternalError("team-tool.handleCancel.interruptRequest", error, `runId=${loaded.manifest.runId}`);
|
|
113
187
|
}
|
|
188
|
+
ctx.abortForegroundRun?.(loaded.manifest.runId);
|
|
114
189
|
for (const taskId of abortResult.abortedIds) {
|
|
115
190
|
appendEvent(loaded.manifest.eventsPath, { type: "task.cancelled", runId: loaded.manifest.runId, taskId, message: cancelMessage, data: cancelData });
|
|
116
191
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import type { PiTeamsConfig } from "../../config/config.ts";
|
|
2
3
|
import type { MetricRegistry } from "../../observability/metric-registry.ts";
|
|
3
4
|
import type { TeamToolDetails } from "../team-tool-types.ts";
|
|
4
5
|
import { toolResult, type PiTeamsToolResult } from "../tool-result.ts";
|
|
@@ -11,8 +12,10 @@ export type TeamContext = Pick<ExtensionContext, "cwd"> & Partial<Pick<Extension
|
|
|
11
12
|
metricRegistry?: MetricRegistry;
|
|
12
13
|
signal?: AbortSignal;
|
|
13
14
|
startForegroundRun?: (runner: (signal?: AbortSignal) => Promise<void>, runId?: string) => void;
|
|
15
|
+
abortForegroundRun?: (runId: string) => boolean;
|
|
14
16
|
onRunStarted?: (runId: string) => void;
|
|
15
17
|
onJsonEvent?: (taskId: string, runId: string, event: unknown) => void;
|
|
18
|
+
config?: PiTeamsConfig;
|
|
16
19
|
};
|
|
17
20
|
|
|
18
21
|
export function withSessionId<T extends Pick<ExtensionContext, "sessionManager">>(ctx: T): T & { sessionId?: string } {
|