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.
- package/CHANGELOG.md +47 -0
- package/README.md +51 -0
- package/agents/analyst.md +11 -11
- package/agents/critic.md +11 -11
- package/agents/executor.md +11 -11
- package/agents/explorer.md +11 -11
- package/agents/planner.md +11 -11
- package/agents/reviewer.md +11 -11
- package/agents/security-reviewer.md +11 -11
- package/agents/test-engineer.md +11 -11
- package/agents/verifier.md +11 -11
- package/agents/writer.md +11 -11
- package/docs/refactor-tasks-phase3.md +394 -394
- package/docs/refactor-tasks-phase4.md +564 -564
- package/docs/refactor-tasks-phase5.md +402 -402
- package/docs/refactor-tasks-phase6.md +662 -662
- package/docs/research-extension-examples.md +297 -297
- package/docs/research-extension-system.md +324 -324
- package/docs/research-optimization-plan.md +548 -548
- package/docs/research-phase10-distillation.md +199 -0
- package/docs/research-phase11-distillation.md +201 -0
- package/docs/research-pi-coding-agent.md +357 -357
- package/docs/research-source-pi-crew-reference.md +174 -174
- package/docs/runtime-flow.md +148 -148
- package/docs/source-runtime-refactor-map.md +83 -83
- package/index.ts +6 -6
- package/package.json +1 -1
- package/src/agents/agent-serializer.ts +34 -34
- package/src/agents/discover-agents.ts +5 -4
- package/src/config/config.ts +28 -4
- package/src/extension/cross-extension-rpc.ts +82 -82
- package/src/extension/management.ts +37 -8
- package/src/extension/notification-router.ts +2 -2
- package/src/extension/register.ts +130 -8
- package/src/extension/registration/commands.ts +11 -9
- package/src/extension/registration/compaction-guard.ts +125 -125
- package/src/extension/registration/subagent-tools.ts +28 -19
- package/src/extension/registration/team-tool.ts +2 -1
- package/src/extension/result-watcher.ts +4 -4
- package/src/extension/run-bundle-schema.ts +8 -4
- package/src/extension/run-import.ts +4 -0
- package/src/extension/run-index.ts +23 -1
- package/src/extension/run-maintenance.ts +43 -24
- package/src/extension/team-tool/api.ts +2 -2
- package/src/extension/team-tool/cancel.ts +76 -4
- package/src/extension/team-tool/context.ts +1 -0
- package/src/extension/team-tool/doctor.ts +8 -1
- package/src/extension/team-tool/handle-settings.ts +188 -0
- package/src/extension/team-tool/inspect.ts +41 -41
- package/src/extension/team-tool/lifecycle-actions.ts +79 -79
- package/src/extension/team-tool/plan.ts +19 -19
- package/src/extension/team-tool/respond.ts +67 -0
- package/src/extension/team-tool/run.ts +6 -4
- package/src/extension/team-tool/status.ts +99 -93
- package/src/extension/team-tool-types.ts +4 -0
- package/src/extension/team-tool.ts +5 -1
- package/src/i18n.ts +184 -0
- package/src/observability/correlation.ts +2 -2
- package/src/observability/event-to-metric.ts +10 -3
- package/src/observability/exporters/adapter.ts +7 -1
- package/src/observability/exporters/otlp-exporter.ts +14 -2
- package/src/observability/exporters/prometheus-exporter.ts +9 -2
- package/src/observability/metric-registry.ts +18 -3
- package/src/observability/metric-retention.ts +11 -3
- package/src/observability/metric-sink.ts +9 -4
- package/src/observability/metrics-primitives.ts +4 -3
- package/src/prompt/prompt-runtime.ts +72 -68
- package/src/runtime/agent-control.ts +63 -63
- package/src/runtime/agent-memory.ts +72 -72
- package/src/runtime/agent-observability.ts +114 -114
- package/src/runtime/async-marker.ts +26 -26
- package/src/runtime/attention-events.ts +28 -23
- package/src/runtime/background-runner.ts +53 -53
- package/src/runtime/child-pi.ts +4 -4
- package/src/runtime/completion-guard.ts +95 -4
- package/src/runtime/concurrency.ts +1 -1
- package/src/runtime/crash-recovery.ts +32 -1
- package/src/runtime/crew-agent-runtime.ts +59 -58
- package/src/runtime/deadletter.ts +14 -4
- package/src/runtime/delivery-coordinator.ts +143 -0
- package/src/runtime/direct-run.ts +35 -35
- package/src/runtime/foreground-control.ts +82 -82
- package/src/runtime/green-contract.ts +46 -46
- package/src/runtime/group-join.ts +106 -106
- package/src/runtime/heartbeat-gradient.ts +28 -28
- package/src/runtime/heartbeat-watcher.ts +48 -4
- package/src/runtime/live-agent-control.ts +87 -87
- package/src/runtime/live-agent-manager.ts +85 -85
- package/src/runtime/live-control-realtime.ts +36 -36
- package/src/runtime/live-session-runtime.ts +305 -305
- package/src/runtime/manifest-cache.ts +2 -2
- package/src/runtime/model-fallback.ts +272 -261
- package/src/runtime/overflow-recovery.ts +157 -0
- package/src/runtime/parallel-research.ts +44 -44
- package/src/runtime/parallel-utils.ts +1 -1
- package/src/runtime/pi-json-output.ts +111 -111
- package/src/runtime/policy-engine.ts +79 -78
- package/src/runtime/post-exit-stdio-guard.ts +2 -2
- package/src/runtime/process-status.ts +56 -56
- package/src/runtime/progress-event-coalescer.ts +43 -43
- package/src/runtime/recovery-recipes.ts +74 -74
- package/src/runtime/retry-executor.ts +5 -0
- package/src/runtime/role-permission.ts +39 -39
- package/src/runtime/runtime-resolver.ts +1 -1
- package/src/runtime/session-resources.ts +25 -0
- package/src/runtime/session-snapshot.ts +59 -0
- package/src/runtime/session-usage.ts +79 -79
- package/src/runtime/sidechain-output.ts +29 -29
- package/src/runtime/stale-reconciler.ts +179 -0
- package/src/runtime/subagent-manager.ts +3 -3
- package/src/runtime/supervisor-contact.ts +59 -0
- package/src/runtime/task-display.ts +38 -38
- package/src/runtime/task-output-context.ts +127 -127
- package/src/runtime/task-runner/live-executor.ts +101 -101
- package/src/runtime/task-runner/progress.ts +119 -111
- package/src/runtime/task-runner/result-utils.ts +14 -14
- package/src/runtime/task-runner/state-helpers.ts +22 -22
- package/src/runtime/task-runner.ts +14 -0
- package/src/runtime/team-runner.ts +9 -10
- package/src/runtime/worker-heartbeat.ts +21 -21
- package/src/runtime/worker-startup.ts +57 -57
- package/src/schema/config-schema.ts +2 -1
- package/src/schema/team-tool-schema.ts +115 -109
- package/src/state/artifact-store.ts +4 -2
- package/src/state/atomic-write.ts +12 -4
- package/src/state/contracts.ts +109 -105
- package/src/state/event-log.ts +3 -4
- package/src/state/jsonl-writer.ts +4 -1
- package/src/state/locks.ts +9 -1
- package/src/state/task-claims.ts +44 -42
- package/src/state/usage.ts +29 -29
- package/src/subagents/async-entry.ts +1 -1
- package/src/subagents/index.ts +3 -3
- package/src/subagents/live/control.ts +1 -1
- package/src/subagents/live/manager.ts +1 -1
- package/src/subagents/live/realtime.ts +1 -1
- package/src/subagents/live/session-runtime.ts +1 -1
- package/src/subagents/manager.ts +1 -1
- package/src/subagents/spawn.ts +1 -1
- package/src/teams/discover-teams.ts +2 -2
- package/src/teams/team-serializer.ts +38 -38
- package/src/types/diff.d.ts +18 -18
- package/src/ui/crew-footer.ts +101 -101
- package/src/ui/crew-select-list.ts +111 -111
- package/src/ui/crew-widget.ts +5 -4
- package/src/ui/dashboard-panes/metrics-pane.ts +34 -34
- package/src/ui/dynamic-border.ts +25 -25
- package/src/ui/layout-primitives.ts +106 -106
- package/src/ui/live-run-sidebar.ts +1 -1
- package/src/ui/loaders.ts +158 -158
- package/src/ui/mascot.ts +3 -2
- package/src/ui/powerbar-publisher.ts +7 -6
- package/src/ui/render-diff.ts +119 -119
- package/src/ui/render-scheduler.ts +54 -14
- package/src/ui/run-dashboard.ts +39 -11
- package/src/ui/run-snapshot-cache.ts +336 -36
- package/src/ui/spinner.ts +17 -17
- package/src/ui/status-colors.ts +58 -54
- package/src/ui/syntax-highlight.ts +116 -116
- package/src/ui/theme-adapter.ts +1 -1
- package/src/ui/transcript-viewer.ts +7 -2
- package/src/utils/atomic-write.ts +33 -0
- package/src/utils/completion-dedupe.ts +63 -63
- package/src/utils/file-coalescer.ts +5 -3
- package/src/utils/frontmatter.ts +68 -36
- package/src/utils/git.ts +262 -262
- package/src/utils/ids.ts +12 -12
- package/src/utils/internal-error.ts +1 -1
- package/src/utils/names.ts +27 -26
- package/src/utils/paths.ts +1 -1
- package/src/utils/redaction.ts +44 -41
- package/src/utils/safe-paths.ts +47 -34
- package/src/utils/sleep.ts +2 -2
- package/src/utils/timings.ts +2 -0
- package/src/utils/visual.ts +9 -1
- package/src/workflows/discover-workflows.ts +4 -1
- package/src/workflows/validate-workflow.ts +40 -40
- package/src/worktree/branch-freshness.ts +45 -45
- package/src/worktree/worktree-manager.ts +6 -1
- package/teams/default.team.md +12 -12
- package/teams/fast-fix.team.md +11 -11
- package/teams/implementation.team.md +18 -18
- package/teams/parallel-research.team.md +14 -14
- package/teams/research.team.md +11 -11
- package/teams/review.team.md +12 -12
- package/workflows/default.workflow.md +29 -29
- package/workflows/fast-fix.workflow.md +22 -22
- package/workflows/implementation.workflow.md +38 -38
- package/workflows/parallel-research.workflow.md +46 -46
- package/workflows/research.workflow.md +22 -22
- 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("
|
|
28
|
-
Type.Literal("
|
|
29
|
-
Type.Literal("
|
|
30
|
-
Type.Literal("
|
|
31
|
-
Type.Literal("
|
|
32
|
-
Type.Literal("
|
|
33
|
-
Type.Literal("
|
|
34
|
-
Type.Literal("
|
|
35
|
-
Type.Literal("
|
|
36
|
-
Type.Literal("
|
|
37
|
-
Type.Literal("
|
|
38
|
-
Type.Literal("
|
|
39
|
-
Type.Literal("
|
|
40
|
-
Type.Literal("
|
|
41
|
-
Type.Literal("
|
|
42
|
-
Type.Literal("
|
|
43
|
-
Type.Literal("
|
|
44
|
-
Type.Literal("
|
|
45
|
-
Type.Literal("
|
|
46
|
-
Type.Literal("
|
|
47
|
-
Type.Literal("
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
Type.Literal("
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
Type.Literal("
|
|
68
|
-
Type.Literal("
|
|
69
|
-
], { description: "
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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(`
|
|
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
|
|
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
|
-
|
|
9
|
-
|
|
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 =
|
|
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 =
|
|
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 {
|
package/src/state/contracts.ts
CHANGED
|
@@ -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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
"run.
|
|
32
|
-
"run.
|
|
33
|
-
"run.
|
|
34
|
-
"run.
|
|
35
|
-
"run.
|
|
36
|
-
"run.
|
|
37
|
-
"run.
|
|
38
|
-
"
|
|
39
|
-
"task.
|
|
40
|
-
"task.
|
|
41
|
-
"task.
|
|
42
|
-
"task.
|
|
43
|
-
"task.
|
|
44
|
-
"task.
|
|
45
|
-
"task.
|
|
46
|
-
"task.
|
|
47
|
-
"
|
|
48
|
-
"review.
|
|
49
|
-
"
|
|
50
|
-
"policy.
|
|
51
|
-
"
|
|
52
|
-
"recovery.
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"worktree.
|
|
57
|
-
"
|
|
58
|
-
"async.
|
|
59
|
-
"async.
|
|
60
|
-
"async.
|
|
61
|
-
"async.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
export function
|
|
84
|
-
return typeof value === "string" &&
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export function
|
|
88
|
-
return
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export function
|
|
92
|
-
return
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function
|
|
96
|
-
return
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export function
|
|
100
|
-
return from === to || (
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export function
|
|
104
|
-
return
|
|
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
|
+
}
|
package/src/state/event-log.ts
CHANGED
|
@@ -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;
|
package/src/state/locks.ts
CHANGED
|
@@ -14,7 +14,15 @@ function lockPath(manifest: TeamRunManifest): string {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
function sleepSync(ms: number): void {
|
|
17
|
-
|
|
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 {
|