pi-crew 0.1.46 → 0.1.51

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 (262) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/agents/analyst.md +11 -11
  3. package/agents/critic.md +11 -11
  4. package/agents/executor.md +11 -11
  5. package/agents/explorer.md +11 -11
  6. package/agents/planner.md +11 -11
  7. package/agents/reviewer.md +11 -11
  8. package/agents/security-reviewer.md +11 -11
  9. package/agents/test-engineer.md +11 -11
  10. package/agents/verifier.md +11 -11
  11. package/agents/writer.md +11 -11
  12. package/docs/next-upgrade-roadmap.md +117 -42
  13. package/docs/refactor-tasks-phase3.md +394 -394
  14. package/docs/refactor-tasks-phase4.md +564 -564
  15. package/docs/refactor-tasks-phase5.md +402 -402
  16. package/docs/refactor-tasks-phase6.md +662 -662
  17. package/docs/research/AGENT-EXECUTION-ARCHITECTURE.md +261 -0
  18. package/docs/research/AGENT-LIFECYCLE-COMPARISON.md +111 -0
  19. package/docs/research/AUDIT_OH_MY_PI.md +261 -0
  20. package/docs/research/AUDIT_PI_CREW.md +457 -0
  21. package/docs/research/CAVEMAN-DEEP-RESEARCH.md +281 -0
  22. package/docs/research/COMPARISON_OH_MY_PI_VS_PI_CREW.md +264 -0
  23. package/docs/research/DEEP-RESEARCH-PI-POWERBAR.md +343 -0
  24. package/docs/research/DEEP_RESEARCH_SUBAGENT_ARCHITECTURE.md +480 -0
  25. package/docs/research/GAP_CLOSURE_IMPLEMENTATION_PLAN.md +354 -0
  26. package/docs/research/IMPLEMENTATION_PLAN.md +385 -0
  27. package/docs/research/LIVE-SESSION-PRODUCTION-READY-PLAN.md +502 -0
  28. package/docs/research/OH-MY-PI-DEEP-RESEARCH-v14.7.6.md +266 -0
  29. package/docs/research/REMAINING-GAPS-PLAN.md +363 -0
  30. package/docs/research/SESSION-SUMMARY-2026-05-08.md +146 -0
  31. package/docs/research/UI-RESPONSIVENESS-AUDIT.md +173 -0
  32. package/docs/research-awesome-agent-skills-distillation.md +100 -100
  33. package/docs/research-extension-examples.md +297 -297
  34. package/docs/research-extension-system.md +324 -324
  35. package/docs/research-oh-my-pi-distillation.md +56 -9
  36. package/docs/research-optimization-plan.md +548 -548
  37. package/docs/research-phase10-distillation.md +198 -198
  38. package/docs/research-phase11-distillation.md +201 -201
  39. package/docs/research-pi-coding-agent.md +357 -357
  40. package/docs/research-source-pi-crew-reference.md +174 -174
  41. package/docs/runtime-flow.md +148 -148
  42. package/docs/source-runtime-refactor-map.md +107 -107
  43. package/index.ts +6 -6
  44. package/package.json +99 -98
  45. package/schema.json +8 -0
  46. package/skills/async-worker-recovery/SKILL.md +42 -42
  47. package/skills/context-artifact-hygiene/SKILL.md +52 -52
  48. package/skills/delegation-patterns/SKILL.md +54 -54
  49. package/skills/mailbox-interactive/SKILL.md +40 -40
  50. package/skills/model-routing-context/SKILL.md +39 -39
  51. package/skills/multi-perspective-review/SKILL.md +58 -58
  52. package/skills/observability-reliability/SKILL.md +41 -41
  53. package/skills/orchestration/SKILL.md +157 -0
  54. package/skills/ownership-session-security/SKILL.md +41 -41
  55. package/skills/pi-extension-lifecycle/SKILL.md +39 -39
  56. package/skills/requirements-to-task-packet/SKILL.md +63 -63
  57. package/skills/resource-discovery-config/SKILL.md +41 -41
  58. package/skills/runtime-state-reader/SKILL.md +44 -44
  59. package/skills/secure-agent-orchestration-review/SKILL.md +45 -45
  60. package/skills/state-mutation-locking/SKILL.md +42 -42
  61. package/skills/systematic-debugging/SKILL.md +67 -67
  62. package/skills/ui-render-performance/SKILL.md +39 -39
  63. package/skills/verification-before-done/SKILL.md +57 -57
  64. package/skills/worktree-isolation/SKILL.md +39 -39
  65. package/src/agents/agent-config.ts +6 -0
  66. package/src/agents/agent-search.ts +98 -0
  67. package/src/agents/agent-serializer.ts +4 -0
  68. package/src/agents/discover-agents.ts +17 -4
  69. package/src/config/config.ts +25 -0
  70. package/src/config/defaults.ts +16 -5
  71. package/src/extension/autonomous-policy.ts +26 -33
  72. package/src/extension/cross-extension-rpc.ts +94 -82
  73. package/src/extension/help.ts +1 -0
  74. package/src/extension/management.ts +5 -0
  75. package/src/extension/project-init.ts +15 -3
  76. package/src/extension/register.ts +78 -19
  77. package/src/extension/registration/commands.ts +33 -1
  78. package/src/extension/registration/compaction-guard.ts +125 -125
  79. package/src/extension/registration/team-tool.ts +6 -4
  80. package/src/extension/run-bundle-schema.ts +89 -89
  81. package/src/extension/run-export.ts +26 -12
  82. package/src/extension/run-index.ts +24 -18
  83. package/src/extension/run-maintenance.ts +68 -62
  84. package/src/extension/team-tool/api.ts +23 -2
  85. package/src/extension/team-tool/cancel.ts +86 -11
  86. package/src/extension/team-tool/context.ts +4 -1
  87. package/src/extension/team-tool/handle-settings.ts +188 -188
  88. package/src/extension/team-tool/inspect.ts +41 -41
  89. package/src/extension/team-tool/intent-policy.ts +42 -0
  90. package/src/extension/team-tool/lifecycle-actions.ts +47 -18
  91. package/src/extension/team-tool/parallel-dispatch.ts +156 -0
  92. package/src/extension/team-tool/plan.ts +19 -19
  93. package/src/extension/team-tool/respond.ts +10 -2
  94. package/src/extension/team-tool/run.ts +3 -2
  95. package/src/extension/team-tool/status.ts +1 -1
  96. package/src/extension/team-tool-types.ts +1 -0
  97. package/src/extension/team-tool.ts +16 -5
  98. package/src/hooks/registry.ts +61 -0
  99. package/src/hooks/types.ts +41 -0
  100. package/src/i18n.ts +184 -184
  101. package/src/observability/exporters/otlp-exporter.ts +77 -77
  102. package/src/prompt/prompt-runtime.ts +72 -72
  103. package/src/runtime/agent-control.ts +108 -2
  104. package/src/runtime/agent-memory.ts +72 -72
  105. package/src/runtime/agent-observability.ts +114 -114
  106. package/src/runtime/async-marker.ts +26 -26
  107. package/src/runtime/async-runner.ts +3 -1
  108. package/src/runtime/attention-events.ts +28 -28
  109. package/src/runtime/background-runner.ts +19 -0
  110. package/src/runtime/cancellation-token.ts +89 -0
  111. package/src/runtime/cancellation.ts +61 -51
  112. package/src/runtime/capability-inventory.ts +116 -0
  113. package/src/runtime/child-pi.ts +2 -1
  114. package/src/runtime/code-summary.ts +247 -0
  115. package/src/runtime/completion-guard.ts +190 -190
  116. package/src/runtime/concurrency.ts +3 -1
  117. package/src/runtime/crash-recovery.ts +181 -0
  118. package/src/runtime/crew-agent-records.ts +35 -7
  119. package/src/runtime/crew-agent-runtime.ts +1 -0
  120. package/src/runtime/custom-tools/irc-tool.ts +201 -0
  121. package/src/runtime/custom-tools/submit-result-tool.ts +90 -0
  122. package/src/runtime/delivery-coordinator.ts +3 -1
  123. package/src/runtime/diagnostic-export.ts +3 -1
  124. package/src/runtime/direct-run.ts +35 -35
  125. package/src/runtime/effectiveness.ts +81 -76
  126. package/src/runtime/event-stream-bridge.ts +92 -0
  127. package/src/runtime/foreground-control.ts +82 -82
  128. package/src/runtime/green-contract.ts +46 -46
  129. package/src/runtime/group-join.ts +106 -106
  130. package/src/runtime/heartbeat-gradient.ts +28 -28
  131. package/src/runtime/heartbeat-watcher.ts +124 -124
  132. package/src/runtime/live-agent-control.ts +88 -88
  133. package/src/runtime/live-agent-manager.ts +78 -2
  134. package/src/runtime/live-control-realtime.ts +36 -36
  135. package/src/runtime/live-extension-bridge.ts +150 -0
  136. package/src/runtime/live-irc.ts +92 -0
  137. package/src/runtime/live-session-health.ts +100 -0
  138. package/src/runtime/live-session-runtime.ts +297 -7
  139. package/src/runtime/mcp-proxy.ts +113 -0
  140. package/src/runtime/notebook-helpers.ts +90 -0
  141. package/src/runtime/orphan-sentinel.ts +7 -0
  142. package/src/runtime/output-validator.ts +187 -0
  143. package/src/runtime/parallel-research.ts +44 -44
  144. package/src/runtime/parallel-utils.ts +57 -0
  145. package/src/runtime/parent-guard.ts +80 -0
  146. package/src/runtime/pi-args.ts +11 -2
  147. package/src/runtime/pi-json-output.ts +111 -111
  148. package/src/runtime/pi-spawn.ts +21 -3
  149. package/src/runtime/policy-engine.ts +79 -79
  150. package/src/runtime/process-status.ts +14 -1
  151. package/src/runtime/progress-event-coalescer.ts +43 -43
  152. package/src/runtime/prose-compressor.ts +164 -0
  153. package/src/runtime/recovery-recipes.ts +74 -74
  154. package/src/runtime/result-extractor.ts +121 -0
  155. package/src/runtime/role-permission.ts +39 -39
  156. package/src/runtime/runtime-resolver.ts +1 -4
  157. package/src/runtime/semaphore.ts +131 -0
  158. package/src/runtime/sensitive-paths.ts +92 -0
  159. package/src/runtime/session-resources.ts +25 -25
  160. package/src/runtime/session-snapshot.ts +59 -59
  161. package/src/runtime/session-usage.ts +79 -79
  162. package/src/runtime/sidechain-output.ts +29 -29
  163. package/src/runtime/stream-preview.ts +177 -0
  164. package/src/runtime/subagent-manager.ts +3 -2
  165. package/src/runtime/subprocess-tool-registry.ts +67 -0
  166. package/src/runtime/supervisor-contact.ts +59 -59
  167. package/src/runtime/task-display.ts +38 -38
  168. package/src/runtime/task-output-context.ts +59 -9
  169. package/src/runtime/task-runner/capabilities.ts +78 -78
  170. package/src/runtime/task-runner/live-executor.ts +2 -0
  171. package/src/runtime/task-runner/progress.ts +119 -119
  172. package/src/runtime/task-runner/prompt-builder.ts +71 -9
  173. package/src/runtime/task-runner/prompt-pipeline.ts +64 -64
  174. package/src/runtime/task-runner/result-utils.ts +14 -14
  175. package/src/runtime/task-runner/run-projection.ts +104 -0
  176. package/src/runtime/task-runner/state-helpers.ts +22 -22
  177. package/src/runtime/task-runner.ts +75 -4
  178. package/src/runtime/team-runner.ts +69 -8
  179. package/src/runtime/worker-heartbeat.ts +21 -21
  180. package/src/runtime/worker-startup.ts +57 -57
  181. package/src/runtime/workspace-tree.ts +298 -0
  182. package/src/runtime/yield-handler.ts +189 -0
  183. package/src/schema/config-schema.ts +7 -0
  184. package/src/schema/team-tool-schema.ts +11 -1
  185. package/src/skills/discover-skills.ts +67 -0
  186. package/src/state/active-run-registry.ts +4 -2
  187. package/src/state/artifact-store.ts +4 -1
  188. package/src/state/atomic-write.ts +50 -1
  189. package/src/state/blob-store.ts +117 -0
  190. package/src/state/contracts.ts +1 -0
  191. package/src/state/event-log-rotation.ts +158 -0
  192. package/src/state/event-log.ts +52 -2
  193. package/src/state/locks.ts +3 -1
  194. package/src/state/mailbox.ts +87 -7
  195. package/src/state/state-store.ts +24 -4
  196. package/src/state/task-claims.ts +44 -44
  197. package/src/state/types.ts +20 -0
  198. package/src/state/usage.ts +29 -29
  199. package/src/subagents/async-entry.ts +1 -1
  200. package/src/subagents/index.ts +3 -3
  201. package/src/subagents/live/control.ts +1 -1
  202. package/src/subagents/live/manager.ts +1 -1
  203. package/src/subagents/live/realtime.ts +1 -1
  204. package/src/subagents/live/session-runtime.ts +1 -1
  205. package/src/subagents/manager.ts +1 -1
  206. package/src/subagents/spawn.ts +1 -1
  207. package/src/teams/team-serializer.ts +38 -38
  208. package/src/types/diff.d.ts +18 -18
  209. package/src/ui/agent-management-overlay.ts +144 -0
  210. package/src/ui/crew-footer.ts +101 -101
  211. package/src/ui/crew-select-list.ts +111 -111
  212. package/src/ui/crew-widget.ts +15 -4
  213. package/src/ui/dashboard-panes/cancellation-pane.ts +43 -0
  214. package/src/ui/dashboard-panes/capability-pane.ts +60 -0
  215. package/src/ui/dashboard-panes/mailbox-pane.ts +35 -11
  216. package/src/ui/dashboard-panes/metrics-pane.ts +34 -34
  217. package/src/ui/dynamic-border.ts +25 -25
  218. package/src/ui/layout-primitives.ts +106 -106
  219. package/src/ui/live-run-sidebar.ts +4 -0
  220. package/src/ui/loaders.ts +158 -158
  221. package/src/ui/powerbar-publisher.ts +83 -15
  222. package/src/ui/render-coalescer.ts +51 -0
  223. package/src/ui/render-diff.ts +119 -119
  224. package/src/ui/render-scheduler.ts +143 -143
  225. package/src/ui/run-dashboard.ts +4 -0
  226. package/src/ui/run-event-bus.ts +209 -0
  227. package/src/ui/run-snapshot-cache.ts +68 -16
  228. package/src/ui/snapshot-types.ts +8 -0
  229. package/src/ui/spinner.ts +17 -17
  230. package/src/ui/status-colors.ts +58 -58
  231. package/src/ui/syntax-highlight.ts +116 -116
  232. package/src/ui/transcript-entries.ts +258 -0
  233. package/src/utils/atomic-write.ts +33 -33
  234. package/src/utils/completion-dedupe.ts +63 -63
  235. package/src/utils/frontmatter.ts +68 -68
  236. package/src/utils/git.ts +262 -262
  237. package/src/utils/ids.ts +17 -12
  238. package/src/utils/incremental-reader.ts +104 -0
  239. package/src/utils/names.ts +27 -27
  240. package/src/utils/redaction.ts +44 -44
  241. package/src/utils/safe-paths.ts +47 -47
  242. package/src/utils/scan-cache.ts +137 -0
  243. package/src/utils/sleep.ts +32 -32
  244. package/src/utils/sse-parser.ts +134 -0
  245. package/src/utils/task-name-generator.ts +337 -0
  246. package/src/utils/visual.ts +33 -2
  247. package/src/workflows/validate-workflow.ts +40 -40
  248. package/src/worktree/branch-freshness.ts +45 -45
  249. package/src/worktree/cleanup.ts +2 -1
  250. package/src/worktree/worktree-manager.ts +11 -3
  251. package/teams/default.team.md +12 -12
  252. package/teams/fast-fix.team.md +11 -11
  253. package/teams/implementation.team.md +18 -18
  254. package/teams/parallel-research.team.md +14 -14
  255. package/teams/research.team.md +11 -11
  256. package/teams/review.team.md +12 -12
  257. package/workflows/default.workflow.md +29 -29
  258. package/workflows/fast-fix.workflow.md +22 -22
  259. package/workflows/implementation.workflow.md +43 -38
  260. package/workflows/parallel-research.workflow.md +46 -46
  261. package/workflows/research.workflow.md +22 -22
  262. package/workflows/review.workflow.md +30 -30
