pi-crew 0.9.68 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/CHANGELOG.md +222 -0
  2. package/NOTICE.md +21 -0
  3. package/README.md +44 -2
  4. package/agents/analyst.md +1 -1
  5. package/agents/cold-verifier.md +3 -1
  6. package/agents/critic.md +1 -1
  7. package/agents/executor.md +1 -1
  8. package/agents/explorer.md +1 -1
  9. package/agents/planner.md +1 -1
  10. package/agents/reviewer.md +1 -1
  11. package/agents/security-reviewer.md +1 -1
  12. package/agents/test-engineer.md +1 -1
  13. package/agents/verifier.md +1 -1
  14. package/agents/writer.md +1 -1
  15. package/dist/index.mjs +68113 -60774
  16. package/docs/README.md +2 -0
  17. package/docs/actions-reference.md +31 -0
  18. package/docs/commands-reference.md +17 -6
  19. package/docs/resource-formats.md +13 -0
  20. package/package.json +4 -2
  21. package/schema.json +503 -91
  22. package/scripts/resource-sampler.mjs +36 -2
  23. package/skills/requirements-to-task-packet/SKILL.md +26 -0
  24. package/skills/widget-rendering/SKILL.md +7 -7
  25. package/src/agents/agent-config.ts +2 -1
  26. package/src/agents/discover-agents.ts +23 -14
  27. package/src/config/config-merge.ts +183 -0
  28. package/src/config/config-validation.ts +687 -0
  29. package/src/config/config.ts +22 -864
  30. package/src/config/defaults.ts +43 -2
  31. package/src/config/drift-detector.ts +1 -1
  32. package/src/config/env-vars.ts +691 -0
  33. package/src/config/role-tools.ts +11 -9
  34. package/src/config/sanitize-project-config.ts +172 -0
  35. package/src/config/types.ts +49 -1
  36. package/src/extension/async-notifier.ts +25 -2
  37. package/src/extension/crew-cleanup.ts +13 -0
  38. package/src/extension/crew-vibes/config.ts +2 -1
  39. package/src/extension/crew-vibes/footer.ts +19 -0
  40. package/src/extension/crew-vibes/index.ts +11 -1
  41. package/src/extension/plan-orchestrate.ts +132 -0
  42. package/src/extension/register.ts +8 -0
  43. package/src/extension/registration/command-registration.ts +1 -0
  44. package/src/extension/registration/commands/dashboard.ts +158 -0
  45. package/src/extension/registration/commands/index.ts +35 -0
  46. package/src/extension/registration/commands/manage.ts +303 -0
  47. package/src/extension/registration/commands/run.ts +228 -0
  48. package/src/extension/registration/commands/shared.ts +639 -0
  49. package/src/extension/registration/commands/status.ts +60 -0
  50. package/src/extension/registration/commands.ts +13 -1224
  51. package/src/extension/registration/foreground-run-controller.ts +10 -2
  52. package/src/extension/registration/lifecycle-handlers.ts +178 -17
  53. package/src/extension/registration/runtime-cleanup.ts +23 -5
  54. package/src/extension/registration/subagent-tools.ts +218 -9
  55. package/src/extension/registration/team-tool.ts +5 -1
  56. package/src/extension/registration/ui.ts +5 -4
  57. package/src/extension/rpc-hmac.ts +5 -3
  58. package/src/extension/team-tool/api/heartbeat.ts +47 -10
  59. package/src/extension/team-tool/api/plan-approval.ts +9 -0
  60. package/src/extension/team-tool/api/task-claims.ts +109 -40
  61. package/src/extension/team-tool/cancel.ts +84 -50
  62. package/src/extension/team-tool/dispatch/index.ts +1 -0
  63. package/src/extension/team-tool/dispatch/run.ts +4 -1
  64. package/src/extension/team-tool/doctor.ts +103 -1
  65. package/src/extension/team-tool/orchestrate.ts +66 -1
  66. package/src/extension/team-tool/plans.ts +192 -0
  67. package/src/extension/team-tool/respond.ts +197 -65
  68. package/src/extension/team-tool/run-deadline.ts +35 -3
  69. package/src/extension/team-tool/run-intent.ts +63 -0
  70. package/src/extension/team-tool/run.ts +74 -20
  71. package/src/extension/team-tool/status.ts +84 -26
  72. package/src/extension/team-tool.ts +11 -2
  73. package/src/hooks/registry.ts +1 -6
  74. package/src/i18n.ts +9 -0
  75. package/src/prompt/prompt-runtime.ts +521 -2
  76. package/src/prompt/worker-events-channel.ts +173 -0
  77. package/src/runtime/README.md +8 -8
  78. package/src/runtime/async-runner.ts +7 -3
  79. package/src/runtime/background-runner.ts +42 -14
  80. package/src/runtime/broker/broker-issuer.ts +9 -2
  81. package/src/runtime/broker/crew-broker-tokens.ts +43 -6
  82. package/src/runtime/broker/crew-broker.ts +838 -10
  83. package/src/runtime/broker/wait-status-cache.ts +157 -0
  84. package/src/runtime/budget-enforcement.ts +281 -0
  85. package/src/runtime/child-pi/child-pi-constants.ts +8 -0
  86. package/src/runtime/child-pi/child-pi-spawn.ts +60 -14
  87. package/src/runtime/child-pi/child-pi-streams.ts +21 -1
  88. package/src/runtime/child-pi/child-pi-timers.ts +324 -0
  89. package/src/runtime/child-pi/child-pi.ts +97 -201
  90. package/src/runtime/child-pi/mock-fixtures.ts +16 -2
  91. package/src/runtime/crew-agent-records.ts +259 -14
  92. package/src/runtime/delegate-spawn.ts +148 -0
  93. package/src/runtime/detached-run-results.ts +90 -0
  94. package/src/runtime/deterministic-ast.ts +2 -1
  95. package/src/runtime/dispatch-batch.ts +945 -0
  96. package/src/runtime/finalize-run.ts +557 -0
  97. package/src/runtime/goal-workflow/adaptive-plan.ts +116 -15
  98. package/src/runtime/goal-workflow/dynamic-workflow-runner.ts +8 -3
  99. package/src/runtime/goal-workflow/goal-state-store.ts +1 -1
  100. package/src/runtime/group-join.ts +11 -125
  101. package/src/runtime/live-session/live-session-runtime.ts +26 -1
  102. package/src/runtime/merge-gate.ts +32 -10
  103. package/src/runtime/merge-loop.ts +130 -0
  104. package/src/runtime/model/model-budget-summary.ts +53 -0
  105. package/src/runtime/model/model-fallback.ts +36 -2
  106. package/src/runtime/model/pi-args.ts +10 -0
  107. package/src/runtime/model/provider-extensions.ts +10 -0
  108. package/src/runtime/orphan-worker-registry.ts +1 -1
  109. package/src/runtime/output/output-validator.ts +45 -0
  110. package/src/runtime/parent-guard.ts +3 -1
  111. package/src/runtime/peer-dep.ts +2 -1
  112. package/src/runtime/per-write-validator.ts +0 -5
  113. package/src/runtime/pi-spawn.ts +61 -15
  114. package/src/runtime/plan-approval.ts +125 -0
  115. package/src/runtime/plan-replan.ts +151 -0
  116. package/src/runtime/process-status.ts +16 -1
  117. package/src/runtime/recovery/checkpoint.ts +0 -18
  118. package/src/runtime/recovery/crash-recovery.ts +111 -46
  119. package/src/runtime/run-tracker.ts +77 -10
  120. package/src/runtime/scheduler-context.ts +98 -0
  121. package/src/runtime/scheduling/coalesce-tasks.ts +5 -0
  122. package/src/runtime/scheduling/global-worker-cap.ts +2 -1
  123. package/src/runtime/scheduling/nested-slots.ts +70 -0
  124. package/src/runtime/scheduling/run-coalesced-task-group.ts +64 -13
  125. package/src/runtime/scheduling/task-graph-scheduler.ts +0 -10
  126. package/src/runtime/settings-store.ts +219 -0
  127. package/src/runtime/spawn-policy.ts +217 -0
  128. package/src/runtime/stale-reconciler.ts +87 -6
  129. package/src/runtime/subagent-manager.ts +25 -1
  130. package/src/runtime/task-output-context.ts +230 -9
  131. package/src/runtime/task-packet.ts +23 -1
  132. package/src/runtime/task-runner/child-executor.ts +106 -7
  133. package/src/runtime/task-runner/post-execution.ts +125 -1
  134. package/src/runtime/task-runner/pre-execution.ts +39 -1
  135. package/src/runtime/task-runner/prompt-builder.ts +51 -1
  136. package/src/runtime/task-runner/retrieval-orchestrator.ts +72 -18
  137. package/src/runtime/task-runner/spec-evidence.ts +403 -0
  138. package/src/runtime/task-runner/state-helpers.ts +26 -24
  139. package/src/runtime/task-runner.ts +11 -0
  140. package/src/runtime/team-runner.ts +132 -1673
  141. package/src/runtime/verification/spec-sandbox.ts +255 -0
  142. package/src/runtime/verification/verification-gates.ts +3 -2
  143. package/src/runtime/verification/verification-worktree.ts +2 -1
  144. package/src/runtime/workflow-phase-advance.ts +100 -0
  145. package/src/runtime/workspace-tree.ts +9 -0
  146. package/src/schema/config-schema.ts +66 -26
  147. package/src/schema/sensitive-config-paths.ts +64 -0
  148. package/src/schema/team-tool-schema.ts +13 -3
  149. package/src/state/README.md +4 -10
  150. package/src/state/atomic-write.ts +20 -3
  151. package/src/state/contracts.ts +38 -0
  152. package/src/state/coordination/mailbox.ts +12 -2
  153. package/src/state/event-log/cursor.ts +223 -0
  154. package/src/state/event-log/event-log-rotation.ts +12 -4
  155. package/src/state/event-log/event-log.ts +152 -369
  156. package/src/state/event-log/sequence-cache.ts +373 -0
  157. package/src/state/event-log/worker-atomic-writer.ts +2 -1
  158. package/src/state/stores/active-run-registry.ts +3 -2
  159. package/src/state/stores/manifest-io.ts +237 -0
  160. package/src/state/stores/ownership-map.ts +162 -0
  161. package/src/state/stores/plan-store.ts +241 -0
  162. package/src/state/stores/run-cache.ts +0 -90
  163. package/src/state/stores/spec-store.ts +189 -0
  164. package/src/state/stores/state-store.ts +139 -232
  165. package/src/state/types.ts +199 -0
  166. package/src/ui/dashboard-panes/plan-pane.ts +136 -0
  167. package/src/ui/dashboard-panes/progress-pane.ts +6 -0
  168. package/src/ui/dashboard-panes/transcript-pane.ts +31 -0
  169. package/src/ui/dock-footer.ts +49 -0
  170. package/src/ui/heartbeat-aggregator.ts +9 -1
  171. package/src/ui/inline-panel/agent-pane.ts +375 -0
  172. package/src/ui/inline-panel/agent-transcript.ts +338 -0
  173. package/src/ui/inline-panel/agent-view-overlay.ts +225 -0
  174. package/src/ui/inline-panel/crew-editor.ts +192 -0
  175. package/src/ui/inline-panel/index.ts +290 -0
  176. package/src/ui/inline-panel/panel-rows.ts +37 -0
  177. package/src/ui/inline-panel/panel-selection.ts +157 -0
  178. package/src/ui/inline-panel/panel-store.ts +111 -0
  179. package/src/ui/inline-panel/view-session-store.ts +36 -0
  180. package/src/ui/keybinding-map.ts +54 -13
  181. package/src/ui/pi-ui-compat.ts +9 -0
  182. package/src/ui/powerbar-publisher.ts +52 -1
  183. package/src/ui/run-dashboard.ts +31 -5
  184. package/src/ui/run-snapshot-cache.ts +57 -30
  185. package/src/ui/snapshot-types.ts +6 -1
  186. package/src/ui/widget/index.ts +176 -22
  187. package/src/ui/widget/task-list.ts +198 -0
  188. package/src/ui/widget/widget-formatters.ts +240 -4
  189. package/src/ui/widget/widget-renderer.ts +243 -38
  190. package/src/ui/widget/widget-types.ts +11 -0
  191. package/src/utils/child-process-shield.ts +106 -0
  192. package/src/utils/file-coalescer.ts +0 -4
  193. package/src/utils/fs-errno.ts +66 -0
  194. package/src/utils/fs-watch.ts +1 -1
  195. package/src/utils/internal-error.ts +3 -1
  196. package/src/utils/paths.ts +11 -3
  197. package/src/utils/redaction.ts +7 -0
  198. package/src/utils/safe-abort.ts +45 -0
  199. package/src/utils/task-name-generator.ts +1 -8
  200. package/src/workflows/discover-workflows.ts +20 -2
  201. package/src/workflows/validate-workflow.ts +7 -1
  202. package/src/workflows/workflow-config.ts +17 -0
  203. package/src/workflows/workflow-serializer.ts +3 -0
  204. package/src/worktree/worktree-manager.ts +22 -0
  205. package/workflows/default.workflow.md +36 -26
  206. package/workflows/strict-fast-fix.workflow.md +26 -0
  207. package/src/agents/agent-search.ts +0 -98
  208. package/src/benchmark/benchmark-runner.ts +0 -313
  209. package/src/benchmark/feedback-loop.ts +0 -73
  210. package/src/config/resilient-parser.ts +0 -117
  211. package/src/extension/crew-vibes/cat-frames.ts +0 -18
  212. package/src/extension/result-watcher.ts +0 -139
  213. package/src/observability/exporters/prometheus-exporter.ts +0 -54
  214. package/src/observability/metric-retention.ts +0 -64
  215. package/src/runtime/compaction/compaction-summary.ts +0 -278
  216. package/src/runtime/errors/crew-errors.ts +0 -162
  217. package/src/runtime/live-session/intercom-bridge.ts +0 -187
  218. package/src/runtime/loop-gates.ts +0 -128
  219. package/src/runtime/metric-parser.ts +0 -36
  220. package/src/runtime/output/stream-preview.ts +0 -184
  221. package/src/runtime/output/tool-progress.ts +0 -278
  222. package/src/runtime/phase-tracker.ts +0 -385
  223. package/src/runtime/pipeline-runner.ts +0 -523
  224. package/src/runtime/process/process-lifecycle.ts +0 -491
  225. package/src/runtime/recovery/retry-runner.ts +0 -330
  226. package/src/runtime/run-drift.ts +0 -219
  227. package/src/runtime/task-quality.ts +0 -199
  228. package/src/runtime/task-runner/run-projection.ts +0 -128
  229. package/src/runtime/verification/post-checks.ts +0 -142
  230. package/src/state/coordination/schedule.ts +0 -166
  231. package/src/state/event-log/jsonl-writer.ts +0 -115
  232. package/src/state/hook-instinct-bridge.ts +0 -94
  233. package/src/state/hook-integrations.ts +0 -51
  234. package/src/state/session-state-map.ts +0 -51
  235. package/src/state/stores/blob-store.ts +0 -308
  236. package/src/state/stores/instinct-store.ts +0 -275
  237. package/src/state/stores/observation-store.ts +0 -176
  238. package/src/state/tiered-eval.ts +0 -480
  239. package/src/state/types-eval.ts +0 -58
  240. package/src/tools/safe-bash-extension.ts +0 -54
  241. package/src/tools/safe-bash.ts +0 -505
  242. package/src/ui/agent-management-overlay.ts +0 -160
  243. package/src/ui/crew-footer.ts +0 -102
  244. package/src/ui/crew-select-list.ts +0 -114
  245. package/src/ui/dashboard-panes/capability-pane.ts +0 -77
  246. package/src/ui/transcript-entries.ts +0 -256
  247. package/src/utils/conflict-detect.ts +0 -721
  248. package/src/utils/fingerprint.ts +0 -180
  249. package/src/utils/gh-protocol.ts +0 -556
  250. package/src/utils/project-detector.ts +0 -160
  251. package/src/utils/sse-parser.ts +0 -131
  252. package/src/workflows/cost-estimator.ts +0 -34
  253. package/src/workflows/intermediate-store.ts +0 -166
