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
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as fsp from "node:fs/promises";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import { DEFAULT_CACHE, DEFAULT_PATHS } from "
|
|
5
|
-
import { errors } from "
|
|
6
|
-
import type { TeamConfig } from "
|
|
7
|
-
import { createRunId, createTaskId } from "
|
|
8
|
-
import { logInternalError } from "
|
|
9
|
-
import { findRepoRoot, projectCrewRoot, userCrewRoot } from "
|
|
10
|
-
import { assertSafePathId, resolveContainedRelativePath, resolveRealContainedPath } from "
|
|
11
|
-
import { toPiSessionId } from "
|
|
12
|
-
import type { WorkflowConfig } from "
|
|
4
|
+
import { DEFAULT_CACHE, DEFAULT_PATHS } from "../../config/defaults.ts";
|
|
5
|
+
import { errors } from "../../errors.ts";
|
|
6
|
+
import type { TeamConfig } from "../../teams/team-config.ts";
|
|
7
|
+
import { createRunId, createTaskId } from "../../utils/ids.ts";
|
|
8
|
+
import { logInternalError } from "../../utils/internal-error.ts";
|
|
9
|
+
import { findRepoRoot, projectCrewRoot, userCrewRoot } from "../../utils/paths.ts";
|
|
10
|
+
import { assertSafePathId, resolveContainedRelativePath, resolveRealContainedPath } from "../../utils/safe-paths.ts";
|
|
11
|
+
import { toPiSessionId } from "../../utils/session-utils.ts";
|
|
12
|
+
import type { WorkflowConfig } from "../../workflows/workflow-config.ts";
|
|
13
|
+
import {
|
|
14
|
+
atomicWriteJson,
|
|
15
|
+
atomicWriteJsonAsync,
|
|
16
|
+
atomicWriteJsonCoalesced,
|
|
17
|
+
flushPendingAtomicWrites,
|
|
18
|
+
readJsonFile,
|
|
19
|
+
} from "../atomic-write.ts";
|
|
20
|
+
import { canTransitionRunStatus, isTeamTaskStatus } from "../contracts.ts";
|
|
21
|
+
import { withRunLock, withRunLockSync } from "../coordination/locks.ts";
|
|
22
|
+
import { appendEvent } from "../event-log/event-log.ts";
|
|
23
|
+
import { reconstructTasksFromEvents } from "../event-log/event-reconstructor.ts";
|
|
24
|
+
import type { TeamRunManifest, TeamTaskState } from "../types.ts";
|
|
25
|
+
import { CURRENT_SCHEMA_VERSION, CURRENT_TASKS_SCHEMA_VERSION } from "../types.ts";
|
|
13
26
|
import { unregisterActiveRun } from "./active-run-registry.ts";
|
|
14
|
-
import { atomicWriteJson, atomicWriteJsonAsync, atomicWriteJsonCoalesced, flushPendingAtomicWrites, readJsonFile } from "./atomic-write.ts";
|
|
15
|
-
import { canTransitionRunStatus } from "./contracts.ts";
|
|
16
|
-
import { appendEvent } from "./event-log.ts";
|
|
17
|
-
import { withRunLock, withRunLockSync } from "./locks.ts";
|
|
18
|
-
import type { TeamRunManifest, TeamTaskState } from "./types.ts";
|
|
19
|
-
import { CURRENT_SCHEMA_VERSION } from "./types.ts";
|
|
20
27
|
|
|
21
28
|
/**
|
|
22
29
|
* stat() the manifest with a brief retry on Windows for the AV-scan window.
|
|
@@ -151,19 +158,33 @@ function scopeBaseRoot(cwd: string): string {
|
|
|
151
158
|
return useProjectState(cwd) ? projectCrewRoot(cwd) : userCrewRoot();
|
|
152
159
|
}
|
|
153
160
|
|
|
161
|
+
// P1-12: the containment verdict (resolveRealContainedPath ancestor walk, ~8-12
|
|
162
|
+
// syscalls) cannot change for a run dir that isn't replaced — memoize positive
|
|
163
|
+
// results per (cwd, runId) with a short TTL. Negatives are NOT cached (so a
|
|
164
|
+
// newly-created run is found promptly). Downstream manifest stat still catches
|
|
165
|
+
// deletions, so a stale positive is harmless.
|
|
166
|
+
const runStateRootCache = new Map<string, { root: string; expiresAt: number }>();
|
|
167
|
+
const RUN_STATE_ROOT_TTL_MS = 10_000;
|
|
168
|
+
const RUN_STATE_ROOT_CACHE_MAX = 256;
|
|
169
|
+
|
|
154
170
|
function resolveRunStateRoot(cwd: string, runId: string): string | undefined {
|
|
155
171
|
assertSafePathId("runId", runId);
|
|
172
|
+
const key = `${cwd}\0${runId}`;
|
|
173
|
+
const now = Date.now();
|
|
174
|
+
const cached = runStateRootCache.get(key);
|
|
175
|
+
if (cached && cached.expiresAt > now) return cached.root;
|
|
156
176
|
const runsRoot = path.join(scopeBaseRoot(cwd), DEFAULT_PATHS.state.runsSubdir);
|
|
157
177
|
const scopedPath = resolveContainedRelativePath(runsRoot, runId, "runId");
|
|
158
178
|
try {
|
|
159
|
-
// Single atomic validation: resolves through symlinks via realpath,
|
|
160
|
-
// verifies containment within runsRoot, and throws ENOENT if missing.
|
|
161
|
-
// Eliminates the TOCTOU window from the previous existsSync + lstatSync
|
|
162
|
-
// + resolveRealContainedPath sequence.
|
|
163
179
|
resolveRealContainedPath(runsRoot, runId);
|
|
164
180
|
} catch {
|
|
165
181
|
return undefined;
|
|
166
182
|
}
|
|
183
|
+
if (runStateRootCache.size >= RUN_STATE_ROOT_CACHE_MAX) {
|
|
184
|
+
const oldest = runStateRootCache.keys().next().value;
|
|
185
|
+
if (oldest !== undefined) runStateRootCache.delete(oldest);
|
|
186
|
+
}
|
|
187
|
+
runStateRootCache.set(key, { root: scopedPath, expiresAt: now + RUN_STATE_ROOT_TTL_MS });
|
|
167
188
|
return scopedPath;
|
|
168
189
|
}
|
|
169
190
|
|
|
@@ -410,7 +431,32 @@ export async function saveRunManifestAsync(manifest: TeamRunManifest): Promise<v
|
|
|
410
431
|
});
|
|
411
432
|
}
|
|
412
433
|
|
|
434
|
+
/**
|
|
435
|
+
* ST-4: Defense-in-depth guard — refuse to persist an empty tasks array over
|
|
436
|
+
* a previously non-empty tasks file. Prevents the cascade where a corrupt
|
|
437
|
+
* tasks.json (→ [] on load via readJsonFile catching SyntaxError) is then
|
|
438
|
+
* persisted as [], permanently destroying the data. If the on-disk file has
|
|
439
|
+
* tasks and the incoming array is empty, we log a warning and refuse.
|
|
440
|
+
*
|
|
441
|
+
* @returns true if the write should proceed, false if it was refused.
|
|
442
|
+
*/
|
|
443
|
+
function shouldPersistTasks(manifest: TeamRunManifest, tasks: TeamTaskState[]): boolean {
|
|
444
|
+
if (tasks.length > 0) return true;
|
|
445
|
+
const existing = extractTaskArray(readJsonFile<unknown>(manifest.tasksPath));
|
|
446
|
+
if (existing.length > 0) {
|
|
447
|
+
console.warn(
|
|
448
|
+
`[state-store] refusing to persist empty tasks over ${existing.length} existing task(s) — possible corrupt-load cascade (runId=${manifest.runId})`,
|
|
449
|
+
);
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
return true;
|
|
453
|
+
}
|
|
454
|
+
|
|
413
455
|
export function saveRunTasks(manifest: TeamRunManifest, tasks: TeamTaskState[]): void {
|
|
456
|
+
// ST-4: refuse to persist [] over a previously-non-empty tasks file.
|
|
457
|
+
// Prevents the cascade where a corrupt tasks.json (→ [] on load) is then
|
|
458
|
+
// persisted as [], permanently destroying the data.
|
|
459
|
+
if (!shouldPersistTasks(manifest, tasks)) return;
|
|
414
460
|
// FIX: Invalidate cache BEFORE atomic write to prevent stale cache serving.
|
|
415
461
|
invalidateRunCache(manifest.stateRoot);
|
|
416
462
|
|
|
@@ -487,6 +533,8 @@ export function saveRunTasks(manifest: TeamRunManifest, tasks: TeamTaskState[]):
|
|
|
487
533
|
*/
|
|
488
534
|
/** @internal */
|
|
489
535
|
export function saveRunTasksCoalesced(manifest: TeamRunManifest, tasks: TeamTaskState[], skipCoalesce: boolean = false): void {
|
|
536
|
+
// ST-4: refuse to persist [] over a previously-non-empty tasks file.
|
|
537
|
+
if (!shouldPersistTasks(manifest, tasks)) return;
|
|
490
538
|
// FIX: Invalidate cache BEFORE atomic write to prevent stale cache serving.
|
|
491
539
|
invalidateRunCache(manifest.stateRoot);
|
|
492
540
|
try {
|
|
@@ -498,6 +546,8 @@ export function saveRunTasksCoalesced(manifest: TeamRunManifest, tasks: TeamTask
|
|
|
498
546
|
}
|
|
499
547
|
|
|
500
548
|
export async function saveRunTasksAsync(manifest: TeamRunManifest, tasks: TeamTaskState[]): Promise<void> {
|
|
549
|
+
// ST-4: refuse to persist [] over a previously-non-empty tasks file.
|
|
550
|
+
if (!shouldPersistTasks(manifest, tasks)) return;
|
|
501
551
|
// FIX: Invalidate cache BEFORE atomic write to prevent stale cache serving.
|
|
502
552
|
invalidateRunCache(manifest.stateRoot);
|
|
503
553
|
try {
|
|
@@ -719,6 +769,185 @@ async function readJsonFileAsync<T>(filePath: string): Promise<T | undefined> {
|
|
|
719
769
|
}
|
|
720
770
|
}
|
|
721
771
|
|
|
772
|
+
/**
|
|
773
|
+
* ST-4: Rename a corrupt file to a quarantine path (`.corrupt-<ts>`) so it is
|
|
774
|
+
* preserved for debugging but no longer read as the primary source of truth.
|
|
775
|
+
*/
|
|
776
|
+
function quarantineCorruptFile(filePath: string): void {
|
|
777
|
+
try {
|
|
778
|
+
fs.renameSync(filePath, `${filePath}.corrupt-${Date.now()}`);
|
|
779
|
+
} catch {
|
|
780
|
+
// Best-effort — if rename fails (file already gone, permission, etc.),
|
|
781
|
+
// we still proceed with reconstruction from the event log.
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* ST-4: Convert event-reconstructor output into TeamTaskState[].
|
|
787
|
+
* Reconstructed tasks carry lifecycle data (id, status, timing) from the
|
|
788
|
+
* event log; auxiliary fields (role, agent, title) are filled with defaults
|
|
789
|
+
* since they are not present in lifecycle events.
|
|
790
|
+
*/
|
|
791
|
+
function reconstructTasksFromEventLog(eventsPath: string, runId: string): TeamTaskState[] {
|
|
792
|
+
try {
|
|
793
|
+
const result = reconstructTasksFromEvents(eventsPath);
|
|
794
|
+
const tasks: TeamTaskState[] = [];
|
|
795
|
+
for (const [, rt] of result.tasks) {
|
|
796
|
+
tasks.push({
|
|
797
|
+
id: rt.id,
|
|
798
|
+
runId,
|
|
799
|
+
role: "unknown",
|
|
800
|
+
agent: "unknown",
|
|
801
|
+
title: "reconstructed from events",
|
|
802
|
+
status: isTeamTaskStatus(rt.status) ? rt.status : "queued",
|
|
803
|
+
dependsOn: [],
|
|
804
|
+
cwd: "",
|
|
805
|
+
startedAt: rt.startedAt,
|
|
806
|
+
finishedAt: rt.finishedAt,
|
|
807
|
+
error: rt.error,
|
|
808
|
+
segment: rt.segment,
|
|
809
|
+
diagnostics: rt.diagnostics,
|
|
810
|
+
metrics: rt.metrics,
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
return tasks;
|
|
814
|
+
} catch {
|
|
815
|
+
return [];
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* ST-4: Load tasks.json with corruption recovery (sync path).
|
|
821
|
+
*
|
|
822
|
+
* Distinguishes:
|
|
823
|
+
* - ENOENT / ENOTDIR → legitimate empty → [] (NOT quarantined).
|
|
824
|
+
* - SyntaxError (parse failure) → corrupt → quarantine `.corrupt-<ts>` AND
|
|
825
|
+
* reconstruct from events.jsonl. If reconstruction yields tasks, persist
|
|
826
|
+
* them so subsequent loads see a valid file.
|
|
827
|
+
* - Non-array JSON (e.g. `{}`) → corrupt → same as SyntaxError.
|
|
828
|
+
* - Valid array → return as-is.
|
|
829
|
+
*/
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* ST-9: Extract the task array from a tasks.json payload.
|
|
833
|
+
*
|
|
834
|
+
* Accepts both the v0 legacy bare-array format and the v1+ envelope
|
|
835
|
+
* `{ schemaVersion, tasks }`. Returns [] for unrecognized shapes.
|
|
836
|
+
*/
|
|
837
|
+
function extractTaskArray(raw: unknown): TeamTaskState[] {
|
|
838
|
+
if (Array.isArray(raw)) return raw as TeamTaskState[];
|
|
839
|
+
if (raw !== null && typeof raw === "object" && "tasks" in raw) {
|
|
840
|
+
const envelope = raw as { tasks?: unknown };
|
|
841
|
+
if (Array.isArray(envelope.tasks)) return envelope.tasks as TeamTaskState[];
|
|
842
|
+
}
|
|
843
|
+
return [];
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* ST-9: Whether `parsed` is a recognizable tasks.json shape (v0 bare array
|
|
848
|
+
* or v1+ envelope). Used to distinguish legitimate formats from corruption.
|
|
849
|
+
*/
|
|
850
|
+
function isRecognizableTasksPayload(parsed: unknown): boolean {
|
|
851
|
+
if (Array.isArray(parsed)) return true;
|
|
852
|
+
if (parsed !== null && typeof parsed === "object" && "tasks" in parsed) {
|
|
853
|
+
return Array.isArray((parsed as { tasks?: unknown }).tasks);
|
|
854
|
+
}
|
|
855
|
+
return false;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* ST-9: Version-check + migration hook for tasks.json.
|
|
860
|
+
*
|
|
861
|
+
* tasks.json has two on-disk shapes:
|
|
862
|
+
* - v0 (current): bare JSON array `TeamTaskState[]` — what saveRunTasks*
|
|
863
|
+
* write today (backward-compatible; no schemaVersion envelope).
|
|
864
|
+
* - v1+ (future): envelope `{ schemaVersion: number, tasks: TeamTaskState[] }`
|
|
865
|
+
* — read-supported defensively for a future write-side switch.
|
|
866
|
+
*
|
|
867
|
+
* This detects the shape and returns the task array. v0 needs NO migration
|
|
868
|
+
* (it IS the current write format). For v1+ envelopes, a schemaVersion
|
|
869
|
+
* mismatch warns (mirroring the manifest check). Future breaking changes
|
|
870
|
+
* add real migration logic here.
|
|
871
|
+
*/
|
|
872
|
+
function migrateTasksFile(parsed: unknown, runId: string): TeamTaskState[] {
|
|
873
|
+
// v0 current: bare array (no schemaVersion envelope) — what writers produce.
|
|
874
|
+
if (Array.isArray(parsed)) {
|
|
875
|
+
// v0 bare array is the CURRENT write format (saveRunTasks* write the
|
|
876
|
+
// array directly, by design — backward compat). Nothing to migrate:
|
|
877
|
+
// return as-is. (v1+ envelope read-support below is defensive, for a
|
|
878
|
+
// future write-side switch.) Do NOT warn here — it would fire for 100%
|
|
879
|
+
// of runs on every load and flood the UI on startup.
|
|
880
|
+
return parsed as TeamTaskState[];
|
|
881
|
+
}
|
|
882
|
+
// v1+ envelope: { schemaVersion, tasks }.
|
|
883
|
+
if (parsed !== null && typeof parsed === "object" && "tasks" in parsed) {
|
|
884
|
+
const envelope = parsed as { schemaVersion?: unknown; tasks?: unknown };
|
|
885
|
+
const detected = typeof envelope.schemaVersion === "number" ? envelope.schemaVersion : 0;
|
|
886
|
+
if (detected !== CURRENT_TASKS_SCHEMA_VERSION) {
|
|
887
|
+
console.warn(
|
|
888
|
+
`[state-store] tasks.json schemaVersion mismatch: expected ${CURRENT_TASKS_SCHEMA_VERSION}, got ${detected}. Run ${runId} may be incompatible.`,
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
return extractTaskArray(parsed);
|
|
893
|
+
}
|
|
894
|
+
function loadTasksWithRecovery(tasksPath: string, eventsPath: string, runId: string): TeamTaskState[] {
|
|
895
|
+
let content: string;
|
|
896
|
+
try {
|
|
897
|
+
content = fs.readFileSync(tasksPath, "utf-8");
|
|
898
|
+
} catch {
|
|
899
|
+
// ENOENT / ENOTDIR / other read errors → empty (retry loop handles
|
|
900
|
+
// transient instability; ENOENT is a legitimate empty run).
|
|
901
|
+
return [];
|
|
902
|
+
}
|
|
903
|
+
let parsed: unknown;
|
|
904
|
+
try {
|
|
905
|
+
parsed = JSON.parse(content);
|
|
906
|
+
} catch {
|
|
907
|
+
// SyntaxError — corrupt file.
|
|
908
|
+
quarantineCorruptFile(tasksPath);
|
|
909
|
+
const reconstructed = reconstructTasksFromEventLog(eventsPath, runId);
|
|
910
|
+
if (reconstructed.length > 0) atomicWriteJson(tasksPath, reconstructed);
|
|
911
|
+
return reconstructed;
|
|
912
|
+
}
|
|
913
|
+
if (!isRecognizableTasksPayload(parsed)) {
|
|
914
|
+
// Neither v0 bare array nor v1+ envelope (e.g. `{}`) — corrupt.
|
|
915
|
+
quarantineCorruptFile(tasksPath);
|
|
916
|
+
const reconstructed = reconstructTasksFromEventLog(eventsPath, runId);
|
|
917
|
+
if (reconstructed.length > 0) atomicWriteJson(tasksPath, reconstructed);
|
|
918
|
+
return reconstructed;
|
|
919
|
+
}
|
|
920
|
+
return migrateTasksFile(parsed, runId);
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
/**
|
|
924
|
+
* ST-4: async twin of {@link loadTasksWithRecovery}.
|
|
925
|
+
*/
|
|
926
|
+
async function loadTasksWithRecoveryAsync(tasksPath: string, eventsPath: string, runId: string): Promise<TeamTaskState[]> {
|
|
927
|
+
let content: string;
|
|
928
|
+
try {
|
|
929
|
+
content = await fs.promises.readFile(tasksPath, "utf-8");
|
|
930
|
+
} catch {
|
|
931
|
+
return [];
|
|
932
|
+
}
|
|
933
|
+
let parsed: unknown;
|
|
934
|
+
try {
|
|
935
|
+
parsed = JSON.parse(content);
|
|
936
|
+
} catch {
|
|
937
|
+
quarantineCorruptFile(tasksPath);
|
|
938
|
+
const reconstructed = reconstructTasksFromEventLog(eventsPath, runId);
|
|
939
|
+
if (reconstructed.length > 0) await atomicWriteJsonAsync(tasksPath, reconstructed);
|
|
940
|
+
return reconstructed;
|
|
941
|
+
}
|
|
942
|
+
if (!isRecognizableTasksPayload(parsed)) {
|
|
943
|
+
quarantineCorruptFile(tasksPath);
|
|
944
|
+
const reconstructed = reconstructTasksFromEventLog(eventsPath, runId);
|
|
945
|
+
if (reconstructed.length > 0) await atomicWriteJsonAsync(tasksPath, reconstructed);
|
|
946
|
+
return reconstructed;
|
|
947
|
+
}
|
|
948
|
+
return migrateTasksFile(parsed, runId);
|
|
949
|
+
}
|
|
950
|
+
|
|
722
951
|
/**
|
|
723
952
|
* Load a run manifest and its tasks by runId.
|
|
724
953
|
* WARNING: This function provides best-effort consistency only. The sentinel-based
|
|
@@ -799,7 +1028,7 @@ export function loadRunManifestById(cwd: string, runId: string): { manifest: Tea
|
|
|
799
1028
|
const freshStat = fs.statSync(manifestPath);
|
|
800
1029
|
manifest = readJsonFile<TeamRunManifest>(manifestPath);
|
|
801
1030
|
const freshTasksStat = fs.existsSync(tasksPath) ? fs.statSync(tasksPath) : undefined;
|
|
802
|
-
tasks =
|
|
1031
|
+
tasks = loadTasksWithRecovery(tasksPath, manifest?.eventsPath ?? path.join(stateRoot, "events.jsonl"), manifest?.runId ?? runId);
|
|
803
1032
|
// If size/mtime didn't change between stat and read, we're consistent.
|
|
804
1033
|
if (
|
|
805
1034
|
freshStat.mtimeMs === manifestStat.mtimeMs &&
|
|
@@ -919,7 +1148,11 @@ export async function loadRunManifestByIdAsync(
|
|
|
919
1148
|
const freshStat = await fs.promises.stat(manifestPath);
|
|
920
1149
|
manifest = await readJsonFileAsync<TeamRunManifest>(manifestPath);
|
|
921
1150
|
const freshTasksStat = await fs.promises.stat(tasksPath).catch(() => undefined);
|
|
922
|
-
tasks =
|
|
1151
|
+
tasks = await loadTasksWithRecoveryAsync(
|
|
1152
|
+
tasksPath,
|
|
1153
|
+
manifest?.eventsPath ?? path.join(stateRoot, "events.jsonl"),
|
|
1154
|
+
manifest?.runId ?? runId,
|
|
1155
|
+
);
|
|
923
1156
|
// If size/mtime didn't change between stat and read, we're consistent.
|
|
924
1157
|
if (
|
|
925
1158
|
freshStat.mtimeMs === manifestStat.mtimeMs &&
|
package/src/state/types.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { CrashClass } from "../runtime/crash-classification.ts";
|
|
2
1
|
import type { CrewAgentProgress } from "../runtime/crew-agent-runtime.ts";
|
|
3
|
-
import type { WorkerHeartbeatState } from "../runtime/worker-heartbeat.ts";
|
|
2
|
+
import type { WorkerHeartbeatState } from "../runtime/heartbeat/worker-heartbeat.ts";
|
|
3
|
+
import type { CrashClass } from "../runtime/recovery/crash-classification.ts";
|
|
4
4
|
import type { TeamRunStatus, TeamTaskStatus } from "./contracts.ts";
|
|
5
|
+
import type { TaskClaimState } from "./coordination/task-claims.ts";
|
|
5
6
|
import type { CoherenceMark, RolloutEntry } from "./decision-ledger.ts";
|
|
6
|
-
import type { TaskClaimState } from "./task-claims.ts";
|
|
7
7
|
|
|
8
8
|
export type { TeamRunStatus, TeamTaskStatus } from "./contracts.ts";
|
|
9
9
|
export type { CoherenceMark, CrewAgentProgress, RolloutEntry };
|
|
@@ -183,6 +183,30 @@ export interface CrewAttentionEventData {
|
|
|
183
183
|
*/
|
|
184
184
|
export const CURRENT_SCHEMA_VERSION = 1 as const;
|
|
185
185
|
|
|
186
|
+
/**
|
|
187
|
+
* ST-9: schemaVersion sentinel for tasks.json (and future goal-state/mailbox
|
|
188
|
+
* files). Mirrors {@link CURRENT_SCHEMA_VERSION} for the manifest.
|
|
189
|
+
*
|
|
190
|
+
* tasks.json has two on-disk shapes:
|
|
191
|
+
* - v0 (legacy): bare JSON array `TeamTaskState[]` — no schemaVersion.
|
|
192
|
+
* - v1+ (current): envelope `{ schemaVersion, tasks }` — see {@link TasksFileData}.
|
|
193
|
+
*
|
|
194
|
+
* The reader detects the shape, warns on version mismatch, and applies
|
|
195
|
+
* migration logic in state-store.ts (the structured migration hook).
|
|
196
|
+
*/
|
|
197
|
+
export const CURRENT_TASKS_SCHEMA_VERSION = 1 as const;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* ST-9: Versioned envelope for tasks.json on disk.
|
|
201
|
+
* Writers produce this shape; readers accept it OR the legacy v0 bare array.
|
|
202
|
+
* `TeamTaskState` is defined later in this module but interface declarations
|
|
203
|
+
* are type-hoisted so the forward reference is valid.
|
|
204
|
+
*/
|
|
205
|
+
export interface TasksFileData {
|
|
206
|
+
schemaVersion: typeof CURRENT_TASKS_SCHEMA_VERSION;
|
|
207
|
+
tasks: TeamTaskState[];
|
|
208
|
+
}
|
|
209
|
+
|
|
186
210
|
export interface TeamRunManifest {
|
|
187
211
|
schemaVersion: typeof CURRENT_SCHEMA_VERSION;
|
|
188
212
|
runId: string;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PR-G3 (UI-3) — single shared Component contract for the UI layer.
|
|
3
|
+
*
|
|
4
|
+
* Five parallel "Component" shapes previously existed across src/ui/:
|
|
5
|
+
*
|
|
6
|
+
* - `RenderableComponent` (layout-primitives.ts)
|
|
7
|
+
* - `WidgetComponent` (widget/index.ts)
|
|
8
|
+
* - `DashboardComponent` (run-dashboard.ts)
|
|
9
|
+
* - `type Component` (transcript-viewer.ts)
|
|
10
|
+
* - `type Component = Container|Text` (tool-renderers/index.ts)
|
|
11
|
+
*
|
|
12
|
+
* They all share the same two core methods (render + invalidate) but could
|
|
13
|
+
* not be used interchangeably, which blocked polymorphic dispatch and shared
|
|
14
|
+
* testing. This module names that shared contract so any UI component can be
|
|
15
|
+
* treated uniformly.
|
|
16
|
+
*
|
|
17
|
+
* This is purely additive: every existing component already structurally
|
|
18
|
+
* satisfies these interfaces (render + invalidate are required everywhere;
|
|
19
|
+
* handleInput is present on the interactive ones). Adapting the existing
|
|
20
|
+
* declarations to `extends`/alias these types changes NO runtime behavior —
|
|
21
|
+
* it only makes the relationship explicit and compiler-enforced.
|
|
22
|
+
*
|
|
23
|
+
* Note: @earendil-works/pi-tui also declares a `Component` interface with the
|
|
24
|
+
* same render+invalidate core (plus an optional handleInput). `CrewComponent`
|
|
25
|
+
* is a structural supertype of it, so pi-tui components opt in automatically.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/** Minimum shared contract every UI component satisfies. */
|
|
29
|
+
export interface CrewComponent {
|
|
30
|
+
/**
|
|
31
|
+
* Drop cached render state so the next `render` recomputes from fresh data.
|
|
32
|
+
* Called on theme changes, resize, or any state mutation that invalidates
|
|
33
|
+
* previously produced lines.
|
|
34
|
+
*/
|
|
35
|
+
invalidate(): void;
|
|
36
|
+
/**
|
|
37
|
+
* Produce rendered lines for the given terminal width.
|
|
38
|
+
* @param width - available terminal width in cells
|
|
39
|
+
* @returns one string per output row (already padded/ANSI-colored)
|
|
40
|
+
*/
|
|
41
|
+
render(width: number): string[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A component that also accepts raw key/input data. Interactive viewers
|
|
46
|
+
* (RunDashboard, DurableTranscriptViewer) and overlays (ConfirmOverlay,
|
|
47
|
+
* MailboxDetailOverlay, AgentPickerOverlay) implement this; purely
|
|
48
|
+
* presentational components (Container, Text, Spacer, Box) do not.
|
|
49
|
+
*/
|
|
50
|
+
export interface InteractiveComponent extends CrewComponent {
|
|
51
|
+
/** Optional stable id (used by overlay stacks for lookup/debug). */
|
|
52
|
+
id?: string;
|
|
53
|
+
/** Consume a raw input chunk (a keypress escape sequence). */
|
|
54
|
+
handleInput(data: string): void;
|
|
55
|
+
}
|
|
@@ -29,7 +29,9 @@ export class CrewSelectList<T = string> {
|
|
|
29
29
|
this.selectedIndex = this.items.length ? 0 : -1;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
invalidate(): void {
|
|
32
|
+
invalidate(): void {
|
|
33
|
+
/* no-op */
|
|
34
|
+
}
|
|
33
35
|
|
|
34
36
|
getSelected(): CrewSelectItem<T> | undefined {
|
|
35
37
|
return this.selectedIndex >= 0 ? this.items[this.selectedIndex] : undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CrewAgentRecord } from "../../runtime/crew-agent-runtime.ts";
|
|
2
|
-
import { type LiveAgentHandle, listLiveAgents, listLiveAgentsByWorkspace } from "../../runtime/live-agent-manager.ts";
|
|
2
|
+
import { type LiveAgentHandle, listLiveAgents, listLiveAgentsByWorkspace } from "../../runtime/live-session/live-agent-manager.ts";
|
|
3
3
|
import { formatCost } from "../../state/usage.ts";
|
|
4
4
|
import { visibleWidth } from "../../utils/visual.ts";
|
|
5
5
|
import { computeLiveDurationMs } from "../live-duration.ts";
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* tailed by `run-snapshot-cache.ts`. Non-DWF runs (no `dwf.phase_*` events)
|
|
9
9
|
* yield `null`, so the progress pane stays unchanged for them.
|
|
10
10
|
*/
|
|
11
|
-
import type { TeamEvent } from "../state/event-log.ts";
|
|
11
|
+
import type { TeamEvent } from "../state/event-log/event-log.ts";
|
|
12
12
|
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
14
|
// Types
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { MetricRegistry } from "../observability/metric-registry.ts";
|
|
2
|
-
import { classifyHeartbeat, heartbeatAgeMs } from "../runtime/heartbeat-gradient.ts";
|
|
2
|
+
import { classifyHeartbeat, heartbeatAgeMs } from "../runtime/heartbeat/heartbeat-gradient.ts";
|
|
3
3
|
import type { TeamTaskState } from "../state/types.ts";
|
|
4
4
|
import type { RunUiSnapshot } from "./snapshot-types.ts";
|
|
5
5
|
|