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,82 +1,82 @@
|
|
|
1
|
-
import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
|
-
import type { TeamToolParamsValue } from "../schema/team-tool-schema.ts";
|
|
3
|
-
import { handleTeamTool } from "./team-tool.ts";
|
|
4
|
-
import { parseLiveControlRealtimeMessage, publishLiveControlRealtime } from "../runtime/live-control-realtime.ts";
|
|
5
|
-
|
|
6
|
-
export interface EventBusLike {
|
|
7
|
-
on(event: string, handler: (data: unknown) => void): (() => void) | void;
|
|
8
|
-
emit(event: string, data: unknown): void;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export type RpcReply<T = unknown> = { success: true; data?: T } | { success: false; error: string };
|
|
12
|
-
export const PI_CREW_RPC_VERSION = 1;
|
|
13
|
-
|
|
14
|
-
export interface PiCrewRpcHandle {
|
|
15
|
-
unsubscribe(): void;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function requestId(raw: unknown): string | undefined {
|
|
19
|
-
return raw && typeof raw === "object" && !Array.isArray(raw) && typeof (raw as { requestId?: unknown }).requestId === "string" ? (raw as { requestId: string }).requestId : undefined;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function reply(events: EventBusLike, channel: string, id: string | undefined, payload: RpcReply): void {
|
|
23
|
-
if (!id) return;
|
|
24
|
-
events.emit(`${channel}:reply:${id}`, payload);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function textOf(result: Awaited<ReturnType<typeof handleTeamTool>>): string {
|
|
28
|
-
return result.content?.map((item) => item.type === "text" ? item.text : "").join("\n") ?? "";
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function on(events: EventBusLike, channel: string, handler: (raw: unknown) => void): () => void {
|
|
32
|
-
const unsub = events.on(channel, handler);
|
|
33
|
-
return typeof unsub === "function" ? unsub : () => {};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function registerPiCrewRpc(events: EventBusLike | undefined, getCtx: () => ExtensionContext | undefined): PiCrewRpcHandle | undefined {
|
|
37
|
-
if (!events) return undefined;
|
|
38
|
-
const unsubs = [
|
|
39
|
-
on(events, "pi-crew:rpc:ping", (raw) => reply(events, "pi-crew:rpc:ping", requestId(raw), { success: true, data: { version: PI_CREW_RPC_VERSION } })),
|
|
40
|
-
on(events, "pi-crew:rpc:run", async (raw) => {
|
|
41
|
-
const id = requestId(raw);
|
|
42
|
-
try {
|
|
43
|
-
const ctx = getCtx();
|
|
44
|
-
if (!ctx) throw new Error("No active pi-crew session context.");
|
|
45
|
-
const params: TeamToolParamsValue = raw && typeof raw === "object" && !Array.isArray(raw) ? { ...(raw as object), action: "run" } as TeamToolParamsValue : { action: "run" };
|
|
46
|
-
const result = await handleTeamTool(params, ctx);
|
|
47
|
-
reply(events, "pi-crew:rpc:run", id, result.isError ? { success: false, error: textOf(result) } : { success: true, data: result.details });
|
|
48
|
-
} catch (error) {
|
|
49
|
-
reply(events, "pi-crew:rpc:run", id, { success: false, error: error instanceof Error ? error.message : String(error) });
|
|
50
|
-
}
|
|
51
|
-
}),
|
|
52
|
-
on(events, "pi-crew:rpc:status", async (raw) => {
|
|
53
|
-
const id = requestId(raw);
|
|
54
|
-
try {
|
|
55
|
-
const ctx = getCtx();
|
|
56
|
-
if (!ctx) throw new Error("No active pi-crew session context.");
|
|
57
|
-
const runId = raw && typeof raw === "object" && !Array.isArray(raw) ? (raw as { runId?: string }).runId : undefined;
|
|
58
|
-
const result = await handleTeamTool({ action: "status", runId }, ctx);
|
|
59
|
-
reply(events, "pi-crew:rpc:status", id, result.isError ? { success: false, error: textOf(result) } : { success: true, data: { text: textOf(result), details: result.details } });
|
|
60
|
-
} catch (error) {
|
|
61
|
-
reply(events, "pi-crew:rpc:status", id, { success: false, error: error instanceof Error ? error.message : String(error) });
|
|
62
|
-
}
|
|
63
|
-
}),
|
|
64
|
-
on(events, "pi-crew:live-control", (raw) => {
|
|
65
|
-
const request = parseLiveControlRealtimeMessage(raw);
|
|
66
|
-
if (request) publishLiveControlRealtime(request);
|
|
67
|
-
}),
|
|
68
|
-
on(events, "pi-crew:rpc:live-control", async (raw) => {
|
|
69
|
-
const id = requestId(raw);
|
|
70
|
-
try {
|
|
71
|
-
const ctx = getCtx();
|
|
72
|
-
if (!ctx) throw new Error("No active pi-crew session context.");
|
|
73
|
-
const obj = raw && typeof raw === "object" && !Array.isArray(raw) ? raw as Record<string, unknown> : {};
|
|
74
|
-
const result = await handleTeamTool({ action: "api", runId: typeof obj.runId === "string" ? obj.runId : undefined, config: { operation: typeof obj.operation === "string" ? obj.operation : "steer-agent", agentId: obj.agentId, message: obj.message, prompt: obj.prompt } }, ctx);
|
|
75
|
-
reply(events, "pi-crew:rpc:live-control", id, result.isError ? { success: false, error: textOf(result) } : { success: true, data: { text: textOf(result), details: result.details } });
|
|
76
|
-
} catch (error) {
|
|
77
|
-
reply(events, "pi-crew:rpc:live-control", id, { success: false, error: error instanceof Error ? error.message : String(error) });
|
|
78
|
-
}
|
|
79
|
-
}),
|
|
80
|
-
];
|
|
81
|
-
return { unsubscribe: () => unsubs.forEach((unsub) => unsub()) };
|
|
82
|
-
}
|
|
1
|
+
import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import type { TeamToolParamsValue } from "../schema/team-tool-schema.ts";
|
|
3
|
+
import { handleTeamTool } from "./team-tool.ts";
|
|
4
|
+
import { parseLiveControlRealtimeMessage, publishLiveControlRealtime } from "../runtime/live-control-realtime.ts";
|
|
5
|
+
|
|
6
|
+
export interface EventBusLike {
|
|
7
|
+
on(event: string, handler: (data: unknown) => void): (() => void) | void;
|
|
8
|
+
emit(event: string, data: unknown): void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type RpcReply<T = unknown> = { success: true; data?: T } | { success: false; error: string };
|
|
12
|
+
export const PI_CREW_RPC_VERSION = 1;
|
|
13
|
+
|
|
14
|
+
export interface PiCrewRpcHandle {
|
|
15
|
+
unsubscribe(): void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function requestId(raw: unknown): string | undefined {
|
|
19
|
+
return raw && typeof raw === "object" && !Array.isArray(raw) && typeof (raw as { requestId?: unknown }).requestId === "string" ? (raw as { requestId: string }).requestId : undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function reply(events: EventBusLike, channel: string, id: string | undefined, payload: RpcReply): void {
|
|
23
|
+
if (!id) return;
|
|
24
|
+
events.emit(`${channel}:reply:${id}`, payload);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function textOf(result: Awaited<ReturnType<typeof handleTeamTool>>): string {
|
|
28
|
+
return result.content?.map((item) => item.type === "text" ? item.text : "").join("\n") ?? "";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function on(events: EventBusLike, channel: string, handler: (raw: unknown) => void): () => void {
|
|
32
|
+
const unsub = events.on(channel, handler);
|
|
33
|
+
return typeof unsub === "function" ? unsub : () => {};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function registerPiCrewRpc(events: EventBusLike | undefined, getCtx: () => ExtensionContext | undefined): PiCrewRpcHandle | undefined {
|
|
37
|
+
if (!events) return undefined;
|
|
38
|
+
const unsubs = [
|
|
39
|
+
on(events, "pi-crew:rpc:ping", (raw) => reply(events, "pi-crew:rpc:ping", requestId(raw), { success: true, data: { version: PI_CREW_RPC_VERSION } })),
|
|
40
|
+
on(events, "pi-crew:rpc:run", async (raw) => {
|
|
41
|
+
const id = requestId(raw);
|
|
42
|
+
try {
|
|
43
|
+
const ctx = getCtx();
|
|
44
|
+
if (!ctx) throw new Error("No active pi-crew session context.");
|
|
45
|
+
const params: TeamToolParamsValue = raw && typeof raw === "object" && !Array.isArray(raw) ? { ...(raw as object), action: "run" } as TeamToolParamsValue : { action: "run" };
|
|
46
|
+
const result = await handleTeamTool(params, ctx);
|
|
47
|
+
reply(events, "pi-crew:rpc:run", id, result.isError ? { success: false, error: textOf(result) } : { success: true, data: result.details });
|
|
48
|
+
} catch (error) {
|
|
49
|
+
reply(events, "pi-crew:rpc:run", id, { success: false, error: error instanceof Error ? error.message : String(error) });
|
|
50
|
+
}
|
|
51
|
+
}),
|
|
52
|
+
on(events, "pi-crew:rpc:status", async (raw) => {
|
|
53
|
+
const id = requestId(raw);
|
|
54
|
+
try {
|
|
55
|
+
const ctx = getCtx();
|
|
56
|
+
if (!ctx) throw new Error("No active pi-crew session context.");
|
|
57
|
+
const runId = raw && typeof raw === "object" && !Array.isArray(raw) ? (raw as { runId?: string }).runId : undefined;
|
|
58
|
+
const result = await handleTeamTool({ action: "status", runId }, ctx);
|
|
59
|
+
reply(events, "pi-crew:rpc:status", id, result.isError ? { success: false, error: textOf(result) } : { success: true, data: { text: textOf(result), details: result.details } });
|
|
60
|
+
} catch (error) {
|
|
61
|
+
reply(events, "pi-crew:rpc:status", id, { success: false, error: error instanceof Error ? error.message : String(error) });
|
|
62
|
+
}
|
|
63
|
+
}),
|
|
64
|
+
on(events, "pi-crew:live-control", (raw) => {
|
|
65
|
+
const request = parseLiveControlRealtimeMessage(raw);
|
|
66
|
+
if (request) publishLiveControlRealtime(request);
|
|
67
|
+
}),
|
|
68
|
+
on(events, "pi-crew:rpc:live-control", async (raw) => {
|
|
69
|
+
const id = requestId(raw);
|
|
70
|
+
try {
|
|
71
|
+
const ctx = getCtx();
|
|
72
|
+
if (!ctx) throw new Error("No active pi-crew session context.");
|
|
73
|
+
const obj = raw && typeof raw === "object" && !Array.isArray(raw) ? raw as Record<string, unknown> : {};
|
|
74
|
+
const result = await handleTeamTool({ action: "api", runId: typeof obj.runId === "string" ? obj.runId : undefined, config: { operation: typeof obj.operation === "string" ? obj.operation : "steer-agent", agentId: obj.agentId, message: obj.message, prompt: obj.prompt } }, ctx);
|
|
75
|
+
reply(events, "pi-crew:rpc:live-control", id, result.isError ? { success: false, error: textOf(result) } : { success: true, data: { text: textOf(result), details: result.details } });
|
|
76
|
+
} catch (error) {
|
|
77
|
+
reply(events, "pi-crew:rpc:live-control", id, { success: false, error: error instanceof Error ? error.message : String(error) });
|
|
78
|
+
}
|
|
79
|
+
}),
|
|
80
|
+
];
|
|
81
|
+
return { unsubscribe: () => unsubs.forEach((unsub) => unsub()) };
|
|
82
|
+
}
|
package/src/extension/help.ts
CHANGED
|
@@ -6,6 +6,8 @@ import { allAgents, discoverAgents } from "../agents/discover-agents.ts";
|
|
|
6
6
|
import type { TeamToolDetails } from "./team-tool-types.ts";
|
|
7
7
|
import { toolResult, type PiTeamsToolResult } from "./tool-result.ts";
|
|
8
8
|
import type { TeamToolParamsValue } from "../schema/team-tool-schema.ts";
|
|
9
|
+
import type { PiTeamsConfig } from "../config/config.ts";
|
|
10
|
+
import { enforceDestructiveIntent } from "./team-tool/intent-policy.ts";
|
|
9
11
|
import type { TeamConfig, TeamRole } from "../teams/team-config.ts";
|
|
10
12
|
import { serializeTeam } from "../teams/team-serializer.ts";
|
|
11
13
|
import { allTeams, discoverTeams } from "../teams/discover-teams.ts";
|
|
@@ -17,6 +19,7 @@ import { hasOwn, parseConfigObject, requireString, sanitizeName } from "../utils
|
|
|
17
19
|
|
|
18
20
|
interface ManagementContext {
|
|
19
21
|
cwd: string;
|
|
22
|
+
config?: PiTeamsConfig;
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
type MutableSource = "user" | "project";
|
|
@@ -359,6 +362,8 @@ export function handleUpdate(params: TeamToolParamsValue, ctx: ManagementContext
|
|
|
359
362
|
}
|
|
360
363
|
|
|
361
364
|
export function handleDelete(params: TeamToolParamsValue, ctx: ManagementContext): PiTeamsToolResult {
|
|
365
|
+
const intentError = enforceDestructiveIntent("delete", params, ctx.config);
|
|
366
|
+
if (intentError) return intentError;
|
|
362
367
|
if (!params.confirm) return result("delete requires confirm: true.", "error", true);
|
|
363
368
|
const resolved = resolveMutable(ctx, params);
|
|
364
369
|
if (resolved.error) return resolved.error;
|
|
@@ -9,7 +9,7 @@ import { notifyActiveRuns } from "./session-summary.ts";
|
|
|
9
9
|
import { LiveRunSidebar } from "../ui/live-run-sidebar.ts";
|
|
10
10
|
import { registerPiCrewRpc, type PiCrewRpcHandle } from "./cross-extension-rpc.ts";
|
|
11
11
|
import { stopCrewWidget, updateCrewWidget, type CrewWidgetState } from "../ui/crew-widget.ts";
|
|
12
|
-
import { clearPiCrewPowerbar, registerPiCrewPowerbarSegments, updatePiCrewPowerbar } from "../ui/powerbar-publisher.ts";
|
|
12
|
+
import { clearPiCrewPowerbar, disposePowerbarCoalescer, registerPiCrewPowerbarSegments, requestPowerbarUpdate, updatePiCrewPowerbar } from "../ui/powerbar-publisher.ts";
|
|
13
13
|
import { loadRunManifestById, updateRunStatus } from "../state/state-store.ts";
|
|
14
14
|
import type { TeamRunManifest } from "../state/types.ts";
|
|
15
15
|
import { terminateActiveChildPiProcesses } from "../subagents/spawn.ts";
|
|
@@ -37,7 +37,7 @@ import { createMetricFileSink, type MetricSink } from "../observability/metric-s
|
|
|
37
37
|
import { OTLPExporter } from "../observability/exporters/otlp-exporter.ts";
|
|
38
38
|
import { HeartbeatWatcher } from "../runtime/heartbeat-watcher.ts";
|
|
39
39
|
import { appendDeadletter } from "../runtime/deadletter.ts";
|
|
40
|
-
import { detectInterruptedRuns } from "../runtime/crash-recovery.ts";
|
|
40
|
+
import { cancelOrphanedRuns, detectInterruptedRuns, purgeStaleActiveRunIndex } from "../runtime/crash-recovery.ts";
|
|
41
41
|
import { DeliveryCoordinator } from "../runtime/delivery-coordinator.ts";
|
|
42
42
|
import { OverflowRecoveryTracker } from "../runtime/overflow-recovery.ts";
|
|
43
43
|
import { tryRegisterSessionCleanup } from "../runtime/session-resources.ts";
|
|
@@ -113,7 +113,7 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
113
113
|
if (currentCtx) {
|
|
114
114
|
const uiConfig = loadConfig(currentCtx.cwd).config.ui;
|
|
115
115
|
updateCrewWidget(currentCtx, widgetState, uiConfig, getManifestCache(currentCtx.cwd), getRunSnapshotCache(currentCtx.cwd));
|
|
116
|
-
|
|
116
|
+
requestPowerbarUpdate(pi.events, currentCtx.cwd, uiConfig, getManifestCache(currentCtx.cwd), getRunSnapshotCache(currentCtx.cwd), currentCtx, widgetState.notificationCount ?? 0);
|
|
117
117
|
}
|
|
118
118
|
});
|
|
119
119
|
};
|
|
@@ -236,12 +236,12 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
236
236
|
pi.events?.emit?.(event, payload);
|
|
237
237
|
},
|
|
238
238
|
);
|
|
239
|
-
const foregroundControllers = new
|
|
239
|
+
const foregroundControllers = new Map<string | symbol, AbortController>();
|
|
240
240
|
let liveSidebarRunId: string | undefined;
|
|
241
241
|
let renderScheduler: RenderScheduler | undefined;
|
|
242
242
|
let preloadTimer: ReturnType<typeof setTimeout> | undefined;
|
|
243
243
|
const stopSessionBoundSubagents = (): void => {
|
|
244
|
-
for (const controller of foregroundControllers) controller.abort();
|
|
244
|
+
for (const controller of foregroundControllers.values()) controller.abort();
|
|
245
245
|
foregroundControllers.clear();
|
|
246
246
|
subagentManager.abortAll();
|
|
247
247
|
terminateActiveChildPiProcesses();
|
|
@@ -277,7 +277,8 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
277
277
|
const startForegroundRun = (ctx: ExtensionContext, runner: (signal?: AbortSignal) => Promise<void>, runId?: string): void => {
|
|
278
278
|
const ownerGeneration = captureSessionGeneration();
|
|
279
279
|
const controller = new AbortController();
|
|
280
|
-
|
|
280
|
+
const key = runId ?? Symbol();
|
|
281
|
+
foregroundControllers.set(key, controller);
|
|
281
282
|
if (ctx.hasUI) {
|
|
282
283
|
setWorkingIndicator(ctx, { frames: ["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"], intervalMs: 80 });
|
|
283
284
|
ctx.ui.setWorkingMessage(runId ? `pi-crew foreground run ${runId}...` : "pi-crew foreground run...");
|
|
@@ -295,9 +296,10 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
295
296
|
}
|
|
296
297
|
}
|
|
297
298
|
if (isContextCurrent(ctx, ownerGeneration)) ctx.ui.notify(`pi-crew foreground run failed: ${message}`, "error");
|
|
299
|
+
else logInternalError("register.foreground-run-failure", error, `runId=${runId} context disposed`);
|
|
298
300
|
})
|
|
299
301
|
.finally(() => {
|
|
300
|
-
foregroundControllers.delete(
|
|
302
|
+
foregroundControllers.delete(key);
|
|
301
303
|
const ownerCurrent = isContextCurrent(ctx, ownerGeneration);
|
|
302
304
|
if (ownerCurrent && ctx.hasUI) {
|
|
303
305
|
setWorkingIndicator(ctx);
|
|
@@ -334,7 +336,7 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
334
336
|
if (ownerCurrent && currentCtx) {
|
|
335
337
|
const config = loadConfig(currentCtx.cwd).config.ui;
|
|
336
338
|
updateCrewWidget(currentCtx, widgetState, config, getManifestCache(currentCtx.cwd), getRunSnapshotCache(currentCtx.cwd));
|
|
337
|
-
|
|
339
|
+
requestPowerbarUpdate(pi.events, currentCtx.cwd, config, getManifestCache(currentCtx.cwd), getRunSnapshotCache(currentCtx.cwd), currentCtx, widgetState.notificationCount ?? 0);
|
|
338
340
|
}
|
|
339
341
|
});
|
|
340
342
|
});
|
|
@@ -350,8 +352,20 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
350
352
|
if (preloadTimer) { clearTimeout(preloadTimer); preloadTimer = undefined; }
|
|
351
353
|
stopSessionBoundSubagents();
|
|
352
354
|
stopAsyncRunNotifier(notifierState);
|
|
355
|
+
|
|
356
|
+
// P0: Purge all stale active-run-index entries on session cleanup.
|
|
357
|
+
// This handles: normal exit, SIGTERM, Ctrl+C — any case where cleanupRuntime fires.
|
|
358
|
+
// For SIGKILL / crash / SIGHUP (where cleanupRuntime does NOT fire),
|
|
359
|
+
// purgeStaleActiveRunIndex() runs at next session_start instead.
|
|
360
|
+
try {
|
|
361
|
+
purgeStaleActiveRunIndex();
|
|
362
|
+
} catch (error) {
|
|
363
|
+
logInternalError("register.cleanupRuntime.purgeStale", error);
|
|
364
|
+
}
|
|
365
|
+
|
|
353
366
|
stopCrewWidget(currentCtx, widgetState, currentCtx ? loadConfig(currentCtx.cwd).config.ui : undefined);
|
|
354
367
|
clearPiCrewPowerbar(pi.events, currentCtx);
|
|
368
|
+
disposePowerbarCoalescer();
|
|
355
369
|
heartbeatWatcher?.dispose();
|
|
356
370
|
metricSink?.dispose();
|
|
357
371
|
eventMetricSub?.dispose();
|
|
@@ -394,6 +408,30 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
394
408
|
if (widgetState.interval) clearInterval(widgetState.interval);
|
|
395
409
|
widgetState.interval = undefined;
|
|
396
410
|
notifyActiveRuns(ctx);
|
|
411
|
+
|
|
412
|
+
// Auto-cancel orphaned runs from dead sessions
|
|
413
|
+
const currentSessionId = (ctx as unknown as Record<string, unknown>).sessionId as string | undefined;
|
|
414
|
+
if (currentSessionId) {
|
|
415
|
+
try {
|
|
416
|
+
const { cancelled } = cancelOrphanedRuns(ctx.cwd, getManifestCache(ctx.cwd), currentSessionId);
|
|
417
|
+
if (cancelled.length > 0) {
|
|
418
|
+
notifyOperator({ id: `orphan_cleanup`, severity: "info", source: "crash-recovery", title: `Cleaned up ${cancelled.length} orphaned run(s)`, body: `Runs from previous sessions were auto-cancelled: ${cancelled.join(", ")}` });
|
|
419
|
+
}
|
|
420
|
+
} catch (error) {
|
|
421
|
+
logInternalError("register.sessionStart.orphanCleanup", error);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Global purge of stale active-run-index entries (temp dirs, dead workers, etc.)
|
|
426
|
+
try {
|
|
427
|
+
const { purged } = purgeStaleActiveRunIndex();
|
|
428
|
+
if (purged.length > 0) {
|
|
429
|
+
notifyOperator({ id: `active_index_purge`, severity: "info", source: "crash-recovery", title: `Purged ${purged.length} stale active-run-index entr${purged.length === 1 ? "y" : "ies"}`, body: `Cleaned up global active run index` });
|
|
430
|
+
}
|
|
431
|
+
} catch (error) {
|
|
432
|
+
logInternalError("register.sessionStart.globalIndexPurge", error);
|
|
433
|
+
}
|
|
434
|
+
|
|
397
435
|
const loadedConfig = loadConfig(ctx.cwd);
|
|
398
436
|
autoRecoveryLast.clear();
|
|
399
437
|
configureNotifications(ctx);
|
|
@@ -474,7 +512,7 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
474
512
|
} else {
|
|
475
513
|
updateCrewWidget(currentCtx, widgetState, config, activeCache, snapshotCache, manifests);
|
|
476
514
|
}
|
|
477
|
-
|
|
515
|
+
requestPowerbarUpdate(pi.events, currentCtx.cwd, config, activeCache, snapshotCache, currentCtx, widgetState.notificationCount ?? 0, manifests);
|
|
478
516
|
// Health notifications: only warn about genuinely running runs
|
|
479
517
|
const now = Date.now();
|
|
480
518
|
for (const run of manifests) {
|
|
@@ -541,6 +579,12 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
541
579
|
});
|
|
542
580
|
} catch { /* older Pi without resources_discover */ }
|
|
543
581
|
|
|
582
|
+
const abortForegroundRun = (runId: string): boolean => {
|
|
583
|
+
const controller = foregroundControllers.get(runId);
|
|
584
|
+
if (!controller) return false;
|
|
585
|
+
controller.abort();
|
|
586
|
+
return true;
|
|
587
|
+
};
|
|
544
588
|
registerCompactionGuard(pi, { foregroundControllers });
|
|
545
589
|
|
|
546
590
|
// Phase 1.4: Permission gate for destructive team actions.
|
|
@@ -552,14 +596,15 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
552
596
|
const action = typeof input.action === "string" ? input.action : undefined;
|
|
553
597
|
const destructiveActions = new Set(["delete", "forget", "prune", "cleanup"]);
|
|
554
598
|
if (!action || !destructiveActions.has(action)) return;
|
|
555
|
-
|
|
599
|
+
const forceBypassesReferenceChecks = action === "delete" && input.force === true;
|
|
600
|
+
if (input.confirm === true || forceBypassesReferenceChecks) return;
|
|
556
601
|
return {
|
|
557
602
|
block: true,
|
|
558
|
-
reason: `Destructive action '${action}' requires confirm=true (or force=true to bypass reference checks).`,
|
|
603
|
+
reason: `Destructive action '${action}' requires confirm=true${action === "delete" ? " (or force=true to bypass reference checks)" : ""}.`,
|
|
559
604
|
};
|
|
560
605
|
});
|
|
561
606
|
|
|
562
|
-
registerTeamTool(pi, { foregroundControllers, startForegroundRun, openLiveSidebar, getManifestCache, getRunSnapshotCache, getMetricRegistry: () => metricRegistry, widgetState, onJsonEvent: (taskId, runId, event) => {
|
|
607
|
+
registerTeamTool(pi, { foregroundControllers, startForegroundRun, abortForegroundRun, openLiveSidebar, getManifestCache, getRunSnapshotCache, getMetricRegistry: () => metricRegistry, widgetState, onJsonEvent: (taskId, runId, event) => {
|
|
563
608
|
const record = event as Record<string, unknown>;
|
|
564
609
|
const eventType = typeof record.type === "string" ? record.type : undefined;
|
|
565
610
|
if (eventType) overflowTracker?.feedEvent(taskId, runId, eventType);
|
|
@@ -567,7 +612,7 @@ export function registerPiTeams(pi: ExtensionAPI): void {
|
|
|
567
612
|
registerSubagentTools(pi, subagentManager, { ownerSessionGeneration: captureSessionGeneration });
|
|
568
613
|
time("register.tools");
|
|
569
614
|
|
|
570
|
-
registerTeamCommands(pi, { startForegroundRun, openLiveSidebar, getManifestCache, getRunSnapshotCache, getMetricRegistry: () => metricRegistry, dismissNotifications: () => {
|
|
615
|
+
registerTeamCommands(pi, { startForegroundRun, abortForegroundRun, openLiveSidebar, getManifestCache, getRunSnapshotCache, getMetricRegistry: () => metricRegistry, dismissNotifications: () => {
|
|
571
616
|
widgetState.notificationCount = 0;
|
|
572
617
|
if (currentCtx) {
|
|
573
618
|
const uiConfig = loadConfig(currentCtx.cwd).config.ui;
|
|
@@ -27,6 +27,7 @@ import type { MetricRegistry } from "../../observability/metric-registry.ts";
|
|
|
27
27
|
|
|
28
28
|
export interface RegisterTeamCommandsDeps {
|
|
29
29
|
startForegroundRun: (ctx: ExtensionContext, runner: (signal?: AbortSignal) => Promise<void>, runId?: string) => void;
|
|
30
|
+
abortForegroundRun: (runId: string) => boolean;
|
|
30
31
|
openLiveSidebar: (ctx: ExtensionContext, runId: string) => void;
|
|
31
32
|
getManifestCache: (cwd: string) => { list(max?: number): TeamRunManifest[] };
|
|
32
33
|
getRunSnapshotCache?: (cwd: string) => ReturnType<typeof createRunSnapshotCache>;
|
|
@@ -135,7 +136,7 @@ export function registerTeamCommands(pi: ExtensionAPI, deps: RegisterTeamCommand
|
|
|
135
136
|
pi.registerCommand("team-run", {
|
|
136
137
|
description: "Manually start a pi-crew run (agent may also use the team tool autonomously)",
|
|
137
138
|
handler: async (args: string, ctx: ExtensionCommandContext) => {
|
|
138
|
-
const result = await handleTeamTool(parseRunArgs(args), { ...teamCommandContext(ctx), metricRegistry: deps.getMetricRegistry?.(), startForegroundRun: (runner, runId) => deps.startForegroundRun(ctx as ExtensionContext, runner, runId), onRunStarted: (runId) => deps.openLiveSidebar(ctx as ExtensionContext, runId) });
|
|
139
|
+
const result = await handleTeamTool(parseRunArgs(args), { ...teamCommandContext(ctx), metricRegistry: deps.getMetricRegistry?.(), startForegroundRun: (runner, runId) => deps.startForegroundRun(ctx as ExtensionContext, runner, runId), abortForegroundRun: deps.abortForegroundRun, onRunStarted: (runId) => deps.openLiveSidebar(ctx as ExtensionContext, runId) });
|
|
139
140
|
await notifyCommandResult(ctx, commandText(result));
|
|
140
141
|
},
|
|
141
142
|
});
|
|
@@ -157,6 +158,21 @@ export function registerTeamCommands(pi: ExtensionAPI, deps: RegisterTeamCommand
|
|
|
157
158
|
} });
|
|
158
159
|
}
|
|
159
160
|
|
|
161
|
+
pi.registerCommand("team-retry", {
|
|
162
|
+
description: "Retry failed/cancelled pi-crew tasks: <runId> [taskId]",
|
|
163
|
+
handler: async (args: string, ctx: ExtensionCommandContext) => {
|
|
164
|
+
const tokens = args.trim().split(/\s+/).filter(Boolean);
|
|
165
|
+
const runId = tokens.shift();
|
|
166
|
+
const taskId = tokens.shift();
|
|
167
|
+
if (!runId) {
|
|
168
|
+
await notifyCommandResult(ctx, "Usage: /team-retry <runId> [taskId]");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const retryResult = await handleTeamTool({ action: "retry", runId, taskId }, teamCommandContext(ctx));
|
|
172
|
+
await notifyCommandResult(ctx, commandText(retryResult));
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
160
176
|
pi.registerCommand("team-respond", {
|
|
161
177
|
description: "Respond to a waiting pi-crew task: <runId> <taskId|--all> <message>",
|
|
162
178
|
handler: async (args: string, ctx: ExtensionCommandContext) => {
|
|
@@ -170,6 +186,22 @@ export function registerTeamCommands(pi: ExtensionAPI, deps: RegisterTeamCommand
|
|
|
170
186
|
},
|
|
171
187
|
});
|
|
172
188
|
|
|
189
|
+
pi.registerCommand("team-follow-up", {
|
|
190
|
+
description: "Send a follow-up prompt to a pi-crew task: <runId> <taskId> <prompt>",
|
|
191
|
+
handler: async (args: string, ctx: ExtensionCommandContext) => {
|
|
192
|
+
const tokens = args.trim().split(/\s+/).filter(Boolean);
|
|
193
|
+
const runId = tokens.shift();
|
|
194
|
+
const taskId = tokens.shift();
|
|
195
|
+
const prompt = tokens.join(" ") || undefined;
|
|
196
|
+
if (!runId || !taskId || !prompt) {
|
|
197
|
+
await notifyCommandResult(ctx, "Usage: /team-follow-up <runId> <taskId> <prompt>. Use /team-respond for waiting-task replies.");
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
const result = await handleTeamTool({ action: "api", runId, config: { operation: "follow-up-agent", taskId, prompt } }, teamCommandContext(ctx));
|
|
201
|
+
await notifyCommandResult(ctx, commandText(result));
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
|
|
173
205
|
pi.registerCommand("team-api", {
|
|
174
206
|
description: "Run safe pi-crew API interop operations: <runId> <operation> [key=value]",
|
|
175
207
|
handler: async (args: string, ctx: ExtensionCommandContext) => {
|