pi-crew 0.9.68 → 0.10.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 +222 -0
- package/NOTICE.md +21 -0
- package/README.md +44 -2
- package/agents/analyst.md +1 -1
- package/agents/cold-verifier.md +3 -1
- package/agents/critic.md +1 -1
- package/agents/executor.md +1 -1
- package/agents/explorer.md +1 -1
- package/agents/planner.md +1 -1
- package/agents/reviewer.md +1 -1
- package/agents/security-reviewer.md +1 -1
- package/agents/test-engineer.md +1 -1
- package/agents/verifier.md +1 -1
- package/agents/writer.md +1 -1
- package/dist/index.mjs +68113 -60774
- package/docs/README.md +2 -0
- package/docs/actions-reference.md +31 -0
- package/docs/commands-reference.md +17 -6
- package/docs/resource-formats.md +13 -0
- package/package.json +4 -2
- package/schema.json +503 -91
- package/scripts/resource-sampler.mjs +36 -2
- package/skills/requirements-to-task-packet/SKILL.md +26 -0
- package/skills/widget-rendering/SKILL.md +7 -7
- package/src/agents/agent-config.ts +2 -1
- package/src/agents/discover-agents.ts +23 -14
- package/src/config/config-merge.ts +183 -0
- package/src/config/config-validation.ts +687 -0
- package/src/config/config.ts +22 -864
- package/src/config/defaults.ts +43 -2
- package/src/config/drift-detector.ts +1 -1
- package/src/config/env-vars.ts +691 -0
- package/src/config/role-tools.ts +11 -9
- package/src/config/sanitize-project-config.ts +172 -0
- package/src/config/types.ts +49 -1
- package/src/extension/async-notifier.ts +25 -2
- package/src/extension/crew-cleanup.ts +13 -0
- package/src/extension/crew-vibes/config.ts +2 -1
- package/src/extension/crew-vibes/footer.ts +19 -0
- package/src/extension/crew-vibes/index.ts +11 -1
- package/src/extension/plan-orchestrate.ts +132 -0
- package/src/extension/register.ts +8 -0
- package/src/extension/registration/command-registration.ts +1 -0
- package/src/extension/registration/commands/dashboard.ts +158 -0
- package/src/extension/registration/commands/index.ts +35 -0
- package/src/extension/registration/commands/manage.ts +303 -0
- package/src/extension/registration/commands/run.ts +228 -0
- package/src/extension/registration/commands/shared.ts +639 -0
- package/src/extension/registration/commands/status.ts +60 -0
- package/src/extension/registration/commands.ts +13 -1224
- package/src/extension/registration/foreground-run-controller.ts +10 -2
- package/src/extension/registration/lifecycle-handlers.ts +178 -17
- package/src/extension/registration/runtime-cleanup.ts +23 -5
- package/src/extension/registration/subagent-tools.ts +218 -9
- package/src/extension/registration/team-tool.ts +5 -1
- package/src/extension/registration/ui.ts +5 -4
- package/src/extension/rpc-hmac.ts +5 -3
- package/src/extension/team-tool/api/heartbeat.ts +47 -10
- package/src/extension/team-tool/api/plan-approval.ts +9 -0
- package/src/extension/team-tool/api/task-claims.ts +109 -40
- package/src/extension/team-tool/cancel.ts +84 -50
- package/src/extension/team-tool/dispatch/index.ts +1 -0
- package/src/extension/team-tool/dispatch/run.ts +4 -1
- package/src/extension/team-tool/doctor.ts +103 -1
- package/src/extension/team-tool/orchestrate.ts +66 -1
- package/src/extension/team-tool/plans.ts +192 -0
- package/src/extension/team-tool/respond.ts +197 -65
- package/src/extension/team-tool/run-deadline.ts +35 -3
- package/src/extension/team-tool/run-intent.ts +63 -0
- package/src/extension/team-tool/run.ts +74 -20
- package/src/extension/team-tool/status.ts +84 -26
- package/src/extension/team-tool.ts +11 -2
- package/src/hooks/registry.ts +1 -6
- package/src/i18n.ts +9 -0
- package/src/prompt/prompt-runtime.ts +521 -2
- package/src/prompt/worker-events-channel.ts +173 -0
- package/src/runtime/README.md +8 -8
- package/src/runtime/async-runner.ts +7 -3
- package/src/runtime/background-runner.ts +42 -14
- package/src/runtime/broker/broker-issuer.ts +9 -2
- package/src/runtime/broker/crew-broker-tokens.ts +43 -6
- package/src/runtime/broker/crew-broker.ts +838 -10
- package/src/runtime/broker/wait-status-cache.ts +157 -0
- package/src/runtime/budget-enforcement.ts +281 -0
- package/src/runtime/child-pi/child-pi-constants.ts +8 -0
- package/src/runtime/child-pi/child-pi-spawn.ts +60 -14
- package/src/runtime/child-pi/child-pi-streams.ts +21 -1
- package/src/runtime/child-pi/child-pi-timers.ts +324 -0
- package/src/runtime/child-pi/child-pi.ts +97 -201
- package/src/runtime/child-pi/mock-fixtures.ts +16 -2
- package/src/runtime/crew-agent-records.ts +259 -14
- package/src/runtime/delegate-spawn.ts +148 -0
- package/src/runtime/detached-run-results.ts +90 -0
- package/src/runtime/deterministic-ast.ts +2 -1
- package/src/runtime/dispatch-batch.ts +945 -0
- package/src/runtime/finalize-run.ts +557 -0
- package/src/runtime/goal-workflow/adaptive-plan.ts +116 -15
- package/src/runtime/goal-workflow/dynamic-workflow-runner.ts +8 -3
- package/src/runtime/goal-workflow/goal-state-store.ts +1 -1
- package/src/runtime/group-join.ts +11 -125
- package/src/runtime/live-session/live-session-runtime.ts +26 -1
- package/src/runtime/merge-gate.ts +32 -10
- package/src/runtime/merge-loop.ts +130 -0
- package/src/runtime/model/model-budget-summary.ts +53 -0
- package/src/runtime/model/model-fallback.ts +36 -2
- package/src/runtime/model/pi-args.ts +10 -0
- package/src/runtime/model/provider-extensions.ts +10 -0
- package/src/runtime/orphan-worker-registry.ts +1 -1
- package/src/runtime/output/output-validator.ts +45 -0
- package/src/runtime/parent-guard.ts +3 -1
- package/src/runtime/peer-dep.ts +2 -1
- package/src/runtime/per-write-validator.ts +0 -5
- package/src/runtime/pi-spawn.ts +61 -15
- package/src/runtime/plan-approval.ts +125 -0
- package/src/runtime/plan-replan.ts +151 -0
- package/src/runtime/process-status.ts +16 -1
- package/src/runtime/recovery/checkpoint.ts +0 -18
- package/src/runtime/recovery/crash-recovery.ts +111 -46
- package/src/runtime/run-tracker.ts +77 -10
- package/src/runtime/scheduler-context.ts +98 -0
- package/src/runtime/scheduling/coalesce-tasks.ts +5 -0
- package/src/runtime/scheduling/global-worker-cap.ts +2 -1
- package/src/runtime/scheduling/nested-slots.ts +70 -0
- package/src/runtime/scheduling/run-coalesced-task-group.ts +64 -13
- package/src/runtime/scheduling/task-graph-scheduler.ts +0 -10
- package/src/runtime/settings-store.ts +219 -0
- package/src/runtime/spawn-policy.ts +217 -0
- package/src/runtime/stale-reconciler.ts +87 -6
- package/src/runtime/subagent-manager.ts +25 -1
- package/src/runtime/task-output-context.ts +230 -9
- package/src/runtime/task-packet.ts +23 -1
- package/src/runtime/task-runner/child-executor.ts +106 -7
- package/src/runtime/task-runner/post-execution.ts +125 -1
- package/src/runtime/task-runner/pre-execution.ts +39 -1
- package/src/runtime/task-runner/prompt-builder.ts +51 -1
- package/src/runtime/task-runner/retrieval-orchestrator.ts +72 -18
- package/src/runtime/task-runner/spec-evidence.ts +403 -0
- package/src/runtime/task-runner/state-helpers.ts +26 -24
- package/src/runtime/task-runner.ts +11 -0
- package/src/runtime/team-runner.ts +132 -1673
- package/src/runtime/verification/spec-sandbox.ts +255 -0
- package/src/runtime/verification/verification-gates.ts +3 -2
- package/src/runtime/verification/verification-worktree.ts +2 -1
- package/src/runtime/workflow-phase-advance.ts +100 -0
- package/src/runtime/workspace-tree.ts +9 -0
- package/src/schema/config-schema.ts +66 -26
- package/src/schema/sensitive-config-paths.ts +64 -0
- package/src/schema/team-tool-schema.ts +13 -3
- package/src/state/README.md +4 -10
- package/src/state/atomic-write.ts +20 -3
- package/src/state/contracts.ts +38 -0
- package/src/state/coordination/mailbox.ts +12 -2
- package/src/state/event-log/cursor.ts +223 -0
- package/src/state/event-log/event-log-rotation.ts +12 -4
- package/src/state/event-log/event-log.ts +152 -369
- package/src/state/event-log/sequence-cache.ts +373 -0
- package/src/state/event-log/worker-atomic-writer.ts +2 -1
- package/src/state/stores/active-run-registry.ts +3 -2
- package/src/state/stores/manifest-io.ts +237 -0
- package/src/state/stores/ownership-map.ts +162 -0
- package/src/state/stores/plan-store.ts +241 -0
- package/src/state/stores/run-cache.ts +0 -90
- package/src/state/stores/spec-store.ts +189 -0
- package/src/state/stores/state-store.ts +139 -232
- package/src/state/types.ts +199 -0
- package/src/ui/dashboard-panes/plan-pane.ts +136 -0
- package/src/ui/dashboard-panes/progress-pane.ts +6 -0
- package/src/ui/dashboard-panes/transcript-pane.ts +31 -0
- package/src/ui/dock-footer.ts +49 -0
- package/src/ui/heartbeat-aggregator.ts +9 -1
- package/src/ui/inline-panel/agent-pane.ts +375 -0
- package/src/ui/inline-panel/agent-transcript.ts +338 -0
- package/src/ui/inline-panel/agent-view-overlay.ts +225 -0
- package/src/ui/inline-panel/crew-editor.ts +192 -0
- package/src/ui/inline-panel/index.ts +290 -0
- package/src/ui/inline-panel/panel-rows.ts +37 -0
- package/src/ui/inline-panel/panel-selection.ts +157 -0
- package/src/ui/inline-panel/panel-store.ts +111 -0
- package/src/ui/inline-panel/view-session-store.ts +36 -0
- package/src/ui/keybinding-map.ts +54 -13
- package/src/ui/pi-ui-compat.ts +9 -0
- package/src/ui/powerbar-publisher.ts +52 -1
- package/src/ui/run-dashboard.ts +31 -5
- package/src/ui/run-snapshot-cache.ts +57 -30
- package/src/ui/snapshot-types.ts +6 -1
- package/src/ui/widget/index.ts +176 -22
- package/src/ui/widget/task-list.ts +198 -0
- package/src/ui/widget/widget-formatters.ts +240 -4
- package/src/ui/widget/widget-renderer.ts +243 -38
- package/src/ui/widget/widget-types.ts +11 -0
- package/src/utils/child-process-shield.ts +106 -0
- package/src/utils/file-coalescer.ts +0 -4
- package/src/utils/fs-errno.ts +66 -0
- package/src/utils/fs-watch.ts +1 -1
- package/src/utils/internal-error.ts +3 -1
- package/src/utils/paths.ts +11 -3
- package/src/utils/redaction.ts +7 -0
- package/src/utils/safe-abort.ts +45 -0
- package/src/utils/task-name-generator.ts +1 -8
- package/src/workflows/discover-workflows.ts +20 -2
- package/src/workflows/validate-workflow.ts +7 -1
- package/src/workflows/workflow-config.ts +17 -0
- package/src/workflows/workflow-serializer.ts +3 -0
- package/src/worktree/worktree-manager.ts +22 -0
- package/workflows/default.workflow.md +36 -26
- package/workflows/strict-fast-fix.workflow.md +26 -0
- package/src/agents/agent-search.ts +0 -98
- package/src/benchmark/benchmark-runner.ts +0 -313
- package/src/benchmark/feedback-loop.ts +0 -73
- package/src/config/resilient-parser.ts +0 -117
- package/src/extension/crew-vibes/cat-frames.ts +0 -18
- package/src/extension/result-watcher.ts +0 -139
- package/src/observability/exporters/prometheus-exporter.ts +0 -54
- package/src/observability/metric-retention.ts +0 -64
- package/src/runtime/compaction/compaction-summary.ts +0 -278
- package/src/runtime/errors/crew-errors.ts +0 -162
- package/src/runtime/live-session/intercom-bridge.ts +0 -187
- package/src/runtime/loop-gates.ts +0 -128
- package/src/runtime/metric-parser.ts +0 -36
- package/src/runtime/output/stream-preview.ts +0 -184
- package/src/runtime/output/tool-progress.ts +0 -278
- package/src/runtime/phase-tracker.ts +0 -385
- package/src/runtime/pipeline-runner.ts +0 -523
- package/src/runtime/process/process-lifecycle.ts +0 -491
- package/src/runtime/recovery/retry-runner.ts +0 -330
- package/src/runtime/run-drift.ts +0 -219
- package/src/runtime/task-quality.ts +0 -199
- package/src/runtime/task-runner/run-projection.ts +0 -128
- package/src/runtime/verification/post-checks.ts +0 -142
- package/src/state/coordination/schedule.ts +0 -166
- package/src/state/event-log/jsonl-writer.ts +0 -115
- package/src/state/hook-instinct-bridge.ts +0 -94
- package/src/state/hook-integrations.ts +0 -51
- package/src/state/session-state-map.ts +0 -51
- package/src/state/stores/blob-store.ts +0 -308
- package/src/state/stores/instinct-store.ts +0 -275
- package/src/state/stores/observation-store.ts +0 -176
- package/src/state/tiered-eval.ts +0 -480
- package/src/state/types-eval.ts +0 -58
- package/src/tools/safe-bash-extension.ts +0 -54
- package/src/tools/safe-bash.ts +0 -505
- package/src/ui/agent-management-overlay.ts +0 -160
- package/src/ui/crew-footer.ts +0 -102
- package/src/ui/crew-select-list.ts +0 -114
- package/src/ui/dashboard-panes/capability-pane.ts +0 -77
- package/src/ui/transcript-entries.ts +0 -256
- package/src/utils/conflict-detect.ts +0 -721
- package/src/utils/fingerprint.ts +0 -180
- package/src/utils/gh-protocol.ts +0 -556
- package/src/utils/project-detector.ts +0 -160
- package/src/utils/sse-parser.ts +0 -131
- package/src/workflows/cost-estimator.ts +0 -34
- package/src/workflows/intermediate-store.ts +0 -166
|
@@ -20,6 +20,7 @@ import { setWorkingIndicator } from "../../ui/pi-ui-compat.ts";
|
|
|
20
20
|
import { requestPowerbarUpdate } from "../../ui/powerbar-publisher.ts";
|
|
21
21
|
import { updateCrewWidget } from "../../ui/widget/index.ts";
|
|
22
22
|
import { logInternalError } from "../../utils/internal-error.ts";
|
|
23
|
+
import { safeAbort } from "../../utils/safe-abort.ts";
|
|
23
24
|
import type { RegistrationContext } from "./registration-types.ts";
|
|
24
25
|
|
|
25
26
|
/**
|
|
@@ -36,7 +37,10 @@ export function installForegroundRunController(pi: ExtensionAPI, ctx: Registrati
|
|
|
36
37
|
ctx.abortForegroundRun = (runId) => {
|
|
37
38
|
const controller = ctx.foregroundTeamRunControllers.get(runId);
|
|
38
39
|
if (!controller) return false;
|
|
39
|
-
|
|
40
|
+
// safe-abort: cancel can race a completed run whose spawned children
|
|
41
|
+
// already exited — Node's child_process signal listener would throw
|
|
42
|
+
// AbortError out of abort() and kill the host (uncaughtException).
|
|
43
|
+
safeAbort(controller, "fg-run.abort-by-run-id");
|
|
40
44
|
return true;
|
|
41
45
|
};
|
|
42
46
|
}
|
|
@@ -142,7 +146,11 @@ function startForegroundRunImpl(
|
|
|
142
146
|
.catch((error) => logInternalError("register.foreground-watchdog", error, `runId=${runId}`));
|
|
143
147
|
}
|
|
144
148
|
const ownerCurrent = ctx.isContextCurrent(extensionCtx, ownerGeneration);
|
|
145
|
-
|
|
149
|
+
// The owner ctx can go stale while the run is in flight (e.g. the
|
|
150
|
+
// user opened an agent session view via /crew-view and the run
|
|
151
|
+
// finishes there): the `hasUI` getter THROWS on a stale ctx, so
|
|
152
|
+
// it must only be touched for the still-current owner.
|
|
153
|
+
if (ownerCurrent && extensionCtx.hasUI) {
|
|
146
154
|
try {
|
|
147
155
|
setWorkingIndicator(extensionCtx);
|
|
148
156
|
extensionCtx.ui.setWorkingMessage();
|
|
@@ -19,13 +19,16 @@ import * as path from "node:path";
|
|
|
19
19
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
20
20
|
import { loadConfig } from "../../config/config.ts";
|
|
21
21
|
import { DEFAULT_UI } from "../../config/defaults.ts";
|
|
22
|
+
import { getCrewEnv } from "../../config/env-vars.ts";
|
|
22
23
|
import { pruneFinishedRuns, pruneUserLevelRuns } from "../../extension/run-maintenance.ts";
|
|
23
24
|
import { type BrokerSpawnCredentials, setActiveBrokerIssuer } from "../../runtime/broker/broker-issuer.ts";
|
|
24
25
|
import { CrewBroker } from "../../runtime/broker/crew-broker.ts";
|
|
25
26
|
import { terminateActiveChildPiProcesses } from "../../runtime/child-pi/child-pi.ts";
|
|
27
|
+
import { forgetDetachedRun, hasDetachedRuns, peekFinishedDetachedRunResults } from "../../runtime/detached-run-results.ts";
|
|
26
28
|
import { listLiveAgents } from "../../runtime/live-session/live-agent-manager.ts";
|
|
27
29
|
import type { createManifestCache } from "../../runtime/manifest-cache.ts";
|
|
28
|
-
import {
|
|
30
|
+
import { configuredModelInfosFromPiConfig } from "../../runtime/model/model-fallback.ts";
|
|
31
|
+
import { cleanupLegacyOrphanTempDirs, cleanupOrphanTempDirs, currentCrewDepth, resolveCrewMaxDepth } from "../../runtime/model/pi-args.ts";
|
|
29
32
|
import { clearProviderQuotaCache, noteProviderResponse } from "../../runtime/model/provider-quota.ts";
|
|
30
33
|
import { noteSessionModel, noteSessionThinking, resolveProviderForResponse } from "../../runtime/model/session-model.ts";
|
|
31
34
|
import { cleanupOrphanWorkers } from "../../runtime/orphan-worker-registry.ts";
|
|
@@ -33,11 +36,13 @@ import { reconcileAllStaleRuns } from "../../runtime/recovery/crash-recovery.ts"
|
|
|
33
36
|
import { CrewScheduler, type ScheduledJob } from "../../runtime/scheduling/scheduler.ts";
|
|
34
37
|
import { tryRegisterSessionCleanup } from "../../runtime/session-resources.ts";
|
|
35
38
|
import { createSessionSnapshot } from "../../runtime/session-snapshot.ts";
|
|
36
|
-
import {
|
|
39
|
+
import { applyCrewSettingsTiersToConfig, loadCrewSettingsTiers } from "../../runtime/settings-store.ts";
|
|
37
40
|
import { loadRunManifestById } from "../../state/stores/state-store.ts";
|
|
38
41
|
import type { TeamRunManifest } from "../../state/types.ts";
|
|
39
42
|
import { summarizeHeartbeats } from "../../ui/heartbeat-aggregator.ts";
|
|
40
|
-
import {
|
|
43
|
+
import { installInlinePanel } from "../../ui/inline-panel/index.ts";
|
|
44
|
+
import { clearSessionSwitchInFlight, markSessionSwitchInFlight } from "../../ui/inline-panel/view-session-store.ts";
|
|
45
|
+
import { requestRender, setExtensionWidget, toPiWidgetPlacement } from "../../ui/pi-ui-compat.ts";
|
|
41
46
|
import {
|
|
42
47
|
registerPiCrewPowerbarSegments,
|
|
43
48
|
requestPowerbarUpdate,
|
|
@@ -73,6 +78,36 @@ export function installSessionLifecycleHandlers(pi: ExtensionAPI, ctx: Registrat
|
|
|
73
78
|
installModelTrackingHandlers(pi);
|
|
74
79
|
}
|
|
75
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Hand over the outcome of runs detached by an agent-view switch.
|
|
83
|
+
*
|
|
84
|
+
* Delivered as a DISPLAYED session entry (not a follow-up queue item): an idle
|
|
85
|
+
* session never flushes a follow-up, so the queued variant left the result
|
|
86
|
+
* invisible. Each result is dropped from the registry only after its send
|
|
87
|
+
* succeeded, and only while the owning session is current — a worker's view
|
|
88
|
+
* session must never receive the parent run's report.
|
|
89
|
+
*/
|
|
90
|
+
function deliverDetachedRunResults(pi: ExtensionAPI, extensionCtx: ExtensionContext): void {
|
|
91
|
+
if (!hasDetachedRuns()) return;
|
|
92
|
+
try {
|
|
93
|
+
// Agent views are in-document panes now, never sessions: the current
|
|
94
|
+
// session is always the main one, so detached results always deliver.
|
|
95
|
+
const inViewSession = false;
|
|
96
|
+
for (const { runId, text } of peekFinishedDetachedRunResults({ inViewSession })) {
|
|
97
|
+
pi.sendMessage({ customType: "pi-crew-run-result", content: text, display: true });
|
|
98
|
+
forgetDetachedRun(runId);
|
|
99
|
+
try {
|
|
100
|
+
extensionCtx.ui.notify(text.split("\n")[0] ?? `pi-crew run finished: ${runId}`, "info");
|
|
101
|
+
} catch {
|
|
102
|
+
/* toast is secondary to the session entry */
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} catch (error) {
|
|
106
|
+
// Keep the entry: the next tick retries rather than losing the outcome.
|
|
107
|
+
logInternalError("register.detachedRunResults", error);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
76
111
|
/**
|
|
77
112
|
* model_select / thinking_level_select:
|
|
78
113
|
* Track what the MAIN session is *actually* running so subagents that
|
|
@@ -123,6 +158,12 @@ function installSessionShutdownHandler(pi: ExtensionAPI, ctx: RegistrationContex
|
|
|
123
158
|
*/
|
|
124
159
|
function installSessionBeforeSwitchHandler(pi: ExtensionAPI, ctx: RegistrationContext): void {
|
|
125
160
|
pi.on("session_before_switch", () => {
|
|
161
|
+
// The switch tears the current turn down right after this handler
|
|
162
|
+
// (teardownCurrent → session.abort()). That turn-abort must NOT cancel
|
|
163
|
+
// a foreground team run that is still forming (see run-deadline.ts):
|
|
164
|
+
// foreground runs survive session switches (P0). Cleared on the next
|
|
165
|
+
// session_start.
|
|
166
|
+
markSessionSwitchInFlight();
|
|
126
167
|
ctx.sessionGeneration++;
|
|
127
168
|
const pendingCount = ctx.lifecycleState.deliveryCoordinator?.getPendingCount() ?? 0;
|
|
128
169
|
try {
|
|
@@ -165,6 +206,9 @@ function installSessionBeforeSwitchHandler(pi: ExtensionAPI, ctx: RegistrationCo
|
|
|
165
206
|
*/
|
|
166
207
|
function installSessionStartHandler(pi: ExtensionAPI, ctx: RegistrationContext): void {
|
|
167
208
|
pi.on("session_start", (_event, extensionCtx) => {
|
|
209
|
+
// Any session start means a pending switch landed — the turn-abort
|
|
210
|
+
// suppression window for that switch is over.
|
|
211
|
+
clearSessionSwitchInFlight();
|
|
168
212
|
runArtifactCleanup(extensionCtx.cwd);
|
|
169
213
|
|
|
170
214
|
// Restore brief mode state from session entries (best-effort).
|
|
@@ -215,8 +259,18 @@ function installSessionStartHandler(pi: ExtensionAPI, ctx: RegistrationContext):
|
|
|
215
259
|
}, 0);
|
|
216
260
|
|
|
217
261
|
const loadedConfig = loadConfig(extensionCtx.cwd);
|
|
218
|
-
|
|
219
|
-
|
|
262
|
+
// Wave 2B (P1 security): crew settings load as TIERS — the project-tier
|
|
263
|
+
// <cwd>/.pi/crew-settings.json is untrusted (a cloned repo can ship it)
|
|
264
|
+
// and now goes through sanitizeProjectConfig + tighten-only guard
|
|
265
|
+
// tiering instead of being applied raw over the sanitized config (the
|
|
266
|
+
// pre-2B `loadCrewSettings` + `applyCrewSettingsToConfig` bypass).
|
|
267
|
+
// See src/runtime/settings-store.ts for the tier pipeline + the
|
|
268
|
+
// scheduledJobs/schedulingEnabled boundary decision.
|
|
269
|
+
const crewSettingsTiers = loadCrewSettingsTiers(extensionCtx.cwd);
|
|
270
|
+
const settingsWarnings = applyCrewSettingsTiersToConfig(loadedConfig.config, crewSettingsTiers);
|
|
271
|
+
if (settingsWarnings.length > 0) {
|
|
272
|
+
(loadedConfig.warnings ??= []).push(...settingsWarnings);
|
|
273
|
+
}
|
|
220
274
|
|
|
221
275
|
// Start scheduler with event-based executor
|
|
222
276
|
const sessionId =
|
|
@@ -229,14 +283,20 @@ function installSessionStartHandler(pi: ExtensionAPI, ctx: RegistrationContext):
|
|
|
229
283
|
// Wire scheduler into handle-schedule.ts so handlers can add/list jobs.
|
|
230
284
|
// EXT-9: module-scoped setter (was globalThis[Symbol.for(...)]).
|
|
231
285
|
registerCrewScheduler(ctx.crewScheduler);
|
|
232
|
-
// Load scheduled jobs from settings if present
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
286
|
+
// Load scheduled jobs from settings if present.
|
|
287
|
+
// BOUNDARY (Wave B2): project-tier scheduledJobs are OPT-IN GATED — the
|
|
288
|
+
// registration loop reads the gated `effectiveScheduledJobs` view (user
|
|
289
|
+
// jobs always; project jobs ONLY when the user-tier global file has BOTH
|
|
290
|
+
// schedulingEnabled:true AND allowProjectScheduledJobs:true).
|
|
291
|
+
// <cwd>/.pi/crew-settings.json remains the persistence store for the
|
|
292
|
+
// user's own `crew schedule add/update/remove` commands (handle-schedule.ts),
|
|
293
|
+
// so crew-schedule users must set both flags in ~/.pi/crew-settings.json.
|
|
294
|
+
// `schedulingEnabled` is user-tier-only (project values always dropped).
|
|
295
|
+
for (const job of crewSettingsTiers.effectiveScheduledJobs) {
|
|
296
|
+
try {
|
|
297
|
+
ctx.crewScheduler.add(job as ScheduledJob);
|
|
298
|
+
} catch {
|
|
299
|
+
/* skip invalid */
|
|
240
300
|
}
|
|
241
301
|
}
|
|
242
302
|
ctx.autoRecoveryLast.clear();
|
|
@@ -255,6 +315,13 @@ function installSessionStartHandler(pi: ExtensionAPI, ctx: RegistrationContext):
|
|
|
255
315
|
});
|
|
256
316
|
const cache = ctx.getManifestCache(extensionCtx.cwd);
|
|
257
317
|
updateCrewWidget(extensionCtx, ctx.widgetState, loadedConfig.config.ui, cache, ctx.getRunSnapshotCache(extensionCtx.cwd));
|
|
318
|
+
// Inline agent panel: keyboard-navigable rows under the prompt + per-agent
|
|
319
|
+
// transcript pane. Installed after the widget so the row projection sees
|
|
320
|
+
// the same caches the widget paint uses.
|
|
321
|
+
installInlinePanel(pi, extensionCtx, loadedConfig.config.ui);
|
|
322
|
+
// Returning from an agent view lands here: report any detached run that
|
|
323
|
+
// finished while the view was open, without waiting for a render tick.
|
|
324
|
+
deliverDetachedRunResults(pi, extensionCtx);
|
|
258
325
|
updatePiCrewPowerbar(
|
|
259
326
|
pi.events,
|
|
260
327
|
extensionCtx.cwd,
|
|
@@ -540,6 +607,44 @@ export function filterManifestsForHealthNotifications(
|
|
|
540
607
|
return manifests.filter((run) => !run.ownerSessionId || run.ownerSessionId === currentSessionId);
|
|
541
608
|
}
|
|
542
609
|
|
|
610
|
+
/**
|
|
611
|
+
* bug-026 sub-issue C: runEventBus event types that mark a run TERMINAL,
|
|
612
|
+
* across BOTH type namespaces seen on the bus — dotted `run.*` strings (kept
|
|
613
|
+
* for parity with classifyEventChannel's WORKER_LIFECYCLE_TYPES, see
|
|
614
|
+
* src/ui/run-event-bus.ts:31-44) and underscore `run_*` (the native
|
|
615
|
+
* RunEventType namespace actually emitted by team-runner, e.g.
|
|
616
|
+
* `run_completed`). Exported for unit testing.
|
|
617
|
+
*/
|
|
618
|
+
export const TERMINAL_RUN_EVENT_TYPES: ReadonlySet<string> = new Set([
|
|
619
|
+
"run.completed",
|
|
620
|
+
"run.failed",
|
|
621
|
+
"run.cancelled",
|
|
622
|
+
"run_completed",
|
|
623
|
+
"run_failed",
|
|
624
|
+
"run_cancelled",
|
|
625
|
+
]);
|
|
626
|
+
|
|
627
|
+
/** True when a runEventBus event type marks the run terminal (either namespace). */
|
|
628
|
+
export function isTerminalRunEventType(type: string): boolean {
|
|
629
|
+
return TERMINAL_RUN_EVENT_TYPES.has(type);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/** Pure filter: drop `runId` from a preloaded-manifest frame (bug-026 sub-issue C eviction). */
|
|
633
|
+
export function evictRunFromManifests(manifests: TeamRunManifest[], runId: string): TeamRunManifest[] {
|
|
634
|
+
return manifests.filter((m) => m.runId !== runId);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Apply a runEventBus payload to a preloaded-manifest frame: evict the run on
|
|
639
|
+
* terminal events, pass through unchanged otherwise. This is the exact logic
|
|
640
|
+
* wired into the setupRenderLoop `runEventBus.onAny` subscription (bug-026
|
|
641
|
+
* sub-issue C); exported so tests can exercise it end-to-end against the
|
|
642
|
+
* real bus without spinning up the full render loop.
|
|
643
|
+
*/
|
|
644
|
+
export function applyTerminalRunEventToManifests(manifests: TeamRunManifest[], event: { type: string; runId: string }): TeamRunManifest[] {
|
|
645
|
+
return isTerminalRunEventType(event.type) ? evictRunFromManifests(manifests, event.runId) : manifests;
|
|
646
|
+
}
|
|
647
|
+
|
|
543
648
|
function setupRenderLoop(
|
|
544
649
|
pi: ExtensionAPI,
|
|
545
650
|
ctx: RegistrationContext,
|
|
@@ -625,13 +730,14 @@ function setupRenderLoop(
|
|
|
625
730
|
|
|
626
731
|
const renderTick = (): void => {
|
|
627
732
|
if (!ctx.currentCtx) return;
|
|
733
|
+
deliverDetachedRunResults(pi, ctx.currentCtx);
|
|
628
734
|
const config = lastPreloadedConfig?.config.ui;
|
|
629
735
|
const activeCache = lastFrameManifestCache ?? ctx.getManifestCache(ctx.currentCtx.cwd);
|
|
630
736
|
const snapshotCache = lastFrameSnapshotCache ?? ctx.getRunSnapshotCache(ctx.currentCtx.cwd);
|
|
631
737
|
const manifests = lastPreloadedManifests;
|
|
632
738
|
if (!lastPreloadedConfig) backgroundPreload();
|
|
633
739
|
if (ctx.uiState.liveSidebarRunId || ctx.uiState.dashboardOpen) {
|
|
634
|
-
const placement = config?.widgetPlacement ?? DEFAULT_UI.widgetPlacement;
|
|
740
|
+
const placement = toPiWidgetPlacement(config?.widgetPlacement ?? DEFAULT_UI.widgetPlacement);
|
|
635
741
|
if (ctx.widgetState.lastVisibility !== "hidden" || ctx.widgetState.lastPlacement !== placement) {
|
|
636
742
|
setExtensionWidget(ctx.currentCtx, "pi-crew", undefined, { placement });
|
|
637
743
|
setExtensionWidget(ctx.currentCtx, "pi-crew-active", undefined, { placement });
|
|
@@ -803,6 +909,12 @@ function setupRenderLoop(
|
|
|
803
909
|
// re-renders within debounceMs of any agent lifecycle event.
|
|
804
910
|
const sched = ctx.renderScheduler;
|
|
805
911
|
const unsubscribeRunEvents = runEventBus.onAny((event) => {
|
|
912
|
+
// bug-026 sub-issue C: evict terminal runs from the preloaded manifest
|
|
913
|
+
// frame so a stale "running" entry cannot persist for the session
|
|
914
|
+
// lifetime. Additive to the renderTick GATE 1 / FIX #1 / GATE 2 snapshot
|
|
915
|
+
// purges (which invalidate the snapshot cache) — those gates do not
|
|
916
|
+
// touch lastPreloadedManifests itself.
|
|
917
|
+
lastPreloadedManifests = applyTerminalRunEventToManifests(lastPreloadedManifests, event);
|
|
806
918
|
sched.schedule({
|
|
807
919
|
runId: event.runId,
|
|
808
920
|
source: "runEventBus",
|
|
@@ -905,7 +1017,7 @@ export function installCrewBrokerLifecycleController(_pi: ExtensionAPI, _ctx: Re
|
|
|
905
1017
|
|
|
906
1018
|
function effectiveEnabled(): boolean {
|
|
907
1019
|
// Env wins over config. PI_CREW_BROKER=1 forces on, =0 forces off.
|
|
908
|
-
const envOverride =
|
|
1020
|
+
const envOverride = getCrewEnv("PI_CREW_BROKER");
|
|
909
1021
|
if (envOverride === "0") return false;
|
|
910
1022
|
// Config block: read fresh so a runtime config update takes effect.
|
|
911
1023
|
try {
|
|
@@ -934,7 +1046,23 @@ export function installCrewBrokerLifecycleController(_pi: ExtensionAPI, _ctx: Re
|
|
|
934
1046
|
starting = (async () => {
|
|
935
1047
|
const cfg = (() => {
|
|
936
1048
|
try {
|
|
937
|
-
|
|
1049
|
+
// B1 battery 2026-08-18 (third config-layer bug): loadConfig()
|
|
1050
|
+
// WITHOUT cwd reads only the user config — the workspace
|
|
1051
|
+
// .crew/config.json (where broker.waitMethodsEnabled:true would
|
|
1052
|
+
// live) was never merged, so the flag stayed default-false even
|
|
1053
|
+
// after the parser + merge fixes. Pass the SAME cwd the broker
|
|
1054
|
+
// itself uses below.
|
|
1055
|
+
return loadConfig(process.cwd()).config.broker;
|
|
1056
|
+
} catch {
|
|
1057
|
+
return undefined;
|
|
1058
|
+
}
|
|
1059
|
+
})();
|
|
1060
|
+
// T3/R5 (ADR-5 §10): governed-nesting + limits config for the delegate
|
|
1061
|
+
// surface — same cwd discipline as the broker block above.
|
|
1062
|
+
const nestingCfg = (() => {
|
|
1063
|
+
try {
|
|
1064
|
+
const c = loadConfig(process.cwd()).config;
|
|
1065
|
+
return { nesting: c.nesting, limits: c.limits };
|
|
938
1066
|
} catch {
|
|
939
1067
|
return undefined;
|
|
940
1068
|
}
|
|
@@ -944,6 +1072,32 @@ export function installCrewBrokerLifecycleController(_pi: ExtensionAPI, _ctx: Re
|
|
|
944
1072
|
socketPath: getBrokerSocketPath(sessionId),
|
|
945
1073
|
maxFrameBytes: cfg?.maxFrameBytes ?? 262144,
|
|
946
1074
|
outboundQueueCap: cfg?.outboundQueueCap ?? 256,
|
|
1075
|
+
// WP-2 review round 1 (P1): thread the capability gate into the
|
|
1076
|
+
// PRODUCTION broker — the constructor default (false) made
|
|
1077
|
+
// config.broker.waitMethodsEnabled a dead knob and the ADR-0
|
|
1078
|
+
// "then true" flip a silent no-op. Fail-closed when unset.
|
|
1079
|
+
waitMethodsEnabled: cfg?.waitMethodsEnabled ?? false,
|
|
1080
|
+
// T3/R5 (ADR-5 §10): governed-nesting capability gate — fail-closed
|
|
1081
|
+
// default; production threads config.nesting (sensitive: user config
|
|
1082
|
+
// only). Nested-slot sizing + admission-time model catalog (ADR-5 §7 —
|
|
1083
|
+
// the production wiring MUST supply it) + workspace gate mirror.
|
|
1084
|
+
nestingEnabled: nestingCfg?.nesting?.enabled ?? false,
|
|
1085
|
+
...(nestingCfg?.nesting?.maxSlots !== undefined ? { nestingMaxSlots: nestingCfg.nesting.maxSlots } : {}),
|
|
1086
|
+
...(nestingCfg?.nesting?.maxDepth !== undefined ? { nestingMaxDepth: nestingCfg.nesting.maxDepth } : {}),
|
|
1087
|
+
// ADR-5 §12: enabling the sensitive USER-config-only flag IS the manual
|
|
1088
|
+
// trust decision for the escalation surface.
|
|
1089
|
+
nestingTrustedEscalation: nestingCfg?.nesting?.enabled === true,
|
|
1090
|
+
...(nestingCfg?.limits?.maxConcurrentWorkers !== undefined
|
|
1091
|
+
? { globalWorkerSemaphore: nestingCfg.limits.maxConcurrentWorkers }
|
|
1092
|
+
: {}),
|
|
1093
|
+
serializeOnPathOverlap: nestingCfg?.limits?.serializeOnPathOverlap ?? false,
|
|
1094
|
+
modelCatalog: () => {
|
|
1095
|
+
try {
|
|
1096
|
+
return configuredModelInfosFromPiConfig(process.cwd()).map((info) => info.fullId);
|
|
1097
|
+
} catch {
|
|
1098
|
+
return undefined;
|
|
1099
|
+
}
|
|
1100
|
+
},
|
|
947
1101
|
enabled: true,
|
|
948
1102
|
cwd: process.cwd(),
|
|
949
1103
|
});
|
|
@@ -960,10 +1114,17 @@ export function installCrewBrokerLifecycleController(_pi: ExtensionAPI, _ctx: Re
|
|
|
960
1114
|
return starting!;
|
|
961
1115
|
}
|
|
962
1116
|
|
|
963
|
-
const issueForChild = async (runId: string, taskId?: string): Promise<BrokerSpawnCredentials | undefined> => {
|
|
1117
|
+
const issueForChild = async (runId: string, taskId?: string, childDepth?: number): Promise<BrokerSpawnCredentials | undefined> => {
|
|
964
1118
|
if (!runId || typeof runId !== "string") return undefined;
|
|
965
1119
|
if (!isRootSession(process.env)) return undefined;
|
|
966
1120
|
if (!effectiveEnabled()) return undefined;
|
|
1121
|
+
// ADR-5 §4 (governed nesting): tokens are minted ONLY for children that
|
|
1122
|
+
// may themselves delegate — childDepth < resolved maxDepth. At the default
|
|
1123
|
+
// maxDepth=2 a delegate-spawned depth-2 grandchild gets NO credentials
|
|
1124
|
+
// (env containment: no PI_CREW_BROKER_SOCKET/TOKEN at depth 2; identity
|
|
1125
|
+
// routing via PI_CREW_BROKER_RUN_ID/TASK_ID is threaded unconditionally
|
|
1126
|
+
// elsewhere). Undefined childDepth = legacy worker spawn (depth 1).
|
|
1127
|
+
if (childDepth !== undefined && childDepth >= resolveCrewMaxDepth(undefined)) return undefined;
|
|
967
1128
|
const sessionId = cachedSessionId;
|
|
968
1129
|
if (!sessionId) return undefined;
|
|
969
1130
|
try {
|
|
@@ -20,12 +20,12 @@
|
|
|
20
20
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
21
21
|
import { loadConfig } from "../../config/config.ts";
|
|
22
22
|
import { clearHooksScoped } from "../../hooks/registry.ts";
|
|
23
|
-
import { terminateActiveChildPiProcesses } from "../../runtime/child-pi/child-pi.ts";
|
|
24
23
|
import { stopAllWatchdogs } from "../../runtime/foreground-watchdog.ts";
|
|
25
24
|
import { clearPiCrewPowerbar, disposePowerbarCoalescer } from "../../ui/powerbar-publisher.ts";
|
|
26
25
|
import { stopCrewWidget } from "../../ui/widget/index.ts";
|
|
27
26
|
import { logInternalError } from "../../utils/internal-error.ts";
|
|
28
27
|
import { clearProjectRootCache } from "../../utils/paths.ts";
|
|
28
|
+
import { safeAbortAll } from "../../utils/safe-abort.ts";
|
|
29
29
|
import { extractSessionId } from "../../utils/session-utils.ts";
|
|
30
30
|
import { stopAsyncRunNotifier } from "../async-notifier.ts";
|
|
31
31
|
import { uninstallCrewGlobalRegistry } from "../team-tool.ts";
|
|
@@ -135,7 +135,9 @@ function buildCleanupRuntime(ctx: RegistrationContext): () => void {
|
|
|
135
135
|
ctx.stopSessionBoundSubagents();
|
|
136
136
|
// P0 fix: also abort foreground team runs on session shutdown (not on session switch).
|
|
137
137
|
// This is the only place where foreground team run controllers should be aborted.
|
|
138
|
-
|
|
138
|
+
// safe-abort: shutdown races completed runs whose spawned children already
|
|
139
|
+
// exited — abort() would throw AbortError from Node's child_process listener.
|
|
140
|
+
safeAbortAll(ctx.foregroundTeamRunControllers.values(), "cleanup-runtime.foreground-team");
|
|
139
141
|
ctx.foregroundTeamRunControllers.clear();
|
|
140
142
|
// RC-01: stop the foreground-run watchdog timers on full shutdown — they were
|
|
141
143
|
// never cleared, so an active (non-terminal) foreground run left its watchdog
|
|
@@ -219,10 +221,26 @@ function buildStopSessionBoundSubagents(ctx: RegistrationContext): () => void {
|
|
|
219
221
|
// Only abort subagent controllers — NOT foreground team runs.
|
|
220
222
|
// Foreground team runs are bound to the session lifecycle; they will be aborted
|
|
221
223
|
// by cleanupRuntime during session_shutdown.
|
|
222
|
-
|
|
224
|
+
// safe-abort: these signals may be linked to spawned children (pipelines pass
|
|
225
|
+
// them into spawn options.signal); aborting after a child exited makes Node's
|
|
226
|
+
// child_process listener throw AbortError synchronously — inside a session
|
|
227
|
+
// event handler that would surface as uncaughtException and kill pi.
|
|
228
|
+
safeAbortAll(ctx.foregroundControllers.values(), "stop-session-bound.foreground");
|
|
223
229
|
ctx.foregroundControllers.clear();
|
|
224
|
-
|
|
225
|
-
|
|
230
|
+
try {
|
|
231
|
+
ctx.subagentManager.abortAll("Session switching — foreground subagents cancelled.");
|
|
232
|
+
} catch (error) {
|
|
233
|
+
logInternalError("stop-session-bound.abortAll", error);
|
|
234
|
+
}
|
|
235
|
+
// Deliberately NOT terminateActiveChildPiProcesses() here: the active
|
|
236
|
+
// child registry holds EVERY child pi spawned in this process —
|
|
237
|
+
// including foreground team-run workers. Killing them on a session
|
|
238
|
+
// switch (resume/new/fork) is exactly the regression "jumping into any
|
|
239
|
+
// session stops the run" (exit 143). Session-bound subagents are
|
|
240
|
+
// cancelled above via their own controllers; foreground runs must
|
|
241
|
+
// survive switches (P0) and are aborted only by cleanupRuntime on real
|
|
242
|
+
// shutdown; the process-exit hook (tryRegisterSessionCleanup) drains
|
|
243
|
+
// the registry when pi actually exits.
|
|
226
244
|
ctx.disposeRenderSchedulerSubscriptions();
|
|
227
245
|
ctx.renderScheduler?.dispose();
|
|
228
246
|
ctx.renderScheduler = undefined;
|