pi-crew 0.2.3 → 0.2.5
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/AGENTS.md +57 -32
- package/CHANGELOG.md +466 -448
- package/LICENSE +21 -21
- package/NOTICE.md +16 -16
- package/README.md +323 -323
- package/docs/FEATURE_INTAKE.md +126 -0
- package/docs/HARNESS.md +86 -0
- package/docs/HARNESS_BACKLOG.md +41 -0
- package/docs/TEST_MATRIX.md +49 -0
- package/docs/actions-reference.md +595 -595
- package/docs/architecture.md +180 -180
- package/docs/code-review-2026-05-11.md +592 -592
- package/docs/commands-reference.md +347 -347
- package/docs/comparison-pi-subagents-vs-pi-crew.md +303 -0
- package/docs/decisions/0001-durable-state.md +41 -0
- package/docs/decisions/0002-child-process-for-async.md +42 -0
- package/docs/decisions/0003-depth-guard.md +36 -0
- package/docs/decisions/0004-execfile-over-exec.md +34 -0
- package/docs/decisions/0005-no-parameter-properties.md +49 -0
- package/docs/decisions/0006-publish-bundled-esm.md +63 -0
- package/docs/decisions/0007-active-run-binary-index.md +54 -0
- package/docs/decisions/0008-child-pi-warm-pool.md +61 -0
- package/docs/decisions/README.md +23 -0
- package/docs/followup-review-round4-2026-05-13.md +107 -0
- package/docs/implementation-plan-top3.md +333 -0
- package/docs/live-mailbox-runtime.md +36 -36
- package/docs/next-upgrade-roadmap.md +808 -808
- package/docs/oh-my-pi-research.md +509 -0
- package/docs/perf/baseline-2026-05.md +113 -0
- package/docs/perf/final-report-2026-05.md +206 -0
- package/docs/perf/sprint-1-report.md +71 -0
- package/docs/perf/sprint-2-report.md +81 -0
- package/docs/perf/sprint-2.5-report.md +53 -0
- package/docs/perf/sprint-3-report.md +36 -0
- package/docs/perf/sprint-4-report.md +47 -0
- package/docs/perf/sprint-5-report.md +51 -0
- package/docs/perf/sprint-6-report.md +94 -0
- package/docs/perf/sprint-7-report.md +74 -0
- package/docs/perf/upgrade-plan-2026-05.md +147 -0
- package/docs/pi-subagents3-deep-analysis.md +508 -0
- package/docs/product/README.md +31 -0
- package/docs/product/platform.md +27 -0
- package/docs/product/runtime-safety.md +37 -0
- package/docs/product/team-run.md +39 -0
- package/docs/product/team-tool.md +37 -0
- package/docs/publishing.md +65 -65
- package/docs/resource-formats.md +134 -134
- package/docs/runtime-analysis-child-vs-live.md +171 -0
- package/docs/runtime-flow.md +148 -148
- package/docs/runtime-migration-in-process-analysis.md +250 -0
- package/docs/stories/README.md +30 -0
- package/docs/stories/backlog.md +36 -0
- package/docs/templates/decision.md +27 -0
- package/docs/templates/story.md +44 -0
- package/docs/templates/validation-report.md +32 -0
- package/docs/usage.md +238 -238
- package/index.ts +7 -6
- package/install.mjs +65 -65
- package/package.json +107 -100
- package/schema.json +222 -222
- package/skills/child-pi-spawning/SKILL.md +213 -0
- package/skills/context-artifact-hygiene/SKILL.md +32 -0
- package/skills/event-log-tracing/SKILL.md +299 -0
- package/skills/git-master/SKILL.md +225 -24
- package/skills/live-agent-lifecycle/SKILL.md +192 -0
- package/skills/mailbox-interactive/SKILL.md +300 -19
- package/skills/model-routing-context/SKILL.md +94 -0
- package/skills/multi-perspective-review/SKILL.md +88 -0
- package/skills/read-only-explorer/SKILL.md +250 -26
- package/skills/safe-bash/SKILL.md +307 -21
- package/skills/verification-before-done/SKILL.md +11 -2
- package/skills/widget-rendering/SKILL.md +258 -0
- package/skills/workspace-isolation/SKILL.md +202 -0
- package/skills/worktree-isolation/SKILL.md +202 -18
- package/src/adapters/claude-adapter.ts +25 -25
- package/src/adapters/codex-adapter.ts +21 -21
- package/src/adapters/cursor-adapter.ts +17 -17
- package/src/adapters/export-util.ts +137 -137
- package/src/adapters/index.ts +15 -15
- package/src/adapters/registry.ts +18 -18
- package/src/adapters/types.ts +23 -23
- package/src/agents/agent-config.ts +38 -38
- package/src/agents/agent-serializer.ts +38 -38
- package/src/agents/discover-agents.ts +121 -118
- package/src/config/config.ts +740 -858
- package/src/config/defaults.ts +96 -96
- package/src/config/drift-detector.ts +211 -211
- package/src/config/markers.ts +327 -327
- package/src/config/resilient-parser.ts +109 -108
- package/src/config/suggestions.ts +74 -74
- package/src/config/types.ts +199 -0
- package/src/extension/async-notifier.ts +123 -89
- package/src/extension/autonomous-policy.ts +169 -169
- package/src/extension/cross-extension-rpc.ts +104 -104
- package/src/extension/help.ts +47 -47
- package/src/extension/import-index.ts +69 -69
- package/src/extension/management.ts +395 -382
- package/src/extension/notification-router.ts +116 -116
- package/src/extension/notification-sink.ts +51 -51
- package/src/extension/project-init.ts +168 -168
- package/src/extension/register.ts +859 -668
- package/src/extension/registration/artifact-cleanup.ts +15 -15
- package/src/extension/registration/command-utils.ts +54 -54
- package/src/extension/registration/commands.ts +559 -452
- package/src/extension/registration/compaction-guard.ts +125 -125
- package/src/extension/registration/subagent-helpers.ts +102 -102
- package/src/extension/registration/subagent-tools.ts +220 -159
- package/src/extension/registration/team-tool.ts +159 -99
- package/src/extension/registration/viewers.ts +29 -0
- package/src/extension/result-watcher.ts +128 -128
- package/src/extension/run-bundle-schema.ts +89 -89
- package/src/extension/run-export.ts +73 -73
- package/src/extension/run-import.ts +84 -84
- package/src/extension/run-index.ts +94 -94
- package/src/extension/run-maintenance.ts +142 -142
- package/src/extension/session-summary.ts +8 -8
- package/src/extension/team-manager-command.ts +96 -96
- package/src/extension/team-recommendation.ts +188 -188
- package/src/extension/team-tool/api.ts +5 -2
- package/src/extension/team-tool/cancel.ts +224 -209
- package/src/extension/team-tool/config-patch.ts +36 -36
- package/src/extension/team-tool/context.ts +60 -60
- package/src/extension/team-tool/doctor.ts +242 -242
- package/src/extension/team-tool/handle-settings.ts +421 -195
- package/src/extension/team-tool/inspect.ts +41 -41
- package/src/extension/team-tool/lifecycle-actions.ts +139 -139
- package/src/extension/team-tool/parallel-dispatch.ts +156 -156
- package/src/extension/team-tool/plan.ts +19 -19
- package/src/extension/team-tool/respond.ts +112 -111
- package/src/extension/team-tool/run.ts +246 -229
- package/src/extension/team-tool/status.ts +110 -110
- package/src/extension/team-tool-types.ts +13 -13
- package/src/extension/team-tool.ts +344 -344
- package/src/extension/tool-result.ts +16 -16
- package/src/extension/validate-resources.ts +77 -77
- package/src/hooks/registry.ts +61 -61
- package/src/hooks/types.ts +40 -40
- package/src/i18n.ts +184 -184
- package/src/observability/correlation.ts +35 -35
- package/src/observability/event-to-metric.ts +68 -68
- package/src/observability/exporters/adapter.ts +30 -30
- package/src/observability/exporters/otlp-exporter.ts +106 -92
- package/src/observability/exporters/prometheus-exporter.ts +54 -54
- package/src/observability/metric-registry.ts +87 -87
- package/src/observability/metric-retention.ts +54 -54
- package/src/observability/metric-sink.ts +81 -56
- package/src/observability/metrics-primitives.ts +167 -167
- package/src/prompt/prompt-runtime.ts +72 -72
- package/src/runtime/adaptive-plan.ts +338 -0
- package/src/runtime/agent-control.ts +169 -169
- 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/async-runner.ts +153 -153
- package/src/runtime/attention-events.ts +28 -28
- package/src/runtime/auto-resume.ts +100 -100
- package/src/runtime/background-runner.ts +122 -89
- package/src/runtime/cancellation.ts +61 -61
- package/src/runtime/capability-inventory.ts +116 -116
- package/src/runtime/child-pi-pool.ts +68 -0
- package/src/runtime/child-pi.ts +541 -461
- package/src/runtime/code-summary.ts +247 -247
- package/src/runtime/compaction-summary.ts +271 -271
- package/src/runtime/concurrency.ts +58 -58
- package/src/runtime/crash-recovery.ts +317 -301
- package/src/runtime/crew-agent-records.ts +379 -281
- package/src/runtime/crew-agent-runtime.ts +60 -60
- package/src/runtime/cross-extension-rpc.ts +72 -0
- package/src/runtime/custom-tools/irc-tool.ts +201 -201
- package/src/runtime/custom-tools/submit-result-tool.ts +90 -90
- package/src/runtime/deadletter.ts +47 -47
- package/src/runtime/delivery-coordinator.ts +176 -176
- package/src/runtime/delta-conflict.ts +360 -360
- package/src/runtime/diagnostic-export.ts +102 -102
- package/src/runtime/direct-run.ts +35 -35
- package/src/runtime/effectiveness.ts +82 -81
- package/src/runtime/errors/crew-errors.ts +166 -0
- package/src/runtime/event-stream-bridge.ts +92 -92
- package/src/runtime/foreground-control.ts +82 -82
- package/src/runtime/green-contract.ts +46 -46
- package/src/runtime/group-join.ts +234 -106
- package/src/runtime/heartbeat-watcher.ts +145 -124
- package/src/runtime/iteration-hooks.ts +267 -267
- package/src/runtime/live-agent-control.ts +88 -88
- package/src/runtime/live-agent-manager.ts +377 -179
- package/src/runtime/live-control-realtime.ts +36 -36
- package/src/runtime/live-session-runtime.ts +676 -600
- package/src/runtime/loop-gates.ts +129 -129
- package/src/runtime/manifest-cache.ts +263 -263
- package/src/runtime/mcp-proxy.ts +113 -113
- package/src/runtime/metric-parser.ts +40 -40
- package/src/runtime/model-fallback.ts +282 -274
- package/src/runtime/model-resolver.ts +118 -0
- package/src/runtime/output-validator.ts +187 -187
- package/src/runtime/overflow-recovery.ts +175 -175
- package/src/runtime/parallel-research.ts +44 -44
- package/src/runtime/parallel-utils.ts +156 -156
- package/src/runtime/parent-guard.ts +80 -80
- package/src/runtime/phase-progress.ts +217 -217
- package/src/runtime/pi-args.ts +165 -165
- package/src/runtime/pi-json-output.ts +111 -111
- package/src/runtime/pi-spawn.ts +167 -167
- package/src/runtime/policy-engine.ts +79 -79
- package/src/runtime/post-checks.ts +125 -125
- package/src/runtime/post-exit-stdio-guard.ts +86 -86
- package/src/runtime/process-status.ts +97 -73
- package/src/runtime/progress-event-coalescer.ts +43 -43
- package/src/runtime/recovery-recipes.ts +74 -74
- package/src/runtime/retry-executor.ts +81 -81
- package/src/runtime/role-permission.ts +39 -39
- package/src/runtime/run-tracker.ts +99 -0
- package/src/runtime/runtime-policy.ts +21 -0
- package/src/runtime/runtime-resolver.ts +94 -91
- package/src/runtime/scheduler.ts +294 -0
- package/src/runtime/semaphore.ts +131 -131
- package/src/runtime/sensitive-paths.ts +92 -92
- package/src/runtime/session-usage.ts +79 -79
- package/src/runtime/settings-store.ts +103 -0
- package/src/runtime/sidechain-output.ts +29 -29
- package/src/runtime/skill-instructions.ts +222 -222
- package/src/runtime/stale-reconciler.ts +198 -189
- package/src/runtime/streaming-output.ts +47 -0
- package/src/runtime/subagent-manager.ts +404 -400
- package/src/runtime/subprocess-tool-registry.ts +67 -67
- package/src/runtime/task-display.ts +38 -38
- package/src/runtime/task-graph-scheduler.ts +122 -122
- package/src/runtime/task-graph.ts +207 -207
- package/src/runtime/task-output-context.ts +177 -177
- package/src/runtime/task-packet.ts +93 -93
- package/src/runtime/task-quality.ts +207 -207
- package/src/runtime/task-runner/capabilities.ts +78 -78
- package/src/runtime/task-runner/live-executor.ts +131 -113
- package/src/runtime/task-runner/progress.ts +119 -119
- package/src/runtime/task-runner/prompt-builder.ts +139 -139
- package/src/runtime/task-runner/prompt-pipeline.ts +64 -64
- package/src/runtime/task-runner/result-utils.ts +14 -14
- package/src/runtime/task-runner/run-projection.ts +103 -103
- package/src/runtime/task-runner/state-helpers.ts +22 -22
- package/src/runtime/task-runner.ts +469 -459
- package/src/runtime/team-runner.ts +693 -945
- package/src/runtime/usage-tracker.ts +71 -0
- package/src/runtime/worker-heartbeat.ts +21 -21
- package/src/runtime/worker-startup.ts +57 -57
- package/src/runtime/workflow-state.ts +187 -187
- package/src/runtime/yield-handler.ts +190 -190
- package/src/schema/config-schema.ts +172 -168
- package/src/schema/team-tool-schema.ts +126 -126
- package/src/schema/validation-types.ts +151 -148
- package/src/skills/discover-skills.ts +67 -67
- package/src/skills/skill-templates.ts +374 -374
- package/src/state/active-run-registry.ts +227 -191
- package/src/state/artifact-store.ts +130 -129
- package/src/state/atomic-write.ts +262 -195
- package/src/state/blob-store.ts +116 -116
- package/src/state/contracts.ts +111 -111
- package/src/state/event-log-rotation.ts +161 -158
- package/src/state/event-log.ts +383 -303
- package/src/state/event-reconstructor.ts +217 -217
- package/src/state/jsonl-writer.ts +82 -82
- package/src/state/locks.ts +146 -146
- package/src/state/mailbox.ts +446 -405
- package/src/state/state-store.ts +364 -351
- package/src/state/task-claims.ts +44 -44
- package/src/state/types.ts +285 -285
- 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 +116 -116
- package/src/teams/team-config.ts +27 -27
- package/src/teams/team-serializer.ts +38 -38
- package/src/types/diff.d.ts +18 -18
- package/src/ui/agent-management-overlay.ts +144 -144
- package/src/ui/crew-widget.ts +487 -370
- package/src/ui/dashboard-panes/agents-pane.ts +109 -28
- package/src/ui/dashboard-panes/cancellation-pane.ts +42 -42
- package/src/ui/dashboard-panes/capability-pane.ts +59 -59
- package/src/ui/dashboard-panes/health-pane.ts +30 -30
- package/src/ui/dashboard-panes/mailbox-pane.ts +35 -35
- package/src/ui/dashboard-panes/progress-pane.ts +30 -30
- package/src/ui/dashboard-panes/transcript-pane.ts +10 -10
- package/src/ui/heartbeat-aggregator.ts +63 -63
- package/src/ui/keybinding-map.ts +97 -94
- package/src/ui/live-conversation-overlay.ts +152 -0
- package/src/ui/live-run-sidebar.ts +180 -180
- package/src/ui/mascot.ts +442 -442
- package/src/ui/overlays/agent-picker-overlay.ts +57 -57
- package/src/ui/overlays/confirm-overlay.ts +58 -58
- package/src/ui/overlays/mailbox-compose-overlay.ts +144 -144
- package/src/ui/overlays/mailbox-compose-preview.ts +63 -63
- package/src/ui/overlays/mailbox-detail-overlay.ts +122 -122
- package/src/ui/pi-ui-compat.ts +57 -57
- package/src/ui/powerbar-publisher.ts +221 -197
- package/src/ui/render-scheduler.ts +216 -143
- package/src/ui/run-action-dispatcher.ts +118 -118
- package/src/ui/run-dashboard.ts +526 -464
- package/src/ui/run-event-bus.ts +208 -208
- package/src/ui/run-snapshot-cache.ts +826 -777
- package/src/ui/settings-overlay.ts +721 -0
- package/src/ui/snapshot-types.ts +86 -70
- package/src/ui/theme-adapter.ts +190 -190
- package/src/ui/tool-progress-formatter.ts +89 -0
- package/src/ui/transcript-cache.ts +94 -94
- package/src/ui/transcript-viewer.ts +335 -335
- package/src/utils/conflict-detect.ts +662 -0
- package/src/utils/file-coalescer.ts +86 -86
- package/src/utils/frontmatter.ts +68 -68
- package/src/utils/fs-watch.ts +88 -31
- package/src/utils/gh-protocol.ts +479 -0
- package/src/utils/ids.ts +17 -17
- package/src/utils/incremental-reader.ts +104 -104
- package/src/utils/internal-error.ts +6 -6
- package/src/utils/names.ts +27 -27
- package/src/utils/paths.ts +102 -63
- package/src/utils/redaction.ts +44 -44
- package/src/utils/safe-paths.ts +47 -47
- package/src/utils/scan-cache.ts +136 -136
- package/src/utils/sse-parser.ts +134 -134
- package/src/utils/task-name-generator.ts +337 -337
- package/src/utils/timings.ts +33 -33
- package/src/utils/visual.ts +243 -198
- package/src/workflows/discover-workflows.ts +139 -139
- package/src/workflows/validate-workflow.ts +40 -40
- package/src/workflows/workflow-config.ts +26 -26
- package/src/workflows/workflow-serializer.ts +32 -32
- package/src/worktree/branch-freshness.ts +45 -45
- package/src/worktree/cleanup.ts +75 -75
- package/src/worktree/worktree-manager.ts +188 -188
- 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/tsconfig.json +19 -19
- package/workflows/default.workflow.md +30 -30
- package/workflows/fast-fix.workflow.md +23 -23
- package/workflows/implementation.workflow.md +43 -43
- package/workflows/parallel-research.workflow.md +46 -46
- package/workflows/research.workflow.md +22 -22
- package/workflows/review.workflow.md +30 -30
- package/skills/task-packet/SKILL.md +0 -28
- package/skills/verify-evidence/SKILL.md +0 -27
package/src/state/blob-store.ts
CHANGED
|
@@ -1,117 +1,117 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import { createHash } from "node:crypto";
|
|
4
|
-
import { resolveRealContainedPath } from "../utils/safe-paths.ts";
|
|
5
|
-
|
|
6
|
-
const SHA256_HEX = /^[a-f0-9]{64}$/i;
|
|
7
|
-
|
|
8
|
-
function validateBlobHash(hash: string): void {
|
|
9
|
-
if (!SHA256_HEX.test(hash)) throw new Error(`Invalid blob hash: ${hash}`);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const BLOBS_DIR = "blobs";
|
|
13
|
-
const BLOB_META_DIR = "blob-metadata";
|
|
14
|
-
const SHA256_PREFIX = "sha256";
|
|
15
|
-
|
|
16
|
-
export interface BlobMetadata {
|
|
17
|
-
blobHash: string;
|
|
18
|
-
blobAlgorithm: string;
|
|
19
|
-
runId: string;
|
|
20
|
-
taskId?: string;
|
|
21
|
-
mime: string;
|
|
22
|
-
producer: string;
|
|
23
|
-
originalPath: string;
|
|
24
|
-
sizeBytes: number;
|
|
25
|
-
redacted: boolean;
|
|
26
|
-
retention: "run" | "project" | "temporary";
|
|
27
|
-
createdAt: string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface BlobWriteResult {
|
|
31
|
-
hash: string;
|
|
32
|
-
algorithm: string;
|
|
33
|
-
blobPath: string;
|
|
34
|
-
metadataPath: string;
|
|
35
|
-
sizeBytes: number;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function sha256Of(content: string | Buffer): string {
|
|
39
|
-
return createHash("sha256").update(typeof content === "string" ? content : content).digest("hex");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Write content-addressed blob to the blobs directory under artifactsRoot.
|
|
44
|
-
* Content is deduplicated by hash; metadata sidecar is always written.
|
|
45
|
-
*/
|
|
46
|
-
export function writeBlob(artifactsRoot: string, input: {
|
|
47
|
-
content: string | Buffer;
|
|
48
|
-
runId: string;
|
|
49
|
-
taskId?: string;
|
|
50
|
-
mime?: string;
|
|
51
|
-
producer: string;
|
|
52
|
-
originalPath: string;
|
|
53
|
-
redacted?: boolean;
|
|
54
|
-
retention?: BlobMetadata["retention"];
|
|
55
|
-
}): BlobWriteResult {
|
|
56
|
-
const content = input.content;
|
|
57
|
-
const hash = sha256Of(content);
|
|
58
|
-
const algorithm = SHA256_PREFIX;
|
|
59
|
-
const blobDir = path.join(artifactsRoot, BLOBS_DIR, algorithm);
|
|
60
|
-
const metaDir = path.join(artifactsRoot, BLOB_META_DIR);
|
|
61
|
-
fs.mkdirSync(blobDir, { recursive: true });
|
|
62
|
-
fs.mkdirSync(metaDir, { recursive: true });
|
|
63
|
-
|
|
64
|
-
const blobPath = path.join(blobDir, hash);
|
|
65
|
-
if (!fs.existsSync(blobPath)) {
|
|
66
|
-
fs.writeFileSync(blobPath, content, typeof input.content === "string" ? "utf-8" : undefined);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const metadata: BlobMetadata = {
|
|
70
|
-
blobHash: hash,
|
|
71
|
-
blobAlgorithm: algorithm,
|
|
72
|
-
runId: input.runId,
|
|
73
|
-
taskId: input.taskId,
|
|
74
|
-
mime: input.mime ?? "text/plain",
|
|
75
|
-
producer: input.producer,
|
|
76
|
-
originalPath: input.originalPath,
|
|
77
|
-
sizeBytes: Buffer.isBuffer(content) ? content.length : Buffer.byteLength(content, "utf-8"),
|
|
78
|
-
redacted: input.redacted ?? false,
|
|
79
|
-
retention: input.retention ?? "run",
|
|
80
|
-
createdAt: new Date().toISOString(),
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const metadataPath = path.join(metaDir, `${hash}.json`);
|
|
84
|
-
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
|
|
85
|
-
|
|
86
|
-
return { hash, algorithm, blobPath: resolveRealContainedPath(artifactsRoot, blobPath), metadataPath: resolveRealContainedPath(artifactsRoot, metadataPath), sizeBytes: metadata.sizeBytes };
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Read a content-addressed blob by hash.
|
|
91
|
-
* Validates hash format and enforces path containment.
|
|
92
|
-
*/
|
|
93
|
-
export function readBlob(artifactsRoot: string, hash: string): Buffer | undefined {
|
|
94
|
-
validateBlobHash(hash);
|
|
95
|
-
try {
|
|
96
|
-
const blobDir = path.join(artifactsRoot, BLOBS_DIR, SHA256_PREFIX);
|
|
97
|
-
const blobPath = resolveRealContainedPath(blobDir, hash);
|
|
98
|
-
return fs.readFileSync(blobPath);
|
|
99
|
-
} catch {
|
|
100
|
-
return undefined;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Read blob metadata by hash.
|
|
106
|
-
* Validates hash format and enforces path containment.
|
|
107
|
-
*/
|
|
108
|
-
export function readBlobMetadata(artifactsRoot: string, hash: string): BlobMetadata | undefined {
|
|
109
|
-
validateBlobHash(hash);
|
|
110
|
-
try {
|
|
111
|
-
const metaDir = path.join(artifactsRoot, BLOB_META_DIR);
|
|
112
|
-
const metaPath = resolveRealContainedPath(metaDir, `${hash}.json`);
|
|
113
|
-
return JSON.parse(fs.readFileSync(metaPath, "utf-8")) as BlobMetadata;
|
|
114
|
-
} catch {
|
|
115
|
-
return undefined;
|
|
116
|
-
}
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { resolveRealContainedPath } from "../utils/safe-paths.ts";
|
|
5
|
+
|
|
6
|
+
const SHA256_HEX = /^[a-f0-9]{64}$/i;
|
|
7
|
+
|
|
8
|
+
function validateBlobHash(hash: string): void {
|
|
9
|
+
if (!SHA256_HEX.test(hash)) throw new Error(`Invalid blob hash: ${hash}`);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const BLOBS_DIR = "blobs";
|
|
13
|
+
const BLOB_META_DIR = "blob-metadata";
|
|
14
|
+
const SHA256_PREFIX = "sha256";
|
|
15
|
+
|
|
16
|
+
export interface BlobMetadata {
|
|
17
|
+
blobHash: string;
|
|
18
|
+
blobAlgorithm: string;
|
|
19
|
+
runId: string;
|
|
20
|
+
taskId?: string;
|
|
21
|
+
mime: string;
|
|
22
|
+
producer: string;
|
|
23
|
+
originalPath: string;
|
|
24
|
+
sizeBytes: number;
|
|
25
|
+
redacted: boolean;
|
|
26
|
+
retention: "run" | "project" | "temporary";
|
|
27
|
+
createdAt: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface BlobWriteResult {
|
|
31
|
+
hash: string;
|
|
32
|
+
algorithm: string;
|
|
33
|
+
blobPath: string;
|
|
34
|
+
metadataPath: string;
|
|
35
|
+
sizeBytes: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function sha256Of(content: string | Buffer): string {
|
|
39
|
+
return createHash("sha256").update(typeof content === "string" ? content : content).digest("hex");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Write content-addressed blob to the blobs directory under artifactsRoot.
|
|
44
|
+
* Content is deduplicated by hash; metadata sidecar is always written.
|
|
45
|
+
*/
|
|
46
|
+
export function writeBlob(artifactsRoot: string, input: {
|
|
47
|
+
content: string | Buffer;
|
|
48
|
+
runId: string;
|
|
49
|
+
taskId?: string;
|
|
50
|
+
mime?: string;
|
|
51
|
+
producer: string;
|
|
52
|
+
originalPath: string;
|
|
53
|
+
redacted?: boolean;
|
|
54
|
+
retention?: BlobMetadata["retention"];
|
|
55
|
+
}): BlobWriteResult {
|
|
56
|
+
const content = input.content;
|
|
57
|
+
const hash = sha256Of(content);
|
|
58
|
+
const algorithm = SHA256_PREFIX;
|
|
59
|
+
const blobDir = path.join(artifactsRoot, BLOBS_DIR, algorithm);
|
|
60
|
+
const metaDir = path.join(artifactsRoot, BLOB_META_DIR);
|
|
61
|
+
fs.mkdirSync(blobDir, { recursive: true });
|
|
62
|
+
fs.mkdirSync(metaDir, { recursive: true });
|
|
63
|
+
|
|
64
|
+
const blobPath = path.join(blobDir, hash);
|
|
65
|
+
if (!fs.existsSync(blobPath)) {
|
|
66
|
+
fs.writeFileSync(blobPath, content, typeof input.content === "string" ? "utf-8" : undefined);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const metadata: BlobMetadata = {
|
|
70
|
+
blobHash: hash,
|
|
71
|
+
blobAlgorithm: algorithm,
|
|
72
|
+
runId: input.runId,
|
|
73
|
+
taskId: input.taskId,
|
|
74
|
+
mime: input.mime ?? "text/plain",
|
|
75
|
+
producer: input.producer,
|
|
76
|
+
originalPath: input.originalPath,
|
|
77
|
+
sizeBytes: Buffer.isBuffer(content) ? content.length : Buffer.byteLength(content, "utf-8"),
|
|
78
|
+
redacted: input.redacted ?? false,
|
|
79
|
+
retention: input.retention ?? "run",
|
|
80
|
+
createdAt: new Date().toISOString(),
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const metadataPath = path.join(metaDir, `${hash}.json`);
|
|
84
|
+
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
|
|
85
|
+
|
|
86
|
+
return { hash, algorithm, blobPath: resolveRealContainedPath(artifactsRoot, blobPath), metadataPath: resolveRealContainedPath(artifactsRoot, metadataPath), sizeBytes: metadata.sizeBytes };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Read a content-addressed blob by hash.
|
|
91
|
+
* Validates hash format and enforces path containment.
|
|
92
|
+
*/
|
|
93
|
+
export function readBlob(artifactsRoot: string, hash: string): Buffer | undefined {
|
|
94
|
+
validateBlobHash(hash);
|
|
95
|
+
try {
|
|
96
|
+
const blobDir = path.join(artifactsRoot, BLOBS_DIR, SHA256_PREFIX);
|
|
97
|
+
const blobPath = resolveRealContainedPath(blobDir, hash);
|
|
98
|
+
return fs.readFileSync(blobPath);
|
|
99
|
+
} catch {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Read blob metadata by hash.
|
|
106
|
+
* Validates hash format and enforces path containment.
|
|
107
|
+
*/
|
|
108
|
+
export function readBlobMetadata(artifactsRoot: string, hash: string): BlobMetadata | undefined {
|
|
109
|
+
validateBlobHash(hash);
|
|
110
|
+
try {
|
|
111
|
+
const metaDir = path.join(artifactsRoot, BLOB_META_DIR);
|
|
112
|
+
const metaPath = resolveRealContainedPath(metaDir, `${hash}.json`);
|
|
113
|
+
return JSON.parse(fs.readFileSync(metaPath, "utf-8")) as BlobMetadata;
|
|
114
|
+
} catch {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
117
|
}
|
package/src/state/contracts.ts
CHANGED
|
@@ -1,111 +1,111 @@
|
|
|
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", "queued", "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.created",
|
|
40
|
-
"task.started",
|
|
41
|
-
"task.progress",
|
|
42
|
-
"task.blocked",
|
|
43
|
-
"task.green",
|
|
44
|
-
"task.red",
|
|
45
|
-
"task.completed",
|
|
46
|
-
"task.failed",
|
|
47
|
-
"task.cancelled",
|
|
48
|
-
"task.skipped",
|
|
49
|
-
"review.approved",
|
|
50
|
-
"review.rejected",
|
|
51
|
-
"policy.action",
|
|
52
|
-
"policy.escalated",
|
|
53
|
-
"recovery.attempted",
|
|
54
|
-
"recovery.escalated",
|
|
55
|
-
"branch.stale",
|
|
56
|
-
"mailbox.timeout",
|
|
57
|
-
"worktree.cleanup",
|
|
58
|
-
"worktree.dirty",
|
|
59
|
-
"async.spawned",
|
|
60
|
-
"async.started",
|
|
61
|
-
"async.completed",
|
|
62
|
-
"async.failed",
|
|
63
|
-
"async.stale",
|
|
64
|
-
"task.waiting",
|
|
65
|
-
"task.resumed",
|
|
66
|
-
"task.retried",
|
|
67
|
-
"supervisor.contact",
|
|
68
|
-
] as const;
|
|
69
|
-
export type TeamEventType = typeof TEAM_EVENT_TYPES[number];
|
|
70
|
-
|
|
71
|
-
export const TEAM_WAKEABLE_EVENT_TYPES: ReadonlySet<TeamEventType> = new Set([
|
|
72
|
-
"run.blocked",
|
|
73
|
-
"run.completed",
|
|
74
|
-
"run.failed",
|
|
75
|
-
"run.cancelled",
|
|
76
|
-
"task.completed",
|
|
77
|
-
"task.failed",
|
|
78
|
-
"task.cancelled",
|
|
79
|
-
"task.skipped",
|
|
80
|
-
"async.completed",
|
|
81
|
-
"async.failed",
|
|
82
|
-
"async.stale",
|
|
83
|
-
]);
|
|
84
|
-
|
|
85
|
-
export function isTeamRunStatus(value: unknown): value is TeamRunStatus {
|
|
86
|
-
return typeof value === "string" && TEAM_RUN_STATUSES.includes(value as TeamRunStatus);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function isTeamTaskStatus(value: unknown): value is TeamTaskStatus {
|
|
90
|
-
return typeof value === "string" && TEAM_TASK_STATUSES.includes(value as TeamTaskStatus);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export function isTerminalRunStatus(status: TeamRunStatus): boolean {
|
|
94
|
-
return TEAM_TERMINAL_RUN_STATUSES.has(status);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function isTerminalTaskStatus(status: TeamTaskStatus): boolean {
|
|
98
|
-
return TEAM_TERMINAL_TASK_STATUSES.has(status);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function canTransitionRunStatus(from: TeamRunStatus, to: TeamRunStatus): boolean {
|
|
102
|
-
return from === to || (TEAM_RUN_STATUS_TRANSITIONS[from]?.includes(to) ?? false);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export function canTransitionTaskStatus(from: TeamTaskStatus, to: TeamTaskStatus): boolean {
|
|
106
|
-
return from === to || (TEAM_TASK_STATUS_TRANSITIONS[from]?.includes(to) ?? false);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export function isWakeableTeamEventType(type: TeamEventType): boolean {
|
|
110
|
-
return TEAM_WAKEABLE_EVENT_TYPES.has(type);
|
|
111
|
-
}
|
|
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", "queued", "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.created",
|
|
40
|
+
"task.started",
|
|
41
|
+
"task.progress",
|
|
42
|
+
"task.blocked",
|
|
43
|
+
"task.green",
|
|
44
|
+
"task.red",
|
|
45
|
+
"task.completed",
|
|
46
|
+
"task.failed",
|
|
47
|
+
"task.cancelled",
|
|
48
|
+
"task.skipped",
|
|
49
|
+
"review.approved",
|
|
50
|
+
"review.rejected",
|
|
51
|
+
"policy.action",
|
|
52
|
+
"policy.escalated",
|
|
53
|
+
"recovery.attempted",
|
|
54
|
+
"recovery.escalated",
|
|
55
|
+
"branch.stale",
|
|
56
|
+
"mailbox.timeout",
|
|
57
|
+
"worktree.cleanup",
|
|
58
|
+
"worktree.dirty",
|
|
59
|
+
"async.spawned",
|
|
60
|
+
"async.started",
|
|
61
|
+
"async.completed",
|
|
62
|
+
"async.failed",
|
|
63
|
+
"async.stale",
|
|
64
|
+
"task.waiting",
|
|
65
|
+
"task.resumed",
|
|
66
|
+
"task.retried",
|
|
67
|
+
"supervisor.contact",
|
|
68
|
+
] as const;
|
|
69
|
+
export type TeamEventType = typeof TEAM_EVENT_TYPES[number];
|
|
70
|
+
|
|
71
|
+
export const TEAM_WAKEABLE_EVENT_TYPES: ReadonlySet<TeamEventType> = new Set([
|
|
72
|
+
"run.blocked",
|
|
73
|
+
"run.completed",
|
|
74
|
+
"run.failed",
|
|
75
|
+
"run.cancelled",
|
|
76
|
+
"task.completed",
|
|
77
|
+
"task.failed",
|
|
78
|
+
"task.cancelled",
|
|
79
|
+
"task.skipped",
|
|
80
|
+
"async.completed",
|
|
81
|
+
"async.failed",
|
|
82
|
+
"async.stale",
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
export function isTeamRunStatus(value: unknown): value is TeamRunStatus {
|
|
86
|
+
return typeof value === "string" && TEAM_RUN_STATUSES.includes(value as TeamRunStatus);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function isTeamTaskStatus(value: unknown): value is TeamTaskStatus {
|
|
90
|
+
return typeof value === "string" && TEAM_TASK_STATUSES.includes(value as TeamTaskStatus);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function isTerminalRunStatus(status: TeamRunStatus): boolean {
|
|
94
|
+
return TEAM_TERMINAL_RUN_STATUSES.has(status);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function isTerminalTaskStatus(status: TeamTaskStatus): boolean {
|
|
98
|
+
return TEAM_TERMINAL_TASK_STATUSES.has(status);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function canTransitionRunStatus(from: TeamRunStatus, to: TeamRunStatus): boolean {
|
|
102
|
+
return from === to || (TEAM_RUN_STATUS_TRANSITIONS[from]?.includes(to) ?? false);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function canTransitionTaskStatus(from: TeamTaskStatus, to: TeamTaskStatus): boolean {
|
|
106
|
+
return from === to || (TEAM_TASK_STATUS_TRANSITIONS[from]?.includes(to) ?? false);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function isWakeableTeamEventType(type: TeamEventType): boolean {
|
|
110
|
+
return TEAM_WAKEABLE_EVENT_TYPES.has(type);
|
|
111
|
+
}
|