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,187 +0,0 @@
1
- /**
2
- * Intercom bridge — workers can escalate questions to the orchestrator.
3
- *
4
- * Pattern origin: pi-subagents/src/intercom-bridge.ts — contact_supervisor tool
5
- * for child agents to escalate decisions, report progress, or ask questions.
6
- *
7
- * This module provides the message queue and correlation logic.
8
- * The actual tool registration happens in task-runner.ts.
9
- */
10
-
11
- // ── Types ────────────────────────────────────────────────────────────────
12
-
13
- export type IntercomUrgency = "low" | "medium" | "high" | "critical";
14
- export type IntercomType = "question" | "escalation" | "progress" | "block";
15
-
16
- export interface IntercomMessage {
17
- type: IntercomType;
18
- taskStepId: string;
19
- content: string;
20
- urgency: IntercomUrgency;
21
- timestamp: number;
22
- timeout?: number; // ms to wait for response
23
- }
24
-
25
- export interface IntercomResponse {
26
- answer: string;
27
- source: "orchestrator" | "human" | "timeout";
28
- timestamp: number;
29
- messageId: string;
30
- }
31
-
32
- // ── Message Queue ────────────────────────────────────────────────────────
33
-
34
- interface PendingMessage {
35
- message: IntercomMessage;
36
- id: string;
37
- resolve: (response: IntercomResponse) => void;
38
- timer?: ReturnType<typeof setTimeout>;
39
- }
40
-
41
- const MAX_QUEUE_SIZE = 100;
42
-
43
- /**
44
- * In-process intercom queue for worker→orchestrator communication.
45
- *
46
- * Each message gets a unique ID. Callers await a response via a Promise.
47
- * If no response arrives within the timeout, resolves with source="timeout".
48
- */
49
- export class IntercomQueue {
50
- private pending = new Map<string, PendingMessage>();
51
- private queue: IntercomMessage[] = [];
52
- /** Monotonic sequence counter — guarantees unique IDs even when many
53
- * messages enqueue within the same millisecond (avoids Date.now()
54
- * collision + weak Math.random birthday-paradox flakes under load). */
55
- private seq = 0;
56
-
57
- /**
58
- * Enqueue a message and return a promise that resolves when the
59
- * orchestrator responds (or times out).
60
- */
61
- enqueue(message: IntercomMessage): Promise<IntercomResponse> {
62
- if (this.pending.size >= MAX_QUEUE_SIZE) {
63
- // Evict oldest
64
- const firstKey = this.pending.keys().next().value;
65
- if (firstKey) this.evict(firstKey, "queue_full");
66
- }
67
-
68
- const id = `icm-${Date.now().toString(36)}-${(this.seq++).toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
69
-
70
- return new Promise<IntercomResponse>((resolve) => {
71
- const entry: PendingMessage = { message, id, resolve };
72
-
73
- // Set timeout if specified
74
- if (message.timeout && message.timeout > 0) {
75
- entry.timer = setTimeout(() => {
76
- resolve({
77
- answer: "No response received within timeout",
78
- source: "timeout",
79
- timestamp: Date.now(),
80
- messageId: id,
81
- });
82
- this.pending.delete(id);
83
- }, message.timeout);
84
- // Defense in depth: never let a pending-message timer block
85
- // process exit. The timer is cleared via respond()/evict() in
86
- // the normal case; .unref() ensures shutdown isn't blocked if
87
- // the queue is abandoned.
88
- if (entry.timer && typeof entry.timer.unref === "function") {
89
- entry.timer.unref();
90
- }
91
- }
92
-
93
- this.pending.set(id, entry);
94
- this.queue.push({ ...message });
95
- });
96
- }
97
-
98
- /**
99
- * Respond to a pending message by ID.
100
- */
101
- respond(messageId: string, answer: string, source: "orchestrator" | "human" = "orchestrator"): boolean {
102
- const entry = this.pending.get(messageId);
103
- if (!entry) return false;
104
-
105
- if (entry.timer) clearTimeout(entry.timer);
106
-
107
- entry.resolve({
108
- answer,
109
- source,
110
- timestamp: Date.now(),
111
- messageId,
112
- });
113
-
114
- this.pending.delete(messageId);
115
- return true;
116
- }
117
-
118
- /**
119
- * Get all pending messages (for orchestrator to process).
120
- */
121
- getPending(): Array<IntercomMessage & { id: string }> {
122
- return [...this.pending.entries()].map(([id, entry]) => ({
123
- ...entry.message,
124
- id,
125
- }));
126
- }
127
-
128
- /**
129
- * Number of pending messages awaiting response.
130
- */
131
- get pendingCount(): number {
132
- return this.pending.size;
133
- }
134
-
135
- /**
136
- * Clean up all pending messages (e.g., on run completion).
137
- */
138
- clear(): void {
139
- for (const [id, entry] of this.pending) {
140
- this.evict(id, "run_complete");
141
- }
142
- this.queue = [];
143
- }
144
-
145
- private evict(id: string, reason: string): void {
146
- const entry = this.pending.get(id);
147
- if (!entry) return;
148
-
149
- if (entry.timer) clearTimeout(entry.timer);
150
-
151
- entry.resolve({
152
- answer: `Message evicted: ${reason}`,
153
- source: "timeout",
154
- timestamp: Date.now(),
155
- messageId: id,
156
- });
157
-
158
- this.pending.delete(id);
159
- }
160
- }
161
-
162
- // ── Singleton per run ────────────────────────────────────────────────────
163
-
164
- const queues = new Map<string, IntercomQueue>();
165
-
166
- /**
167
- * Get or create an intercom queue for a run.
168
- */
169
- export function getIntercomQueue(runId: string): IntercomQueue {
170
- let queue = queues.get(runId);
171
- if (!queue) {
172
- queue = new IntercomQueue();
173
- queues.set(runId, queue);
174
- }
175
- return queue;
176
- }
177
-
178
- /**
179
- * Clean up intercom queue for a completed run.
180
- */
181
- export function cleanupIntercomQueue(runId: string): void {
182
- const queue = queues.get(runId);
183
- if (queue) {
184
- queue.clear();
185
- queues.delete(runId);
186
- }
187
- }
@@ -1,128 +0,0 @@
1
- /**
2
- * Dual-gate loop control — determines whether a task should auto-resume
3
- * based on meaningful progress and turn limits.
4
- *
5
- * Gate 1: At least one signal of meaningful progress must be true.
6
- * Gate 2: The auto-resume turn count must be below the maximum.
7
- *
8
- * Distilled from pi-autoresearch's dual-gate loop pattern.
9
- */
10
- import * as fs from "node:fs";
11
- import type { TeamTaskState } from "../state/types.ts";
12
-
13
- /**
14
- * Signal indicating what kind of progress a task has made.
15
- */
16
- export interface TaskProgressSignal {
17
- /** Whether the task has edited files or produced non-empty results. */
18
- editedFiles: boolean;
19
- /** Whether the task has produced artifacts in the artifacts directory. */
20
- producedArtifacts: boolean;
21
- /** Whether the task has run tests (detected via result text). */
22
- ranTests: boolean;
23
- }
24
-
25
- /**
26
- * Runtime state for auto-resume turn tracking.
27
- */
28
- export interface AutoResumeRuntime {
29
- /** Current number of auto-resume turns taken. */
30
- autoResumeTurns: number;
31
- /** Maximum allowed auto-resume turns. */
32
- maxTurns: number;
33
- }
34
-
35
- /** Keywords in result text that indicate tests were run. */
36
- const TEST_KEYWORDS = [
37
- "test passed",
38
- "test failed",
39
- "tests passed",
40
- "tests failed",
41
- "test results",
42
- "jest",
43
- "vitest",
44
- "mocha",
45
- "pytest",
46
- "npx test",
47
- "npm test",
48
- "cargo test",
49
- "all tests",
50
- "test suite",
51
- "✓",
52
- "✗",
53
- "PASS ",
54
- " FAIL ",
55
- ] as const;
56
-
57
- /**
58
- * Determine whether a task should auto-resume based on dual-gate logic.
59
- *
60
- * Gate 1 (meaningful progress): at least one of editedFiles, producedArtifacts,
61
- * or ranTests must be true.
62
- * Gate 2 (turn limit): autoResumeTurns must be strictly less than maxTurns.
63
- *
64
- * Both gates must pass for auto-resume to be allowed.
65
- */
66
- export function shouldAutoResume(runtime: AutoResumeRuntime, taskProgress: TaskProgressSignal): boolean {
67
- // Gate 2: Turn limit check (check first — cheaper)
68
- if (runtime.autoResumeTurns >= runtime.maxTurns) {
69
- return false;
70
- }
71
-
72
- // Gate 1: Meaningful progress check
73
- const hasProgress = taskProgress.editedFiles || taskProgress.producedArtifacts || taskProgress.ranTests;
74
- if (!hasProgress) {
75
- return false;
76
- }
77
-
78
- return true;
79
- }
80
-
81
- /**
82
- * Compute the task progress signal from a task's state and artifacts directory.
83
- *
84
- * - editedFiles: true if task has artifact files or a non-empty result
85
- * - producedArtifacts: true if artifacts directory contains task-specific files
86
- * - ranTests: true if result text contains test-related keywords
87
- *
88
- * @param task - The task state to analyze
89
- * @param artifactsDir - Path to the artifacts directory
90
- * @returns TaskProgressSignal indicating what progress was made
91
- */
92
- export function computeTaskProgressSignal(task: TeamTaskState, artifactsDir: string): TaskProgressSignal {
93
- // editedFiles: check if task has artifact descriptors or a non-empty result
94
- const hasResultArtifact = task.resultArtifact !== undefined;
95
- const hasNonEmptyResult = task.resultArtifact?.path !== undefined;
96
- const editedFiles = hasResultArtifact || hasNonEmptyResult;
97
-
98
- // producedArtifacts: check artifacts directory for task-specific files
99
- let producedArtifacts = false;
100
- if (artifactsDir && fs.existsSync(artifactsDir)) {
101
- try {
102
- const entries = fs.readdirSync(artifactsDir);
103
- // Look for files/directories that START with the task ID prefix.
104
- // Using startsWith instead of includes to avoid false positives
105
- // from short task IDs like "01" matching unrelated entries.
106
- const taskPrefix = task.id.replace(/[^a-zA-Z0-9_-]/g, "");
107
- producedArtifacts = entries.some((entry) => {
108
- const baseName = entry.split(".")[0] ?? entry;
109
- return baseName.startsWith(taskPrefix);
110
- });
111
- } catch {
112
- producedArtifacts = false;
113
- }
114
- }
115
-
116
- // ranTests: check if result-related fields contain test keywords
117
- let ranTests = false;
118
- const textToSearch = [
119
- task.error ?? "",
120
- task.terminalEvidence ? JSON.stringify(task.terminalEvidence) : "",
121
- task.diagnostics ? JSON.stringify(task.diagnostics) : "",
122
- ].join(" ");
123
-
124
- const lowerText = textToSearch.toLowerCase();
125
- ranTests = TEST_KEYWORDS.some((keyword) => lowerText.includes(keyword.toLowerCase()));
126
-
127
- return { editedFiles, producedArtifacts, ranTests };
128
- }
@@ -1,36 +0,0 @@
1
- /**
2
- * Metric names that are denied to prevent prototype pollution.
3
- */
4
- export const DENIED_METRIC_NAMES: ReadonlySet<string> = new Set(["__proto__", "constructor", "prototype"]);
5
-
6
- const METRIC_LINE_RE = /^CREW_METRIC\s+(\w+)=(\S+)$/;
7
-
8
- /**
9
- * Parse CREW_METRIC lines from worker stdout.
10
- *
11
- * Lines must match the pattern: `CREW_METRIC name=value`
12
- * - `name` must be a word character sequence (alphanumeric + underscore)
13
- * - `value` must parse as a valid finite number
14
- * - Denied names (__proto__, constructor, prototype) are silently skipped
15
- *
16
- * @param output - Raw worker stdout text
17
- * @returns Map of metric name → numeric value
18
- */
19
- export function parseMetricLines(output: string): Record<string, number> {
20
- const metrics: Record<string, number> = {};
21
-
22
- for (const line of output.split("\n")) {
23
- const match = METRIC_LINE_RE.exec(line);
24
- if (!match) continue;
25
-
26
- const [, name, rawValue] = match;
27
- if (DENIED_METRIC_NAMES.has(name)) continue;
28
-
29
- const value = Number(rawValue);
30
- if (!Number.isFinite(value)) continue;
31
-
32
- metrics[name] = value;
33
- }
34
-
35
- return metrics;
36
- }
@@ -1,184 +0,0 @@
1
- // ─── Streaming Preview ───────────────────────────────────────────────────────
2
- // Captures incremental worker output during task execution for live preview.
3
- // Used by the UI layer to show partial results before task completion.
4
-
5
- import { TAIL_CAPTURE_STREAM_STAGE } from "../compaction/compact-stages/index.ts";
6
- import type { ParsedPiUsage } from "./pi-json-output.ts";
7
-
8
- export interface ToolCallPreview {
9
- toolName: string;
10
- inputPreview: string;
11
- startedAt: number;
12
- }
13
-
14
- export interface StreamPreview {
15
- taskId: string;
16
- runId: string;
17
- /** Cumulative text output captured so far */
18
- textBuffer: string;
19
- /** Current tool call in progress (if any) */
20
- activeToolCall: ToolCallPreview | null;
21
- /** Completed tool calls count */
22
- toolCallCount: number;
23
- /** Current turn number (incremented per assistant message) */
24
- turnCount: number;
25
- /** Token usage snapshot */
26
- usage: Partial<ParsedPiUsage> | null;
27
- /** Wall-clock start time */
28
- startedAt: number;
29
- /** Last update timestamp */
30
- lastUpdatedAt: number;
31
- /** Whether the task has completed */
32
- finished: boolean;
33
- }
34
-
35
- export function createStreamPreview(taskId: string, runId: string): StreamPreview {
36
- const now = Date.now();
37
- return {
38
- taskId,
39
- runId,
40
- textBuffer: "",
41
- activeToolCall: null,
42
- toolCallCount: 0,
43
- turnCount: 0,
44
- usage: null,
45
- startedAt: now,
46
- lastUpdatedAt: now,
47
- finished: false,
48
- };
49
- }
50
-
51
- /** Max text buffer size — drop oldest content when exceeded */
52
- const MAX_TEXT_BUFFER = 16_384;
53
- /** Max tool input preview length */
54
- const MAX_INPUT_PREVIEW = 512;
55
-
56
- const asRecord = (value: unknown): Record<string, unknown> | undefined =>
57
- value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;
58
-
59
- function truncateWithEllipsis(text: string, maxLength: number): string {
60
- if (text.length <= maxLength) return text;
61
- return text.slice(0, maxLength - 3) + "...";
62
- }
63
-
64
- function extractTextFromContent(content: unknown): string {
65
- if (typeof content === "string") return content;
66
- if (!Array.isArray(content)) return "";
67
- const parts: string[] = [];
68
- for (const part of content) {
69
- const obj = asRecord(part);
70
- if (!obj) continue;
71
- if (obj.type === "text" && typeof obj.text === "string") parts.push(obj.text);
72
- else if (typeof obj.content === "string") parts.push(obj.content);
73
- }
74
- return parts.join("");
75
- }
76
-
77
- /**
78
- * Feed a JSON event from Pi's stdout into the preview, updating it in place.
79
- * Returns true if the preview was modified.
80
- */
81
- export function feedJsonEvent(preview: StreamPreview, event: unknown): boolean {
82
- const obj = asRecord(event);
83
- if (!obj) return false;
84
-
85
- let modified = false;
86
- preview.lastUpdatedAt = Date.now();
87
-
88
- // Detect tool calls
89
- if (obj.type === "tool_call" || obj.type === "tool_use" || obj.type === "toolCall") {
90
- const toolName = typeof obj.name === "string" ? obj.name : typeof obj.tool === "string" ? obj.tool : "unknown";
91
- const rawInput = obj.input ?? obj.arguments ?? obj.params ?? "";
92
- const inputStr = typeof rawInput === "string" ? rawInput : JSON.stringify(rawInput);
93
- preview.activeToolCall = {
94
- toolName,
95
- inputPreview: truncateWithEllipsis(inputStr, MAX_INPUT_PREVIEW),
96
- startedAt: Date.now(),
97
- };
98
- preview.toolCallCount++;
99
- modified = true;
100
- }
101
-
102
- // Detect tool results (clear active tool call)
103
- if (obj.type === "tool_result" || obj.type === "toolResult" || obj.type === "result") {
104
- preview.activeToolCall = null;
105
- modified = true;
106
- }
107
-
108
- // Detect assistant text output
109
- const message = asRecord(obj.message);
110
- if (message?.role === "assistant" || obj.role === "assistant") {
111
- preview.turnCount++;
112
- const text = extractTextFromContent(message?.content ?? obj.content);
113
- if (text) {
114
- const appended = preview.textBuffer.length > 0 ? preview.textBuffer + "\n" + text : text;
115
- // Sprint 5: refactored onto the stage-chain. TAIL_CAPTURE_STREAM_STAGE
116
- // is a 16_384-char tail-capture stage with no marker (the UI shows
117
- // raw text without a prefix). It is bit-equivalent to the inline
118
- // `appended.slice(appended.length - MAX_TEXT_BUFFER)` for inputs at
119
- // or below the cap (returns verbatim) and equivalent for over-cap
120
- // inputs (returns last MAX_TEXT_BUFFER chars).
121
- preview.textBuffer = TAIL_CAPTURE_STREAM_STAGE.apply(appended);
122
- }
123
- modified = true;
124
- }
125
-
126
- // Detect direct text/final output
127
- if (typeof obj.text === "string" && obj.text.trim()) {
128
- const appended = preview.textBuffer.length > 0 ? preview.textBuffer + "\n" + obj.text : obj.text;
129
- preview.textBuffer = TAIL_CAPTURE_STREAM_STAGE.apply(appended);
130
- modified = true;
131
- }
132
-
133
- // Detect usage
134
- const rawUsage = obj.usage ?? obj.tokenUsage ?? obj.tokens ?? obj.stats;
135
- if (rawUsage && typeof rawUsage === "object") {
136
- const u = asRecord(rawUsage);
137
- if (u) {
138
- preview.usage = {
139
- input: typeof u.input === "number" ? u.input : preview.usage?.input,
140
- output: typeof u.output === "number" ? u.output : preview.usage?.output,
141
- cacheRead: typeof u.cacheRead === "number" ? u.cacheRead : preview.usage?.cacheRead,
142
- cacheWrite: typeof u.cacheWrite === "number" ? u.cacheWrite : preview.usage?.cacheWrite,
143
- cost: typeof u.cost === "number" ? u.cost : preview.usage?.cost,
144
- turns: typeof u.turns === "number" ? u.turns : preview.usage?.turns,
145
- };
146
- modified = true;
147
- }
148
- }
149
-
150
- return modified;
151
- }
152
-
153
- /** Mark preview as finished */
154
- export function finishStreamPreview(preview: StreamPreview): void {
155
- preview.finished = true;
156
- preview.activeToolCall = null;
157
- preview.lastUpdatedAt = Date.now();
158
- }
159
-
160
- /** Render a compact one-line status for the preview */
161
- export function renderPreviewStatus(preview: StreamPreview): string {
162
- const elapsed = Math.round((Date.now() - preview.startedAt) / 1000);
163
- const parts: string[] = [];
164
-
165
- if (preview.finished) {
166
- parts.push("✓ done");
167
- } else if (preview.activeToolCall) {
168
- parts.push(`⚙ ${preview.activeToolCall.toolName}`);
169
- } else {
170
- parts.push("⟳ thinking");
171
- }
172
-
173
- parts.push(`T${preview.turnCount}`);
174
- parts.push(`${preview.toolCallCount} tools`);
175
-
176
- if (preview.usage?.input) {
177
- const inK = Math.round(preview.usage.input / 1024);
178
- parts.push(`${inK}k in`);
179
- }
180
-
181
- parts.push(`${elapsed}s`);
182
-
183
- return parts.join(" | ");
184
- }