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
|
@@ -442,7 +442,9 @@ class SelectSubmenu {
|
|
|
442
442
|
this.scrollOffset = Math.max(0, this.selectedIndex - Math.floor(this.maxVisible / 2));
|
|
443
443
|
}
|
|
444
444
|
|
|
445
|
-
invalidate(): void {
|
|
445
|
+
invalidate(): void {
|
|
446
|
+
/* no-op */
|
|
447
|
+
}
|
|
446
448
|
|
|
447
449
|
private ensureVisible(): void {
|
|
448
450
|
if (this.selectedIndex < this.scrollOffset) {
|
|
@@ -535,7 +537,9 @@ class TextinputSubmenu {
|
|
|
535
537
|
this.onCancel = onCancel;
|
|
536
538
|
}
|
|
537
539
|
|
|
538
|
-
invalidate(): void {
|
|
540
|
+
invalidate(): void {
|
|
541
|
+
/* no-op */
|
|
542
|
+
}
|
|
539
543
|
|
|
540
544
|
render(width: number): string[] {
|
|
541
545
|
const lines: string[] = [];
|
|
@@ -585,7 +589,6 @@ class AgentOverridesSubmenu {
|
|
|
585
589
|
private selectedIndex = 0;
|
|
586
590
|
private editField: "model" | "thinking" | null = null;
|
|
587
591
|
private editBuffer = "";
|
|
588
|
-
private readonly onApply: (overrides: Record<string, unknown>) => void;
|
|
589
592
|
private readonly onCancel: () => void;
|
|
590
593
|
|
|
591
594
|
constructor(
|
|
@@ -595,7 +598,6 @@ class AgentOverridesSubmenu {
|
|
|
595
598
|
onCancel: () => void,
|
|
596
599
|
) {
|
|
597
600
|
this.theme = theme;
|
|
598
|
-
this.onApply = onApply;
|
|
599
601
|
this.onCancel = onCancel;
|
|
600
602
|
const existing = (config.agents as Record<string, unknown>)?.overrides as
|
|
601
603
|
| Record<string, { model?: string; thinking?: string }>
|
|
@@ -616,7 +618,9 @@ class AgentOverridesSubmenu {
|
|
|
616
618
|
];
|
|
617
619
|
}
|
|
618
620
|
|
|
619
|
-
invalidate(): void {
|
|
621
|
+
invalidate(): void {
|
|
622
|
+
/* no-op */
|
|
623
|
+
}
|
|
620
624
|
|
|
621
625
|
render(width: number): string[] {
|
|
622
626
|
if (this.editField) return this.renderEdit(width);
|
|
@@ -661,7 +665,10 @@ class AgentOverridesSubmenu {
|
|
|
661
665
|
}
|
|
662
666
|
|
|
663
667
|
handleInput(data: string): void {
|
|
664
|
-
if (this.editField)
|
|
668
|
+
if (this.editField) {
|
|
669
|
+
this.handleEditInput(data);
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
665
672
|
|
|
666
673
|
const k = keyOf(data);
|
|
667
674
|
if (k === "up" || k === "k") {
|
|
@@ -738,7 +745,6 @@ class SettingsOverlay {
|
|
|
738
745
|
private scrollOffset = 0;
|
|
739
746
|
private maxVisible = 10;
|
|
740
747
|
private submenu: SelectSubmenu | TextinputSubmenu | AgentOverridesSubmenu | null = null;
|
|
741
|
-
private submenuSettingId: string | null = null;
|
|
742
748
|
private changedValues = new Map<string, unknown>();
|
|
743
749
|
|
|
744
750
|
constructor(config: Record<string, unknown>, theme: CrewTheme, callbacks: SettingsOverlayCallbacks) {
|
|
@@ -926,7 +932,6 @@ class SettingsOverlay {
|
|
|
926
932
|
}
|
|
927
933
|
case "enum": {
|
|
928
934
|
if (!def.values?.length) return;
|
|
929
|
-
this.submenuSettingId = def.id;
|
|
930
935
|
this.submenu = new SelectSubmenu(
|
|
931
936
|
def.label,
|
|
932
937
|
def.description ?? "",
|
|
@@ -937,17 +942,14 @@ class SettingsOverlay {
|
|
|
937
942
|
this.changedValues.set(def.id, value);
|
|
938
943
|
this.callbacks.onChange(def.id, value);
|
|
939
944
|
this.submenu = null;
|
|
940
|
-
this.submenuSettingId = null;
|
|
941
945
|
},
|
|
942
946
|
() => {
|
|
943
947
|
this.submenu = null;
|
|
944
|
-
this.submenuSettingId = null;
|
|
945
948
|
},
|
|
946
949
|
);
|
|
947
950
|
break;
|
|
948
951
|
}
|
|
949
952
|
case "number": {
|
|
950
|
-
this.submenuSettingId = def.id;
|
|
951
953
|
this.submenu = new TextinputSubmenu(
|
|
952
954
|
def.label,
|
|
953
955
|
def.description ?? "",
|
|
@@ -963,17 +965,14 @@ class SettingsOverlay {
|
|
|
963
965
|
this.callbacks.onChange(def.id, undefined);
|
|
964
966
|
}
|
|
965
967
|
this.submenu = null;
|
|
966
|
-
this.submenuSettingId = null;
|
|
967
968
|
},
|
|
968
969
|
() => {
|
|
969
970
|
this.submenu = null;
|
|
970
|
-
this.submenuSettingId = null;
|
|
971
971
|
},
|
|
972
972
|
);
|
|
973
973
|
break;
|
|
974
974
|
}
|
|
975
975
|
case "string": {
|
|
976
|
-
this.submenuSettingId = def.id;
|
|
977
976
|
this.submenu = new TextinputSubmenu(
|
|
978
977
|
def.label,
|
|
979
978
|
def.description ?? "",
|
|
@@ -983,11 +982,9 @@ class SettingsOverlay {
|
|
|
983
982
|
this.changedValues.set(def.id, value || undefined);
|
|
984
983
|
this.callbacks.onChange(def.id, value || undefined);
|
|
985
984
|
this.submenu = null;
|
|
986
|
-
this.submenuSettingId = null;
|
|
987
985
|
},
|
|
988
986
|
() => {
|
|
989
987
|
this.submenu = null;
|
|
990
|
-
this.submenuSettingId = null;
|
|
991
988
|
},
|
|
992
989
|
);
|
|
993
990
|
break;
|
|
@@ -1000,11 +997,9 @@ class SettingsOverlay {
|
|
|
1000
997
|
this.changedValues.set("agents.overrides", overrides);
|
|
1001
998
|
this.callbacks.onChange("agents.overrides", overrides);
|
|
1002
999
|
this.submenu = null;
|
|
1003
|
-
this.submenuSettingId = null;
|
|
1004
1000
|
},
|
|
1005
1001
|
() => {
|
|
1006
1002
|
this.submenu = null;
|
|
1007
|
-
this.submenuSettingId = null;
|
|
1008
1003
|
},
|
|
1009
1004
|
);
|
|
1010
1005
|
break;
|
|
@@ -1015,7 +1010,6 @@ class SettingsOverlay {
|
|
|
1015
1010
|
typeof this.changedValues.get(def.id) === "string"
|
|
1016
1011
|
? (this.changedValues.get(def.id) as string)
|
|
1017
1012
|
: ((currentValueFor(this.config, def.id) as string | undefined) ?? "");
|
|
1018
|
-
this.submenuSettingId = def.id;
|
|
1019
1013
|
this.submenu = new SelectSubmenu(
|
|
1020
1014
|
def.label,
|
|
1021
1015
|
def.description ?? "",
|
|
@@ -1026,11 +1020,9 @@ class SettingsOverlay {
|
|
|
1026
1020
|
this.changedValues.set(def.id, value);
|
|
1027
1021
|
this.callbacks.onAction?.(def.action!, value);
|
|
1028
1022
|
this.submenu = null;
|
|
1029
|
-
this.submenuSettingId = null;
|
|
1030
1023
|
},
|
|
1031
1024
|
() => {
|
|
1032
1025
|
this.submenu = null;
|
|
1033
|
-
this.submenuSettingId = null;
|
|
1034
1026
|
},
|
|
1035
1027
|
);
|
|
1036
1028
|
break;
|
package/src/ui/snapshot-types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CrewAgentRecord } from "../runtime/crew-agent-runtime.ts";
|
|
2
|
-
import type { TeamEvent } from "../state/event-log.ts";
|
|
2
|
+
import type { TeamEvent } from "../state/event-log/event-log.ts";
|
|
3
3
|
import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
4
4
|
import type { DwfPhaseState } from "./dwf-phase-display.ts";
|
|
5
5
|
|
package/src/ui/status-colors.ts
CHANGED
|
@@ -30,7 +30,6 @@ export function colorForStatus(status: RunStatus): CrewThemeColor {
|
|
|
30
30
|
return "warning";
|
|
31
31
|
case "needs_attention":
|
|
32
32
|
return "warning";
|
|
33
|
-
case "queued":
|
|
34
33
|
default:
|
|
35
34
|
return "dim";
|
|
36
35
|
}
|
|
@@ -100,7 +99,6 @@ function glyphColor(glyph: string): CrewThemeColor {
|
|
|
100
99
|
case "◦":
|
|
101
100
|
case "·":
|
|
102
101
|
return "dim";
|
|
103
|
-
case "▶":
|
|
104
102
|
default:
|
|
105
103
|
return "accent";
|
|
106
104
|
}
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
32
|
import { writeFileSync } from "node:fs";
|
|
33
|
-
import { listLiveAgents } from "../runtime/live-agent-manager.ts";
|
|
33
|
+
import { listLiveAgents } from "../runtime/live-session/live-agent-manager.ts";
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Minimal UI surface this module needs. Deliberately narrower than
|
package/src/ui/theme-adapter.ts
CHANGED
|
@@ -94,12 +94,6 @@ export const THEME_COLOR_FALLBACKS: Record<CrewThemeColor, string> = {
|
|
|
94
94
|
bashMode: "\x1b[32m",
|
|
95
95
|
};
|
|
96
96
|
|
|
97
|
-
/** Map a thinking intensity level (0–5) to a theme color slot. */
|
|
98
|
-
export function thinkingColorForLevel(level: number): CrewThemeColor {
|
|
99
|
-
const slots: CrewThemeColor[] = ["thinkingOff", "thinkingMinimal", "thinkingLow", "thinkingMedium", "thinkingHigh", "thinkingXhigh"];
|
|
100
|
-
return slots[Math.min(Math.max(level, 0), 5)] ?? "thinkingOff";
|
|
101
|
-
}
|
|
102
|
-
|
|
103
97
|
export interface CrewTheme {
|
|
104
98
|
fg(color: CrewThemeColor, text: string): string;
|
|
105
99
|
bg?(color: CrewThemeBg, text: string): string;
|
|
@@ -202,6 +196,31 @@ function asUnsubscribe(value: unknown): Unsubscribe | undefined {
|
|
|
202
196
|
return undefined;
|
|
203
197
|
}
|
|
204
198
|
|
|
199
|
+
/**
|
|
200
|
+
* Register a "change" listener through whichever event API the theme object
|
|
201
|
+
* exposes (DOM EventTarget, Node EventEmitter `on`/`off`, or its legacy
|
|
202
|
+
* `addListener`/`removeListener` alias) and return a teardown that removes it.
|
|
203
|
+
* A registration is only performed when a paired removal exists, guaranteeing
|
|
204
|
+
* no orphaned listener (UI-6). Returns undefined when no recognised event API
|
|
205
|
+
* is present so the caller falls back to signature polling (UI-11).
|
|
206
|
+
*/
|
|
207
|
+
function registerChangeListener(record: Record<string, unknown>, emit: () => void): Unsubscribe | undefined {
|
|
208
|
+
const apis: Array<{ register: string; revoke: string }> = [
|
|
209
|
+
{ register: "addEventListener", revoke: "removeEventListener" },
|
|
210
|
+
{ register: "on", revoke: "off" },
|
|
211
|
+
{ register: "addListener", revoke: "removeListener" },
|
|
212
|
+
];
|
|
213
|
+
for (const { register, revoke } of apis) {
|
|
214
|
+
const registerFn = record[register];
|
|
215
|
+
const revokeFn = record[revoke];
|
|
216
|
+
if (typeof registerFn === "function" && typeof revokeFn === "function") {
|
|
217
|
+
(registerFn as (...args: unknown[]) => void).call(record, "change", emit);
|
|
218
|
+
return () => (revokeFn as (...args: unknown[]) => void).call(record, "change", emit);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
|
|
205
224
|
function startThemeSourceSubscription(theme: object, subscription: ThemeSourceSubscription): void {
|
|
206
225
|
const record = theme as Record<string, unknown>;
|
|
207
226
|
const emit = () => {
|
|
@@ -212,12 +231,13 @@ function startThemeSourceSubscription(theme: object, subscription: ThemeSourceSu
|
|
|
212
231
|
subscription.unsubscribeSource = asUnsubscribe(result);
|
|
213
232
|
return;
|
|
214
233
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
234
|
+
// Event-driven detection (UI-6/UI-11): register through whichever event API
|
|
235
|
+
// the theme exposes. registerChangeListener only registers when a paired
|
|
236
|
+
// removal exists, so every listener is teardown-able. Falls back to polling
|
|
237
|
+
// below when no event API is recognised.
|
|
238
|
+
const removeListener = registerChangeListener(record, emit);
|
|
239
|
+
if (removeListener) {
|
|
240
|
+
subscription.unsubscribeSource = removeListener;
|
|
221
241
|
return;
|
|
222
242
|
}
|
|
223
243
|
subscription.pollTimer = setInterval(() => {
|
|
@@ -230,7 +250,7 @@ function startThemeSourceSubscription(theme: object, subscription: ThemeSourceSu
|
|
|
230
250
|
}
|
|
231
251
|
|
|
232
252
|
export function subscribeThemeChange(theme: unknown, callback: () => void): () => void {
|
|
233
|
-
if (!theme || typeof theme !== "object") return () =>
|
|
253
|
+
if (!theme || typeof theme !== "object") return () => undefined;
|
|
234
254
|
const key = theme;
|
|
235
255
|
let subscription = themeSubscriptions.get(key);
|
|
236
256
|
if (!subscription) {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { type Container, Text, visibleWidth } from "@earendil-works/pi-tui";
|
|
10
10
|
import type { CrewAgentRecord } from "../../runtime/crew-agent-runtime.ts";
|
|
11
11
|
import { truncateToWidth } from "../../utils/visual.ts";
|
|
12
|
+
import type { CrewComponent } from "../component.ts";
|
|
12
13
|
import { formatDuration, formatTokens, truncLine } from "../format-helpers.ts";
|
|
13
14
|
import { spinnerFrame } from "../spinner.ts";
|
|
14
15
|
import type { CrewTheme } from "../theme-adapter.ts";
|
|
@@ -31,6 +32,13 @@ export interface ToolRenderer {
|
|
|
31
32
|
|
|
32
33
|
export type Component = Container | Text;
|
|
33
34
|
|
|
35
|
+
// PR-G3 (UI-3): `Container | Text` structurally satisfies the shared
|
|
36
|
+
// CrewComponent contract — both come from @earendil-works/pi-tui and each
|
|
37
|
+
// implements the library `Component` interface (render + invalidate). The
|
|
38
|
+
// assertion below makes the relationship compiler-enforced without changing
|
|
39
|
+
// the exported union type or any behavior.
|
|
40
|
+
type _AssertComponentIsCrewComponent = Container extends CrewComponent ? (Text extends CrewComponent ? true : never) : never;
|
|
41
|
+
|
|
34
42
|
// ── ANSI-aware padding ─────────────────────────────────────────────────
|
|
35
43
|
|
|
36
44
|
/** Pad a string (which may contain ANSI codes) to a target VISUAL width. */
|
|
@@ -3,6 +3,7 @@ import { agentOutputPath, readCrewAgents } from "../runtime/crew-agent-records.t
|
|
|
3
3
|
import type { TeamRunManifest } from "../state/types.ts";
|
|
4
4
|
import { resolveRealContainedPath } from "../utils/safe-paths.ts";
|
|
5
5
|
import { pad, truncate, truncateToVisualLines } from "../utils/visual.ts";
|
|
6
|
+
import type { InteractiveComponent } from "./component.ts";
|
|
6
7
|
import { renderDiff } from "./render-diff.ts";
|
|
7
8
|
import { colorForStatus, iconForStatus, type RunStatus } from "./status-colors.ts";
|
|
8
9
|
import { highlightCode, highlightJson } from "./syntax-highlight.ts";
|
|
@@ -10,11 +11,11 @@ import type { CrewTheme } from "./theme-adapter.ts";
|
|
|
10
11
|
import { asCrewTheme, subscribeThemeChange } from "./theme-adapter.ts";
|
|
11
12
|
import { DEFAULT_TRANSCRIPT_TAIL_BYTES, getTranscriptCacheEntry, readTranscriptLinesCached } from "./transcript-cache.ts";
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
// PR-G3 (UI-3): alias the shared InteractiveComponent contract (render +
|
|
15
|
+
// invalidate + handleInput). Structurally identical to the previous local
|
|
16
|
+
// declaration — no behavior change. DurableTextViewer / DurableTranscriptViewer
|
|
17
|
+
// also have an extra `dispose()`, which is allowed (superset).
|
|
18
|
+
type Component = InteractiveComponent;
|
|
18
19
|
|
|
19
20
|
type TranscriptTheme = CrewTheme;
|
|
20
21
|
|
|
@@ -282,7 +283,9 @@ export class DurableTextViewer implements Component {
|
|
|
282
283
|
this.unsubscribeTheme = subscribeThemeChange(theme, () => this.invalidate());
|
|
283
284
|
}
|
|
284
285
|
|
|
285
|
-
invalidate(): void {
|
|
286
|
+
invalidate(): void {
|
|
287
|
+
/* no-op */
|
|
288
|
+
}
|
|
286
289
|
|
|
287
290
|
dispose(): void {
|
|
288
291
|
this.unsubscribeTheme();
|
|
@@ -344,23 +347,38 @@ export class DurableTranscriptViewer implements Component {
|
|
|
344
347
|
private fullTranscript = false;
|
|
345
348
|
private maxTailBytes: number;
|
|
346
349
|
private readonly unsubscribeTheme: () => void;
|
|
350
|
+
// UI-9: transcript content is read once when the viewer opens and then
|
|
351
|
+
// reused for per-keypress scroll math, so handleInput no longer performs a
|
|
352
|
+
// synchronous fs read on every keystroke. The cache is refreshed on the
|
|
353
|
+
// explicit full/tail toggle ("f") and kept fresh by the periodic render()
|
|
354
|
+
// call (which still reads, TTL-cached at ~500ms). `readTranscript` defaults
|
|
355
|
+
// to the module function and is overridable for tests.
|
|
356
|
+
private readTranscript: typeof readRunTranscript;
|
|
357
|
+
private cached: ReadRunTranscriptResult;
|
|
347
358
|
|
|
348
359
|
constructor(
|
|
349
360
|
manifest: TeamRunManifest,
|
|
350
361
|
theme: unknown,
|
|
351
362
|
done: (result: undefined) => void,
|
|
352
363
|
taskId?: string,
|
|
353
|
-
options: { maxTailBytes?: number } = {},
|
|
364
|
+
options: { maxTailBytes?: number; readTranscript?: typeof readRunTranscript } = {},
|
|
354
365
|
) {
|
|
355
366
|
this.manifest = manifest;
|
|
356
367
|
this.theme = asCrewTheme(theme);
|
|
357
368
|
this.done = done;
|
|
358
369
|
this.taskId = taskId;
|
|
359
370
|
this.maxTailBytes = options.maxTailBytes ?? DEFAULT_TRANSCRIPT_TAIL_BYTES;
|
|
371
|
+
this.readTranscript = options.readTranscript ?? readRunTranscript;
|
|
360
372
|
this.unsubscribeTheme = subscribeThemeChange(theme, () => this.invalidate());
|
|
373
|
+
this.cached = this.readTranscript(this.manifest, this.taskId, {
|
|
374
|
+
full: this.fullTranscript,
|
|
375
|
+
maxTailBytes: this.maxTailBytes,
|
|
376
|
+
});
|
|
361
377
|
}
|
|
362
378
|
|
|
363
|
-
invalidate(): void {
|
|
379
|
+
invalidate(): void {
|
|
380
|
+
/* no-op */
|
|
381
|
+
}
|
|
364
382
|
|
|
365
383
|
dispose(): void {
|
|
366
384
|
this.unsubscribeTheme();
|
|
@@ -371,10 +389,7 @@ export class DurableTranscriptViewer implements Component {
|
|
|
371
389
|
this.done(undefined);
|
|
372
390
|
return;
|
|
373
391
|
}
|
|
374
|
-
const content =
|
|
375
|
-
full: this.fullTranscript,
|
|
376
|
-
maxTailBytes: this.maxTailBytes,
|
|
377
|
-
}).lines;
|
|
392
|
+
const content = this.cached.lines;
|
|
378
393
|
const maxScroll = Math.max(0, content.length - this.lastHeight);
|
|
379
394
|
if (data === "k" || data === "\u001b[A") {
|
|
380
395
|
this.scroll = Math.max(0, this.scroll - 1);
|
|
@@ -400,14 +415,23 @@ export class DurableTranscriptViewer implements Component {
|
|
|
400
415
|
this.fullTranscript = !this.fullTranscript;
|
|
401
416
|
this.scroll = 0;
|
|
402
417
|
this.autoScroll = !this.fullTranscript;
|
|
418
|
+
// The full/tail toggle changes the read options, so refresh the cache
|
|
419
|
+
// with a single explicit read rather than per keystroke.
|
|
420
|
+
this.cached = this.readTranscript(this.manifest, this.taskId, {
|
|
421
|
+
full: this.fullTranscript,
|
|
422
|
+
maxTailBytes: this.maxTailBytes,
|
|
423
|
+
});
|
|
403
424
|
}
|
|
404
425
|
}
|
|
405
426
|
|
|
406
427
|
render(width: number): string[] {
|
|
407
|
-
const data =
|
|
428
|
+
const data = this.readTranscript(this.manifest, this.taskId, {
|
|
408
429
|
full: this.fullTranscript,
|
|
409
430
|
maxTailBytes: this.maxTailBytes,
|
|
410
431
|
});
|
|
432
|
+
// Keep the per-keypress cache in sync with the latest rendered content
|
|
433
|
+
// (the read is TTL-cached at ~500ms, so this stays cheap on each tick).
|
|
434
|
+
this.cached = data;
|
|
411
435
|
return renderViewerBase(
|
|
412
436
|
{
|
|
413
437
|
theme: this.theme,
|
package/src/ui/widget/index.ts
CHANGED
|
@@ -100,12 +100,26 @@ const onResize = (): void => {
|
|
|
100
100
|
}, 120);
|
|
101
101
|
};
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
/**
|
|
104
|
+
* F-1: install the single, guarded terminal-resize listener (exported for
|
|
105
|
+
* testability). The stdout "resize" listener is only registered when it can
|
|
106
|
+
* ALSO be removed later (off()/removeListener()); otherwise it is skipped so
|
|
107
|
+
* it can never leak across widget reinstalls (UI-7).
|
|
108
|
+
*/
|
|
109
|
+
export function installResizeListener(): void {
|
|
104
110
|
if (resizeListenerInstalled) return;
|
|
105
111
|
resizeListenerInstalled = true;
|
|
106
112
|
process.on("SIGWINCH", onResize);
|
|
107
113
|
// Windows has no SIGWINCH; Node emits "resize" on stdout instead.
|
|
108
|
-
|
|
114
|
+
// Guard (UI-7): only register when removal is possible too. Older runtimes
|
|
115
|
+
// or mock stdouts that expose only `on` would otherwise add a listener that
|
|
116
|
+
// uninstallResizeListener() can never address → leak on every reinstall.
|
|
117
|
+
if (
|
|
118
|
+
typeof process.stdout?.on === "function" &&
|
|
119
|
+
(typeof process.stdout?.off === "function" || typeof process.stdout?.removeListener === "function")
|
|
120
|
+
) {
|
|
121
|
+
process.stdout.on("resize", onResize);
|
|
122
|
+
}
|
|
109
123
|
}
|
|
110
124
|
|
|
111
125
|
/**
|
|
@@ -123,15 +137,24 @@ export function uninstallResizeListener(): void {
|
|
|
123
137
|
activeResizeTarget = undefined;
|
|
124
138
|
process.off("SIGWINCH", onResize);
|
|
125
139
|
// Windows has no SIGWINCH; Node emits "resize" on stdout instead.
|
|
126
|
-
|
|
140
|
+
// Remove via whichever API is present. The listener was only registered
|
|
141
|
+
// when removal was possible (UI-7), so at most one of these applies; both
|
|
142
|
+
// are no-ops if no listener was ever added.
|
|
143
|
+
if (typeof process.stdout?.off === "function") {
|
|
144
|
+
process.stdout.off("resize", onResize);
|
|
145
|
+
} else if (typeof process.stdout?.removeListener === "function") {
|
|
146
|
+
process.stdout.removeListener("resize", onResize);
|
|
147
|
+
}
|
|
127
148
|
}
|
|
128
149
|
|
|
129
|
-
|
|
150
|
+
import type { CrewComponent } from "../component.ts";
|
|
130
151
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
152
|
+
// ── Widget Component ──────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
// PR-G3 (UI-3): extend the shared CrewComponent contract (render +
|
|
155
|
+
// invalidate). Structurally identical to the previous declaration — no
|
|
156
|
+
// behavior change.
|
|
157
|
+
interface WidgetComponent extends CrewComponent {}
|
|
135
158
|
|
|
136
159
|
class CrewWidgetComponent implements WidgetComponent {
|
|
137
160
|
private readonly model: CrewWidgetModel;
|
|
@@ -302,7 +325,7 @@ class CrewWidgetComponent implements WidgetComponent {
|
|
|
302
325
|
|
|
303
326
|
// ── Re-export listLiveAgents for buildSignature ───────────────────────
|
|
304
327
|
|
|
305
|
-
import { listLiveAgents } from "../../runtime/live-agent-manager.ts";
|
|
328
|
+
import { listLiveAgents } from "../../runtime/live-session/live-agent-manager.ts";
|
|
306
329
|
|
|
307
330
|
// ── Public API ────────────────────────────────────────────────────────
|
|
308
331
|
|
|
@@ -5,11 +5,65 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { CrewAgentRecord } from "../../runtime/crew-agent-runtime.ts";
|
|
8
|
-
import type { LiveAgentHandle } from "../../runtime/live-agent-manager.ts";
|
|
8
|
+
import type { LiveAgentHandle } from "../../runtime/live-session/live-agent-manager.ts";
|
|
9
9
|
import { getTaskUsage } from "../../runtime/usage-tracker.ts";
|
|
10
10
|
import { visibleWidth } from "../../utils/visual.ts";
|
|
11
11
|
import { computeLiveDurationMs } from "../live-duration.ts";
|
|
12
12
|
|
|
13
|
+
// ── No-color mode (UI-10) ─────────────────────────────────────────────
|
|
14
|
+
// Comprehensive color/emoji suppression. Color + emoji formatting is
|
|
15
|
+
// disabled when (a) NO_COLOR is set to a non-empty value (de-facto standard,
|
|
16
|
+
// https://bixense.com/clicolors/), OR (b) stdout is not a TTY (piped/
|
|
17
|
+
// redirected — escape codes & wide glyphs are noise there). Detected once at
|
|
18
|
+
// module init; `paint()` and the notification-badge emoji decision consult
|
|
19
|
+
// `colorEnabled`, so EVERY formatter in this file emits plain strings when the
|
|
20
|
+
// mode is active. Previously (line ~179) only the notification badge checked
|
|
21
|
+
// `NO_COLOR !== "1"` — a partial check that ignored the broader NO_COLOR-any-
|
|
22
|
+
// value standard and the non-TTY case, so color codes could still leak to
|
|
23
|
+
// pipes/logs. This makes the suppression uniform across the file.
|
|
24
|
+
|
|
25
|
+
const RESET = "\x1b[0m";
|
|
26
|
+
const ANSI_SGR_RE = /\u001b\[[0-9;]*m/g;
|
|
27
|
+
|
|
28
|
+
// Status foreground colors used by the activity formatters below.
|
|
29
|
+
const COLOR_RED = "\x1b[31m"; // failures
|
|
30
|
+
const COLOR_YELLOW = "\x1b[33m"; // needs-attention / warning
|
|
31
|
+
|
|
32
|
+
function computeColorEnabled(): boolean {
|
|
33
|
+
if (process.env.NO_COLOR !== undefined && process.env.NO_COLOR !== "") return false;
|
|
34
|
+
// Treat both `false` and `undefined` (piped/redirected/captured) as non-TTY:
|
|
35
|
+
// color is only enabled when stdout is positively a TTY.
|
|
36
|
+
if (process.stdout?.isTTY !== true) return false;
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let colorEnabled = computeColorEnabled();
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Wrap `text` in an ANSI SGR sequence (e.g. `\x1b[31m`) when color is active.
|
|
44
|
+
* In no-color mode the text is returned verbatim — no escape sequences — so the
|
|
45
|
+
* formatters are safe to feed into logs, pipes, and other non-TTY sinks.
|
|
46
|
+
*/
|
|
47
|
+
function paint(text: string, sgr: string): string {
|
|
48
|
+
if (!colorEnabled || !text) return text;
|
|
49
|
+
return `${sgr}${text}${RESET}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Strip ANSI SGR escape sequences from a string. */
|
|
53
|
+
export function stripAnsi(text: string): string {
|
|
54
|
+
return text.replace(ANSI_SGR_RE, "");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Re-evaluate the color mode from the current env/stdout (repeat of init). */
|
|
58
|
+
export function __resetColorMode(): void {
|
|
59
|
+
colorEnabled = computeColorEnabled();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Test-only: force the color mode into a known state. */
|
|
63
|
+
export function __setColorModeForTest(enabled: boolean): void {
|
|
64
|
+
colorEnabled = enabled;
|
|
65
|
+
}
|
|
66
|
+
|
|
13
67
|
// ── Token formatting ──────────────────────────────────────────────────
|
|
14
68
|
|
|
15
69
|
// V-1: fixed visible widths for per-agent numeric metrics so columns don't
|
|
@@ -111,14 +165,14 @@ export function agentActivity(agent: CrewAgentRecord, liveHandle?: LiveAgentHand
|
|
|
111
165
|
const cleaned = recent.replace(/\s+/g, " ").trim();
|
|
112
166
|
return cleaned.length > 60 ? cleaned.slice(0, 60) + "…" : cleaned;
|
|
113
167
|
}
|
|
114
|
-
if (agent.progress?.activityState === "needs_attention") return "needs attention";
|
|
168
|
+
if (agent.progress?.activityState === "needs_attention") return paint("needs attention", COLOR_YELLOW);
|
|
115
169
|
if (agent.status === "queued") return "queued";
|
|
116
170
|
if (agent.status === "running") {
|
|
117
171
|
const age = agent.startedAt ? Date.now() - new Date(agent.startedAt).getTime() : Infinity;
|
|
118
172
|
if (age < 5000 && !agent.progress?.currentTool) return "spawning…";
|
|
119
173
|
return "thinking…";
|
|
120
174
|
}
|
|
121
|
-
if (agent.status === "failed") return agent.error ?? "failed";
|
|
175
|
+
if (agent.status === "failed") return paint(agent.error ?? "failed", COLOR_RED);
|
|
122
176
|
return "done";
|
|
123
177
|
}
|
|
124
178
|
|
|
@@ -176,7 +230,11 @@ export const NOTIFICATION_BADGE_CAP = 99;
|
|
|
176
230
|
export function notificationBadge(count: number | undefined, env: NodeJS.ProcessEnv = process.env): string {
|
|
177
231
|
if (!count || count <= 0) return "";
|
|
178
232
|
const term = `${env.TERM ?? ""} ${env.WT_SESSION ?? ""} ${env.TERM_PROGRAM ?? ""}`.toLowerCase();
|
|
179
|
-
|
|
233
|
+
// UI-10: emoji is formatting too — gate it on the comprehensive color mode
|
|
234
|
+
// (NO_COLOR / non-TTY) in addition to a per-call env NO_COLOR check (standard:
|
|
235
|
+
// any non-empty value disables) and the dumb-terminal fallback.
|
|
236
|
+
const envNoColor = env.NO_COLOR !== undefined && env.NO_COLOR !== "";
|
|
237
|
+
const supportsEmoji = colorEnabled && !envNoColor && !term.includes("dumb");
|
|
180
238
|
const label = count > NOTIFICATION_BADGE_CAP ? `${NOTIFICATION_BADGE_CAP}+ alerts` : `${count} alerts`;
|
|
181
239
|
return supportsEmoji ? ` · ${label}` : ` [${label}]`;
|
|
182
240
|
}
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { listRecentRuns } from "../../extension/run-index.ts";
|
|
8
|
-
import { reconcileAllStaleRuns } from "../../runtime/crash-recovery.ts";
|
|
9
8
|
import { readCrewAgents } from "../../runtime/crew-agent-records.ts";
|
|
10
9
|
import type { CrewAgentRecord } from "../../runtime/crew-agent-runtime.ts";
|
|
11
|
-
import { evictStaleLiveAgentHandles } from "../../runtime/live-agent-manager.ts";
|
|
10
|
+
import { evictStaleLiveAgentHandles } from "../../runtime/live-session/live-agent-manager.ts";
|
|
12
11
|
import type { ManifestCache } from "../../runtime/manifest-cache.ts";
|
|
13
12
|
import { isDisplayActiveRun } from "../../runtime/process-status.ts";
|
|
13
|
+
import { reconcileAllStaleRuns } from "../../runtime/recovery/crash-recovery.ts";
|
|
14
14
|
import type { TeamRunManifest } from "../../state/types.ts";
|
|
15
15
|
import type { RunSnapshotCache } from "../snapshot-types.ts";
|
|
16
16
|
import type { WidgetRun } from "./widget-types.ts";
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Extracted from crew-widget.ts.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { listLiveAgents } from "../../runtime/live-agent-manager.ts";
|
|
7
|
+
import { listLiveAgents } from "../../runtime/live-session/live-agent-manager.ts";
|
|
8
8
|
import { isFinishedRunStatus } from "../../runtime/process-status.ts";
|
|
9
9
|
import { truncate } from "../../utils/visual.ts";
|
|
10
10
|
import { Box, Text } from "../layout-primitives.ts";
|