pi-crew 0.1.46 → 0.1.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/CHANGELOG.md +97 -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 +24 -0
  70. package/src/config/defaults.ts +11 -0
  71. package/src/extension/autonomous-policy.ts +26 -33
  72. package/src/extension/cross-extension-rpc.ts +82 -82
  73. package/src/extension/help.ts +1 -0
  74. package/src/extension/management.ts +5 -0
  75. package/src/extension/register.ts +58 -13
  76. package/src/extension/registration/commands.ts +33 -1
  77. package/src/extension/registration/compaction-guard.ts +125 -125
  78. package/src/extension/registration/team-tool.ts +6 -4
  79. package/src/extension/run-bundle-schema.ts +89 -89
  80. package/src/extension/run-index.ts +24 -18
  81. package/src/extension/run-maintenance.ts +68 -62
  82. package/src/extension/team-tool/api.ts +23 -2
  83. package/src/extension/team-tool/cancel.ts +86 -11
  84. package/src/extension/team-tool/context.ts +3 -0
  85. package/src/extension/team-tool/handle-settings.ts +188 -188
  86. package/src/extension/team-tool/inspect.ts +41 -41
  87. package/src/extension/team-tool/intent-policy.ts +42 -0
  88. package/src/extension/team-tool/lifecycle-actions.ts +47 -18
  89. package/src/extension/team-tool/parallel-dispatch.ts +156 -0
  90. package/src/extension/team-tool/plan.ts +19 -19
  91. package/src/extension/team-tool/respond.ts +10 -2
  92. package/src/extension/team-tool/run.ts +3 -2
  93. package/src/extension/team-tool/status.ts +1 -1
  94. package/src/extension/team-tool-types.ts +1 -0
  95. package/src/extension/team-tool.ts +13 -3
  96. package/src/hooks/registry.ts +61 -0
  97. package/src/hooks/types.ts +41 -0
  98. package/src/i18n.ts +184 -184
  99. package/src/observability/exporters/otlp-exporter.ts +77 -77
  100. package/src/prompt/prompt-runtime.ts +72 -72
  101. package/src/runtime/agent-control.ts +108 -2
  102. package/src/runtime/agent-memory.ts +72 -72
  103. package/src/runtime/agent-observability.ts +114 -114
  104. package/src/runtime/async-marker.ts +26 -26
  105. package/src/runtime/async-runner.ts +3 -1
  106. package/src/runtime/attention-events.ts +28 -28
  107. package/src/runtime/background-runner.ts +19 -0
  108. package/src/runtime/cancellation-token.ts +89 -0
  109. package/src/runtime/cancellation.ts +61 -51
  110. package/src/runtime/capability-inventory.ts +116 -0
  111. package/src/runtime/child-pi.ts +2 -1
  112. package/src/runtime/code-summary.ts +247 -0
  113. package/src/runtime/completion-guard.ts +190 -190
  114. package/src/runtime/crash-recovery.ts +181 -0
  115. package/src/runtime/crew-agent-records.ts +35 -7
  116. package/src/runtime/crew-agent-runtime.ts +1 -0
  117. package/src/runtime/custom-tools/irc-tool.ts +201 -0
  118. package/src/runtime/custom-tools/submit-result-tool.ts +90 -0
  119. package/src/runtime/delivery-coordinator.ts +3 -1
  120. package/src/runtime/direct-run.ts +35 -35
  121. package/src/runtime/effectiveness.ts +81 -76
  122. package/src/runtime/event-stream-bridge.ts +90 -0
  123. package/src/runtime/foreground-control.ts +82 -82
  124. package/src/runtime/green-contract.ts +46 -46
  125. package/src/runtime/group-join.ts +106 -106
  126. package/src/runtime/heartbeat-gradient.ts +28 -28
  127. package/src/runtime/heartbeat-watcher.ts +124 -124
  128. package/src/runtime/live-agent-control.ts +88 -88
  129. package/src/runtime/live-agent-manager.ts +78 -2
  130. package/src/runtime/live-control-realtime.ts +36 -36
  131. package/src/runtime/live-extension-bridge.ts +150 -0
  132. package/src/runtime/live-irc.ts +92 -0
  133. package/src/runtime/live-session-health.ts +100 -0
  134. package/src/runtime/live-session-runtime.ts +297 -7
  135. package/src/runtime/mcp-proxy.ts +113 -0
  136. package/src/runtime/notebook-helpers.ts +90 -0
  137. package/src/runtime/orphan-sentinel.ts +7 -0
  138. package/src/runtime/output-validator.ts +187 -0
  139. package/src/runtime/parallel-research.ts +44 -44
  140. package/src/runtime/parallel-utils.ts +57 -0
  141. package/src/runtime/parent-guard.ts +80 -0
  142. package/src/runtime/pi-json-output.ts +111 -111
  143. package/src/runtime/policy-engine.ts +79 -79
  144. package/src/runtime/progress-event-coalescer.ts +43 -43
  145. package/src/runtime/prose-compressor.ts +164 -0
  146. package/src/runtime/recovery-recipes.ts +74 -74
  147. package/src/runtime/result-extractor.ts +121 -0
  148. package/src/runtime/role-permission.ts +39 -39
  149. package/src/runtime/runtime-resolver.ts +1 -4
  150. package/src/runtime/semaphore.ts +131 -0
  151. package/src/runtime/sensitive-paths.ts +92 -0
  152. package/src/runtime/session-resources.ts +25 -25
  153. package/src/runtime/session-snapshot.ts +59 -59
  154. package/src/runtime/session-usage.ts +79 -79
  155. package/src/runtime/sidechain-output.ts +29 -29
  156. package/src/runtime/stream-preview.ts +177 -0
  157. package/src/runtime/subagent-manager.ts +3 -2
  158. package/src/runtime/subprocess-tool-registry.ts +67 -0
  159. package/src/runtime/supervisor-contact.ts +59 -59
  160. package/src/runtime/task-display.ts +38 -38
  161. package/src/runtime/task-output-context.ts +59 -9
  162. package/src/runtime/task-runner/capabilities.ts +78 -78
  163. package/src/runtime/task-runner/live-executor.ts +2 -0
  164. package/src/runtime/task-runner/progress.ts +119 -119
  165. package/src/runtime/task-runner/prompt-builder.ts +70 -8
  166. package/src/runtime/task-runner/prompt-pipeline.ts +64 -64
  167. package/src/runtime/task-runner/result-utils.ts +14 -14
  168. package/src/runtime/task-runner/run-projection.ts +104 -0
  169. package/src/runtime/task-runner/state-helpers.ts +22 -22
  170. package/src/runtime/task-runner.ts +75 -4
  171. package/src/runtime/team-runner.ts +60 -8
  172. package/src/runtime/worker-heartbeat.ts +21 -21
  173. package/src/runtime/worker-startup.ts +57 -57
  174. package/src/runtime/workspace-tree.ts +298 -0
  175. package/src/runtime/yield-handler.ts +189 -0
  176. package/src/schema/config-schema.ts +6 -0
  177. package/src/schema/team-tool-schema.ts +11 -1
  178. package/src/skills/discover-skills.ts +67 -0
  179. package/src/state/active-run-registry.ts +4 -2
  180. package/src/state/artifact-store.ts +4 -1
  181. package/src/state/atomic-write.ts +50 -1
  182. package/src/state/blob-store.ts +117 -0
  183. package/src/state/contracts.ts +1 -0
  184. package/src/state/event-log-rotation.ts +158 -0
  185. package/src/state/event-log.ts +52 -2
  186. package/src/state/mailbox.ts +87 -7
  187. package/src/state/state-store.ts +24 -4
  188. package/src/state/task-claims.ts +44 -44
  189. package/src/state/types.ts +20 -0
  190. package/src/state/usage.ts +29 -29
  191. package/src/subagents/async-entry.ts +1 -1
  192. package/src/subagents/index.ts +3 -3
  193. package/src/subagents/live/control.ts +1 -1
  194. package/src/subagents/live/manager.ts +1 -1
  195. package/src/subagents/live/realtime.ts +1 -1
  196. package/src/subagents/live/session-runtime.ts +1 -1
  197. package/src/subagents/manager.ts +1 -1
  198. package/src/subagents/spawn.ts +1 -1
  199. package/src/teams/team-serializer.ts +38 -38
  200. package/src/types/diff.d.ts +18 -18
  201. package/src/ui/agent-management-overlay.ts +144 -0
  202. package/src/ui/crew-footer.ts +101 -101
  203. package/src/ui/crew-select-list.ts +111 -111
  204. package/src/ui/crew-widget.ts +11 -2
  205. package/src/ui/dashboard-panes/cancellation-pane.ts +43 -0
  206. package/src/ui/dashboard-panes/capability-pane.ts +60 -0
  207. package/src/ui/dashboard-panes/mailbox-pane.ts +35 -11
  208. package/src/ui/dashboard-panes/metrics-pane.ts +34 -34
  209. package/src/ui/dynamic-border.ts +25 -25
  210. package/src/ui/layout-primitives.ts +106 -106
  211. package/src/ui/live-run-sidebar.ts +4 -0
  212. package/src/ui/loaders.ts +158 -158
  213. package/src/ui/powerbar-publisher.ts +77 -15
  214. package/src/ui/render-coalescer.ts +51 -0
  215. package/src/ui/render-diff.ts +119 -119
  216. package/src/ui/render-scheduler.ts +143 -143
  217. package/src/ui/run-dashboard.ts +4 -0
  218. package/src/ui/run-event-bus.ts +209 -0
  219. package/src/ui/run-snapshot-cache.ts +68 -16
  220. package/src/ui/snapshot-types.ts +8 -0
  221. package/src/ui/spinner.ts +17 -17
  222. package/src/ui/status-colors.ts +58 -58
  223. package/src/ui/syntax-highlight.ts +116 -116
  224. package/src/ui/transcript-entries.ts +258 -0
  225. package/src/utils/atomic-write.ts +33 -33
  226. package/src/utils/completion-dedupe.ts +63 -63
  227. package/src/utils/frontmatter.ts +68 -68
  228. package/src/utils/git.ts +262 -262
  229. package/src/utils/ids.ts +17 -12
  230. package/src/utils/incremental-reader.ts +104 -0
  231. package/src/utils/names.ts +27 -27
  232. package/src/utils/redaction.ts +44 -44
  233. package/src/utils/safe-paths.ts +47 -47
  234. package/src/utils/scan-cache.ts +137 -0
  235. package/src/utils/sleep.ts +32 -32
  236. package/src/utils/sse-parser.ts +134 -0
  237. package/src/utils/task-name-generator.ts +337 -0
  238. package/src/utils/visual.ts +33 -2
  239. package/src/workflows/validate-workflow.ts +40 -40
  240. package/src/worktree/branch-freshness.ts +45 -45
  241. package/src/worktree/cleanup.ts +2 -1
  242. package/teams/default.team.md +12 -12
  243. package/teams/fast-fix.team.md +11 -11
  244. package/teams/implementation.team.md +18 -18
  245. package/teams/parallel-research.team.md +14 -14
  246. package/teams/research.team.md +11 -11
  247. package/teams/review.team.md +12 -12
  248. package/workflows/default.workflow.md +29 -29
  249. package/workflows/fast-fix.workflow.md +22 -22
  250. package/workflows/implementation.workflow.md +38 -38
  251. package/workflows/parallel-research.workflow.md +46 -46
  252. package/workflows/research.workflow.md +22 -22
  253. 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,90 @@
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
+ result.toolArgs = JSON.stringify(record.args).slice(0, 200);
57
+ } catch {
58
+ /* skip */
59
+ }
60
+ }
61
+ if (typeof record.intent === "string") result.intent = record.intent;
62
+
63
+ // Extract tokens from usage/message_end events
64
+ const usage = record.usage ?? (record.message as Record<string, unknown> | undefined)?.usage;
65
+ if (usage && typeof usage === "object") {
66
+ const u = usage as Record<string, unknown>;
67
+ const input = typeof u.input === "number" ? u.input : 0;
68
+ const output = typeof u.output === "number" ? u.output : 0;
69
+ if (input || output) result.tokens = input + output;
70
+ }
71
+
72
+ // Attach extracted tool data via subprocess tool registry
73
+ if (result.toolName && subprocessToolRegistry.hasHandler(result.toolName)) {
74
+ const handler = subprocessToolRegistry.getHandler(result.toolName);
75
+ if (handler?.extractData) {
76
+ const extracted = handler.extractData({
77
+ toolName: result.toolName,
78
+ toolCallId: (record.toolCallId as string) ?? "",
79
+ args: record.args as Record<string, unknown> | undefined,
80
+ result: record.result as SubprocessToolEvent["result"],
81
+ isError: record.isError as boolean | undefined,
82
+ });
83
+ if (extracted !== undefined) {
84
+ result.extractedToolData = { [result.toolName]: extracted };
85
+ }
86
+ }
87
+ }
88
+
89
+ return result;
90
+ }
@@ -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
+ }