pi-crew 0.9.55 → 0.9.57
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 +1 -1
- package/CHANGELOG.md +58 -0
- package/README.md +53 -5
- package/dist/index.mjs +5666 -4370
- package/docs/README.md +54 -0
- package/package.json +12 -13
- package/skills/real-test-pi-crew/SKILL.md +93 -3
- package/src/agents/discover-agents.ts +68 -10
- package/src/benchmark/feedback-loop.ts +1 -1
- package/src/config/config.ts +2 -1
- package/src/config/types.ts +12 -0
- package/src/extension/action-suggestions.ts +16 -63
- package/src/extension/async-notifier.ts +3 -3
- package/src/extension/command-completions.ts +2 -2
- package/src/extension/crew-cleanup.ts +3 -3
- package/src/extension/crew-vibes/footer.ts +4 -2
- package/src/extension/cross-extension-rpc.ts +10 -5
- package/src/extension/knowledge-injection.ts +57 -10
- package/src/extension/register.ts +2 -2
- package/src/extension/registration/artifact-cleanup.ts +41 -3
- package/src/extension/registration/commands.ts +4 -4
- package/src/extension/registration/context-builder.ts +10 -10
- package/src/extension/registration/crash-recovery-cache.ts +3 -3
- package/src/extension/registration/foreground-run-controller.ts +1 -1
- package/src/extension/registration/lazy-configurers.ts +2 -2
- package/src/extension/registration/lifecycle-handlers.ts +12 -12
- package/src/extension/registration/lifecycle.ts +2 -2
- package/src/extension/registration/observability.ts +2 -2
- package/src/extension/registration/registration-types.ts +3 -3
- package/src/extension/registration/runtime-cleanup.ts +1 -1
- package/src/extension/registration/subagent-helpers.ts +2 -2
- package/src/extension/registration/subagent-manager-setup.ts +2 -2
- package/src/extension/registration/subagent-tools.ts +21 -16
- package/src/extension/registration/team-tool.ts +109 -17
- package/src/extension/registration/viewers.ts +3 -4
- package/src/extension/registration/wire-cross-extension.ts +7 -7
- package/src/extension/rpc-hmac.ts +3 -2
- package/src/extension/run-bundle-schema.ts +13 -4
- package/src/extension/run-export.ts +2 -2
- package/src/extension/run-index.ts +2 -3
- package/src/extension/run-maintenance.ts +57 -1
- package/src/extension/team-manager-command.ts +3 -1
- package/src/extension/team-tool/anchor.ts +8 -2
- package/src/extension/team-tool/api.ts +117 -39
- package/src/extension/team-tool/cancel.ts +23 -8
- package/src/extension/team-tool/chain-executor.ts +1 -1
- package/src/extension/team-tool/dispatch/automate.ts +17 -2
- package/src/extension/team-tool/dispatch/control.ts +17 -2
- package/src/extension/team-tool/dispatch/manage.ts +34 -2
- package/src/extension/team-tool/dispatch/run.ts +17 -2
- package/src/extension/team-tool/dispatch/status.ts +37 -5
- package/src/extension/team-tool/doctor.ts +2 -2
- package/src/extension/team-tool/explain.ts +1 -1
- package/src/extension/team-tool/goal-wrap.ts +5 -5
- package/src/extension/team-tool/goal.ts +30 -9
- package/src/extension/team-tool/handle-schedule.ts +37 -19
- package/src/extension/team-tool/inspect.ts +21 -5
- package/src/extension/team-tool/lifecycle-actions.ts +53 -10
- package/src/extension/team-tool/parallel-dispatch.ts +14 -5
- package/src/extension/team-tool/param-error.ts +138 -0
- package/src/extension/team-tool/respond.ts +20 -6
- package/src/extension/team-tool/run.ts +27 -17
- package/src/extension/team-tool/status.ts +17 -6
- package/src/extension/team-tool.ts +26 -19
- package/src/hooks/registry.ts +1 -1
- package/src/i18n.ts +22 -0
- package/src/observability/event-bus.ts +1 -1
- package/src/prompt/prompt-runtime.ts +2 -2
- package/src/runtime/README.md +35 -0
- package/src/runtime/agent-control.ts +1 -1
- package/src/runtime/anchor-manager.ts +0 -2
- package/src/runtime/async-runner.ts +1 -1
- package/src/runtime/attention-events.ts +3 -2
- package/src/runtime/auto-summarize.ts +0 -2
- package/src/runtime/background-runner.ts +103 -30
- package/src/runtime/{crew-broker-child.ts → broker/crew-broker-child.ts} +2 -2
- package/src/runtime/{crew-broker-client.ts → broker/crew-broker-client.ts} +4 -7
- package/src/runtime/{crew-broker.ts → broker/crew-broker.ts} +13 -21
- package/src/runtime/chain-runner.ts +50 -5
- package/src/runtime/{child-pi-constants.ts → child-pi/child-pi-constants.ts} +1 -1
- package/src/runtime/{child-pi-kill.ts → child-pi/child-pi-kill.ts} +3 -3
- package/src/runtime/{child-pi-spawn.ts → child-pi/child-pi-spawn.ts} +28 -12
- package/src/runtime/{child-pi-steering.ts → child-pi/child-pi-steering.ts} +1 -1
- package/src/runtime/{child-pi-streams.ts → child-pi/child-pi-streams.ts} +23 -4
- package/src/runtime/{child-pi-transcript.ts → child-pi/child-pi-transcript.ts} +6 -6
- package/src/runtime/{child-pi.ts → child-pi/child-pi.ts} +47 -78
- package/src/runtime/compaction/compact-stages/bounded-tail.ts +84 -0
- package/src/runtime/{compact-stages → compaction/compact-stages}/index.ts +2 -2
- package/src/runtime/{compaction-summary.ts → compaction/compaction-summary.ts} +3 -3
- package/src/runtime/{tool-output-pruner.ts → compaction/tool-output-pruner.ts} +1 -1
- package/src/runtime/crew-agent-records.ts +48 -2
- package/src/runtime/custom-tools/irc-tool.ts +2 -2
- package/src/runtime/delivery-coordinator.ts +0 -3
- package/src/runtime/diagnostic-export.ts +5 -4
- package/src/runtime/foreground-control.ts +1 -1
- package/src/runtime/foreground-watchdog.ts +1 -1
- package/src/runtime/{adaptive-plan.ts → goal-workflow/adaptive-plan.ts} +8 -8
- package/src/runtime/{dynamic-workflow-context.ts → goal-workflow/dynamic-workflow-context.ts} +16 -16
- package/src/runtime/{dynamic-workflow-runner.ts → goal-workflow/dynamic-workflow-runner.ts} +10 -10
- package/src/runtime/{goal-achievement.ts → goal-workflow/goal-achievement.ts} +3 -3
- package/src/runtime/{goal-evaluator.ts → goal-workflow/goal-evaluator.ts} +8 -8
- package/src/runtime/{goal-loop-runner.ts → goal-workflow/goal-loop-runner.ts} +27 -25
- package/src/runtime/{goal-state-store.ts → goal-workflow/goal-state-store.ts} +8 -8
- package/src/runtime/{plan-templates.ts → goal-workflow/plan-templates.ts} +1 -1
- package/src/runtime/group-join.ts +3 -3
- package/src/runtime/handoff-manager.ts +0 -2
- package/src/runtime/{heartbeat-watcher.ts → heartbeat/heartbeat-watcher.ts} +7 -7
- package/src/runtime/iteration-hooks.ts +1 -1
- package/src/runtime/{live-agent-control.ts → live-session/live-agent-control.ts} +2 -2
- package/src/runtime/{live-agent-manager.ts → live-session/live-agent-manager.ts} +4 -4
- package/src/runtime/{live-session-runtime.ts → live-session/live-session-runtime.ts} +39 -41
- package/src/runtime/manifest-cache.ts +6 -2
- package/src/runtime/{model-fallback.ts → model/model-fallback.ts} +1 -1
- package/src/runtime/{pi-args.ts → model/pi-args.ts} +20 -5
- package/src/runtime/model/provider-extensions.ts +116 -0
- package/src/runtime/{runtime-policy.ts → model/runtime-policy.ts} +2 -2
- package/src/runtime/{runtime-resolver.ts → model/runtime-resolver.ts} +3 -3
- package/src/runtime/{runtime-warmup.ts → model/runtime-warmup.ts} +8 -8
- package/src/runtime/orphan-worker-registry.ts +1 -1
- package/src/runtime/{progress-tracker.ts → output/progress-tracker.ts} +1 -1
- package/src/runtime/{result-extractor.ts → output/result-extractor.ts} +4 -4
- package/src/runtime/{sidechain-output.ts → output/sidechain-output.ts} +2 -2
- package/src/runtime/{stream-preview.ts → output/stream-preview.ts} +1 -1
- package/src/runtime/{streaming-output.ts → output/streaming-output.ts} +2 -2
- package/src/runtime/{tool-progress.ts → output/tool-progress.ts} +1 -1
- package/src/runtime/parent-guard.ts +30 -10
- package/src/runtime/pipeline-runner.ts +2 -2
- package/src/runtime/policy-engine.ts +2 -2
- package/src/runtime/{cancellation.ts → process/cancellation.ts} +1 -1
- package/src/runtime/{post-exit-stdio-guard.ts → process/post-exit-stdio-guard.ts} +1 -1
- package/src/runtime/{process-lifecycle.ts → process/process-lifecycle.ts} +3 -3
- package/src/runtime/{checkpoint.ts → recovery/checkpoint.ts} +4 -4
- package/src/runtime/{crash-recovery.ts → recovery/crash-recovery.ts} +93 -21
- package/src/runtime/{overflow-recovery.ts → recovery/overflow-recovery.ts} +1 -1
- package/src/runtime/{recovery-recipes.ts → recovery/recovery-recipes.ts} +1 -1
- package/src/runtime/{retry-executor.ts → recovery/retry-executor.ts} +2 -2
- package/src/runtime/{retry-runner.ts → recovery/retry-runner.ts} +2 -8
- package/src/runtime/run-tracker.ts +1 -1
- package/src/runtime/run-worker.ts +3 -3
- package/src/runtime/{coalesce-tasks.ts → scheduling/coalesce-tasks.ts} +3 -3
- package/src/runtime/{concurrency.ts → scheduling/concurrency.ts} +9 -3
- package/src/runtime/{parallel-research.ts → scheduling/parallel-research.ts} +4 -2
- package/src/runtime/{run-coalesced-task-group.ts → scheduling/run-coalesced-task-group.ts} +95 -36
- package/src/runtime/{semaphore.ts → scheduling/semaphore.ts} +4 -1
- package/src/runtime/{task-graph-scheduler.ts → scheduling/task-graph-scheduler.ts} +1 -1
- package/src/runtime/session-resources.ts +1 -1
- package/src/runtime/settings-store.ts +1 -1
- package/src/runtime/single-agent-compose.ts +1 -0
- package/src/runtime/stale-reconciler.ts +1 -1
- package/src/runtime/subagent-manager.ts +1 -1
- package/src/runtime/supervisor-contact.ts +28 -13
- package/src/runtime/task-output-context.ts +7 -5
- package/src/runtime/task-runner/child-executor.ts +67 -31
- package/src/runtime/task-runner/live-executor.ts +8 -8
- package/src/runtime/task-runner/post-execution.ts +12 -12
- package/src/runtime/task-runner/pre-execution.ts +6 -6
- package/src/runtime/task-runner/progress.ts +1 -1
- package/src/runtime/task-runner/run-projection.ts +1 -1
- package/src/runtime/task-runner/scaffold-executor.ts +1 -1
- package/src/runtime/task-runner/state-helpers.ts +2 -2
- package/src/runtime/task-runner.ts +4 -4
- package/src/runtime/team-runner.ts +441 -175
- package/src/runtime/{completion-guard.ts → verification/completion-guard.ts} +1 -1
- package/src/runtime/{green-contract.ts → verification/green-contract.ts} +1 -1
- package/src/runtime/{post-checks.ts → verification/post-checks.ts} +4 -4
- package/src/runtime/{verification-gates.ts → verification/verification-gates.ts} +5 -5
- package/src/runtime/{verification-integrity.ts → verification/verification-integrity.ts} +1 -1
- package/src/schema/config-schema.ts +1 -0
- package/src/schema/team-tool-schema.ts +149 -185
- package/src/state/README.md +44 -0
- package/src/state/atomic-write.ts +24 -3
- package/src/state/{locks.ts → coordination/locks.ts} +118 -38
- package/src/state/{mailbox.ts → coordination/mailbox.ts} +14 -8
- package/src/state/{schedule.ts → coordination/schedule.ts} +3 -3
- package/src/state/{task-claims.ts → coordination/task-claims.ts} +1 -1
- package/src/state/decision-ledger.ts +1 -1
- package/src/state/{event-log-rotation.ts → event-log/event-log-rotation.ts} +189 -31
- package/src/state/{event-log.ts → event-log/event-log.ts} +77 -39
- package/src/state/{jsonl-writer.ts → event-log/jsonl-writer.ts} +14 -6
- package/src/state/hook-instinct-bridge.ts +2 -2
- package/src/state/{active-run-registry.ts → stores/active-run-registry.ts} +7 -7
- package/src/state/{artifact-store.ts → stores/artifact-store.ts} +62 -4
- package/src/state/{blob-store.ts → stores/blob-store.ts} +3 -3
- package/src/state/{health-store.ts → stores/health-store.ts} +3 -3
- package/src/state/{instinct-store.ts → stores/instinct-store.ts} +3 -3
- package/src/state/{observation-store.ts → stores/observation-store.ts} +2 -2
- package/src/state/{run-cache.ts → stores/run-cache.ts} +4 -4
- package/src/state/{run-graph.ts → stores/run-graph.ts} +4 -4
- package/src/state/{run-metrics.ts → stores/run-metrics.ts} +3 -3
- package/src/state/{state-store.ts → stores/state-store.ts} +254 -21
- package/src/state/types.ts +27 -3
- package/src/ui/component.ts +55 -0
- package/src/ui/crew-select-list.ts +3 -1
- package/src/ui/dashboard-panes/agents-pane.ts +1 -1
- package/src/ui/dwf-phase-display.ts +1 -1
- package/src/ui/heartbeat-aggregator.ts +1 -1
- package/src/ui/keybinding-map.ts +179 -6
- package/src/ui/layout-primitives.ts +9 -5
- package/src/ui/live-conversation-overlay.ts +1 -1
- package/src/ui/live-run-sidebar.ts +1 -1
- package/src/ui/overlay-stack.ts +148 -0
- package/src/ui/overlays/agent-picker-overlay.ts +1 -1
- package/src/ui/overlays/mailbox-compose-overlay.ts +1 -1
- package/src/ui/overlays/mailbox-detail-overlay.ts +2 -2
- package/src/ui/powerbar-publisher.ts +1 -1
- package/src/ui/render-scheduler.ts +1 -1
- package/src/ui/run-action-dispatcher.ts +3 -3
- package/src/ui/run-dashboard.ts +5 -33
- package/src/ui/run-event-bus.ts +3 -3
- package/src/ui/run-snapshot-cache.ts +37 -18
- package/src/ui/settings-overlay.ts +13 -21
- package/src/ui/snapshot-types.ts +1 -1
- package/src/ui/status-colors.ts +0 -2
- package/src/ui/terminal-status.ts +1 -1
- package/src/ui/theme-adapter.ts +33 -13
- package/src/ui/tool-renderers/index.ts +8 -0
- package/src/ui/transcript-viewer.ts +37 -13
- package/src/ui/widget/index.ts +32 -9
- package/src/ui/widget/widget-formatters.ts +62 -4
- package/src/ui/widget/widget-model.ts +2 -2
- package/src/ui/widget/widget-renderer.ts +1 -1
- package/src/utils/conflict-detect.ts +58 -5
- package/src/utils/ndjson.ts +2 -2
- package/src/utils/redaction.ts +39 -0
- package/src/utils/socket-path.ts +1 -1
- package/src/workflows/workflow-config.ts +1 -1
- package/src/worktree/cleanup.ts +1 -1
- package/src/worktree/worktree-manager.ts +111 -47
- package/workflows/chain.workflow.md +2 -2
- package/dist/build-meta.json +0 -16807
- package/index.bundle.ts +0 -25
- package/scripts/bench-check.mjs +0 -96
- package/scripts/bench-cold-start.mjs +0 -216
- package/scripts/build-bundle.mjs +0 -92
- package/scripts/check-all-skills.ts +0 -294
- package/scripts/check-bundle-size.mjs +0 -38
- package/scripts/check-bundle-staleness.mjs +0 -97
- package/scripts/check-conflict-markers.mjs +0 -91
- package/scripts/check-lazy-imports.mjs +0 -28
- package/scripts/dev-runner.mjs +0 -106
- package/scripts/profile-startup.mjs +0 -125
- package/scripts/release-smoke.mjs +0 -74
- package/scripts/run-bench.mjs +0 -47
- package/scripts/run-real-chain.ts +0 -41
- package/scripts/test-issue-29-crash.ts +0 -111
- package/scripts/test-issue-29-e2e.ts +0 -183
- package/scripts/test-issue-29-real-runtime.ts +0 -330
- package/scripts/test-issue-29-real-tasks.ts +0 -387
- package/scripts/test-issue-29-team-tool.ts +0 -105
- package/scripts/test-runner.mjs +0 -84
- package/scripts/verify-flicker-fix.ts +0 -109
- package/scripts/verify-skill.ts +0 -550
- package/scripts/watch-bundle.mjs +0 -259
- package/scripts/watchdog-harness.ts +0 -119
- package/src/adapters/claude-adapter.ts +0 -23
- package/src/adapters/codex-adapter.ts +0 -21
- package/src/adapters/cursor-adapter.ts +0 -17
- package/src/adapters/export-util.ts +0 -143
- package/src/adapters/index.ts +0 -15
- package/src/adapters/registry.ts +0 -18
- package/src/adapters/types.ts +0 -23
- package/src/runtime/code-summary.ts +0 -292
- package/src/runtime/cross-extension-rpc.ts +0 -143
- package/src/runtime/hidden-handoff.ts +0 -407
- package/src/runtime/prose-compressor.ts +0 -162
- package/src/subagents/async-entry.ts +0 -1
- package/src/subagents/index.ts +0 -3
- package/src/subagents/live/control.ts +0 -1
- package/src/subagents/live/manager.ts +0 -1
- package/src/subagents/live/realtime.ts +0 -1
- package/src/subagents/live/session-runtime.ts +0 -1
- package/src/subagents/manager.ts +0 -1
- package/src/subagents/spawn.ts +0 -1
- package/test-integration-check.ts +0 -118
- /package/src/runtime/{broker-issuer.ts → broker/broker-issuer.ts} +0 -0
- /package/src/runtime/{crew-broker-tokens.ts → broker/crew-broker-tokens.ts} +0 -0
- /package/src/runtime/{child-pi-pool.ts → child-pi/child-pi-pool.ts} +0 -0
- /package/src/runtime/{compact-pipeline.ts → compaction/compact-pipeline.ts} +0 -0
- /package/src/runtime/{compact-stages → compaction/compact-stages}/ansi-strip-stage.ts +0 -0
- /package/src/runtime/{compact-stages → compaction/compact-stages}/blank-collapse-stage.ts +0 -0
- /package/src/runtime/{compact-stages → compaction/compact-stages}/deduplicate-stage.ts +0 -0
- /package/src/runtime/{compact-stages → compaction/compact-stages}/head-snap-stage.ts +0 -0
- /package/src/runtime/{compact-stages → compaction/compact-stages}/tail-capture-stage.ts +0 -0
- /package/src/runtime/{compact-stages → compaction/compact-stages}/truncation-stage.ts +0 -0
- /package/src/runtime/{important-line-classifier.ts → compaction/important-line-classifier.ts} +0 -0
- /package/src/runtime/{heartbeat-gradient.ts → heartbeat/heartbeat-gradient.ts} +0 -0
- /package/src/runtime/{worker-heartbeat.ts → heartbeat/worker-heartbeat.ts} +0 -0
- /package/src/runtime/{worker-startup.ts → heartbeat/worker-startup.ts} +0 -0
- /package/src/runtime/{intercom-bridge.ts → live-session/intercom-bridge.ts} +0 -0
- /package/src/runtime/{live-control-realtime.ts → live-session/live-control-realtime.ts} +0 -0
- /package/src/runtime/{live-extension-bridge.ts → live-session/live-extension-bridge.ts} +0 -0
- /package/src/runtime/{live-irc.ts → live-session/live-irc.ts} +0 -0
- /package/src/runtime/{live-session-health.ts → live-session/live-session-health.ts} +0 -0
- /package/src/runtime/{model-resolver.ts → model/model-resolver.ts} +0 -0
- /package/src/runtime/{model-scope.ts → model/model-scope.ts} +0 -0
- /package/src/runtime/{output-validator.ts → output/output-validator.ts} +0 -0
- /package/src/runtime/{pi-json-output.ts → output/pi-json-output.ts} +0 -0
- /package/src/runtime/{progress-event-coalescer.ts → output/progress-event-coalescer.ts} +0 -0
- /package/src/runtime/{cancellation-token.ts → process/cancellation-token.ts} +0 -0
- /package/src/runtime/{zombie-scanner.ts → process/zombie-scanner.ts} +0 -0
- /package/src/runtime/{crash-classification.ts → recovery/crash-classification.ts} +0 -0
- /package/src/runtime/{batch-barrier.ts → scheduling/batch-barrier.ts} +0 -0
- /package/src/runtime/{global-worker-cap.ts → scheduling/global-worker-cap.ts} +0 -0
- /package/src/runtime/{parallel-utils.ts → scheduling/parallel-utils.ts} +0 -0
- /package/src/runtime/{scheduler.ts → scheduling/scheduler.ts} +0 -0
- /package/src/runtime/{task-graph.ts → scheduling/task-graph.ts} +0 -0
- /package/src/runtime/{verification-worktree.ts → verification/verification-worktree.ts} +0 -0
- /package/src/state/{event-reconstructor.ts → event-log/event-reconstructor.ts} +0 -0
- /package/src/state/{worker-atomic-writer.ts → event-log/worker-atomic-writer.ts} +0 -0
|
@@ -3,9 +3,9 @@ import * as path from "node:path";
|
|
|
3
3
|
import { allAgents, discoverAgents } from "../agents/discover-agents.ts";
|
|
4
4
|
import { loadConfig } from "../config/config.ts";
|
|
5
5
|
import { atomicWriteFile } from "../state/atomic-write.ts";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { createRunPaths, loadRunManifestById, saveRunManifestAsync, updateRunStatus } from "../state/state-store.ts";
|
|
6
|
+
import { withRunLockSync } from "../state/coordination/locks.ts";
|
|
7
|
+
import { appendEvent, appendEventFireAndForget } from "../state/event-log/event-log.ts";
|
|
8
|
+
import { createRunPaths, loadRunManifestById, saveRunManifestAsync, updateRunStatus } from "../state/stores/state-store.ts";
|
|
9
9
|
import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
10
10
|
import { allTeams, discoverTeams } from "../teams/discover-teams.ts";
|
|
11
11
|
import { errorMessage } from "../utils/guards.ts";
|
|
@@ -35,7 +35,7 @@ async function executeTeamRun(...args: Parameters<typeof ExecuteTeamRunFn>): Pro
|
|
|
35
35
|
// FIX (split-scope install): prime the ESM peer dep BEFORE team-runner is
|
|
36
36
|
// imported, so its transitive skill-instructions.ts can read getAgentDir()
|
|
37
37
|
// from the primed cache instead of crashing on `Cannot find module`.
|
|
38
|
-
await primePeerDep().catch(() =>
|
|
38
|
+
await primePeerDep().catch(() => undefined);
|
|
39
39
|
// LAZY: avoid pulling team-runner into background-runner at module load time.
|
|
40
40
|
const mod = await import("./team-runner.ts");
|
|
41
41
|
_cachedExecuteTeamRun = mod.executeTeamRun;
|
|
@@ -45,12 +45,12 @@ async function executeTeamRun(...args: Parameters<typeof ExecuteTeamRunFn>): Pro
|
|
|
45
45
|
|
|
46
46
|
import { logInternalError } from "../utils/internal-error.ts";
|
|
47
47
|
import { writeAsyncStartMarker } from "./async-marker.ts";
|
|
48
|
-
import { terminateActiveChildPiProcesses } from "./child-pi.ts";
|
|
48
|
+
import { terminateActiveChildPiProcesses } from "./child-pi/child-pi.ts";
|
|
49
49
|
import { directTeamAndWorkflowFromRun } from "./direct-run.ts";
|
|
50
|
+
import { resolveCrewRuntime, runtimeResolutionState } from "./model/runtime-resolver.ts";
|
|
50
51
|
import { unregisterWorker } from "./orphan-worker-registry.ts";
|
|
51
|
-
import { expandParallelResearchWorkflow } from "./parallel-research.ts";
|
|
52
52
|
import { startParentGuard, stopParentGuard } from "./parent-guard.ts";
|
|
53
|
-
import {
|
|
53
|
+
import { expandParallelResearchWorkflow } from "./scheduling/parallel-research.ts";
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
56
|
* Debug logger gated behind PI_CREW_DEBUG env var. Writes to background.log
|
|
@@ -110,7 +110,47 @@ function argValue(name: string): string | undefined {
|
|
|
110
110
|
return process.argv[index + 1];
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Fire-and-forget event log for signal handlers. Extracted to module level
|
|
115
|
+
* (from inside main()) so the exported SIGINT handler installer (test seam)
|
|
116
|
+
* and the inline signal-handler loop inside main() can both use it.
|
|
117
|
+
* Pure function of its arguments — no closure captures from main().
|
|
118
|
+
*/
|
|
119
|
+
function signalLog(sig: string, eventsPath: string): void {
|
|
120
|
+
const runId = argValue("--run-id");
|
|
121
|
+
if (runId && eventsPath) {
|
|
122
|
+
appendEventFireAndForget(eventsPath, {
|
|
123
|
+
type: "async.failed",
|
|
124
|
+
runId,
|
|
125
|
+
message: `Background runner received ${sig} — exiting.`,
|
|
126
|
+
data: { signal: sig, pid: process.pid },
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* RT-2 SIGINT handler installer — exported as a test seam so integration tests
|
|
133
|
+
* can exercise the REAL handler logic (process.exitCode = 130, NOT
|
|
134
|
+
* process.exit(130)) without re-implementing it in a harness copy.
|
|
135
|
+
*
|
|
136
|
+
* Behavior is IDENTICAL to the previous inline handler in main() — pure
|
|
137
|
+
* extraction, zero logic change.
|
|
138
|
+
*/
|
|
139
|
+
export function installBackgroundRunnerSigintHandler(abortController: AbortController, eventsPath: string): void {
|
|
140
|
+
process.on("SIGINT", () => {
|
|
141
|
+
signalLog("SIGINT", eventsPath);
|
|
142
|
+
// RT-2 FIX: Mirror the CORE-7 pattern at the interrupt guard (:146-151).
|
|
143
|
+
// Do NOT call process.exit(130) — it bypasses the finally/runCleanup block
|
|
144
|
+
// in main(), orphaning child-pi workers (they have no parent-guard, see
|
|
145
|
+
// RT-19). Setting exitCode lets the event loop drain naturally so main()'s
|
|
146
|
+
// finally block runs terminateActiveChildPiProcesses + unregisterWorker.
|
|
147
|
+
abortController.abort();
|
|
148
|
+
stopParentGuard();
|
|
149
|
+
process.exitCode = 130;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function startInterruptGuard(
|
|
114
154
|
manifest: { runId: string; stateRoot: string; eventsPath: string },
|
|
115
155
|
abortController: AbortController,
|
|
116
156
|
stopParentGuard: () => void,
|
|
@@ -119,6 +159,13 @@ function startInterruptGuard(
|
|
|
119
159
|
// FIX: Made configurable via PI_CREW_INTERRUPT_GUARD_INTERVAL_MS env var.
|
|
120
160
|
// Default 250ms balances fast SIGINT response against filesystem overhead.
|
|
121
161
|
const interruptGuardInterval = Number(process.env.PI_CREW_INTERRUPT_GUARD_INTERVAL_MS) || 250;
|
|
162
|
+
// RT-4 FIX: Module-local gate so the interrupt body runs only once per
|
|
163
|
+
// interrupt request. Without this, the guard re-fires every
|
|
164
|
+
// interruptGuardInterval (250ms) — each tick does a full
|
|
165
|
+
// terminateActiveChildPiProcesses sweep + sync appendEvent = ~4×/s steady
|
|
166
|
+
// state. The ack write stops the re-fire; this gate is defense-in-depth if
|
|
167
|
+
// the ack write fails (e.g. transient fs error).
|
|
168
|
+
let interruptHandled = false;
|
|
122
169
|
const interval = setInterval(() => {
|
|
123
170
|
try {
|
|
124
171
|
if (!fs.existsSync(controlPath)) return;
|
|
@@ -127,6 +174,25 @@ function startInterruptGuard(
|
|
|
127
174
|
};
|
|
128
175
|
const last = parsed.requests?.at(-1);
|
|
129
176
|
if (last?.type === "interrupt" && last?.acknowledged !== true) {
|
|
177
|
+
// RT-4 FIX: Gate ensures the interrupt body runs only once even if
|
|
178
|
+
// the synchronous ack write below fails.
|
|
179
|
+
if (interruptHandled) return;
|
|
180
|
+
interruptHandled = true;
|
|
181
|
+
|
|
182
|
+
// RT-4 FIX: Write acknowledged:true back to foreground-control.json
|
|
183
|
+
// SYNCHRONOUSLY. This stops the guard from re-firing on the next tick
|
|
184
|
+
// (250ms). Must be sync because this is a setInterval polling callback
|
|
185
|
+
// — we cannot await in a polling callback.
|
|
186
|
+
try {
|
|
187
|
+
const reqs = parsed.requests ?? [];
|
|
188
|
+
if (reqs.length > 0) {
|
|
189
|
+
reqs[reqs.length - 1].acknowledged = true;
|
|
190
|
+
atomicWriteFile(controlPath, JSON.stringify(parsed, null, 2));
|
|
191
|
+
}
|
|
192
|
+
} catch {
|
|
193
|
+
/* best-effort ack — interruptHandled gate prevents re-fire */
|
|
194
|
+
}
|
|
195
|
+
|
|
130
196
|
appendEvent(manifest.eventsPath, {
|
|
131
197
|
type: "async.interrupt_detected",
|
|
132
198
|
runId: manifest.runId,
|
|
@@ -335,19 +401,8 @@ async function main(): Promise<void> {
|
|
|
335
401
|
|
|
336
402
|
// Scrub macOS malloc vars BEFORE anything else — must be clean for all child processes
|
|
337
403
|
scrubProcessEnv();
|
|
338
|
-
//
|
|
339
|
-
//
|
|
340
|
-
const signalLog = (sig: string, eventsPath: string): void => {
|
|
341
|
-
const runId = argValue("--run-id");
|
|
342
|
-
if (runId && eventsPath) {
|
|
343
|
-
appendEvent(eventsPath, {
|
|
344
|
-
type: "async.failed",
|
|
345
|
-
runId,
|
|
346
|
-
message: `Background runner received ${sig} — exiting.`,
|
|
347
|
-
data: { signal: sig, pid: process.pid },
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
};
|
|
404
|
+
// signalLog is now defined at module level (shared by the exported SIGINT
|
|
405
|
+
// handler installer and the inline signal-handler loop below).
|
|
351
406
|
// BUG #17 FIX: Compute exitCodePath at module load time using args,
|
|
352
407
|
// NOT by referencing `manifest` (declared inside main() and not in scope at module load).
|
|
353
408
|
const exitCodePath = ((): string | undefined => {
|
|
@@ -422,10 +477,7 @@ async function main(): Promise<void> {
|
|
|
422
477
|
// Trigger graceful shutdown via abort signal so finally block runs
|
|
423
478
|
abortController.abort();
|
|
424
479
|
});
|
|
425
|
-
|
|
426
|
-
signalLog("SIGINT", manifest.eventsPath);
|
|
427
|
-
process.exit(130);
|
|
428
|
-
});
|
|
480
|
+
installBackgroundRunnerSigintHandler(abortController, manifest.eventsPath);
|
|
429
481
|
// BUG #17: Catch ALL signals to identify what kills the background runner
|
|
430
482
|
for (const sig of [
|
|
431
483
|
"SIGHUP",
|
|
@@ -527,7 +579,7 @@ async function main(): Promise<void> {
|
|
|
527
579
|
// compilation of team-runner.ts, the event loop must not drain prematurely.
|
|
528
580
|
// The interval is always cleared in the finally block, so the delay is
|
|
529
581
|
// bounded by the 5s interval. The event loop exit is deferred at most 5s.
|
|
530
|
-
const keepAlive = setInterval(() =>
|
|
582
|
+
const keepAlive = setInterval(() => undefined, 5000);
|
|
531
583
|
|
|
532
584
|
// WATCHDOG (anti-zombie): if the run exceeds MAX_BACKGROUND_RUN_MS without
|
|
533
585
|
// completing, abort it and force-exit. Without this, a hung team run
|
|
@@ -598,9 +650,9 @@ async function main(): Promise<void> {
|
|
|
598
650
|
debugLog(`[background-runner] short-circuiting ${manifest.runKind} (synthetic team/workflow)`);
|
|
599
651
|
if (manifest.runKind === "goal-loop") {
|
|
600
652
|
// LAZY: defer dynamic import of ./goal-loop-runner.ts to its call site.
|
|
601
|
-
const { runGoalLoop } = await import("./goal-loop-runner.ts");
|
|
653
|
+
const { runGoalLoop } = await import("./goal-workflow/goal-loop-runner.ts");
|
|
602
654
|
// LAZY: defer dynamic import of ./goal-state-store.ts to its call site.
|
|
603
|
-
const { GoalStore } = await import("./goal-state-store.ts");
|
|
655
|
+
const { GoalStore } = await import("./goal-workflow/goal-state-store.ts");
|
|
604
656
|
// LAZY: defer dynamic import of ../agents/discover-agents.ts to its call site.
|
|
605
657
|
const { discoverAgents, allAgents } = await import("../agents/discover-agents.ts");
|
|
606
658
|
const store = new GoalStore(manifest.cwd);
|
|
@@ -639,7 +691,7 @@ async function main(): Promise<void> {
|
|
|
639
691
|
};
|
|
640
692
|
} else {
|
|
641
693
|
// LAZY: defer dynamic import of ./dynamic-workflow-runner.ts to its call site.
|
|
642
|
-
const { runDynamicWorkflow } = await import("./dynamic-workflow-runner.ts");
|
|
694
|
+
const { runDynamicWorkflow } = await import("./goal-workflow/dynamic-workflow-runner.ts");
|
|
643
695
|
// LAZY: defer dynamic import of ../workflows/discover-workflows.ts to its call site.
|
|
644
696
|
const { allWorkflows, discoverWorkflows } = await import("../workflows/discover-workflows.ts");
|
|
645
697
|
const wf = allWorkflows(discoverWorkflows(manifest.cwd)).find((w) => w.name === manifest.workflow);
|
|
@@ -774,7 +826,7 @@ async function main(): Promise<void> {
|
|
|
774
826
|
const manifestToUse = loaded?.manifest ?? manifest;
|
|
775
827
|
if (manifestToUse) {
|
|
776
828
|
// LAZY: live-agent-manager only needed on failure cleanup path; avoid module load at hot path.
|
|
777
|
-
const { terminateLiveAgentsForRun } = await import("./live-agent-manager.ts");
|
|
829
|
+
const { terminateLiveAgentsForRun } = await import("./live-session/live-agent-manager.ts");
|
|
778
830
|
void terminateLiveAgentsForRun(manifestToUse.runId, "failed", appendEvent, manifestToUse.eventsPath).catch((error) =>
|
|
779
831
|
logInternalError("background-runner.terminate", error, `runId=${manifestToUse.runId}`),
|
|
780
832
|
);
|
|
@@ -832,6 +884,27 @@ try {
|
|
|
832
884
|
// called process.exit(1) directly, bypassing the finally block and leaving
|
|
833
885
|
// orphaned child processes.
|
|
834
886
|
exitDueToRejection = true;
|
|
887
|
+
// RT-3 FIX: Startup failures (lock-fail, missing manifest, pre-try throws)
|
|
888
|
+
// previously wrote NO event and left exitCode at 0. The run stayed 'queued'
|
|
889
|
+
// until the stale reconciler reaped it. Now write async.failed so the
|
|
890
|
+
// notifier/foreground detects the failure immediately, and set exitCode=1
|
|
891
|
+
// so the process exits non-zero.
|
|
892
|
+
try {
|
|
893
|
+
const mCwd = argValue("--cwd");
|
|
894
|
+
const mRunId = argValue("--run-id");
|
|
895
|
+
if (mCwd && mRunId) {
|
|
896
|
+
const mEventsPath = createRunPaths(mCwd, mRunId).eventsPath;
|
|
897
|
+
appendEvent(mEventsPath, {
|
|
898
|
+
type: "async.failed",
|
|
899
|
+
runId: mRunId,
|
|
900
|
+
message: errorMessage(err),
|
|
901
|
+
data: { stack: err instanceof Error ? err.stack : undefined },
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
} catch {
|
|
905
|
+
/* best-effort — don't let event-write failure mask the original error */
|
|
906
|
+
}
|
|
907
|
+
process.exitCode = 1;
|
|
835
908
|
// FIX: Call stopParentGuard directly here as a safety net in case the
|
|
836
909
|
// finally block (which calls runCleanup→stopParentGuard) does not complete.
|
|
837
910
|
// This ensures the parent guard is stopped in ALL exit paths: normal
|
|
@@ -41,7 +41,7 @@ export interface StartChildBrokerClientOptions {
|
|
|
41
41
|
|
|
42
42
|
const NOOP_HANDLE: ChildBrokerClientHandle = {
|
|
43
43
|
active: false,
|
|
44
|
-
close: async () =>
|
|
44
|
+
close: async () => undefined,
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
export function startChildBrokerClient(options: StartChildBrokerClientOptions = {}): ChildBrokerClientHandle {
|
|
@@ -75,7 +75,7 @@ export function startChildBrokerClient(options: StartChildBrokerClientOptions =
|
|
|
75
75
|
// Establish the persistent connection so the broker can push events. This
|
|
76
76
|
// is fire-and-forget: on any failure the client becomes sticky-fallback and
|
|
77
77
|
// the file-poll steering path remains fully functional.
|
|
78
|
-
void client.reconnect().catch(() =>
|
|
78
|
+
void client.reconnect().catch(() => undefined);
|
|
79
79
|
|
|
80
80
|
return {
|
|
81
81
|
get active() {
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
import { randomUUID } from "node:crypto";
|
|
25
25
|
import * as net from "node:net";
|
|
26
26
|
|
|
27
|
-
import { logInternalError } from "
|
|
28
|
-
import { BrokerError, encodeBrokerFrame, NdjsonDecoder } from "
|
|
29
|
-
import { redactSecretString } from "
|
|
27
|
+
import { logInternalError } from "../../utils/internal-error.ts";
|
|
28
|
+
import { BrokerError, encodeBrokerFrame, NdjsonDecoder } from "../../utils/ndjson.ts";
|
|
29
|
+
import { redactSecretString } from "../../utils/redaction.ts";
|
|
30
30
|
|
|
31
31
|
/** Shape of an unsolicited event frame pushed by the broker (mailbox.message,
|
|
32
32
|
* team.event, etc.). */
|
|
@@ -95,7 +95,6 @@ export class CrewBrokerClient {
|
|
|
95
95
|
private socket: net.Socket | null = null;
|
|
96
96
|
private decoder: NdjsonDecoder | null = null;
|
|
97
97
|
private readonly pending = new Map<string, PendingRequest>();
|
|
98
|
-
private attempts = 0;
|
|
99
98
|
/** Listeners attached to the current socket; kept for explicit close(). */
|
|
100
99
|
private readonly socketListeners: Array<{
|
|
101
100
|
event: string;
|
|
@@ -234,7 +233,6 @@ export class CrewBrokerClient {
|
|
|
234
233
|
async reconnect(): Promise<boolean> {
|
|
235
234
|
// Close the current socket if any.
|
|
236
235
|
this.teardownSocket();
|
|
237
|
-
this.attempts = 0;
|
|
238
236
|
this._mode = "unstarted";
|
|
239
237
|
const res = await this.request("ping", null);
|
|
240
238
|
return res.ok;
|
|
@@ -309,7 +307,6 @@ export class CrewBrokerClient {
|
|
|
309
307
|
if (this.closed) {
|
|
310
308
|
return { ok: false, fallback: true, errorCode: "closed" };
|
|
311
309
|
}
|
|
312
|
-
this.attempts = attempt + 1;
|
|
313
310
|
const result = await this.attemptHello(netModule);
|
|
314
311
|
if (result.ok) {
|
|
315
312
|
// Belt-and-suspenders: a successful hello can still race
|
|
@@ -460,7 +457,7 @@ export class CrewBrokerClient {
|
|
|
460
457
|
return;
|
|
461
458
|
}
|
|
462
459
|
// Hello ack: must include `result.ok === true` and matching id.
|
|
463
|
-
if (frame.id
|
|
460
|
+
if (frame.id?.startsWith("hello-")) {
|
|
464
461
|
if (frame.error) {
|
|
465
462
|
const code = (frame.error as { code?: string }).code ?? "auth";
|
|
466
463
|
finish({ ok: false, fallback: true, errorCode: code });
|
|
@@ -23,8 +23,6 @@
|
|
|
23
23
|
|
|
24
24
|
import * as fsp from "node:fs/promises";
|
|
25
25
|
import * as net from "node:net";
|
|
26
|
-
|
|
27
|
-
import { readEventsCursor } from "../state/event-log.ts";
|
|
28
26
|
import {
|
|
29
27
|
appendMailboxMessageAsync,
|
|
30
28
|
type MailboxMessage,
|
|
@@ -32,14 +30,15 @@ import {
|
|
|
32
30
|
type MailboxMessagePriority,
|
|
33
31
|
readMailbox,
|
|
34
32
|
registerMailboxAppendObserver,
|
|
35
|
-
} from "
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
33
|
+
} from "../../state/coordination/mailbox.ts";
|
|
34
|
+
import { readEventsCursor } from "../../state/event-log/event-log.ts";
|
|
35
|
+
import { loadRunManifestById } from "../../state/stores/state-store.ts";
|
|
36
|
+
import { runEventBus } from "../../ui/run-event-bus.ts";
|
|
37
|
+
import { logInternalError } from "../../utils/internal-error.ts";
|
|
38
|
+
import { BrokerError, encodeBrokerFrame, MAX_BROKER_FRAME_BYTES, NdjsonDecoder } from "../../utils/ndjson.ts";
|
|
39
|
+
import { redactSecretString } from "../../utils/redaction.ts";
|
|
40
|
+
import { resolveRealContainedPath } from "../../utils/safe-paths.ts";
|
|
41
|
+
import { getBrokerSocketPath, prepareBrokerSocketDir, removeStaleBrokerSocket } from "../../utils/socket-path.ts";
|
|
43
42
|
import { BrokerTokenRegistry } from "./crew-broker-tokens.ts";
|
|
44
43
|
|
|
45
44
|
/** Protocol version negotiated at `hello` time. Bump on breaking change. */
|
|
@@ -103,7 +102,6 @@ export class CrewBroker {
|
|
|
103
102
|
private server: net.Server | null = null;
|
|
104
103
|
private resolvedSocketPath: string | null = null;
|
|
105
104
|
private stopped = false;
|
|
106
|
-
private starting = false;
|
|
107
105
|
private startingPromise: Promise<void> | null = null;
|
|
108
106
|
private readonly connections = new Set<ServerConnection>();
|
|
109
107
|
/** Connections indexed by runId for live message fanout (Phase 1.3). */
|
|
@@ -186,16 +184,10 @@ export class CrewBroker {
|
|
|
186
184
|
}
|
|
187
185
|
if (this.server) return Promise.resolve();
|
|
188
186
|
if (this.startingPromise) return this.startingPromise;
|
|
189
|
-
this.
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
})
|
|
194
|
-
.catch((err) => {
|
|
195
|
-
this.starting = false;
|
|
196
|
-
this.startingPromise = null;
|
|
197
|
-
throw err;
|
|
198
|
-
});
|
|
187
|
+
this.startingPromise = this.doStart().catch((err) => {
|
|
188
|
+
this.startingPromise = null;
|
|
189
|
+
throw err;
|
|
190
|
+
});
|
|
199
191
|
return this.startingPromise;
|
|
200
192
|
}
|
|
201
193
|
|
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
* - Supports per-step overrides for model, skill, thinking
|
|
7
7
|
* - Accumulates handoffs between steps
|
|
8
8
|
* - Executes steps sequentially with context passing
|
|
9
|
-
*
|
|
10
|
-
* @see docs/pi-boomerang-integration-plan.md
|
|
11
9
|
*/
|
|
12
10
|
|
|
13
11
|
import { errorMessage } from "../utils/guards.ts";
|
|
@@ -148,7 +146,7 @@ export class ChainRunner {
|
|
|
148
146
|
}
|
|
149
147
|
}
|
|
150
148
|
|
|
151
|
-
const stepStrings =
|
|
149
|
+
const stepStrings = this.splitChainSteps(chainString).map((s) => s.trim());
|
|
152
150
|
|
|
153
151
|
const steps: ChainStep[] = stepStrings.map((step, index) => {
|
|
154
152
|
return this.parseStep(step, index);
|
|
@@ -236,8 +234,8 @@ export class ChainRunner {
|
|
|
236
234
|
|
|
237
235
|
// Emit progress event if eventsPath provided
|
|
238
236
|
if (eventsPath) {
|
|
239
|
-
// LAZY: defer dynamic import of ../state/event-log.ts to its call site.
|
|
240
|
-
const { appendEventAsync } = await import("../state/event-log.ts");
|
|
237
|
+
// LAZY: defer dynamic import of ../state/event-log/event-log.ts to its call site.
|
|
238
|
+
const { appendEventAsync } = await import("../state/event-log/event-log.ts");
|
|
241
239
|
await appendEventAsync(eventsPath, {
|
|
242
240
|
type: "chain.step_completed",
|
|
243
241
|
runId: "chain",
|
|
@@ -277,6 +275,53 @@ export class ChainRunner {
|
|
|
277
275
|
};
|
|
278
276
|
}
|
|
279
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Split a chain string on top-level `->` arrows, ignoring arrows inside
|
|
280
|
+
* quotes. Prevents inline goals containing `->` (e.g. "Change A -> B")
|
|
281
|
+
* from being incorrectly split into separate steps.
|
|
282
|
+
*/
|
|
283
|
+
private splitChainSteps(chainString: string): string[] {
|
|
284
|
+
const parts: string[] = [];
|
|
285
|
+
let current = "";
|
|
286
|
+
let inQuote = false;
|
|
287
|
+
let quoteChar = "";
|
|
288
|
+
|
|
289
|
+
for (let i = 0; i < chainString.length; i++) {
|
|
290
|
+
const ch = chainString[i]!;
|
|
291
|
+
|
|
292
|
+
// Handle escape sequences inside quotes (e.g. \" or \')
|
|
293
|
+
if (inQuote && ch === "\\" && i + 1 < chainString.length) {
|
|
294
|
+
current += ch + (chainString[i + 1] ?? "");
|
|
295
|
+
i++;
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Toggle quote state on unescaped quote characters
|
|
300
|
+
if (ch === '"' || ch === "'") {
|
|
301
|
+
if (!inQuote) {
|
|
302
|
+
inQuote = true;
|
|
303
|
+
quoteChar = ch;
|
|
304
|
+
} else if (ch === quoteChar) {
|
|
305
|
+
inQuote = false;
|
|
306
|
+
}
|
|
307
|
+
current += ch;
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Split on -> only when not inside quotes
|
|
312
|
+
if (!inQuote && ch === "-" && chainString[i + 1] === ">") {
|
|
313
|
+
parts.push(current);
|
|
314
|
+
current = "";
|
|
315
|
+
i++; // skip the '>'
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
current += ch;
|
|
320
|
+
}
|
|
321
|
+
parts.push(current);
|
|
322
|
+
return parts;
|
|
323
|
+
}
|
|
324
|
+
|
|
280
325
|
/**
|
|
281
326
|
* Parse a single step from the chain string.
|
|
282
327
|
* Includes type safety checks for ChainStep parsing (H3).
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* and makes the timing budget configurable from one place.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { DEFAULT_CHILD_PI } from "
|
|
11
|
+
import { DEFAULT_CHILD_PI } from "../../config/defaults.ts";
|
|
12
12
|
|
|
13
13
|
/** Post-exit window during which stdio is guarded against late writes. */
|
|
14
14
|
export const POST_EXIT_STDIO_GUARD_MS = DEFAULT_CHILD_PI.postExitStdioGuardMs;
|
|
@@ -17,10 +17,10 @@
|
|
|
17
17
|
|
|
18
18
|
import type { ChildProcess } from "node:child_process";
|
|
19
19
|
import { spawn } from "node:child_process";
|
|
20
|
-
import { DEFAULT_CHILD_PI } from "
|
|
21
|
-
import { logInternalError } from "
|
|
20
|
+
import { DEFAULT_CHILD_PI } from "../../config/defaults.ts";
|
|
21
|
+
import { logInternalError } from "../../utils/internal-error.ts";
|
|
22
|
+
import { TailCaptureStage } from "../compaction/compact-stages/tail-capture-stage.ts";
|
|
22
23
|
import { HARD_KILL_MS } from "./child-pi-constants.ts";
|
|
23
|
-
import { TailCaptureStage } from "./compact-stages/tail-capture-stage.ts";
|
|
24
24
|
|
|
25
25
|
const MAX_CAPTURE_BYTES = DEFAULT_CHILD_PI.maxCaptureBytes;
|
|
26
26
|
|
|
@@ -19,12 +19,12 @@
|
|
|
19
19
|
import type { SpawnOptions } from "node:child_process";
|
|
20
20
|
import * as fs from "node:fs";
|
|
21
21
|
import * as path from "node:path";
|
|
22
|
-
import { WINDOWS_ESSENTIAL_ENV_VARS } from "
|
|
23
|
-
import { buildScopedAllowList, sanitizeEnvSecrets } from "
|
|
24
|
-
import { logInternalError } from "
|
|
22
|
+
import { WINDOWS_ESSENTIAL_ENV_VARS } from "../../utils/env-allowlist.ts";
|
|
23
|
+
import { buildScopedAllowList, sanitizeEnvSecrets } from "../../utils/env-filter.ts";
|
|
24
|
+
import { logInternalError } from "../../utils/internal-error.ts";
|
|
25
|
+
import { buildPiWorkerArgs } from "../model/pi-args.ts";
|
|
26
|
+
import { getPiSpawnCommand } from "../pi-spawn.ts";
|
|
25
27
|
import type { ChildPiRunInput, ChildPiRunResult } from "./child-pi.ts";
|
|
26
|
-
import { buildPiWorkerArgs } from "./pi-args.ts";
|
|
27
|
-
import { getPiSpawnCommand } from "./pi-spawn.ts";
|
|
28
28
|
|
|
29
29
|
// ── Env allowlist (base set always passed to children) ──────────────────
|
|
30
30
|
// Provider API keys are injected dynamically via buildScopedAllowList() only
|
|
@@ -68,7 +68,8 @@ export const BASE_ALLOWLIST: string[] = [
|
|
|
68
68
|
* 1. Validate cwd (realpath + isDirectory) — fall back to lexical path on ENOENT.
|
|
69
69
|
* 2. Filter env vars to the allowlist (model-aware provider key scoping).
|
|
70
70
|
* 3. Validate NODE_PATH against safe prefixes (/opt, /lib, /usr, /home).
|
|
71
|
-
* 4. Add PI_CREW_PARENT_PID for the child-side parent-guard
|
|
71
|
+
* 4. Add PI_CREW_PARENT_PID for the child-side parent-guard (currently UNUSED —
|
|
72
|
+
* see note at the PI_CREW_PARENT_PID assignment below).
|
|
72
73
|
*/
|
|
73
74
|
export function buildChildPiSpawnOptions(cwd: string, env: NodeJS.ProcessEnv, model?: string): SpawnOptions {
|
|
74
75
|
// SECURITY FIX (Issue #1): Validate cwd before passing to spawn.
|
|
@@ -131,17 +132,32 @@ export function buildChildPiSpawnOptions(cwd: string, env: NodeJS.ProcessEnv, mo
|
|
|
131
132
|
|
|
132
133
|
return {
|
|
133
134
|
cwd: validatedCwd,
|
|
134
|
-
env: {
|
|
135
|
+
env: {
|
|
136
|
+
...filteredEnv,
|
|
137
|
+
// PI_CREW_PARENT_PID is set so that child pi workers could theoretically
|
|
138
|
+
// run a parent-guard (parent-guard.ts). However, as of RT-19/RT-2, this
|
|
139
|
+
// env var has ZERO consumers: child workers are the external `pi` binary
|
|
140
|
+
// (@earendil-works/pi-coding-agent) which does NOT read
|
|
141
|
+
// PI_CREW_PARENT_PID or call startParentGuard (grep of Pi dist = 0 matches).
|
|
142
|
+
// The deeper fix (wiring startParentGuard into the pi worker entry point)
|
|
143
|
+
// is DEFERRED because workers are an external binary pi-crew doesn't control.
|
|
144
|
+
//
|
|
145
|
+
// Orphan-mitigation for this gap relies on:
|
|
146
|
+
// 1. The RT-2 SIGINT fix in background-runner.ts (abort + exitCode pattern
|
|
147
|
+
// lets the finally/runCleanup block terminate child-pi processes).
|
|
148
|
+
// 2. The reactive zombie-scanner.ts sweep (finds workers whose
|
|
149
|
+
// PI_CREW_PARENT_PID points at a dead PID).
|
|
150
|
+
PI_CREW_PARENT_PID: String(process.pid),
|
|
151
|
+
},
|
|
135
152
|
stdio: ["ignore", "pipe", "pipe"], // stdin=ignore: child doesn't wait for input; task comes via CLI args
|
|
136
153
|
detached: process.platform !== "win32",
|
|
137
154
|
setsid: true,
|
|
138
155
|
// NOTE: setsid creates a new session; the child process becomes the session leader
|
|
139
156
|
// and its parent becomes that session leader (still the team-runner in the same
|
|
140
|
-
// process group). PI_CREW_PARENT_PID is set before spawn using process.pid (team-runner)
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
// sees its direct parent (team-runner) as alive. This is correct for the parent-guard model.
|
|
157
|
+
// process group). PI_CREW_PARENT_PID is set before spawn using process.pid (team-runner),
|
|
158
|
+
// but see the comment above — child pi workers do NOT actually consume it. The
|
|
159
|
+
// parent-guard model would check direct parent liveness via process.kill(pid, 0),
|
|
160
|
+
// but this is only implemented in background-runner.ts, not in the worker binary.
|
|
145
161
|
windowsHide: true,
|
|
146
162
|
} as SpawnOptions;
|
|
147
163
|
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import * as fs from "node:fs";
|
|
16
|
-
import { logInternalError } from "
|
|
16
|
+
import { logInternalError } from "../../utils/internal-error.ts";
|
|
17
17
|
import { killProcessTree } from "./child-pi-kill.ts";
|
|
18
18
|
|
|
19
19
|
/** Action emitted by the controller when a `turn_end` event is processed. */
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
* child-pi-constants.ts (MAX_*_CHARS, MAX_LINE_BUFFER_BYTES).
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { logInternalError } from "
|
|
11
|
+
import { logInternalError } from "../../utils/internal-error.ts";
|
|
12
|
+
import { extractText } from "../output/pi-json-output.ts";
|
|
12
13
|
import type { ChildPiRunInput } from "./child-pi.ts";
|
|
13
14
|
import { MAX_ASSISTANT_TEXT_CHARS, MAX_LINE_BUFFER_BYTES, MAX_TOOL_INPUT_CHARS, MAX_TOOL_RESULT_CHARS } from "./child-pi-constants.ts";
|
|
14
15
|
import { appendTranscript, compactString, compactValue, flushPendingTranscriptWrites } from "./child-pi-transcript.ts";
|
|
15
|
-
import { extractText } from "./pi-json-output.ts";
|
|
16
16
|
|
|
17
17
|
function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
18
18
|
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
@@ -87,6 +87,18 @@ function compactChildPiEvent(event: unknown): unknown | undefined {
|
|
|
87
87
|
stopReason: record.stopReason,
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
|
+
// P2-25: pass supervisor_contact events through with their full payload. The
|
|
91
|
+
// generic fallthrough below strips everything but `type`, which made the
|
|
92
|
+
// feature dead — the contact never reached recordSupervisorContact.
|
|
93
|
+
if (record.type === "supervisor_contact" || record.type === "crew_supervisor_contact") {
|
|
94
|
+
return {
|
|
95
|
+
type: record.type,
|
|
96
|
+
taskId: record.taskId,
|
|
97
|
+
reason: record.reason,
|
|
98
|
+
message: record.message,
|
|
99
|
+
data: record.data,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
90
102
|
return record.type ? { type: record.type } : undefined;
|
|
91
103
|
}
|
|
92
104
|
|
|
@@ -187,9 +199,16 @@ export class ChildPiLineObserver {
|
|
|
187
199
|
new Error(`Line buffer exceeded ${MAX_LINE_BUFFER_BYTES} bytes; force-flushing`),
|
|
188
200
|
`bufferLen=${this.buffer.length}`,
|
|
189
201
|
);
|
|
190
|
-
|
|
202
|
+
// RT-9: split on newlines first so each complete \n-delimited JSON event
|
|
203
|
+
// is emitted as its own line. Previously the ENTIRE buffer was flushed
|
|
204
|
+
// as one "line", merging all events → JSON.parse failure → usage/turn/
|
|
205
|
+
// transcript events lost. Now only the trailing partial (no terminating
|
|
206
|
+
// newline) is force-flushed; complete lines keep per-line identity. When
|
|
207
|
+
// there are no newlines at all (the original RT-F8 single-huge-line case)
|
|
208
|
+
// split yields a single element and behavior is unchanged.
|
|
209
|
+
const overflowLines = this.buffer.split(/\r?\n/);
|
|
191
210
|
this.buffer = "";
|
|
192
|
-
this.emitLine(line);
|
|
211
|
+
for (const line of overflowLines) this.emitLine(line);
|
|
193
212
|
return;
|
|
194
213
|
}
|
|
195
214
|
const lines = this.buffer.split(/\r?\n/);
|
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import * as fs from "node:fs";
|
|
22
|
-
import { DEFAULT_CHILD_PI } from "
|
|
23
|
-
import { logInternalError } from "
|
|
24
|
-
import { redactJsonLine } from "
|
|
25
|
-
import { resolveRealContainedPath } from "
|
|
22
|
+
import { DEFAULT_CHILD_PI } from "../../config/defaults.ts";
|
|
23
|
+
import { logInternalError } from "../../utils/internal-error.ts";
|
|
24
|
+
import { redactJsonLine } from "../../utils/redaction.ts";
|
|
25
|
+
import { resolveRealContainedPath } from "../../utils/safe-paths.ts";
|
|
26
|
+
import { applyCompactPipeline } from "../compaction/compact-pipeline.ts";
|
|
27
|
+
import { TruncationStage } from "../compaction/compact-stages/truncation-stage.ts";
|
|
26
28
|
import type { ChildPiRunInput } from "./child-pi.ts";
|
|
27
|
-
import { applyCompactPipeline } from "./compact-pipeline.ts";
|
|
28
|
-
import { TruncationStage } from "./compact-stages/truncation-stage.ts";
|
|
29
29
|
|
|
30
30
|
// ── Transcript batch buffer (OPT-PHASE3) ────────────────────────────────
|
|
31
31
|
// Instead of open/write/close per line (3 syscalls × N), accumulate lines
|