@@ -1,278 +0,0 @@
1
- /**
2
- * Tool Progress Event System
3
- *
4
- * Provides real-time visibility into tool execution within child Pi workers.
5
- *
6
- * Event flow:
7
- * 1. Child Pi emits JSON events on stdout (tool_execution_start, tool_execution_end, etc.)
8
- * 2. child-pi.ts parses these events and passes them to onJsonEvent callback
9
- * 3. task-runner.ts calls applyAgentProgressEvent() to update task state
10
- * 4. This module provides structured types and utilities for the event system
11
- */
12
-
13
- import type { CrewAgentProgress } from "../../state/types.ts";
14
-
15
- // ── Event Types ─────────────────────────────────────────────────────────
16
-
17
- export interface ToolExecutionStartEvent {
18
- type: "tool_execution_start";
19
- toolName: string;
20
- toolCallId: string;
21
- args?: Record<string, unknown>;
22
- timestamp: number;
23
- }
24
-
25
- export interface ToolExecutionEndEvent {
26
- type: "tool_execution_end";
27
- toolName: string;
28
- toolCallId: string;
29
- result?: unknown;
30
- timestamp: number;
31
- }
32
-
33
- export interface ToolExecutionUpdateEvent {
34
- type: "tool_execution_update";
35
- toolName: string;
36
- toolCallId: string;
37
- partialResult?: unknown;
38
- timestamp: number;
39
- }
40
-
41
- export interface ToolExecutionErrorEvent {
42
- type: "tool_execution_error" | "tool_execution_failed";
43
- toolName: string;
44
- toolCallId: string;
45
- error?: string;
46
- timestamp: number;
47
- }
48
-
49
- export interface MessageEndEvent {
50
- type: "message_end";
51
- message: {
52
- role: "assistant" | "user" | "system";
53
- content?: unknown[];
54
- usage?: UsageStats;
55
- model?: string;
56
- stopReason?: string;
57
- };
58
- timestamp: number;
59
- }
60
-
61
- export interface UsageStats {
62
- input: number;
63
- output: number;
64
- cacheRead?: number;
65
- cacheWrite?: number;
66
- cost?: { total: number };
67
- turns?: number;
68
- totalTokens?: number;
69
- }
70
-
71
- // Union type of all tool progress events
72
- export type ToolProgressEvent =
73
- | ToolExecutionStartEvent
74
- | ToolExecutionEndEvent
75
- | ToolExecutionUpdateEvent
76
- | ToolExecutionErrorEvent
77
- | MessageEndEvent;
78
-
79
- // ── Event Utilities ───────────────────────────────────────────────────────
80
-
81
- /**
82
- * Extract tool name from any event type
83
- */
84
- export function getToolName(event: ToolProgressEvent): string | undefined {
85
- if ("toolName" in event) return event.toolName;
86
- return undefined;
87
- }
88
-
89
- /**
90
- * Check if event indicates tool is running
91
- */
92
- export function isToolRunning(event: ToolProgressEvent): boolean {
93
- return event.type === "tool_execution_start";
94
- }
95
-
96
- /**
97
- * Check if event indicates tool completed
98
- */
99
- export function isToolComplete(event: ToolProgressEvent): boolean {
100
- return event.type === "tool_execution_end";
101
- }
102
-
103
- /**
104
- * Check if event indicates tool failed
105
- */
106
- export function isToolError(event: ToolProgressEvent): boolean {
107
- return event.type === "tool_execution_error" || event.type === "tool_execution_failed";
108
- }
109
-
110
- /**
111
- * Get usage stats from message_end event
112
- */
113
- export function getUsage(event: ToolProgressEvent): UsageStats | undefined {
114
- if (event.type === "message_end" && event.message?.usage) {
115
- return event.message.usage as UsageStats;
116
- }
117
- return undefined;
118
- }
119
-
120
- // ── Progress Display ──────────────────────────────────────────────────────
121
-
122
- export interface ToolProgressDisplay {
123
- /** Current/last tool being executed */
124
- currentTool?: string;
125
- /** Preview of tool arguments (truncated) */
126
- currentToolArgs?: string;
127
- /** When tool started */
128
- currentToolStartedAt?: string;
129
- /** All recent tools with their args */
130
- recentTools: Array<{
131
- tool: string;
132
- args?: string;
133
- startedAt?: string;
134
- endedAt?: string;
135
- status: "running" | "done" | "error";
136
- }>;
137
- /** Token usage snapshot */
138
- tokens?: number;
139
- /** Context window usage percentage */
140
- contextPercent?: number;
141
- /** Total tool count */
142
- toolCount: number;
143
- /** Last activity timestamp */
144
- lastActivityAt?: string;
145
- /** Activity state */
146
- activityState: "active" | "idle" | "done";
147
- }
148
-
149
- /**
150
- * Format tool progress for display
151
- */
152
- export function formatToolProgress(progress: CrewAgentProgress, maxContextTokens = 128000): ToolProgressDisplay {
153
- const recentTools: Array<{
154
- tool: string;
155
- args?: string;
156
- startedAt?: string;
157
- endedAt?: string;
158
- status: "running" | "done" | "error";
159
- }> = progress.recentTools.map((t) => ({
160
- tool: t.tool,
161
- args: t.args,
162
- startedAt: t.startedAt,
163
- endedAt: t.endedAt,
164
- status: t.endedAt ? ("done" as const) : ("running" as const),
165
- }));
166
-
167
- // If there's a currentTool but no endedAt, it's still running
168
- const currentRunning = progress.recentTools.find((t) => !t.endedAt && t.tool === progress.currentTool);
169
- if (currentRunning && progress.currentTool) {
170
- recentTools.push({
171
- tool: progress.currentTool,
172
- args: progress.currentToolArgs,
173
- startedAt: progress.currentToolStartedAt,
174
- endedAt: undefined as string | undefined,
175
- status: "running" as const,
176
- });
177
- }
178
-
179
- const tokens = progress.tokens ?? 0;
180
- const contextPercent = maxContextTokens > 0 ? Math.round((tokens / maxContextTokens) * 100) : 0;
181
-
182
- return {
183
- currentTool: progress.currentTool,
184
- currentToolArgs: progress.currentToolArgs,
185
- currentToolStartedAt: progress.currentToolStartedAt,
186
- recentTools,
187
- tokens,
188
- contextPercent,
189
- toolCount: progress.toolCount,
190
- lastActivityAt: progress.lastActivityAt,
191
- activityState: progress.activityState as "active" | "idle" | "done",
192
- };
193
- }
194
-
195
- /**
196
- * Format a single line summary of current tool
197
- */
198
- export function formatCurrentToolLine(progress: CrewAgentProgress): string {
199
- if (!progress.currentTool) return "";
200
-
201
- const args = progress.currentToolArgs
202
- ? ` ${progress.currentToolArgs.slice(0, 50)}${progress.currentToolArgs.length > 50 ? "..." : ""}`
203
- : "";
204
-
205
- const toolCount = progress.toolCount > 0 ? ` (${progress.toolCount})` : "";
206
-
207
- return `${progress.currentTool}${args}${toolCount}`;
208
- }
209
-
210
- /**
211
- * Format token usage for display
212
- */
213
- export function formatTokenUsage(progress: CrewAgentProgress, maxTokens = 128000): string {
214
- const tokens = progress.tokens ?? 0;
215
- const percent = maxTokens > 0 ? Math.round((tokens / maxTokens) * 100) : 0;
216
- return `${tokens.toLocaleString()} / ${maxTokens.toLocaleString()} (${percent}%)`;
217
- }
218
-
219
- // ── Progress Bar Rendering ────────────────────────────────────────────────
220
-
221
- export interface ProgressBarOptions {
222
- width?: number;
223
- showPercent?: boolean;
224
- showCount?: boolean;
225
- }
226
-
227
- /**
228
- * Render a progress bar for tool execution
229
- */
230
- export function renderProgressBar(progress: CrewAgentProgress, options: ProgressBarOptions = {}): string {
231
- const width = options.width ?? 20;
232
- const showPercent = options.showPercent ?? true;
233
- const showCount = options.showCount ?? true;
234
-
235
- // Calculate based on recent tools (max 10)
236
- const recentCount = Math.min(progress.recentTools.length, 10);
237
- const filled = Math.round((recentCount / 10) * width);
238
- const empty = width - filled;
239
-
240
- const bar = "█".repeat(filled) + "░".repeat(empty);
241
- const percent = showPercent ? ` ${progress.toolCount} tools` : "";
242
- const tokens = progress.tokens ? ` | ${(progress.tokens / 1000).toFixed(1)}k tokens` : "";
243
-
244
- return `[${bar}]${percent}${tokens}`;
245
- }
246
-
247
- // ── Event Filtering ──────────────────────────────────────────────────────
248
-
249
- /**
250
- * Filter events to only tool execution events
251
- */
252
- export function filterToolEvents(events: ToolProgressEvent[]): ToolProgressEvent[] {
253
- return events.filter(
254
- (e) =>
255
- e.type === "tool_execution_start" ||
256
- e.type === "tool_execution_end" ||
257
- e.type === "tool_execution_update" ||
258
- e.type === "tool_execution_error" ||
259
- e.type === "tool_execution_failed",
260
- );
261
- }
262
-
263
- /**
264
- * Get events for a specific tool
265
- */
266
- export function getEventsForTool(events: ToolProgressEvent[], toolName: string): ToolProgressEvent[] {
267
- return events.filter((e) => {
268
- if ("toolName" in e) return e.toolName === toolName;
269
- return false;
270
- });
271
- }
272
-
273
- /**
274
- * Check if any event indicates an error
275
- */
276
- export function hasError(events: ToolProgressEvent[]): boolean {
277
- return events.some(isToolError);
278
- }
@@ -1,385 +0,0 @@
1
- /**
2
- * Phase Tracker — marks phase transitions with timestamps and metrics.
3
- *
4
- * Tracks workflow phases (assessment, implementation, verification, etc.)
5
- * with start/complete/skip lifecycle and phase-level metrics.
6
- *
7
- * @file src/runtime/phase-tracker.ts
8
- */
9
-
10
- import { EventEmitter } from "node:events";
11
-
12
- /** Phase status. */
13
- export type PhaseStatus = "active" | "completed" | "skipped" | "failed";
14
-
15
- /** Metrics collected for a phase. */
16
- export interface PhaseMetrics {
17
- /** Number of tasks completed in this phase. */
18
- tasksCompleted?: number;
19
- /** Number of tasks failed in this phase. */
20
- tasksFailed?: number;
21
- /** Total tokens used in this phase. */
22
- tokensUsed?: number;
23
- /** Number of subagents spawned in this phase. */
24
- subagentsSpawned?: number;
25
- /** Custom metadata key-value pairs. */
26
- custom?: Record<string, unknown>;
27
- }
28
-
29
- /** A tracked phase. */
30
- export interface Phase {
31
- /** Unique phase name/identifier. */
32
- name: string;
33
- /** ISO timestamp when phase started. */
34
- startTime: string;
35
- /** ISO timestamp when phase ended (if ended). */
36
- endTime?: string;
37
- /** Duration in milliseconds (if ended). */
38
- durationMs?: number;
39
- /** Current phase status. */
40
- status: PhaseStatus;
41
- /** Collected metrics for this phase. */
42
- metrics?: PhaseMetrics;
43
- /** Order index (0-based). */
44
- index: number;
45
- }
46
-
47
- /** Event emitted on phase lifecycle changes. */
48
- export interface PhaseLifecycleEvent {
49
- type: "phase:started" | "phase:completed" | "phase:skipped" | "phase:failed";
50
- phase: Phase;
51
- }
52
-
53
- /** Default empty metrics. */
54
- function emptyMetrics(): PhaseMetrics {
55
- return {
56
- tasksCompleted: 0,
57
- tasksFailed: 0,
58
- tokensUsed: 0,
59
- subagentsSpawned: 0,
60
- };
61
- }
62
-
63
- /**
64
- * PhaseTracker manages workflow phase lifecycle.
65
- *
66
- * @example
67
- * ```typescript
68
- * const tracker = new PhaseTracker();
69
- * tracker.start("assessment");
70
- * // ... do work ...
71
- * tracker.complete("assessment", { tasksCompleted: 5, tokensUsed: 12000 });
72
- * tracker.start("implementation");
73
- * ```
74
- */
75
- export class PhaseTracker extends EventEmitter {
76
- private phases: Phase[] = [];
77
- private currentPhaseName: string | null = null;
78
- private phaseMetrics: Map<string, PhaseMetrics> = new Map();
79
-
80
- /**
81
- * Start a new phase, completing the previous one if any.
82
- *
83
- * @param name - Phase name (e.g., "assessment", "implementation").
84
- * @param metrics - Optional initial metrics for the phase.
85
- * @returns The started Phase object.
86
- */
87
- start(name: string, metrics?: PhaseMetrics): Phase {
88
- // HIGH-8: Prevent duplicate phases - check if phase already exists
89
- if (this.phases.some((p) => p.name === name)) {
90
- throw new Error(`Phase "${name}" already exists. Duplicate phases are not allowed.`);
91
- }
92
-
93
- // Complete previous phase before starting new one (only if active)
94
- if (this.currentPhaseName !== null) {
95
- this.completeIfActive(this.currentPhaseName);
96
- }
97
-
98
- const phase: Phase = {
99
- name,
100
- startTime: new Date().toISOString(),
101
- status: "active",
102
- index: this.phases.length,
103
- metrics: metrics ?? emptyMetrics(),
104
- };
105
-
106
- this.phases.push(phase);
107
- this.currentPhaseName = name;
108
- this.phaseMetrics.set(name, phase.metrics!);
109
-
110
- const event: PhaseLifecycleEvent = { type: "phase:started", phase };
111
- this.emit("phase:started", event);
112
- return phase;
113
- }
114
-
115
- /**
116
- * Complete a phase with optional metrics update.
117
- *
118
- * @param name - Phase name to complete.
119
- * @param metrics - Optional metrics to merge/update.
120
- */
121
- complete(name: string, metrics?: Partial<PhaseMetrics>): void {
122
- const phase = this.phases.find((p) => p.name === name);
123
- if (!phase) {
124
- throw new Error(`Phase "${name}" not found`);
125
- }
126
- if (phase.status !== "active") {
127
- throw new Error(`Phase "${name}" is not active (status: ${phase.status})`);
128
- }
129
-
130
- const now = new Date();
131
- const startMs = new Date(phase.startTime).getTime();
132
- const endMs = now.getTime();
133
-
134
- phase.endTime = now.toISOString();
135
- phase.durationMs = endMs - startMs;
136
- phase.status = "completed";
137
-
138
- // Merge provided metrics with existing
139
- if (metrics) {
140
- const existing = this.phaseMetrics.get(name) ?? emptyMetrics();
141
- phase.metrics = {
142
- tasksCompleted: metrics.tasksCompleted ?? existing.tasksCompleted,
143
- tasksFailed: metrics.tasksFailed ?? existing.tasksFailed,
144
- tokensUsed: metrics.tokensUsed ?? existing.tokensUsed,
145
- subagentsSpawned: metrics.subagentsSpawned ?? existing.subagentsSpawned,
146
- custom: { ...existing.custom, ...metrics.custom },
147
- };
148
- this.phaseMetrics.set(name, phase.metrics);
149
- }
150
-
151
- this.emit("phase:completed", {
152
- type: "phase:completed",
153
- phase,
154
- } as PhaseLifecycleEvent);
155
- }
156
-
157
- /**
158
- * Skip the current active phase without metrics.
159
- *
160
- * @param name - Phase name to skip.
161
- * @param reason - Optional reason for skipping.
162
- */
163
- skip(name: string, reason?: string): void {
164
- const phase = this.phases.find((p) => p.name === name);
165
- if (!phase) {
166
- throw new Error(`Phase "${name}" not found`);
167
- }
168
- if (phase.status !== "active") {
169
- throw new Error(`Phase "${name}" is not active (status: ${phase.status})`);
170
- }
171
-
172
- const now = new Date();
173
- const startMs = new Date(phase.startTime).getTime();
174
- const endMs = now.getTime();
175
-
176
- phase.endTime = now.toISOString();
177
- phase.durationMs = endMs - startMs;
178
- phase.status = "skipped";
179
-
180
- // Clear current phase since we're done with it
181
- if (this.currentPhaseName === name) {
182
- this.currentPhaseName = null;
183
- }
184
-
185
- this.emit("phase:skipped", {
186
- type: "phase:skipped",
187
- phase,
188
- } as PhaseLifecycleEvent);
189
- }
190
-
191
- /**
192
- * Mark a phase as failed.
193
- *
194
- * @param name - Phase name to fail.
195
- * @param error - Optional error information.
196
- */
197
- fail(name: string, error?: string): void {
198
- const phase = this.phases.find((p) => p.name === name);
199
- if (!phase) {
200
- throw new Error(`Phase "${name}" not found`);
201
- }
202
- if (phase.status !== "active") {
203
- throw new Error(`Phase "${name}" is not active (status: ${phase.status})`);
204
- }
205
-
206
- const now = new Date();
207
- const startMs = new Date(phase.startTime).getTime();
208
- const endMs = now.getTime();
209
-
210
- phase.endTime = now.toISOString();
211
- phase.durationMs = endMs - startMs;
212
- phase.status = "failed";
213
-
214
- if (error) {
215
- const existing = this.phaseMetrics.get(name) ?? emptyMetrics();
216
- phase.metrics = {
217
- ...existing,
218
- custom: { ...existing.custom, error },
219
- };
220
- this.phaseMetrics.set(name, phase.metrics);
221
- }
222
-
223
- // Clear current phase since we're done with it
224
- if (this.currentPhaseName === name) {
225
- this.currentPhaseName = null;
226
- }
227
-
228
- this.emit("phase:failed", {
229
- type: "phase:failed",
230
- phase,
231
- } as PhaseLifecycleEvent);
232
- }
233
-
234
- /**
235
- * Complete a phase only if it is currently active. Does not throw if the
236
- * phase is already completed, skipped, or failed.
237
- *
238
- * @param name - Phase name to complete.
239
- * @param metrics - Optional metrics to merge/update.
240
- */
241
- completeIfActive(name: string, metrics?: Partial<PhaseMetrics>): void {
242
- const phase = this.phases.find((p) => p.name === name);
243
- if (phase && phase.status === "active") {
244
- this.complete(name, metrics);
245
- }
246
- }
247
-
248
- /**
249
- * Get all phases.
250
- * @returns Copy of phases array.
251
- */
252
- getPhases(): Phase[] {
253
- return [...this.phases];
254
- }
255
-
256
- /**
257
- * Get phases filtered by status.
258
- * @param status - Status to filter by.
259
- * @returns Filtered phases.
260
- */
261
- getPhasesByStatus(status: PhaseStatus): Phase[] {
262
- return this.phases.filter((p) => p.status === status);
263
- }
264
-
265
- /**
266
- * Get the current active phase.
267
- * @returns Current phase or null if none active.
268
- */
269
- getCurrentPhase(): Phase | null {
270
- return this.phases.find((p) => p.status === "active") ?? null;
271
- }
272
-
273
- /**
274
- * Get a specific phase by name.
275
- * @param name - Phase name.
276
- * @returns Phase or undefined.
277
- */
278
- getPhase(name: string): Phase | undefined {
279
- return this.phases.find((p) => p.name === name);
280
- }
281
-
282
- /**
283
- * Get metrics for a phase.
284
- * @param name - Phase name.
285
- * @returns Metrics or undefined.
286
- */
287
- getMetrics(name: string): PhaseMetrics | undefined {
288
- return this.phaseMetrics.get(name);
289
- }
290
-
291
- /**
292
- * Update metrics for the current phase.
293
- * @param updates - Partial metrics to merge.
294
- */
295
- updateCurrentMetrics(updates: Partial<PhaseMetrics>): void {
296
- if (!this.currentPhaseName) return;
297
- const existing = this.phaseMetrics.get(this.currentPhaseName) ?? emptyMetrics();
298
- const updated: PhaseMetrics = {
299
- tasksCompleted: updates.tasksCompleted ?? existing.tasksCompleted,
300
- tasksFailed: updates.tasksFailed ?? existing.tasksFailed,
301
- tokensUsed: updates.tokensUsed ?? existing.tokensUsed,
302
- subagentsSpawned: updates.subagentsSpawned ?? existing.subagentsSpawned,
303
- custom: { ...existing.custom, ...updates.custom },
304
- };
305
- this.phaseMetrics.set(this.currentPhaseName, updated);
306
- const phase = this.getPhase(this.currentPhaseName);
307
- if (phase) {
308
- phase.metrics = updated;
309
- }
310
- }
311
-
312
- /**
313
- * Add tokens to the current phase's metrics.
314
- * @param tokens - Number of tokens to add.
315
- */
316
- addTokensToCurrent(tokens: number): void {
317
- if (!this.currentPhaseName) return;
318
- const existing = this.phaseMetrics.get(this.currentPhaseName) ?? emptyMetrics();
319
- existing.tokensUsed = (existing.tokensUsed ?? 0) + tokens;
320
- this.phaseMetrics.set(this.currentPhaseName, existing);
321
- const phase = this.getPhase(this.currentPhaseName);
322
- if (phase) {
323
- phase.metrics = existing;
324
- }
325
- }
326
-
327
- /**
328
- * Get total duration across all completed phases.
329
- * @returns Total milliseconds or 0.
330
- */
331
- totalDuration(): number {
332
- return this.phases.reduce((sum, p) => sum + (p.durationMs ?? 0), 0);
333
- }
334
-
335
- /**
336
- * Get summary statistics for all phases.
337
- * @returns Summary object.
338
- */
339
- summary(): {
340
- totalPhases: number;
341
- active: number;
342
- completed: number;
343
- skipped: number;
344
- failed: number;
345
- totalDurationMs: number;
346
- } {
347
- return {
348
- totalPhases: this.phases.length,
349
- active: this.phases.filter((p) => p.status === "active").length,
350
- completed: this.phases.filter((p) => p.status === "completed").length,
351
- skipped: this.phases.filter((p) => p.status === "skipped").length,
352
- failed: this.phases.filter((p) => p.status === "failed").length,
353
- totalDurationMs: this.totalDuration(),
354
- };
355
- }
356
-
357
- /**
358
- * Check if a phase exists.
359
- * @param name - Phase name.
360
- * @returns True if phase exists.
361
- */
362
- hasPhase(name: string): boolean {
363
- return this.phases.some((p) => p.name === name);
364
- }
365
-
366
- /**
367
- * Reset all phases (for testing or recovery).
368
- */
369
- reset(): void {
370
- this.phases = [];
371
- this.currentPhaseName = null;
372
- this.phaseMetrics.clear();
373
- }
374
-
375
- /**
376
- * Dispose of resources (EventEmitter listeners).
377
- * Call this when the tracker is no longer needed.
378
- */
379
- dispose(): void {
380
- this.removeAllListeners();
381
- this.phases = [];
382
- this.currentPhaseName = null;
383
- this.phaseMetrics.clear();
384
- }
385
- }