pi-crew 0.11.0 → 0.11.2
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 +155 -9
- package/README.md +161 -1037
- package/agents/verifier.md +18 -7
- package/dist/index.mjs +744 -90644
- package/docs/README.md +57 -46
- package/docs/architecture.md +87 -33
- package/docs/commands-reference.md +9 -5
- package/docs/troubleshooting.md +3 -2
- package/package.json +1 -3
- package/schema.json +39 -0
- package/skills/real-test-pi-crew/REPORT-TEMPLATE.md +2 -0
- package/skills/real-test-pi-crew/SKILL.md +371 -34
- package/src/agents/agent-config.ts +1 -1
- package/src/agents/discover-agents.ts +1 -1
- package/src/config/config-validation.ts +15 -2
- package/src/config/config.ts +47 -13
- package/src/config/defaults.ts +0 -1
- package/src/config/env-vars.ts +35 -0
- package/src/config/types.ts +19 -5
- package/src/errors.ts +2 -2
- package/src/extension/async-notifier.ts +23 -0
- package/src/extension/crew-vibes/config.ts +0 -21
- package/src/extension/crew-vibes/index.ts +0 -2
- package/src/extension/crew-vibes/render.ts +1 -50
- package/src/extension/help.ts +21 -12
- package/src/extension/knowledge-injection.ts +2 -1
- package/src/extension/management.ts +8 -3
- package/src/extension/notification-sink.ts +17 -0
- package/src/extension/register.ts +7 -2
- package/src/extension/registration/command-utils.ts +28 -2
- package/src/extension/registration/commands/dashboard.ts +11 -1
- package/src/extension/registration/commands/manage.ts +36 -19
- package/src/extension/registration/commands/run.ts +24 -2
- package/src/extension/registration/commands/shared.ts +23 -1
- package/src/extension/registration/commands/status.ts +25 -2
- package/src/extension/registration/context-builder.ts +8 -2
- package/src/extension/registration/health-notify-policy.ts +100 -0
- package/src/extension/registration/lazy-configurers.ts +35 -0
- package/src/extension/registration/lifecycle-handlers.ts +91 -30
- package/src/extension/registration/lifecycle.ts +75 -10
- package/src/extension/registration/observability.ts +98 -35
- package/src/extension/registration/registration-types.ts +7 -5
- package/src/extension/registration/runtime-cleanup.ts +9 -3
- package/src/extension/registration/subagent-helpers.ts +38 -0
- package/src/extension/registration/subagent-tools.ts +16 -6
- package/src/extension/registration/team-tool.ts +10 -3
- package/src/extension/registration/terminal-status-wiring.ts +172 -0
- package/src/extension/registration/viewers.ts +6 -0
- package/src/extension/registration/wire-cross-extension.ts +28 -0
- package/src/extension/run-compare.ts +220 -0
- package/src/extension/run-export.ts +37 -5
- package/src/extension/run-maintenance.ts +155 -5
- package/src/extension/team-tool/dispatch/index.ts +3 -2
- package/src/extension/team-tool/dispatch/manage.ts +5 -2
- package/src/extension/team-tool/goal.ts +4 -1
- package/src/extension/team-tool/handle-settings.ts +33 -4
- package/src/extension/team-tool/health-monitor.ts +21 -7
- package/src/extension/team-tool/lifecycle-actions.ts +49 -1
- package/src/extension/team-tool/plan.ts +10 -0
- package/src/extension/team-tool/routing-hint.ts +63 -0
- package/src/extension/team-tool/status.ts +4 -0
- package/src/extension/team-tool.ts +52 -6
- package/src/extension/webhook-notify.ts +382 -0
- package/src/observability/metric-sink.ts +12 -2
- package/src/prompt/prompt-runtime.ts +82 -31
- package/src/prompt/worker-events-channel.ts +12 -0
- package/src/runtime/README.md +1 -1
- package/src/runtime/async-runner.ts +87 -1
- package/src/runtime/background-runner.ts +313 -234
- package/src/runtime/broker/crew-broker.ts +17 -11
- package/src/runtime/broker/delegate/shadow-lifecycle.ts +92 -0
- package/src/runtime/broker/wait-status-cache.ts +1 -1
- package/src/runtime/child-pi/child-pi-timers.ts +1 -1
- package/src/runtime/child-pi/mock-fixtures.ts +48 -0
- package/src/runtime/crew-agent-records.ts +337 -45
- package/src/runtime/deadletter.ts +43 -1
- package/src/runtime/delegate-spawn.ts +5 -1
- package/src/runtime/dispatch-batch.ts +72 -5
- package/src/runtime/goal-workflow/goal-loop-runner.ts +73 -4
- package/src/runtime/heartbeat/heartbeat-watcher.ts +7 -0
- package/src/runtime/model/model-fallback.ts +21 -1
- package/src/runtime/model/pi-args.ts +8 -10
- package/src/runtime/recovery/crash-recovery.ts +25 -1
- package/src/runtime/run-worker.ts +12 -1
- package/src/runtime/scheduling/global-worker-cap.ts +13 -6
- package/src/runtime/scheduling/run-coalesced-task-group.ts +27 -1
- package/src/runtime/scheduling/scheduler.ts +49 -13
- package/src/runtime/scheduling/semaphore.ts +148 -20
- package/src/runtime/scratchpad/README.md +1 -1
- package/src/runtime/scratchpad/protocol.ts +1 -1
- package/src/runtime/settings-store.ts +1 -1
- package/src/runtime/skill-instructions.ts +22 -0
- package/src/runtime/stale-reconciler.ts +85 -13
- package/src/runtime/task-display.ts +1 -1
- package/src/runtime/task-runner/pre-execution.ts +26 -2
- package/src/runtime/task-runner/prompt-builder.ts +142 -45
- package/src/runtime/task-runner.ts +21 -1
- package/src/runtime/team-runner.ts +38 -1
- package/src/runtime/workspace-lock.ts +4 -1
- package/src/schema/config-schema.ts +18 -0
- package/src/schema/team-tool-schema.ts +17 -0
- package/src/state/atomic-write.ts +53 -0
- package/src/state/contracts.ts +109 -0
- package/src/state/coordination/locks.ts +191 -33
- package/src/state/coordination/mailbox.ts +140 -15
- package/src/state/crew-init.ts +87 -12
- package/src/state/event-log/cursor.ts +37 -1
- package/src/state/event-log/event-log-rotation.ts +72 -7
- package/src/state/stores/active-run-registry.ts +13 -1
- package/src/state/stores/state-store.ts +112 -22
- package/src/state/types.ts +4 -0
- package/src/ui/adaptive-card.ts +65 -0
- package/src/ui/agents-jobs-browser.ts +70 -64
- package/src/ui/card-colors.ts +36 -7
- package/src/ui/dashboard-panes/agents-pane.ts +55 -14
- package/src/ui/dashboard-panes/cancellation-pane.ts +0 -42
- package/src/ui/dashboard-panes/health-pane.ts +7 -5
- package/src/ui/dashboard-panes/mailbox-pane.ts +22 -6
- package/src/ui/dashboard-panes/metrics-pane.ts +15 -7
- package/src/ui/dashboard-panes/pane-theme.ts +21 -0
- package/src/ui/dashboard-panes/plan-pane.ts +63 -30
- package/src/ui/dashboard-panes/progress-pane.ts +3 -2
- package/src/ui/dashboard-panes/schedules-pane.ts +44 -21
- package/src/ui/dashboard-panes/transcript-pane.ts +11 -5
- package/src/ui/dwf-phase-display.ts +3 -20
- package/src/ui/format-helpers.ts +22 -0
- package/src/ui/heartbeat-aggregator.ts +34 -0
- package/src/ui/inline-panel/crew-editor.ts +13 -3
- package/src/ui/inline-panel/index.ts +60 -4
- package/src/ui/keybinding-map.ts +251 -35
- package/src/ui/live-conversation-overlay.ts +180 -47
- package/src/ui/live-run-sidebar.ts +134 -55
- package/src/ui/mascot.ts +32 -16
- package/src/ui/overlays/agent-picker-overlay.ts +81 -26
- package/src/ui/overlays/confirm-overlay.ts +55 -29
- package/src/ui/overlays/help-overlay.ts +108 -53
- package/src/ui/overlays/mailbox-compose-overlay.ts +89 -50
- package/src/ui/overlays/mailbox-detail-overlay.ts +137 -57
- package/src/ui/powerbar-publisher.ts +0 -1
- package/src/ui/rail.ts +333 -0
- package/src/ui/run-dashboard.ts +193 -79
- package/src/ui/run-snapshot-cache.ts +18 -1
- package/src/ui/settings-overlay.ts +81 -39
- package/src/ui/spinner.ts +26 -2
- package/src/ui/terminal-status.ts +7 -1
- package/src/ui/theme-adapter.ts +0 -45
- package/src/ui/theme-discovery.ts +12 -6
- package/src/ui/tool-progress-formatter.ts +128 -9
- package/src/ui/tool-renderers/brief-mode.ts +10 -67
- package/src/ui/tool-renderers/index.ts +374 -523
- package/src/ui/transcript-viewer.ts +30 -12
- package/src/ui/widget/index.ts +32 -52
- package/src/ui/widget/task-list.ts +64 -32
- package/src/ui/widget/widget-formatters.ts +3 -402
- package/src/ui/widget/widget-model.ts +28 -7
- package/src/ui/widget/widget-renderer.ts +201 -128
- package/src/ui/widget/widget-types.ts +0 -2
- package/src/utils/incremental-reader.ts +11 -3
- package/src/utils/paths.ts +94 -12
- package/src/utils/project-markers.ts +40 -0
- package/src/utils/visual.ts +0 -4
- package/src/worktree/worktree-manager.ts +206 -26
- package/workflows/distill.workflow.md +3 -3
- package/workflows/fast-fix.workflow.md +1 -1
- package/workflows/plan-execute.workflow.md +1 -1
- package/workflows/review.workflow.md +1 -1
- package/workflows/strict-fast-fix.workflow.md +1 -1
- package/docs/migration-v0.4-v0.5.md +0 -208
- package/docs/runtime-flow.md +0 -148
- package/src/extension/crew-vibes/figures.ts +0 -22
- package/src/extension/crew-vibes/font-detect.ts +0 -71
- package/src/ui/dynamic-border.ts +0 -35
- package/src/ui/loaders.ts +0 -6
- package/src/ui/overlay-stack.ts +0 -148
|
@@ -82,7 +82,11 @@ const SEEN_STEER_ID_CAP = 1024;
|
|
|
82
82
|
* Factory-shaped so multiple prompt-runtime instances (tests, parallel
|
|
83
83
|
* workers) get independent sets.
|
|
84
84
|
*/
|
|
85
|
-
export function createSeenSteerIdSet(): {
|
|
85
|
+
export function createSeenSteerIdSet(): {
|
|
86
|
+
markOrSkip: (id?: string) => boolean;
|
|
87
|
+
unmark: (id?: string) => void;
|
|
88
|
+
size: () => number;
|
|
89
|
+
} {
|
|
86
90
|
const seen: string[] = [];
|
|
87
91
|
const set = new Set<string>();
|
|
88
92
|
return {
|
|
@@ -98,6 +102,15 @@ export function createSeenSteerIdSet(): { markOrSkip: (id?: string) => boolean;
|
|
|
98
102
|
}
|
|
99
103
|
return true;
|
|
100
104
|
},
|
|
105
|
+
unmark(id?: string): void {
|
|
106
|
+
// F-L2: a steer whose DELIVERY failed must release its dedup mark so
|
|
107
|
+
// the retry (after the poll's offset rewind) is not skipped as a
|
|
108
|
+
// duplicate — that would silently drop the steer instead.
|
|
109
|
+
if (id === undefined) return;
|
|
110
|
+
if (!set.delete(id)) return;
|
|
111
|
+
const idx = seen.indexOf(id);
|
|
112
|
+
if (idx !== -1) seen.splice(idx, 1);
|
|
113
|
+
},
|
|
101
114
|
size: () => set.size,
|
|
102
115
|
};
|
|
103
116
|
}
|
|
@@ -861,42 +874,80 @@ export default function registerPiTeamsPromptRuntime(pi: ExtensionAPI): void {
|
|
|
861
874
|
try {
|
|
862
875
|
const buf = Buffer.alloc(stat.size - lastOffset);
|
|
863
876
|
fs.readSync(fd, buf, 0, buf.length, lastOffset);
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
877
|
+
const offsetBefore = lastOffset;
|
|
878
|
+
let cursor = 0;
|
|
879
|
+
// F-L2 (live battery 2026-09-21, run team_20260921035840): a steer
|
|
880
|
+
// written while the extension is still LOADING makes pi.sendMessage
|
|
881
|
+
// throw ("Extension runtime not initialized" — pi's loader gates
|
|
882
|
+
// action methods until bindExtensions). The old code advanced
|
|
883
|
+
// lastOffset to stat.size BEFORE the line loop and its per-line
|
|
884
|
+
// catch swallowed that throw as "malformed line" — the entry was
|
|
885
|
+
// then silently lost forever (the offset was already past it).
|
|
886
|
+
// Walk the buffer by byte offset and advance only past lines with
|
|
887
|
+
// a terminal verdict (delivered / non-steer / rejected / malformed);
|
|
888
|
+
// a DELIVERY failure rewinds to the failing line's start so the
|
|
889
|
+
// next tick re-reads it (with its dedup id unmarked).
|
|
890
|
+
while (cursor < buf.length) {
|
|
891
|
+
const nl = buf.indexOf(10, cursor);
|
|
892
|
+
const end = nl === -1 ? buf.length : nl;
|
|
893
|
+
const line = buf.subarray(cursor, end).toString("utf8");
|
|
894
|
+
if (line.length === 0) {
|
|
895
|
+
cursor = end + 1;
|
|
896
|
+
continue;
|
|
897
|
+
}
|
|
867
898
|
try {
|
|
868
899
|
const entry = JSON.parse(line) as SteerEntry;
|
|
869
|
-
if (entry.type
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
900
|
+
if (entry.type === "steer") {
|
|
901
|
+
// FIX-S1: cross-channel dedup. The broker writes the
|
|
902
|
+
// same steer to both the mailbox (live fanout via the
|
|
903
|
+
// onSteer callback below) and this JSONL file. A
|
|
904
|
+
// connected worker receives it via the broker first
|
|
905
|
+
// and via this poll second; the seen-id set ensures
|
|
906
|
+
// only the first arrival reaches pi.sendMessage.
|
|
907
|
+
const entryId =
|
|
908
|
+
typeof (entry as { id?: unknown }).id === "string" ? (entry as { id: string }).id : undefined;
|
|
909
|
+
if (seenSteers.markOrSkip(entryId)) {
|
|
910
|
+
// FIX-02: sanitize each steer entry before forwarding
|
|
911
|
+
// to pi.sendMessage. Reject oversized payloads,
|
|
912
|
+
// excessive newlines, and control characters.
|
|
913
|
+
const sanitized = sanitizeSteerMessage(entry);
|
|
914
|
+
if (sanitized.valid && sanitized.message !== undefined) {
|
|
915
|
+
try {
|
|
916
|
+
pi.sendMessage(
|
|
917
|
+
{ customType: "crew-steer", content: sanitized.message, display: false },
|
|
918
|
+
{ deliverAs: "steer" },
|
|
919
|
+
);
|
|
920
|
+
} catch (sendErr) {
|
|
921
|
+
// Delivery failed (e.g. pre-bind notInitialized).
|
|
922
|
+
// Release the dedup mark so the retry after the
|
|
923
|
+
// offset rewind is not skipped as a duplicate, then
|
|
924
|
+
// rewind: cursor stays at this line's start so the
|
|
925
|
+
// next tick re-reads the entry.
|
|
926
|
+
seenSteers.unmark(entryId);
|
|
927
|
+
logInternalError(
|
|
928
|
+
"prompt-runtime.steer-delivery-failed",
|
|
929
|
+
sendErr instanceof Error ? sendErr : new Error(String(sendErr)),
|
|
930
|
+
`line-preview=${line.slice(0, 64)}`,
|
|
931
|
+
"warn",
|
|
932
|
+
);
|
|
933
|
+
break;
|
|
934
|
+
}
|
|
935
|
+
} else {
|
|
936
|
+
logInternalError(
|
|
937
|
+
"prompt-runtime.steer-rejected",
|
|
938
|
+
new Error(sanitized.reason ?? "steer-sanitization-failed"),
|
|
939
|
+
`line-preview=${line.slice(0, 64)}`,
|
|
940
|
+
"warn",
|
|
941
|
+
);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
891
944
|
}
|
|
892
|
-
pi.sendMessage(
|
|
893
|
-
{ customType: "crew-steer", content: sanitized.message, display: false },
|
|
894
|
-
{ deliverAs: "steer" },
|
|
895
|
-
);
|
|
896
945
|
} catch {
|
|
897
|
-
// Malformed line — skip
|
|
946
|
+
// Malformed line — skip (terminal verdict — advance past)
|
|
898
947
|
}
|
|
948
|
+
cursor = end + 1;
|
|
899
949
|
}
|
|
950
|
+
lastOffset = offsetBefore + Math.min(cursor, buf.length);
|
|
900
951
|
} finally {
|
|
901
952
|
try {
|
|
902
953
|
fs.closeSync(fd);
|
|
@@ -107,6 +107,18 @@ export function createWorkerEventsChannel(options: WorkerEventsChannelOptions =
|
|
|
107
107
|
const write = (item: { type: string; data: Record<string, unknown>; droppedSinceLast: number }): boolean => {
|
|
108
108
|
try {
|
|
109
109
|
appendEvent(eventsPath as string, {
|
|
110
|
+
// TeamEvent contract: `time` (ISO string) is REQUIRED — the import
|
|
111
|
+
// bundle validator (run-bundle-schema.ts validateEvent) rejects the
|
|
112
|
+
// whole bundle when any event lacks it, and event readers sort by it.
|
|
113
|
+
// The channel's default appender is a raw O_APPEND writer (NOT
|
|
114
|
+
// event-log.ts appendEvent, which stamps time itself), so the stamp
|
|
115
|
+
// must happen HERE — once, at the single choke point both emit()
|
|
116
|
+
// and emitTerminal() pass through. Found live 2026-09-21: every
|
|
117
|
+
// worker.started/completed of a tmux-surface run was written
|
|
118
|
+
// time-less, and exporting that run produced an un-importable bundle
|
|
119
|
+
// ("events[i].time must be a string"). Uses the injected clock so
|
|
120
|
+
// tests stay deterministic.
|
|
121
|
+
time: new Date(now()).toISOString(),
|
|
110
122
|
type: item.type,
|
|
111
123
|
runId,
|
|
112
124
|
taskId,
|
package/src/runtime/README.md
CHANGED
|
@@ -32,4 +32,4 @@ clusters are extracted into subdirectories; the remaining files stay at the root
|
|
|
32
32
|
- **observability** — `effectiveness.ts`, `agent-observability.ts`, `command-trace.ts`
|
|
33
33
|
- **misc** — `stale-reconciler.ts`, `parent-guard.ts`, `manifest-cache.ts`, `event-stream-bridge.ts`, `policy-engine.ts`, `role-permission.ts`, `workspace-lock.ts`, `workspace-tree.ts`, `session-*.ts`, ...
|
|
34
34
|
|
|
35
|
-
See `docs/COVERAGE-ASSESSMENT-2026-08-01.md` § "Source reorganization" for the reorg history + remaining-cluster plan.
|
|
35
|
+
See `docs/archive/COVERAGE-ASSESSMENT-2026-08-01.md` § "Source reorganization" for the reorg history + remaining-cluster plan.
|
|
@@ -209,6 +209,9 @@ export const BACKGROUND_RUNNER_ENV_ALLOWLIST: string[] = [
|
|
|
209
209
|
"PI_TEAMS_PI_BIN",
|
|
210
210
|
"PI_TEAMS_MOCK_CHILD_PI",
|
|
211
211
|
"PI_CREW_ALLOW_MOCK",
|
|
212
|
+
// Entry guard for the detached runner main() (background-runner.ts skips main()
|
|
213
|
+
// on plain imports — the inline async test seam lazy-imports it).
|
|
214
|
+
"PI_CREW_BACKGROUND_RUNNER_ENTRY",
|
|
212
215
|
// Phase 1.5: worker-thread atomic writer opt-in (RFC 15).
|
|
213
216
|
"PI_CREW_WORKER_ATOMIC_WRITER",
|
|
214
217
|
"PI_TEAMS_WORKER_ATOMIC_WRITER",
|
|
@@ -262,7 +265,87 @@ export function buildBrokerStdinLine(runId: string, socketPath: string, tasks: R
|
|
|
262
265
|
return `${JSON.stringify({ v: 2, runId, socketPath, tasks })}\n`;
|
|
263
266
|
}
|
|
264
267
|
|
|
268
|
+
/**
|
|
269
|
+
* IN-PROCESS ASYNC TEST SEAM (kills two classes of test pain):
|
|
270
|
+
* 1. Windows Defender first-spawn stall — CI ground truth (run 36093514388
|
|
271
|
+
* attempt 3): the detached background-runner spawn stalled >300s with the
|
|
272
|
+
* task left "queued" while the event loop stayed healthy. No second
|
|
273
|
+
* process to come up = no per-file-hash AV scan inside a test deadline.
|
|
274
|
+
* 2. Orphan-tmpdir leak — a detached runner survives the test's rmSync and
|
|
275
|
+
* keeps writing run state into a deleted tree (source of the ~3.4k
|
|
276
|
+
* pi-crew-* debris, 2026-09-26 triage). In-process, the run dies with the
|
|
277
|
+
* test process.
|
|
278
|
+
*
|
|
279
|
+
* Gated by BOTH PI_CREW_TEST_ASYNC_INLINE=1 AND the existing test-fixture
|
|
280
|
+
* gate PI_CREW_ALLOW_MOCK=1 (defense-in-depth: production never sets either).
|
|
281
|
+
* The inline host reuses background-runner's executeBackgroundRun core —
|
|
282
|
+
* same code path as the detached runner, no drift.
|
|
283
|
+
*/
|
|
284
|
+
export function isInlineAsyncTestSeamActive(): boolean {
|
|
285
|
+
return getCrewEnv("PI_CREW_TEST_ASYNC_INLINE") === "1" && getCrewEnv("PI_CREW_ALLOW_MOCK") === "1";
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async function runInlineBackgroundTeamRun(manifest: TeamRunManifest, logPath: string): Promise<SpawnBackgroundTeamRunResult> {
|
|
289
|
+
const log = (message: string): void => {
|
|
290
|
+
try {
|
|
291
|
+
fs.appendFileSync(logPath, `${message}\n`, "utf-8");
|
|
292
|
+
} catch {
|
|
293
|
+
/* best-effort */
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
log(`[pi-crew] inline test seam: executing run ${manifest.runId} in-process (no detached runner) pid=${process.pid}`);
|
|
297
|
+
await appendEventAsync(manifest.eventsPath, {
|
|
298
|
+
type: "async.spawned",
|
|
299
|
+
runId: manifest.runId,
|
|
300
|
+
data: { pid: process.pid, logPath, inline: true },
|
|
301
|
+
});
|
|
302
|
+
// Fire-and-forget — spawn() semantics: the caller must NOT await the run.
|
|
303
|
+
// Catch-all: a late rejection after the test ended must not become an
|
|
304
|
+
// unhandledRejection that fails the whole file.
|
|
305
|
+
void (async () => {
|
|
306
|
+
try {
|
|
307
|
+
// LAZY: defer the core import — background-runner must not load in the
|
|
308
|
+
// normal spawn path (and its module-scope main() is entry-guarded).
|
|
309
|
+
const { executeBackgroundRun } = await import("./background-runner.ts"); // LAZY: defer the core import — background-runner must not load in the normal spawn path
|
|
310
|
+
const { loadRunManifestById } = await import("../state/stores/state-store.ts"); // LAZY: state-store pulls the whole stores chain
|
|
311
|
+
const { withRunLockSync } = await import("../state/coordination/locks.ts"); // LAZY: coordination locks only needed on the inline path
|
|
312
|
+
const loaded = withRunLockSync(manifest, () => loadRunManifestById(manifest.cwd, manifest.runId), { staleMs: 30_000 });
|
|
313
|
+
const effectiveManifest = loaded?.manifest ?? manifest;
|
|
314
|
+
const tasks = loaded?.tasks ?? [];
|
|
315
|
+
await executeBackgroundRun({ manifest: effectiveManifest, tasks }, { log: (message) => log(`[inline] ${message}`) });
|
|
316
|
+
} catch (error) {
|
|
317
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
318
|
+
log(`[pi-crew] inline test seam run ${manifest.runId} FAILED: ${message}`);
|
|
319
|
+
try {
|
|
320
|
+
const { loadRunManifestByIdAsync: reload, updateRunStatus } = await import("../state/stores/state-store.ts"); // LAZY: failure-path imports only
|
|
321
|
+
const fresh = (await reload(manifest.cwd, manifest.runId))?.manifest;
|
|
322
|
+
if (fresh) updateRunStatus(fresh, "failed", `inline test seam: ${message}`);
|
|
323
|
+
else
|
|
324
|
+
await appendEventAsync(manifest.eventsPath, {
|
|
325
|
+
type: "async.failed",
|
|
326
|
+
runId: manifest.runId,
|
|
327
|
+
message: `inline test seam: ${message}`,
|
|
328
|
+
});
|
|
329
|
+
if (fresh)
|
|
330
|
+
await appendEventAsync(fresh.eventsPath, {
|
|
331
|
+
type: "async.failed",
|
|
332
|
+
runId: fresh.runId,
|
|
333
|
+
message: `inline test seam: ${message}`,
|
|
334
|
+
});
|
|
335
|
+
} catch {
|
|
336
|
+
/* best-effort */
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
})();
|
|
340
|
+
return { pid: process.pid, logPath };
|
|
341
|
+
}
|
|
342
|
+
|
|
265
343
|
export async function spawnBackgroundTeamRun(manifest: TeamRunManifest): Promise<SpawnBackgroundTeamRunResult> {
|
|
344
|
+
if (isInlineAsyncTestSeamActive()) {
|
|
345
|
+
const logPath = path.join(manifest.stateRoot, "background.log");
|
|
346
|
+
fs.mkdirSync(manifest.stateRoot, { recursive: true });
|
|
347
|
+
return runInlineBackgroundTeamRun(manifest, logPath);
|
|
348
|
+
}
|
|
266
349
|
// FIX (2026-07-02, perf review F-critical): use packageRoot() instead of
|
|
267
350
|
// import.meta.url-relative path. The previous path.resolve walks
|
|
268
351
|
// <bundleDir>/background-runner.ts, which is correct in src/ but BROKEN
|
|
@@ -291,7 +374,10 @@ export async function spawnBackgroundTeamRun(manifest: TeamRunManifest): Promise
|
|
|
291
374
|
// `npm root -g` probe. No-op when pi-crew and pi are co-located. See
|
|
292
375
|
// src/runtime/peer-dep.ts.
|
|
293
376
|
const peerDepDir = resolvePeerDepDir();
|
|
294
|
-
const childEnv = buildBackgroundRunnerEnv(
|
|
377
|
+
const childEnv = buildBackgroundRunnerEnv({
|
|
378
|
+
...(peerDepDir ? { ...filteredEnv, [PEER_DEP_DIR_ENV]: peerDepDir } : filteredEnv),
|
|
379
|
+
PI_CREW_BACKGROUND_RUNNER_ENTRY: "1",
|
|
380
|
+
});
|
|
295
381
|
|
|
296
382
|
const loader = resolveTypeScriptLoader();
|
|
297
383
|
if (!loader) {
|