pi-crew 0.1.41 → 0.1.44

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 (191) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +51 -0
  3. package/agents/analyst.md +11 -11
  4. package/agents/critic.md +11 -11
  5. package/agents/executor.md +11 -11
  6. package/agents/explorer.md +11 -11
  7. package/agents/planner.md +11 -11
  8. package/agents/reviewer.md +11 -11
  9. package/agents/security-reviewer.md +11 -11
  10. package/agents/test-engineer.md +11 -11
  11. package/agents/verifier.md +11 -11
  12. package/agents/writer.md +11 -11
  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-extension-examples.md +297 -297
  18. package/docs/research-extension-system.md +324 -324
  19. package/docs/research-optimization-plan.md +548 -548
  20. package/docs/research-phase10-distillation.md +199 -0
  21. package/docs/research-phase11-distillation.md +201 -0
  22. package/docs/research-pi-coding-agent.md +357 -357
  23. package/docs/research-source-pi-crew-reference.md +174 -174
  24. package/docs/runtime-flow.md +148 -148
  25. package/docs/source-runtime-refactor-map.md +83 -83
  26. package/index.ts +6 -6
  27. package/package.json +1 -1
  28. package/src/agents/agent-serializer.ts +34 -34
  29. package/src/agents/discover-agents.ts +5 -4
  30. package/src/config/config.ts +28 -4
  31. package/src/extension/cross-extension-rpc.ts +82 -82
  32. package/src/extension/management.ts +37 -8
  33. package/src/extension/notification-router.ts +2 -2
  34. package/src/extension/register.ts +130 -8
  35. package/src/extension/registration/commands.ts +11 -9
  36. package/src/extension/registration/compaction-guard.ts +125 -125
  37. package/src/extension/registration/subagent-tools.ts +28 -19
  38. package/src/extension/registration/team-tool.ts +2 -1
  39. package/src/extension/result-watcher.ts +4 -4
  40. package/src/extension/run-bundle-schema.ts +8 -4
  41. package/src/extension/run-import.ts +4 -0
  42. package/src/extension/run-index.ts +23 -1
  43. package/src/extension/run-maintenance.ts +43 -24
  44. package/src/extension/team-tool/api.ts +2 -2
  45. package/src/extension/team-tool/cancel.ts +76 -4
  46. package/src/extension/team-tool/context.ts +1 -0
  47. package/src/extension/team-tool/doctor.ts +8 -1
  48. package/src/extension/team-tool/handle-settings.ts +188 -0
  49. package/src/extension/team-tool/inspect.ts +41 -41
  50. package/src/extension/team-tool/lifecycle-actions.ts +79 -79
  51. package/src/extension/team-tool/plan.ts +19 -19
  52. package/src/extension/team-tool/respond.ts +67 -0
  53. package/src/extension/team-tool/run.ts +6 -4
  54. package/src/extension/team-tool/status.ts +99 -93
  55. package/src/extension/team-tool-types.ts +4 -0
  56. package/src/extension/team-tool.ts +5 -1
  57. package/src/i18n.ts +184 -0
  58. package/src/observability/correlation.ts +2 -2
  59. package/src/observability/event-to-metric.ts +10 -3
  60. package/src/observability/exporters/adapter.ts +7 -1
  61. package/src/observability/exporters/otlp-exporter.ts +14 -2
  62. package/src/observability/exporters/prometheus-exporter.ts +9 -2
  63. package/src/observability/metric-registry.ts +18 -3
  64. package/src/observability/metric-retention.ts +11 -3
  65. package/src/observability/metric-sink.ts +9 -4
  66. package/src/observability/metrics-primitives.ts +4 -3
  67. package/src/prompt/prompt-runtime.ts +72 -68
  68. package/src/runtime/agent-control.ts +63 -63
  69. package/src/runtime/agent-memory.ts +72 -72
  70. package/src/runtime/agent-observability.ts +114 -114
  71. package/src/runtime/async-marker.ts +26 -26
  72. package/src/runtime/attention-events.ts +28 -23
  73. package/src/runtime/background-runner.ts +53 -53
  74. package/src/runtime/child-pi.ts +4 -4
  75. package/src/runtime/completion-guard.ts +95 -4
  76. package/src/runtime/concurrency.ts +1 -1
  77. package/src/runtime/crash-recovery.ts +32 -1
  78. package/src/runtime/crew-agent-runtime.ts +59 -58
  79. package/src/runtime/deadletter.ts +14 -4
  80. package/src/runtime/delivery-coordinator.ts +143 -0
  81. package/src/runtime/direct-run.ts +35 -35
  82. package/src/runtime/foreground-control.ts +82 -82
  83. package/src/runtime/green-contract.ts +46 -46
  84. package/src/runtime/group-join.ts +106 -106
  85. package/src/runtime/heartbeat-gradient.ts +28 -28
  86. package/src/runtime/heartbeat-watcher.ts +48 -4
  87. package/src/runtime/live-agent-control.ts +87 -87
  88. package/src/runtime/live-agent-manager.ts +85 -85
  89. package/src/runtime/live-control-realtime.ts +36 -36
  90. package/src/runtime/live-session-runtime.ts +305 -305
  91. package/src/runtime/manifest-cache.ts +2 -2
  92. package/src/runtime/model-fallback.ts +272 -261
  93. package/src/runtime/overflow-recovery.ts +157 -0
  94. package/src/runtime/parallel-research.ts +44 -44
  95. package/src/runtime/parallel-utils.ts +1 -1
  96. package/src/runtime/pi-json-output.ts +111 -111
  97. package/src/runtime/policy-engine.ts +79 -78
  98. package/src/runtime/post-exit-stdio-guard.ts +2 -2
  99. package/src/runtime/process-status.ts +56 -56
  100. package/src/runtime/progress-event-coalescer.ts +43 -43
  101. package/src/runtime/recovery-recipes.ts +74 -74
  102. package/src/runtime/retry-executor.ts +5 -0
  103. package/src/runtime/role-permission.ts +39 -39
  104. package/src/runtime/runtime-resolver.ts +1 -1
  105. package/src/runtime/session-resources.ts +25 -0
  106. package/src/runtime/session-snapshot.ts +59 -0
  107. package/src/runtime/session-usage.ts +79 -79
  108. package/src/runtime/sidechain-output.ts +29 -29
  109. package/src/runtime/stale-reconciler.ts +179 -0
  110. package/src/runtime/subagent-manager.ts +3 -3
  111. package/src/runtime/supervisor-contact.ts +59 -0
  112. package/src/runtime/task-display.ts +38 -38
  113. package/src/runtime/task-output-context.ts +127 -127
  114. package/src/runtime/task-runner/live-executor.ts +101 -101
  115. package/src/runtime/task-runner/progress.ts +119 -111
  116. package/src/runtime/task-runner/result-utils.ts +14 -14
  117. package/src/runtime/task-runner/state-helpers.ts +22 -22
  118. package/src/runtime/task-runner.ts +14 -0
  119. package/src/runtime/team-runner.ts +9 -10
  120. package/src/runtime/worker-heartbeat.ts +21 -21
  121. package/src/runtime/worker-startup.ts +57 -57
  122. package/src/schema/config-schema.ts +2 -1
  123. package/src/schema/team-tool-schema.ts +115 -109
  124. package/src/state/artifact-store.ts +4 -2
  125. package/src/state/atomic-write.ts +12 -4
  126. package/src/state/contracts.ts +109 -105
  127. package/src/state/event-log.ts +3 -4
  128. package/src/state/jsonl-writer.ts +4 -1
  129. package/src/state/locks.ts +9 -1
  130. package/src/state/task-claims.ts +44 -42
  131. package/src/state/usage.ts +29 -29
  132. package/src/subagents/async-entry.ts +1 -1
  133. package/src/subagents/index.ts +3 -3
  134. package/src/subagents/live/control.ts +1 -1
  135. package/src/subagents/live/manager.ts +1 -1
  136. package/src/subagents/live/realtime.ts +1 -1
  137. package/src/subagents/live/session-runtime.ts +1 -1
  138. package/src/subagents/manager.ts +1 -1
  139. package/src/subagents/spawn.ts +1 -1
  140. package/src/teams/discover-teams.ts +2 -2
  141. package/src/teams/team-serializer.ts +38 -38
  142. package/src/types/diff.d.ts +18 -18
  143. package/src/ui/crew-footer.ts +101 -101
  144. package/src/ui/crew-select-list.ts +111 -111
  145. package/src/ui/crew-widget.ts +5 -4
  146. package/src/ui/dashboard-panes/metrics-pane.ts +34 -34
  147. package/src/ui/dynamic-border.ts +25 -25
  148. package/src/ui/layout-primitives.ts +106 -106
  149. package/src/ui/live-run-sidebar.ts +1 -1
  150. package/src/ui/loaders.ts +158 -158
  151. package/src/ui/mascot.ts +3 -2
  152. package/src/ui/powerbar-publisher.ts +7 -6
  153. package/src/ui/render-diff.ts +119 -119
  154. package/src/ui/render-scheduler.ts +54 -14
  155. package/src/ui/run-dashboard.ts +39 -11
  156. package/src/ui/run-snapshot-cache.ts +336 -36
  157. package/src/ui/spinner.ts +17 -17
  158. package/src/ui/status-colors.ts +58 -54
  159. package/src/ui/syntax-highlight.ts +116 -116
  160. package/src/ui/theme-adapter.ts +1 -1
  161. package/src/ui/transcript-viewer.ts +7 -2
  162. package/src/utils/atomic-write.ts +33 -0
  163. package/src/utils/completion-dedupe.ts +63 -63
  164. package/src/utils/file-coalescer.ts +5 -3
  165. package/src/utils/frontmatter.ts +68 -36
  166. package/src/utils/git.ts +262 -262
  167. package/src/utils/ids.ts +12 -12
  168. package/src/utils/internal-error.ts +1 -1
  169. package/src/utils/names.ts +27 -26
  170. package/src/utils/paths.ts +1 -1
  171. package/src/utils/redaction.ts +44 -41
  172. package/src/utils/safe-paths.ts +47 -34
  173. package/src/utils/sleep.ts +2 -2
  174. package/src/utils/timings.ts +2 -0
  175. package/src/utils/visual.ts +9 -1
  176. package/src/workflows/discover-workflows.ts +4 -1
  177. package/src/workflows/validate-workflow.ts +40 -40
  178. package/src/worktree/branch-freshness.ts +45 -45
  179. package/src/worktree/worktree-manager.ts +6 -1
  180. package/teams/default.team.md +12 -12
  181. package/teams/fast-fix.team.md +11 -11
  182. package/teams/implementation.team.md +18 -18
  183. package/teams/parallel-research.team.md +14 -14
  184. package/teams/research.team.md +11 -11
  185. package/teams/review.team.md +12 -12
  186. package/workflows/default.workflow.md +29 -29
  187. package/workflows/fast-fix.workflow.md +22 -22
  188. package/workflows/implementation.workflow.md +38 -38
  189. package/workflows/parallel-research.workflow.md +46 -46
  190. package/workflows/research.workflow.md +22 -22
  191. package/workflows/review.workflow.md +30 -30