@@ -1,76 +1,81 @@
1
- import type { CrewRuntimeConfig } from "../config/config.ts";
2
- import type { PolicyDecision, TeamRunManifest, TeamTaskState } from "../state/types.ts";
3
-
4
- export type EffectivenessGuardMode = "off" | "warn" | "block" | "fail";
5
- export type WorkerExecutionState = "enabled" | "disabled/scaffold";
6
- export type RunEffectivenessSeverity = "ok" | "warning" | "blocked" | "failed";
7
-
8
- export interface RunEffectivenessSummary {
9
- completed: number;
10
- observable: number;
11
- noObservedWorkTaskIds: string[];
12
- needsAttentionTaskIds: string[];
13
- workerExecution: WorkerExecutionState;
14
- guardMode: EffectivenessGuardMode;
15
- severity: RunEffectivenessSeverity;
16
- }
17
-
18
- export function taskHasObservableWorkerActivity(task: TeamTaskState): boolean {
19
- return Boolean(
20
- (task.agentProgress?.toolCount ?? 0) > 0
21
- || task.usage
22
- || task.transcriptArtifact
23
- || task.modelAttempts?.some((attempt) => attempt.success)
24
- || task.jsonEvents,
25
- );
26
- }
27
-
28
- export function resolveEffectivenessGuardMode(runtimeConfig: CrewRuntimeConfig | undefined, manifest?: TeamRunManifest): EffectivenessGuardMode {
29
- const configured = runtimeConfig?.effectivenessGuard;
30
- if (configured === "off" || configured === "warn" || configured === "block" || configured === "fail") return configured;
31
- if (manifest?.runtimeResolution?.safety === "explicit_dry_run") return "off";
32
- return "warn";
33
- }
34
-
35
- export function evaluateRunEffectiveness(input: { manifest?: TeamRunManifest; tasks: TeamTaskState[]; executeWorkers: boolean; runtimeConfig?: CrewRuntimeConfig }): RunEffectivenessSummary {
36
- const completedTasks = input.tasks.filter((task) => task.status === "completed");
37
- const noObservedWorkTasks = completedTasks.filter((task) => !taskHasObservableWorkerActivity(task));
38
- const needsAttentionTasks = input.tasks.filter((task) => task.agentProgress?.activityState === "needs_attention");
39
- const workerExecution: WorkerExecutionState = input.executeWorkers ? "enabled" : "disabled/scaffold";
40
- const guardMode = resolveEffectivenessGuardMode(input.runtimeConfig, input.manifest);
41
- const observable = Math.max(0, completedTasks.length - noObservedWorkTasks.length - needsAttentionTasks.length);
42
- let severity: RunEffectivenessSeverity = "ok";
43
- if (input.executeWorkers && guardMode !== "off" && noObservedWorkTasks.length > 0) {
44
- severity = guardMode === "fail" ? "failed" : guardMode === "block" ? "blocked" : "warning";
45
- }
46
- return {
47
- completed: completedTasks.length,
48
- observable,
49
- noObservedWorkTaskIds: noObservedWorkTasks.map((task) => task.id),
50
- needsAttentionTaskIds: needsAttentionTasks.map((task) => task.id),
51
- workerExecution,
52
- guardMode,
53
- severity,
54
- };
55
- }
56
-
57
- export function formatRunEffectivenessLines(summary: RunEffectivenessSummary): string[] {
58
- return [
59
- `Score: ${summary.observable}/${Math.max(1, summary.completed)} completed task(s) with observable worker activity`,
60
- `Worker execution: ${summary.workerExecution}`,
61
- `Guard: ${summary.guardMode} severity=${summary.severity}`,
62
- `No observable worker activity: ${summary.noObservedWorkTaskIds.length ? summary.noObservedWorkTaskIds.join(", ") : "none"}`,
63
- `Needs attention: ${summary.needsAttentionTaskIds.length ? summary.needsAttentionTaskIds.join(", ") : "none"}`,
64
- ];
65
- }
66
-
67
- export function effectivenessPolicyDecision(summary: RunEffectivenessSummary): PolicyDecision | undefined {
68
- if (summary.severity !== "warning" && summary.severity !== "blocked" && summary.severity !== "failed") return undefined;
69
- const action = summary.severity === "failed" ? "fail" : summary.severity === "blocked" ? "block" : "notify";
70
- return {
71
- action,
72
- reason: "ineffective_worker",
73
- message: `Run effectiveness guard ${summary.guardMode}: no observable worker activity for ${summary.noObservedWorkTaskIds.join(", ")}.`,
74
- createdAt: new Date().toISOString(),
75
- };
76
- }
1
+ import { permissionForRole } from "./role-permission.ts";
2
+ import type { CrewRuntimeConfig } from "../config/config.ts";
3
+ import type { PolicyDecision, TeamRunManifest, TeamTaskState } from "../state/types.ts";
4
+
5
+ export type EffectivenessGuardMode = "off" | "warn" | "block" | "fail";
6
+ export type WorkerExecutionState = "enabled" | "disabled/scaffold";
7
+ export type RunEffectivenessSeverity = "ok" | "warning" | "blocked" | "failed";
8
+
9
+ export interface RunEffectivenessSummary {
10
+ completed: number;
11
+ observable: number;
12
+ noObservedWorkTaskIds: string[];
13
+ needsAttentionTaskIds: string[];
14
+ workerExecution: WorkerExecutionState;
15
+ guardMode: EffectivenessGuardMode;
16
+ severity: RunEffectivenessSeverity;
17
+ }
18
+
19
+ export function taskHasObservableWorkerActivity(task: TeamTaskState): boolean {
20
+ return Boolean(
21
+ (task.agentProgress?.toolCount ?? 0) > 0
22
+ || task.usage
23
+ || task.transcriptArtifact
24
+ || task.modelAttempts?.some((attempt) => attempt.success)
25
+ || task.jsonEvents,
26
+ );
27
+ }
28
+
29
+ export function resolveEffectivenessGuardMode(runtimeConfig: CrewRuntimeConfig | undefined, manifest?: TeamRunManifest): EffectivenessGuardMode {
30
+ const configured = runtimeConfig?.effectivenessGuard;
31
+ if (configured === "off" || configured === "warn" || configured === "block" || configured === "fail") return configured;
32
+ if (manifest?.runtimeResolution?.safety === "explicit_dry_run") return "off";
33
+ return "warn";
34
+ }
35
+
36
+ export function evaluateRunEffectiveness(input: { manifest?: TeamRunManifest; tasks: TeamTaskState[]; executeWorkers: boolean; runtimeConfig?: CrewRuntimeConfig }): RunEffectivenessSummary {
37
+ const completedTasks = input.tasks.filter((task) => task.status === "completed");
38
+ const noObservedWorkTasks = completedTasks.filter((task) => !taskHasObservableWorkerActivity(task));
39
+ const needsAttentionTasks = input.tasks.filter((task) => task.agentProgress?.activityState === "needs_attention");
40
+ const workerExecution: WorkerExecutionState = input.executeWorkers ? "enabled" : "disabled/scaffold";
41
+ const guardMode = resolveEffectivenessGuardMode(input.runtimeConfig, input.manifest);
42
+ const observable = Math.max(0, completedTasks.length - noObservedWorkTasks.length - needsAttentionTasks.length);
43
+ let severity: RunEffectivenessSeverity = "ok";
44
+ if (input.executeWorkers && guardMode !== "off" && noObservedWorkTasks.length > 0) {
45
+ severity = guardMode === "fail" ? "failed" : guardMode === "block" ? "blocked" : "warning";
46
+ // P0.1: default warn escalates to blocked for mutating-role tasks without observed work
47
+ if (severity === "warning" && noObservedWorkTasks.some((task) => permissionForRole(task.role) !== "read_only")) {
48
+ severity = "blocked";
49
+ }
50
+ }
51
+ return {
52
+ completed: completedTasks.length,
53
+ observable,
54
+ noObservedWorkTaskIds: noObservedWorkTasks.map((task) => task.id),
55
+ needsAttentionTaskIds: needsAttentionTasks.map((task) => task.id),
56
+ workerExecution,
57
+ guardMode,
58
+ severity,
59
+ };
60
+ }
61
+
62
+ export function formatRunEffectivenessLines(summary: RunEffectivenessSummary): string[] {
63
+ return [
64
+ `Score: ${summary.observable}/${Math.max(1, summary.completed)} completed task(s) with observable worker activity`,
65
+ `Worker execution: ${summary.workerExecution}`,
66
+ `Guard: ${summary.guardMode} severity=${summary.severity}`,
67
+ `No observable worker activity: ${summary.noObservedWorkTaskIds.length ? summary.noObservedWorkTaskIds.join(", ") : "none"}`,
68
+ `Needs attention: ${summary.needsAttentionTaskIds.length ? summary.needsAttentionTaskIds.join(", ") : "none"}`,
69
+ ];
70
+ }
71
+
72
+ export function effectivenessPolicyDecision(summary: RunEffectivenessSummary): PolicyDecision | undefined {
73
+ if (summary.severity !== "warning" && summary.severity !== "blocked" && summary.severity !== "failed") return undefined;
74
+ const action = summary.severity === "failed" ? "fail" : summary.severity === "blocked" ? "block" : "notify";
75
+ return {
76
+ action,
77
+ reason: "ineffective_worker",
78
+ message: `Run effectiveness guard ${summary.guardMode}: no observable worker activity for ${summary.noObservedWorkTaskIds.join(", ")}.`,
79
+ createdAt: new Date().toISOString(),
80
+ };
81
+ }
@@ -0,0 +1,92 @@
1
+ import { runEventBus } from "../ui/run-event-bus.ts";
2
+ import { subprocessToolRegistry, type SubprocessToolEvent } from "./subprocess-tool-registry.ts";
3
+
4
+ export interface StreamBridgeEvent {
5
+ runId: string;
6
+ taskId: string;
7
+ eventType: string;
8
+ toolName?: string;
9
+ toolArgs?: string;
10
+ intent?: string;
11
+ tokens?: number;
12
+ timestamp: number;
13
+ extractedToolData?: Record<string, unknown>;
14
+ }
15
+
16
+ const activeBridges = new Map<string, (event: StreamBridgeEvent) => void>();
17
+
18
+ export function registerStreamBridge(runId: string): { handler: (event: StreamBridgeEvent) => void; dispose: () => void } {
19
+ const existing = activeBridges.get(runId);
20
+ if (existing) {
21
+ return { handler: existing, dispose: () => unregisterStreamBridge(runId) };
22
+ }
23
+
24
+ const handler = (event: StreamBridgeEvent) => {
25
+ runEventBus.emit({
26
+ type: "worker_status",
27
+ runId: event.runId,
28
+ taskId: event.taskId,
29
+ data: event,
30
+ });
31
+ };
32
+
33
+ activeBridges.set(runId, handler);
34
+ return { handler, dispose: () => unregisterStreamBridge(runId) };
35
+ }
36
+
37
+ export function unregisterStreamBridge(runId: string): void {
38
+ activeBridges.delete(runId);
39
+ }
40
+
41
+ export function bridgeEventFromJsonEvent(runId: string, taskId: string, event: unknown): StreamBridgeEvent | null {
42
+ if (!event || typeof event !== "object") return null;
43
+ const record = event as Record<string, unknown>;
44
+ const type = typeof record.type === "string" ? record.type : "";
45
+
46
+ const result: StreamBridgeEvent = {
47
+ runId,
48
+ taskId,
49
+ eventType: type,
50
+ timestamp: Date.now(),
51
+ };
52
+
53
+ if (typeof record.toolName === "string") result.toolName = record.toolName;
54
+ if (record.args && typeof record.args === "object") {
55
+ try {
56
+ const json = JSON.stringify(record.args);
57
+ // Truncate at a JSON boundary to avoid breaking structure
58
+ result.toolArgs = json.length > 200 ? json.slice(0, 197) + "..." : json;
59
+ } catch {
60
+ /* skip */
61
+ }
62
+ }
63
+ if (typeof record.intent === "string") result.intent = record.intent;
64
+
65
+ // Extract tokens from usage/message_end events
66
+ const usage = record.usage ?? (record.message as Record<string, unknown> | undefined)?.usage;
67
+ if (usage && typeof usage === "object") {
68
+ const u = usage as Record<string, unknown>;
69
+ const input = typeof u.input === "number" ? u.input : 0;
70
+ const output = typeof u.output === "number" ? u.output : 0;
71
+ if (input || output) result.tokens = input + output;
72
+ }
73
+
74
+ // Attach extracted tool data via subprocess tool registry
75
+ if (result.toolName && subprocessToolRegistry.hasHandler(result.toolName)) {
76
+ const handler = subprocessToolRegistry.getHandler(result.toolName);
77
+ if (handler?.extractData) {
78
+ const extracted = handler.extractData({
79
+ toolName: result.toolName,
80
+ toolCallId: (record.toolCallId as string) ?? "",
81
+ args: record.args as Record<string, unknown> | undefined,
82
+ result: record.result as SubprocessToolEvent["result"],
83
+ isError: record.isError as boolean | undefined,
84
+ });
85
+ if (extracted !== undefined) {
86
+ result.extractedToolData = { [result.toolName]: extracted };
87
+ }
88
+ }
89
+ }
90
+
91
+ return result;
92
+ }
@@ -1,82 +1,82 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { appendEvent } from "../state/event-log.ts";
4
- import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
5
- import { checkProcessLiveness, isActiveRunStatus } from "./process-status.ts";
6
- import { readCrewAgents } from "./crew-agent-records.ts";
7
-
8
- export type ForegroundControlRequestType = "interrupt" | "status";
9
-
10
- export interface ForegroundControlStatus {
11
- runId: string;
12
- status: TeamRunManifest["status"];
13
- active: boolean;
14
- asyncPid?: number;
15
- asyncAlive?: boolean;
16
- runningTasks: string[];
17
- runningAgents: string[];
18
- controlPath: string;
19
- lastRequest?: ForegroundControlRequest;
20
- }
21
-
22
- export interface ForegroundControlRequest {
23
- id: string;
24
- type: ForegroundControlRequestType;
25
- createdAt: string;
26
- reason: string;
27
- acknowledged: boolean;
28
- }
29
-
30
- export function foregroundControlPath(manifest: TeamRunManifest): string {
31
- return path.join(manifest.stateRoot, "foreground-control.json");
32
- }
33
-
34
- function readLastRequest(controlPath: string): ForegroundControlRequest | undefined {
35
- if (!fs.existsSync(controlPath)) return undefined;
36
- try {
37
- const parsed = JSON.parse(fs.readFileSync(controlPath, "utf-8")) as { requests?: ForegroundControlRequest[] };
38
- return parsed.requests?.at(-1);
39
- } catch {
40
- return undefined;
41
- }
42
- }
43
-
44
- export function readForegroundControlStatus(manifest: TeamRunManifest, tasks: TeamTaskState[]): ForegroundControlStatus {
45
- const controlPath = foregroundControlPath(manifest);
46
- const asyncAlive = manifest.async?.pid !== undefined ? checkProcessLiveness(manifest.async.pid).alive : undefined;
47
- return {
48
- runId: manifest.runId,
49
- status: manifest.status,
50
- active: isActiveRunStatus(manifest.status),
51
- asyncPid: manifest.async?.pid,
52
- asyncAlive,
53
- runningTasks: tasks.filter((task) => task.status === "running").map((task) => task.id),
54
- runningAgents: readCrewAgents(manifest).filter((agent) => agent.status === "running").map((agent) => agent.id),
55
- controlPath,
56
- lastRequest: readLastRequest(controlPath),
57
- };
58
- }
59
-
60
- export function writeForegroundInterruptRequest(manifest: TeamRunManifest, reason = "User requested foreground interrupt."): ForegroundControlRequest {
61
- const controlPath = foregroundControlPath(manifest);
62
- let requests: ForegroundControlRequest[] = [];
63
- if (fs.existsSync(controlPath)) {
64
- try {
65
- const parsed = JSON.parse(fs.readFileSync(controlPath, "utf-8")) as { requests?: ForegroundControlRequest[] };
66
- requests = Array.isArray(parsed.requests) ? parsed.requests : [];
67
- } catch {
68
- requests = [];
69
- }
70
- }
71
- const request: ForegroundControlRequest = {
72
- id: `fg_${Date.now().toString(36)}_${Math.random().toString(16).slice(2, 10)}`,
73
- type: "interrupt",
74
- createdAt: new Date().toISOString(),
75
- reason,
76
- acknowledged: false,
77
- };
78
- fs.mkdirSync(path.dirname(controlPath), { recursive: true });
79
- fs.writeFileSync(controlPath, `${JSON.stringify({ requests: [...requests, request] }, null, 2)}\n`, "utf-8");
80
- appendEvent(manifest.eventsPath, { type: "foreground.interrupt_requested", runId: manifest.runId, message: reason, data: { requestId: request.id, controlPath } });
81
- return request;
82
- }
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { appendEvent } from "../state/event-log.ts";
4
+ import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
5
+ import { checkProcessLiveness, isActiveRunStatus } from "./process-status.ts";
6
+ import { readCrewAgents } from "./crew-agent-records.ts";
7
+
8
+ export type ForegroundControlRequestType = "interrupt" | "status";
9
+
10
+ export interface ForegroundControlStatus {
11
+ runId: string;
12
+ status: TeamRunManifest["status"];
13
+ active: boolean;
14
+ asyncPid?: number;
15
+ asyncAlive?: boolean;
16
+ runningTasks: string[];
17
+ runningAgents: string[];
18
+ controlPath: string;
19
+ lastRequest?: ForegroundControlRequest;
20
+ }
21
+
22
+ export interface ForegroundControlRequest {
23
+ id: string;
24
+ type: ForegroundControlRequestType;
25
+ createdAt: string;
26
+ reason: string;
27
+ acknowledged: boolean;
28
+ }
29
+
30
+ export function foregroundControlPath(manifest: TeamRunManifest): string {
31
+ return path.join(manifest.stateRoot, "foreground-control.json");
32
+ }
33
+
34
+ function readLastRequest(controlPath: string): ForegroundControlRequest | undefined {
35
+ if (!fs.existsSync(controlPath)) return undefined;
36
+ try {
37
+ const parsed = JSON.parse(fs.readFileSync(controlPath, "utf-8")) as { requests?: ForegroundControlRequest[] };
38
+ return parsed.requests?.at(-1);
39
+ } catch {
40
+ return undefined;
41
+ }
42
+ }
43
+
44
+ export function readForegroundControlStatus(manifest: TeamRunManifest, tasks: TeamTaskState[]): ForegroundControlStatus {
45
+ const controlPath = foregroundControlPath(manifest);
46
+ const asyncAlive = manifest.async?.pid !== undefined ? checkProcessLiveness(manifest.async.pid).alive : undefined;
47
+ return {
48
+ runId: manifest.runId,
49
+ status: manifest.status,
50
+ active: isActiveRunStatus(manifest.status),
51
+ asyncPid: manifest.async?.pid,
52
+ asyncAlive,
53
+ runningTasks: tasks.filter((task) => task.status === "running").map((task) => task.id),
54
+ runningAgents: readCrewAgents(manifest).filter((agent) => agent.status === "running").map((agent) => agent.id),
55
+ controlPath,
56
+ lastRequest: readLastRequest(controlPath),
57
+ };
58
+ }
59
+
60
+ export function writeForegroundInterruptRequest(manifest: TeamRunManifest, reason = "User requested foreground interrupt."): ForegroundControlRequest {
61
+ const controlPath = foregroundControlPath(manifest);
62
+ let requests: ForegroundControlRequest[] = [];
63
+ if (fs.existsSync(controlPath)) {
64
+ try {
65
+ const parsed = JSON.parse(fs.readFileSync(controlPath, "utf-8")) as { requests?: ForegroundControlRequest[] };
66
+ requests = Array.isArray(parsed.requests) ? parsed.requests : [];
67
+ } catch {
68
+ requests = [];
69
+ }
70
+ }
71
+ const request: ForegroundControlRequest = {
72
+ id: `fg_${Date.now().toString(36)}_${Math.random().toString(16).slice(2, 10)}`,
73
+ type: "interrupt",
74
+ createdAt: new Date().toISOString(),
75
+ reason,
76
+ acknowledged: false,
77
+ };
78
+ fs.mkdirSync(path.dirname(controlPath), { recursive: true });
79
+ fs.writeFileSync(controlPath, `${JSON.stringify({ requests: [...requests, request] }, null, 2)}\n`, "utf-8");
80
+ appendEvent(manifest.eventsPath, { type: "foreground.interrupt_requested", runId: manifest.runId, message: reason, data: { requestId: request.id, controlPath } });
81
+ return request;
82
+ }
@@ -1,46 +1,46 @@
1
- import type { GreenLevel, VerificationContract, VerificationEvidence } from "../state/types.ts";
2
-
3
- const GREEN_ORDER: Record<GreenLevel, number> = {
4
- none: 0,
5
- targeted: 1,
6
- package: 2,
7
- workspace: 3,
8
- merge_ready: 4,
9
- };
10
-
11
- export interface GreenContractOutcome {
12
- requiredGreenLevel: GreenLevel;
13
- observedGreenLevel: GreenLevel;
14
- satisfied: boolean;
15
- }
16
-
17
- export function greenLevelSatisfies(observed: GreenLevel, required: GreenLevel): boolean {
18
- return GREEN_ORDER[observed] >= GREEN_ORDER[required];
19
- }
20
-
21
- export function evaluateGreenContract(contract: VerificationContract, evidence?: VerificationEvidence): GreenContractOutcome {
22
- const observedGreenLevel = evidence?.observedGreenLevel ?? "none";
23
- return {
24
- requiredGreenLevel: contract.requiredGreenLevel,
25
- observedGreenLevel,
26
- satisfied: greenLevelSatisfies(observedGreenLevel, contract.requiredGreenLevel),
27
- };
28
- }
29
-
30
- export function inferGreenLevelFromTask(success: boolean, contract: VerificationContract): GreenLevel {
31
- if (!success) return "none";
32
- if (contract.requiredGreenLevel === "none") return "none";
33
- return contract.allowManualEvidence ? contract.requiredGreenLevel : "targeted";
34
- }
35
-
36
- export function createVerificationEvidence(contract: VerificationContract, success: boolean, notes: string): VerificationEvidence {
37
- const observedGreenLevel = inferGreenLevelFromTask(success, contract);
38
- const outcome = evaluateGreenContract(contract, { requiredGreenLevel: contract.requiredGreenLevel, observedGreenLevel, satisfied: false, commands: [], notes });
39
- return {
40
- requiredGreenLevel: contract.requiredGreenLevel,
41
- observedGreenLevel,
42
- satisfied: outcome.satisfied,
43
- commands: contract.commands.map((cmd) => ({ cmd, status: "not_run" as const })),
44
- notes,
45
- };
46
- }
1
+ import type { GreenLevel, VerificationContract, VerificationEvidence } from "../state/types.ts";
2
+
3
+ const GREEN_ORDER: Record<GreenLevel, number> = {
4
+ none: 0,
5
+ targeted: 1,
6
+ package: 2,
7
+ workspace: 3,
8
+ merge_ready: 4,
9
+ };
10
+
11
+ export interface GreenContractOutcome {
12
+ requiredGreenLevel: GreenLevel;
13
+ observedGreenLevel: GreenLevel;
14
+ satisfied: boolean;
15
+ }
16
+
17
+ export function greenLevelSatisfies(observed: GreenLevel, required: GreenLevel): boolean {
18
+ return GREEN_ORDER[observed] >= GREEN_ORDER[required];
19
+ }
20
+
21
+ export function evaluateGreenContract(contract: VerificationContract, evidence?: VerificationEvidence): GreenContractOutcome {
22
+ const observedGreenLevel = evidence?.observedGreenLevel ?? "none";
23
+ return {
24
+ requiredGreenLevel: contract.requiredGreenLevel,
25
+ observedGreenLevel,
26
+ satisfied: greenLevelSatisfies(observedGreenLevel, contract.requiredGreenLevel),
27
+ };
28
+ }
29
+
30
+ export function inferGreenLevelFromTask(success: boolean, contract: VerificationContract): GreenLevel {
31
+ if (!success) return "none";
32
+ if (contract.requiredGreenLevel === "none") return "none";
33
+ return contract.allowManualEvidence ? contract.requiredGreenLevel : "targeted";
34
+ }
35
+
36
+ export function createVerificationEvidence(contract: VerificationContract, success: boolean, notes: string): VerificationEvidence {
37
+ const observedGreenLevel = inferGreenLevelFromTask(success, contract);
38
+ const outcome = evaluateGreenContract(contract, { requiredGreenLevel: contract.requiredGreenLevel, observedGreenLevel, satisfied: false, commands: [], notes });
39
+ return {
40
+ requiredGreenLevel: contract.requiredGreenLevel,
41
+ observedGreenLevel,
42
+ satisfied: outcome.satisfied,
43
+ commands: contract.commands.map((cmd) => ({ cmd, status: "not_run" as const })),
44
+ notes,
45
+ };
46
+ }