@@ -1,109 +1,115 @@
1
- import { Type } from "typebox";
2
-
3
- const SkillOverride = Type.Unsafe({
4
- description: "Skill name(s) to inject, array of skill names, or false to disable role defaults.",
5
- anyOf: [
6
- { type: "string" },
7
- { type: "array", items: { type: "string" } },
8
- { type: "boolean" },
9
- ],
10
- });
11
-
12
- const FreeformConfig = Type.Unsafe({
13
- description: "Resource config for management actions.",
14
- type: "object",
15
- additionalProperties: true,
16
- });
17
-
18
- export const TeamToolParams = Type.Object({
19
- action: Type.Optional(Type.Union([
20
- Type.Literal("run"),
21
- Type.Literal("plan"),
22
- Type.Literal("status"),
23
- Type.Literal("list"),
24
- Type.Literal("get"),
25
- Type.Literal("cancel"),
26
- Type.Literal("resume"),
27
- Type.Literal("create"),
28
- Type.Literal("update"),
29
- Type.Literal("delete"),
30
- Type.Literal("doctor"),
31
- Type.Literal("cleanup"),
32
- Type.Literal("events"),
33
- Type.Literal("artifacts"),
34
- Type.Literal("worktrees"),
35
- Type.Literal("forget"),
36
- Type.Literal("summary"),
37
- Type.Literal("prune"),
38
- Type.Literal("export"),
39
- Type.Literal("import"),
40
- Type.Literal("imports"),
41
- Type.Literal("help"),
42
- Type.Literal("validate"),
43
- Type.Literal("config"),
44
- Type.Literal("init"),
45
- Type.Literal("recommend"),
46
- Type.Literal("autonomy"),
47
- Type.Literal("api"),
48
- ], { description: "Team action. Defaults to 'list' when omitted." })),
49
- resource: Type.Optional(Type.Union([
50
- Type.Literal("agent"),
51
- Type.Literal("team"),
52
- Type.Literal("workflow"),
53
- ], { description: "Resource kind for get/create/update/delete/list. Defaults to all for list." })),
54
- team: Type.Optional(Type.String({ description: "Team name, e.g. default or implementation." })),
55
- workflow: Type.Optional(Type.String({ description: "Workflow name, e.g. default or review." })),
56
- role: Type.Optional(Type.String({ description: "Role name to run directly within a team." })),
57
- agent: Type.Optional(Type.String({ description: "Agent name to inspect or run directly." })),
58
- goal: Type.Optional(Type.String({ description: "High-level objective for a team run." })),
59
- task: Type.Optional(Type.String({ description: "Concrete task text for direct role/agent execution." })),
60
- runId: Type.Optional(Type.String({ description: "Run ID for status, cancel, or resume." })),
61
- async: Type.Optional(Type.Boolean({ description: "Run in background when execution support is enabled." })),
62
- workspaceMode: Type.Optional(Type.Union([
63
- Type.Literal("single"),
64
- Type.Literal("worktree"),
65
- ], { description: "Workspace isolation mode. Worktree mode is planned after MVP." })),
66
- context: Type.Optional(Type.Union([
67
- Type.Literal("fresh"),
68
- Type.Literal("fork"),
69
- ], { description: "Child context mode for workers." })),
70
- cwd: Type.Optional(Type.String({ description: "Working directory override." })),
71
- model: Type.Optional(Type.String({ description: "Model override for direct runs." })),
72
- skill: Type.Optional(SkillOverride),
73
- scope: Type.Optional(Type.Union([
74
- Type.Literal("user"),
75
- Type.Literal("project"),
76
- Type.Literal("both"),
77
- ], { description: "Resource scope for discovery or management." })),
78
- config: Type.Optional(FreeformConfig),
79
- dryRun: Type.Optional(Type.Boolean({ description: "Preview a management mutation without writing files." })),
80
- confirm: Type.Optional(Type.Boolean({ description: "Required for destructive management actions." })),
81
- force: Type.Optional(Type.Boolean({ description: "Override reference checks for destructive management actions." })),
82
- keep: Type.Optional(Type.Integer({ minimum: 0, description: "Number of finished runs to keep for prune." })),
83
- updateReferences: Type.Optional(Type.Boolean({ description: "When renaming agents or workflows, update team references in the same project/user scope." })),
84
- });
85
-
86
- export interface TeamToolParamsValue {
87
- action?: "run" | "plan" | "status" | "list" | "get" | "cancel" | "resume" | "create" | "update" | "delete" | "doctor" | "cleanup" | "events" | "artifacts" | "worktrees" | "forget" | "summary" | "prune" | "export" | "import" | "imports" | "help" | "validate" | "config" | "init" | "recommend" | "autonomy" | "api";
88
- resource?: "agent" | "team" | "workflow";
89
- team?: string;
90
- workflow?: string;
91
- role?: string;
92
- agent?: string;
93
- goal?: string;
94
- task?: string;
95
- runId?: string;
96
- async?: boolean;
97
- workspaceMode?: "single" | "worktree";
98
- context?: "fresh" | "fork";
99
- cwd?: string;
100
- model?: string;
101
- skill?: string | string[] | boolean;
102
- scope?: "user" | "project" | "both";
103
- config?: unknown;
104
- dryRun?: boolean;
105
- confirm?: boolean;
106
- force?: boolean;
107
- keep?: number;
108
- updateReferences?: boolean;
109
- }
1
+ import { Type } from "typebox";
2
+
3
+ const SkillOverride = Type.Unsafe({
4
+ description: "Skill name(s) to inject, array of skill names, or false to disable role defaults.",
5
+ anyOf: [
6
+ { type: "string" },
7
+ { type: "array", items: { type: "string" } },
8
+ { type: "boolean" },
9
+ ],
10
+ });
11
+
12
+ const FreeformConfig = Type.Unsafe({
13
+ description: "Resource config for management actions.",
14
+ type: "object",
15
+ additionalProperties: true,
16
+ });
17
+
18
+ export const TeamToolParams = Type.Object({
19
+ action: Type.Optional(Type.Union([
20
+ Type.Literal("run"),
21
+ Type.Literal("plan"),
22
+ Type.Literal("status"),
23
+ Type.Literal("list"),
24
+ Type.Literal("get"),
25
+ Type.Literal("cancel"),
26
+ Type.Literal("resume"),
27
+ Type.Literal("respond"),
28
+ Type.Literal("create"),
29
+ Type.Literal("update"),
30
+ Type.Literal("delete"),
31
+ Type.Literal("doctor"),
32
+ Type.Literal("cleanup"),
33
+ Type.Literal("events"),
34
+ Type.Literal("artifacts"),
35
+ Type.Literal("worktrees"),
36
+ Type.Literal("forget"),
37
+ Type.Literal("summary"),
38
+ Type.Literal("prune"),
39
+ Type.Literal("export"),
40
+ Type.Literal("import"),
41
+ Type.Literal("imports"),
42
+ Type.Literal("help"),
43
+ Type.Literal("validate"),
44
+ Type.Literal("config"),
45
+ Type.Literal("init"),
46
+ Type.Literal("recommend"),
47
+ Type.Literal("autonomy"),
48
+ Type.Literal("api"),
49
+ Type.Literal("settings"),
50
+ ], { description: "Team action. Defaults to 'list' when omitted." })),
51
+ resource: Type.Optional(Type.Union([
52
+ Type.Literal("agent"),
53
+ Type.Literal("team"),
54
+ Type.Literal("workflow"),
55
+ ], { description: "Resource kind for get/create/update/delete/list. Defaults to all for list." })),
56
+ team: Type.Optional(Type.String({ description: "Team name, e.g. default or implementation." })),
57
+ workflow: Type.Optional(Type.String({ description: "Workflow name, e.g. default or review." })),
58
+ role: Type.Optional(Type.String({ description: "Role name to run directly within a team." })),
59
+ agent: Type.Optional(Type.String({ description: "Agent name to inspect or run directly." })),
60
+ goal: Type.Optional(Type.String({ description: "High-level objective for a team run." })),
61
+ task: Type.Optional(Type.String({ description: "Concrete task text for direct role/agent execution." })),
62
+ runId: Type.Optional(Type.String({ description: "Run ID for status, cancel, or resume." })),
63
+ taskId: Type.Optional(Type.String({ description: "Task ID for respond action." })),
64
+ message: Type.Optional(Type.String({ description: "Message for respond action." })),
65
+ async: Type.Optional(Type.Boolean({ description: "Run in background when execution support is enabled." })),
66
+ workspaceMode: Type.Optional(Type.Union([
67
+ Type.Literal("single"),
68
+ Type.Literal("worktree"),
69
+ ], { description: "Workspace isolation mode. Worktree mode is planned after MVP." })),
70
+ context: Type.Optional(Type.Union([
71
+ Type.Literal("fresh"),
72
+ Type.Literal("fork"),
73
+ ], { description: "Child context mode for workers." })),
74
+ cwd: Type.Optional(Type.String({ description: "Working directory override." })),
75
+ model: Type.Optional(Type.String({ description: "Model override for direct runs." })),
76
+ skill: Type.Optional(SkillOverride),
77
+ scope: Type.Optional(Type.Union([
78
+ Type.Literal("user"),
79
+ Type.Literal("project"),
80
+ Type.Literal("both"),
81
+ ], { description: "Resource scope for discovery or management." })),
82
+ config: Type.Optional(FreeformConfig),
83
+ dryRun: Type.Optional(Type.Boolean({ description: "Preview a management mutation without writing files." })),
84
+ confirm: Type.Optional(Type.Boolean({ description: "Required for destructive management actions." })),
85
+ force: Type.Optional(Type.Boolean({ description: "Override reference checks for destructive management actions." })),
86
+ keep: Type.Optional(Type.Integer({ minimum: 0, description: "Number of finished runs to keep for prune." })),
87
+ updateReferences: Type.Optional(Type.Boolean({ description: "When renaming agents or workflows, update team references in the same project/user scope." })),
88
+ });
89
+
90
+ export interface TeamToolParamsValue {
91
+ action?: "run" | "plan" | "status" | "list" | "get" | "cancel" | "resume" | "respond" | "create" | "update" | "delete" | "doctor" | "cleanup" | "events" | "artifacts" | "worktrees" | "forget" | "summary" | "prune" | "export" | "import" | "imports" | "help" | "validate" | "config" | "init" | "recommend" | "autonomy" | "api" | "settings";
92
+ resource?: "agent" | "team" | "workflow";
93
+ team?: string;
94
+ workflow?: string;
95
+ role?: string;
96
+ agent?: string;
97
+ goal?: string;
98
+ task?: string;
99
+ runId?: string;
100
+ taskId?: string;
101
+ message?: string;
102
+ async?: boolean;
103
+ workspaceMode?: "single" | "worktree";
104
+ context?: "fresh" | "fork";
105
+ cwd?: string;
106
+ model?: string;
107
+ skill?: string | string[] | boolean;
108
+ scope?: "user" | "project" | "both";
109
+ config?: Record<string, unknown>;
110
+ dryRun?: boolean;
111
+ confirm?: boolean;
112
+ force?: boolean;
113
+ keep?: number;
114
+ updateReferences?: boolean;
115
+ }
@@ -105,10 +105,12 @@ function resolveInside(baseDir: string, relativePath: string): string {
105
105
  export function writeArtifact(artifactsRoot: string, options: ArtifactWriteOptions): ArtifactDescriptor {
106
106
  const filePath = resolveInside(artifactsRoot, options.relativePath);
107
107
  fs.mkdirSync(artifactsRoot, { recursive: true });
108
- if (fs.lstatSync(artifactsRoot).isSymbolicLink()) throw new Error(`Path is outside ${path.dirname(artifactsRoot)}: ${artifactsRoot}`);
108
+ if (fs.lstatSync(artifactsRoot).isSymbolicLink()) throw new Error(`Artifacts root is a symbolic link — not allowed: ${artifactsRoot}`);
109
109
  resolveRealContainedPath(path.dirname(artifactsRoot), path.basename(artifactsRoot));
110
110
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
111
111
  resolveRealContainedPath(artifactsRoot, path.dirname(filePath));
112
+ // Compute hash on original content for integrity verification.
113
+ const contentHash = hashContent(options.content);
112
114
  const content = redactSecretString(options.content);
113
115
  atomicWriteFile(filePath, content);
114
116
  const stats = fs.statSync(filePath);
@@ -118,7 +120,7 @@ export function writeArtifact(artifactsRoot: string, options: ArtifactWriteOptio
118
120
  createdAt: new Date().toISOString(),
119
121
  producer: options.producer,
120
122
  sizeBytes: stats.size,
121
- contentHash: hashContent(content),
123
+ contentHash,
122
124
  retention: options.retention ?? "run",
123
125
  };
124
126
  }
@@ -5,8 +5,16 @@ import { logInternalError } from "../utils/internal-error.ts";
5
5
  const RETRYABLE_RENAME_CODES = new Set(["EPERM", "EBUSY", "EACCES"]);
6
6
 
7
7
  function sleepSync(ms: number): void {
8
- const buffer = new SharedArrayBuffer(4);
9
- Atomics.wait(new Int32Array(buffer), 0, 0, ms);
8
+ try {
9
+ const buffer = new SharedArrayBuffer(4);
10
+ Atomics.wait(new Int32Array(buffer), 0, 0, ms);
11
+ } catch {
12
+ // Fallback for environments without SharedArrayBuffer / Atomics.wait support.
13
+ const deadline = Date.now() + ms;
14
+ while (Date.now() < deadline) {
15
+ // Busy-wait — only used as last-resort, retry counts are capped.
16
+ }
17
+ }
10
18
  }
11
19
 
12
20
  function sleep(ms: number): Promise<void> {
@@ -17,7 +25,7 @@ function isRetryableRenameError(error: unknown): boolean {
17
25
  return Boolean(error && typeof error === "object" && "code" in error && RETRYABLE_RENAME_CODES.has(String((error as NodeJS.ErrnoException).code)));
18
26
  }
19
27
 
20
- export function __test__renameWithRetry(tempPath: string, filePath: string, retries = 20, rename: (oldPath: string, newPath: string) => void = fs.renameSync): void {
28
+ export function __test__renameWithRetry(tempPath: string, filePath: string, retries = 5, rename: (oldPath: string, newPath: string) => void = fs.renameSync): void {
21
29
  let lastError: unknown;
22
30
  for (let attempt = 0; attempt <= retries; attempt++) {
23
31
  try {
@@ -32,7 +40,7 @@ export function __test__renameWithRetry(tempPath: string, filePath: string, retr
32
40
  throw lastError;
33
41
  }
34
42
 
35
- export async function __test__renameWithRetryAsync(tempPath: string, filePath: string, retries = 20, rename: (oldPath: string, newPath: string) => Promise<void> = (source, destination) => fs.promises.rename(source, destination)): Promise<void> {
43
+ export async function __test__renameWithRetryAsync(tempPath: string, filePath: string, retries = 5, rename: (oldPath: string, newPath: string) => Promise<void> = (source, destination) => fs.promises.rename(source, destination)): Promise<void> {
36
44
  let lastError: unknown;
37
45
  for (let attempt = 0; attempt <= retries; attempt++) {
38
46
  try {
@@ -1,105 +1,109 @@
1
- export const TEAM_RUN_STATUSES = ["queued", "planning", "running", "blocked", "completed", "failed", "cancelled"] as const;
2
- export type TeamRunStatus = typeof TEAM_RUN_STATUSES[number];
3
-
4
- export const TEAM_TASK_STATUSES = ["queued", "running", "completed", "failed", "cancelled", "skipped"] as const;
5
- export type TeamTaskStatus = typeof TEAM_TASK_STATUSES[number];
6
-
7
- export const TEAM_TERMINAL_RUN_STATUSES: ReadonlySet<TeamRunStatus> = new Set(["blocked", "completed", "failed", "cancelled"]);
8
- export const TEAM_TERMINAL_TASK_STATUSES: ReadonlySet<TeamTaskStatus> = new Set(["completed", "failed", "cancelled", "skipped"]);
9
-
10
- export const TEAM_RUN_STATUS_TRANSITIONS: Readonly<Record<TeamRunStatus, readonly TeamRunStatus[]>> = {
11
- queued: ["planning", "running", "cancelled", "failed"],
12
- planning: ["running", "blocked", "cancelled", "failed"],
13
- running: ["blocked", "completed", "failed", "cancelled"],
14
- blocked: ["running", "cancelled", "failed"],
15
- completed: ["running", "cancelled"],
16
- failed: ["running", "cancelled"],
17
- cancelled: ["running"],
18
- };
19
-
20
- export const TEAM_TASK_STATUS_TRANSITIONS: Readonly<Record<TeamTaskStatus, readonly TeamTaskStatus[]>> = {
21
- queued: ["running", "cancelled", "skipped", "failed"],
22
- running: ["completed", "failed", "cancelled", "queued"],
23
- completed: ["queued"],
24
- failed: ["queued", "cancelled"],
25
- cancelled: ["queued"],
26
- skipped: ["queued", "cancelled"],
27
- };
28
-
29
- export const TEAM_EVENT_TYPES = [
30
- "run.created",
31
- "run.queued",
32
- "run.planning",
33
- "run.running",
34
- "run.blocked",
35
- "run.completed",
36
- "run.failed",
37
- "run.cancelled",
38
- "task.started",
39
- "task.progress",
40
- "task.blocked",
41
- "task.green",
42
- "task.red",
43
- "task.completed",
44
- "task.failed",
45
- "task.cancelled",
46
- "task.skipped",
47
- "review.approved",
48
- "review.rejected",
49
- "policy.action",
50
- "policy.escalated",
51
- "recovery.attempted",
52
- "recovery.escalated",
53
- "branch.stale",
54
- "mailbox.timeout",
55
- "worktree.cleanup",
56
- "worktree.dirty",
57
- "async.spawned",
58
- "async.started",
59
- "async.completed",
60
- "async.failed",
61
- "async.stale",
62
- ] as const;
63
- export type TeamEventType = typeof TEAM_EVENT_TYPES[number];
64
-
65
- export const TEAM_WAKEABLE_EVENT_TYPES: ReadonlySet<TeamEventType> = new Set([
66
- "run.blocked",
67
- "run.completed",
68
- "run.failed",
69
- "run.cancelled",
70
- "task.completed",
71
- "task.failed",
72
- "task.cancelled",
73
- "task.skipped",
74
- "async.completed",
75
- "async.failed",
76
- "async.stale",
77
- ]);
78
-
79
- export function isTeamRunStatus(value: unknown): value is TeamRunStatus {
80
- return typeof value === "string" && TEAM_RUN_STATUSES.includes(value as TeamRunStatus);
81
- }
82
-
83
- export function isTeamTaskStatus(value: unknown): value is TeamTaskStatus {
84
- return typeof value === "string" && TEAM_TASK_STATUSES.includes(value as TeamTaskStatus);
85
- }
86
-
87
- export function isTerminalRunStatus(status: TeamRunStatus): boolean {
88
- return TEAM_TERMINAL_RUN_STATUSES.has(status);
89
- }
90
-
91
- export function isTerminalTaskStatus(status: TeamTaskStatus): boolean {
92
- return TEAM_TERMINAL_TASK_STATUSES.has(status);
93
- }
94
-
95
- export function canTransitionRunStatus(from: TeamRunStatus, to: TeamRunStatus): boolean {
96
- return from === to || (TEAM_RUN_STATUS_TRANSITIONS[from]?.includes(to) ?? false);
97
- }
98
-
99
- export function canTransitionTaskStatus(from: TeamTaskStatus, to: TeamTaskStatus): boolean {
100
- return from === to || (TEAM_TASK_STATUS_TRANSITIONS[from]?.includes(to) ?? false);
101
- }
102
-
103
- export function isWakeableTeamEventType(type: TeamEventType): boolean {
104
- return TEAM_WAKEABLE_EVENT_TYPES.has(type);
105
- }
1
+ export const TEAM_RUN_STATUSES = ["queued", "planning", "running", "blocked", "completed", "failed", "cancelled"] as const;
2
+ export type TeamRunStatus = typeof TEAM_RUN_STATUSES[number];
3
+
4
+ export const TEAM_TASK_STATUSES = ["queued", "running", "waiting", "completed", "failed", "cancelled", "skipped"] as const;
5
+ export type TeamTaskStatus = typeof TEAM_TASK_STATUSES[number];
6
+
7
+ export const TEAM_TERMINAL_RUN_STATUSES: ReadonlySet<TeamRunStatus> = new Set(["blocked", "completed", "failed", "cancelled"]);
8
+ export const TEAM_TERMINAL_TASK_STATUSES: ReadonlySet<TeamTaskStatus> = new Set(["completed", "failed", "cancelled", "skipped"]);
9
+
10
+ export const TEAM_RUN_STATUS_TRANSITIONS: Readonly<Record<TeamRunStatus, readonly TeamRunStatus[]>> = {
11
+ queued: ["planning", "running", "cancelled", "failed"],
12
+ planning: ["running", "blocked", "cancelled", "failed"],
13
+ running: ["blocked", "completed", "failed", "cancelled"],
14
+ blocked: ["running", "cancelled", "failed"],
15
+ completed: ["running", "cancelled"],
16
+ failed: ["running", "cancelled"],
17
+ cancelled: ["running"],
18
+ };
19
+
20
+ export const TEAM_TASK_STATUS_TRANSITIONS: Readonly<Record<TeamTaskStatus, readonly TeamTaskStatus[]>> = {
21
+ queued: ["running", "cancelled", "skipped", "failed"],
22
+ running: ["completed", "failed", "cancelled", "queued", "waiting"],
23
+ waiting: ["running", "completed", "failed", "cancelled"],
24
+ completed: ["queued"],
25
+ failed: ["queued", "cancelled"],
26
+ cancelled: ["queued"],
27
+ skipped: ["queued", "cancelled"],
28
+ };
29
+
30
+ export const TEAM_EVENT_TYPES = [
31
+ "run.created",
32
+ "run.queued",
33
+ "run.planning",
34
+ "run.running",
35
+ "run.blocked",
36
+ "run.completed",
37
+ "run.failed",
38
+ "run.cancelled",
39
+ "task.started",
40
+ "task.progress",
41
+ "task.blocked",
42
+ "task.green",
43
+ "task.red",
44
+ "task.completed",
45
+ "task.failed",
46
+ "task.cancelled",
47
+ "task.skipped",
48
+ "review.approved",
49
+ "review.rejected",
50
+ "policy.action",
51
+ "policy.escalated",
52
+ "recovery.attempted",
53
+ "recovery.escalated",
54
+ "branch.stale",
55
+ "mailbox.timeout",
56
+ "worktree.cleanup",
57
+ "worktree.dirty",
58
+ "async.spawned",
59
+ "async.started",
60
+ "async.completed",
61
+ "async.failed",
62
+ "async.stale",
63
+ "task.waiting",
64
+ "task.resumed",
65
+ "supervisor.contact",
66
+ ] as const;
67
+ export type TeamEventType = typeof TEAM_EVENT_TYPES[number];
68
+
69
+ export const TEAM_WAKEABLE_EVENT_TYPES: ReadonlySet<TeamEventType> = new Set([
70
+ "run.blocked",
71
+ "run.completed",
72
+ "run.failed",
73
+ "run.cancelled",
74
+ "task.completed",
75
+ "task.failed",
76
+ "task.cancelled",
77
+ "task.skipped",
78
+ "async.completed",
79
+ "async.failed",
80
+ "async.stale",
81
+ ]);
82
+
83
+ export function isTeamRunStatus(value: unknown): value is TeamRunStatus {
84
+ return typeof value === "string" && TEAM_RUN_STATUSES.includes(value as TeamRunStatus);
85
+ }
86
+
87
+ export function isTeamTaskStatus(value: unknown): value is TeamTaskStatus {
88
+ return typeof value === "string" && TEAM_TASK_STATUSES.includes(value as TeamTaskStatus);
89
+ }
90
+
91
+ export function isTerminalRunStatus(status: TeamRunStatus): boolean {
92
+ return TEAM_TERMINAL_RUN_STATUSES.has(status);
93
+ }
94
+
95
+ export function isTerminalTaskStatus(status: TeamTaskStatus): boolean {
96
+ return TEAM_TERMINAL_TASK_STATUSES.has(status);
97
+ }
98
+
99
+ export function canTransitionRunStatus(from: TeamRunStatus, to: TeamRunStatus): boolean {
100
+ return from === to || (TEAM_RUN_STATUS_TRANSITIONS[from]?.includes(to) ?? false);
101
+ }
102
+
103
+ export function canTransitionTaskStatus(from: TeamTaskStatus, to: TeamTaskStatus): boolean {
104
+ return from === to || (TEAM_TASK_STATUS_TRANSITIONS[from]?.includes(to) ?? false);
105
+ }
106
+
107
+ export function isWakeableTeamEventType(type: TeamEventType): boolean {
108
+ return TEAM_WAKEABLE_EVENT_TYPES.has(type);
109
+ }
@@ -28,6 +28,7 @@ export interface TeamEventMetadata {
28
28
  sessionIdentity?: TeamEventSessionIdentity;
29
29
  ownership?: TeamEventOwnership;
30
30
  nudgeId?: string;
31
+ appended?: boolean;
31
32
  fingerprint?: string;
32
33
  confidence?: "low" | "medium" | "high";
33
34
  }
@@ -66,9 +67,7 @@ export function scanSequence(eventsPath: string): number {
66
67
  try {
67
68
  const event = JSON.parse(line) as TeamEvent;
68
69
  max = Math.max(max, event.metadata?.seq ?? 0);
69
- } catch {
70
- max += 1;
71
- }
70
+ } catch { /* skip corrupt lines without incrementing sequence */ }
72
71
  }
73
72
  return max;
74
73
  }
@@ -131,7 +130,7 @@ export function appendEvent(eventsPath: string, event: AppendTeamEvent): TeamEve
131
130
  try {
132
131
  if (fs.existsSync(eventsPath) && fs.statSync(eventsPath).size > MAX_EVENTS_BYTES) {
133
132
  logInternalError("event-log.size-limit", new Error(`events file ${eventsPath} exceeds ${MAX_EVENTS_BYTES} bytes`), `eventsPath=${eventsPath}`);
134
- return fullEvent;
133
+ return { ...fullEvent, metadata: { ...(fullEvent.metadata ?? { seq: 0, provenance: "team_runner" }), appended: false } };
135
134
  }
136
135
  } catch (error) {
137
136
  logInternalError("event-log.size-check", error, `eventsPath=${eventsPath}`);
@@ -66,7 +66,10 @@ export function createJsonlWriter(filePath: string | undefined, source: Drainabl
66
66
  if (!closed) source.resume();
67
67
  });
68
68
  }
69
- } catch {}
69
+ } catch (writeError) {
70
+ // Log the error — silently dropping events is dangerous.
71
+ process.stderr.write(`[pi-crew] jsonl-writer: write failed ${filePath}: ${writeError instanceof Error ? writeError.message : String(writeError)}\n`);
72
+ }
70
73
  },
71
74
  async close() {
72
75
  if (!stream || closed) return;
@@ -14,7 +14,15 @@ function lockPath(manifest: TeamRunManifest): string {
14
14
  }
15
15
 
16
16
  function sleepSync(ms: number): void {
17
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
17
+ try {
18
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
19
+ } catch {
20
+ // Fallback for environments without SharedArrayBuffer / Atomics.wait support.
21
+ const deadline = Date.now() + ms;
22
+ while (Date.now() < deadline) {
23
+ // Busy-wait — only used as last-resort, retry counts are capped.
24
+ }
25
+ }
18
26
  }
19
27
 
20
28
  function parseCreatedAtFromLock(raw: string): number | undefined {