pi-crew 0.9.53 → 0.9.55
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 +45 -0
- package/package.json +6 -3
- package/scripts/clean-strip-types.mjs +60 -0
- package/src/adapters/claude-adapter.ts +23 -0
- package/src/adapters/codex-adapter.ts +21 -0
- package/src/adapters/cursor-adapter.ts +17 -0
- package/src/adapters/export-util.ts +143 -0
- package/src/adapters/index.ts +15 -0
- package/src/adapters/registry.ts +18 -0
- package/src/adapters/types.ts +23 -0
- package/src/agents/agent-config.ts +194 -0
- package/src/agents/agent-search.ts +98 -0
- package/src/agents/agent-serializer.ts +38 -0
- package/src/agents/discover-agents.ts +623 -0
- package/src/benchmark/benchmark-runner.ts +313 -0
- package/src/benchmark/feedback-loop.ts +73 -0
- package/src/config/config.ts +1270 -0
- package/src/config/defaults.ts +193 -0
- package/src/config/drift-detector.ts +290 -0
- package/src/config/markers.ts +330 -0
- package/src/config/resilient-parser.ts +117 -0
- package/src/config/role-tools.ts +118 -0
- package/src/config/suggestions.ts +75 -0
- package/src/config/types.ts +280 -0
- package/src/errors.ts +194 -0
- package/src/extension/action-suggestions.ts +117 -0
- package/src/extension/async-notifier.ts +179 -0
- package/src/extension/autonomous-policy.ts +209 -0
- package/src/extension/command-completions.ts +120 -0
- package/src/extension/context-status-injection.ts +185 -0
- package/src/extension/crew-autocomplete.ts +131 -0
- package/src/extension/crew-cleanup.ts +168 -0
- package/src/extension/crew-input-router.ts +93 -0
- package/src/extension/crew-shortcuts.ts +87 -0
- package/src/extension/crew-vibes/cat-frames.ts +18 -0
- package/src/extension/crew-vibes/config.ts +195 -0
- package/src/extension/crew-vibes/figures.ts +101 -0
- package/src/extension/crew-vibes/font-detect.ts +71 -0
- package/src/extension/crew-vibes/footer.ts +292 -0
- package/src/extension/crew-vibes/index.ts +432 -0
- package/src/extension/crew-vibes/provider-usage.ts +396 -0
- package/src/extension/crew-vibes/render.ts +206 -0
- package/src/extension/crew-vibes/speed.ts +286 -0
- package/src/extension/cross-extension-rpc.ts +350 -0
- package/src/extension/help.ts +62 -0
- package/src/extension/import-index.ts +74 -0
- package/src/extension/knowledge-injection.ts +402 -0
- package/src/extension/management.ts +571 -0
- package/src/extension/message-renderers.ts +108 -0
- package/src/extension/notification-router.ts +152 -0
- package/src/extension/notification-sink.ts +54 -0
- package/src/extension/pi-api.ts +56 -0
- package/src/extension/plan-orchestrate.ts +302 -0
- package/src/extension/project-init.ts +170 -0
- package/src/extension/register.ts +113 -0
- package/src/extension/registration/artifact-cleanup.ts +20 -0
- package/src/extension/registration/command-registration.ts +58 -0
- package/src/extension/registration/command-utils.ts +58 -0
- package/src/extension/registration/commands.ts +1228 -0
- package/src/extension/registration/compaction-guard.ts +333 -0
- package/src/extension/registration/context-builder.ts +135 -0
- package/src/extension/registration/crash-recovery-cache.ts +54 -0
- package/src/extension/registration/foreground-run-controller.ts +238 -0
- package/src/extension/registration/hook-registration.ts +119 -0
- package/src/extension/registration/lazy-configurers.ts +124 -0
- package/src/extension/registration/lifecycle-handlers.ts +923 -0
- package/src/extension/registration/lifecycle.ts +259 -0
- package/src/extension/registration/observability.ts +322 -0
- package/src/extension/registration/registration-types.ts +158 -0
- package/src/extension/registration/runtime-cleanup.ts +230 -0
- package/src/extension/registration/subagent-helpers.ts +125 -0
- package/src/extension/registration/subagent-manager-setup.ts +330 -0
- package/src/extension/registration/subagent-tools.ts +456 -0
- package/src/extension/registration/team-tool.ts +225 -0
- package/src/extension/registration/tool-registration.ts +50 -0
- package/src/extension/registration/ui.ts +172 -0
- package/src/extension/registration/viewers.ts +121 -0
- package/src/extension/registration/wire-cross-extension.ts +37 -0
- package/src/extension/result-watcher.ts +139 -0
- package/src/extension/rpc-hmac.ts +256 -0
- package/src/extension/run-bundle-schema.ts +104 -0
- package/src/extension/run-export.ts +97 -0
- package/src/extension/run-import.ts +168 -0
- package/src/extension/run-index.ts +131 -0
- package/src/extension/run-maintenance.ts +190 -0
- package/src/extension/session-summary.ts +18 -0
- package/src/extension/team-manager-command.ts +153 -0
- package/src/extension/team-onboard.ts +180 -0
- package/src/extension/team-recommendation.ts +288 -0
- package/src/extension/team-tool/anchor.ts +172 -0
- package/src/extension/team-tool/api.ts +1244 -0
- package/src/extension/team-tool/auto-summarize.ts +142 -0
- package/src/extension/team-tool/cache-control.ts +19 -0
- package/src/extension/team-tool/cancel.ts +337 -0
- package/src/extension/team-tool/chain-dispatch.ts +95 -0
- package/src/extension/team-tool/chain-executor.ts +379 -0
- package/src/extension/team-tool/config-patch.ts +50 -0
- package/src/extension/team-tool/context.ts +149 -0
- package/src/extension/team-tool/destructive-gate.ts +52 -0
- package/src/extension/team-tool/dispatch/automate.ts +80 -0
- package/src/extension/team-tool/dispatch/control.ts +42 -0
- package/src/extension/team-tool/dispatch/index.ts +95 -0
- package/src/extension/team-tool/dispatch/manage.ts +161 -0
- package/src/extension/team-tool/dispatch/run.ts +53 -0
- package/src/extension/team-tool/dispatch/status.ts +187 -0
- package/src/extension/team-tool/doctor.ts +431 -0
- package/src/extension/team-tool/explain.ts +282 -0
- package/src/extension/team-tool/failure-patterns.ts +118 -0
- package/src/extension/team-tool/goal-wrap.ts +330 -0
- package/src/extension/team-tool/goal.ts +556 -0
- package/src/extension/team-tool/handle-schedule.ts +332 -0
- package/src/extension/team-tool/handle-settings.ts +520 -0
- package/src/extension/team-tool/health-monitor.ts +481 -0
- package/src/extension/team-tool/inspect.ts +88 -0
- package/src/extension/team-tool/intent-policy.ts +45 -0
- package/src/extension/team-tool/lifecycle-actions.ts +615 -0
- package/src/extension/team-tool/orchestrate.ts +98 -0
- package/src/extension/team-tool/parallel-dispatch.ts +183 -0
- package/src/extension/team-tool/plan.ts +50 -0
- package/src/extension/team-tool/respond.ts +150 -0
- package/src/extension/team-tool/run-deadline.ts +66 -0
- package/src/extension/team-tool/run-not-found.ts +49 -0
- package/src/extension/team-tool/run.ts +1051 -0
- package/src/extension/team-tool/status.ts +260 -0
- package/src/extension/team-tool/workflow-manage.ts +261 -0
- package/src/extension/team-tool-types.ts +26 -0
- package/src/extension/team-tool.ts +750 -0
- package/src/extension/tool-result.ts +16 -0
- package/src/extension/validate-resources.ts +108 -0
- package/src/hooks/registry.ts +200 -0
- package/src/hooks/types.ts +69 -0
- package/src/i18n.ts +212 -0
- package/src/observability/correlation.ts +50 -0
- package/src/observability/event-bus.ts +86 -0
- package/src/observability/event-to-metric.ts +170 -0
- package/src/observability/exporters/adapter.ts +27 -0
- package/src/observability/exporters/otlp-exporter.ts +356 -0
- package/src/observability/exporters/prometheus-exporter.ts +54 -0
- package/src/observability/metric-registry.ts +100 -0
- package/src/observability/metric-retention.ts +64 -0
- package/src/observability/metric-sink.ts +99 -0
- package/src/observability/metrics-primitives.ts +219 -0
- package/src/plugins/plugin-define.ts +6 -0
- package/src/plugins/plugin-registry.ts +32 -0
- package/src/plugins/plugins/index.ts +3 -0
- package/src/plugins/plugins/nextjs.ts +19 -0
- package/src/plugins/plugins/vite.ts +10 -0
- package/src/plugins/plugins/vitest.ts +9 -0
- package/src/prompt/prompt-runtime.ts +380 -0
- package/src/runtime/adaptive-plan.ts +563 -0
- package/src/runtime/agent-control.ts +215 -0
- package/src/runtime/agent-memory.ts +81 -0
- package/src/runtime/agent-observability.ts +122 -0
- package/src/runtime/anchor-manager.ts +475 -0
- package/src/runtime/async-marker.ts +32 -0
- package/src/runtime/async-runner.ts +381 -0
- package/src/runtime/attention-events.ts +28 -0
- package/src/runtime/auto-summarize.ts +344 -0
- package/src/runtime/background-runner.ts +840 -0
- package/src/runtime/batch-barrier.ts +147 -0
- package/src/runtime/broker-issuer.ts +39 -0
- package/src/runtime/cancellation-token.ts +99 -0
- package/src/runtime/cancellation.ts +99 -0
- package/src/runtime/capability-inventory.ts +137 -0
- package/src/runtime/chain-parser.ts +237 -0
- package/src/runtime/chain-runner.ts +606 -0
- package/src/runtime/checkpoint.ts +291 -0
- package/src/runtime/child-pi-constants.ts +42 -0
- package/src/runtime/child-pi-kill.ts +180 -0
- package/src/runtime/child-pi-pool.ts +68 -0
- package/src/runtime/child-pi-spawn.ts +287 -0
- package/src/runtime/child-pi-steering.ts +128 -0
- package/src/runtime/child-pi-streams.ts +296 -0
- package/src/runtime/child-pi-transcript.ts +169 -0
- package/src/runtime/child-pi.ts +1105 -0
- package/src/runtime/coalesce-tasks.ts +268 -0
- package/src/runtime/code-summary.ts +292 -0
- package/src/runtime/command-trace.ts +105 -0
- package/src/runtime/compact-pipeline.ts +56 -0
- package/src/runtime/compact-stages/ansi-strip-stage.ts +25 -0
- package/src/runtime/compact-stages/blank-collapse-stage.ts +31 -0
- package/src/runtime/compact-stages/deduplicate-stage.ts +34 -0
- package/src/runtime/compact-stages/head-snap-stage.ts +57 -0
- package/src/runtime/compact-stages/index.ts +23 -0
- package/src/runtime/compact-stages/tail-capture-stage.ts +77 -0
- package/src/runtime/compact-stages/truncation-stage.ts +74 -0
- package/src/runtime/compaction-summary.ts +278 -0
- package/src/runtime/completion-guard.ts +207 -0
- package/src/runtime/concurrency.ts +58 -0
- package/src/runtime/crash-classification.ts +229 -0
- package/src/runtime/crash-recovery.ts +594 -0
- package/src/runtime/crew-agent-records.ts +595 -0
- package/src/runtime/crew-agent-runtime.ts +62 -0
- package/src/runtime/crew-broker-child.ts +88 -0
- package/src/runtime/crew-broker-client.ts +673 -0
- package/src/runtime/crew-broker-tokens.ts +159 -0
- package/src/runtime/crew-broker.ts +1304 -0
- package/src/runtime/crew-hooks.ts +227 -0
- package/src/runtime/cross-extension-rpc.ts +143 -0
- package/src/runtime/custom-tools/irc-tool.ts +282 -0
- package/src/runtime/custom-tools/submit-result-tool.ts +102 -0
- package/src/runtime/deadletter.ts +47 -0
- package/src/runtime/delivery-coordinator.ts +211 -0
- package/src/runtime/delta-conflict.ts +348 -0
- package/src/runtime/deterministic-ast.ts +161 -0
- package/src/runtime/diagnostic-export.ts +171 -0
- package/src/runtime/direct-run.ts +45 -0
- package/src/runtime/dwf-state-store.ts +102 -0
- package/src/runtime/dynamic-workflow-context.ts +1013 -0
- package/src/runtime/dynamic-workflow-runner.ts +325 -0
- package/src/runtime/effectiveness.ts +90 -0
- package/src/runtime/errors/crew-errors.ts +162 -0
- package/src/runtime/event-stream-bridge.ts +98 -0
- package/src/runtime/foreground-control.ts +193 -0
- package/src/runtime/foreground-watchdog.ts +132 -0
- package/src/runtime/global-worker-cap.ts +96 -0
- package/src/runtime/goal-achievement.ts +148 -0
- package/src/runtime/goal-evaluator.ts +365 -0
- package/src/runtime/goal-loop-runner.ts +825 -0
- package/src/runtime/goal-state-store.ts +219 -0
- package/src/runtime/green-contract.ts +55 -0
- package/src/runtime/group-join.ts +267 -0
- package/src/runtime/handoff-manager.ts +598 -0
- package/src/runtime/heartbeat-gradient.ts +36 -0
- package/src/runtime/heartbeat-watcher.ts +239 -0
- package/src/runtime/hidden-handoff.ts +407 -0
- package/src/runtime/important-line-classifier.ts +140 -0
- package/src/runtime/intercom-bridge.ts +187 -0
- package/src/runtime/iteration-hooks.ts +305 -0
- package/src/runtime/live-agent-control.ts +136 -0
- package/src/runtime/live-agent-manager.ts +704 -0
- package/src/runtime/live-control-realtime.ts +56 -0
- package/src/runtime/live-extension-bridge.ts +148 -0
- package/src/runtime/live-irc.ts +97 -0
- package/src/runtime/live-session-health.ts +108 -0
- package/src/runtime/live-session-runtime.ts +1146 -0
- package/src/runtime/loop-gates.ts +128 -0
- package/src/runtime/manifest-cache.ts +375 -0
- package/src/runtime/mcp-proxy.ts +105 -0
- package/src/runtime/metric-parser.ts +36 -0
- package/src/runtime/model-fallback.ts +409 -0
- package/src/runtime/model-resolver.ts +126 -0
- package/src/runtime/model-scope.ts +158 -0
- package/src/runtime/orphan-worker-registry.ts +465 -0
- package/src/runtime/output-validator.ts +202 -0
- package/src/runtime/overflow-recovery.ts +206 -0
- package/src/runtime/parallel-research.ts +68 -0
- package/src/runtime/parallel-utils.ts +160 -0
- package/src/runtime/parent-guard.ts +134 -0
- package/src/runtime/path-overlap.ts +150 -0
- package/src/runtime/peer-dep.ts +292 -0
- package/src/runtime/per-write-validator.ts +181 -0
- package/src/runtime/phase-progress.ts +217 -0
- package/src/runtime/phase-tracker.ts +385 -0
- package/src/runtime/pi-args.ts +655 -0
- package/src/runtime/pi-json-output.ts +170 -0
- package/src/runtime/pi-spawn.ts +273 -0
- package/src/runtime/pipeline-runner.ts +523 -0
- package/src/runtime/plan-templates.ts +201 -0
- package/src/runtime/policy-engine.ts +115 -0
- package/src/runtime/post-checks.ts +142 -0
- package/src/runtime/post-exit-stdio-guard.ts +109 -0
- package/src/runtime/process-lifecycle.ts +491 -0
- package/src/runtime/process-status.ts +154 -0
- package/src/runtime/progress-event-coalescer.ts +44 -0
- package/src/runtime/progress-tracker.ts +124 -0
- package/src/runtime/prose-compressor.ts +162 -0
- package/src/runtime/recovery-recipes.ts +203 -0
- package/src/runtime/replace.ts +570 -0
- package/src/runtime/resilient-edit.ts +153 -0
- package/src/runtime/result-extractor.ts +217 -0
- package/src/runtime/retry-executor.ts +109 -0
- package/src/runtime/retry-runner.ts +336 -0
- package/src/runtime/role-permission.ts +59 -0
- package/src/runtime/run-coalesced-task-group.ts +308 -0
- package/src/runtime/run-drift.ts +219 -0
- package/src/runtime/run-tracker.ts +116 -0
- package/src/runtime/run-worker.ts +78 -0
- package/src/runtime/runtime-policy.ts +29 -0
- package/src/runtime/runtime-resolver.ts +172 -0
- package/src/runtime/runtime-warmup.ts +160 -0
- package/src/runtime/scheduler.ts +353 -0
- package/src/runtime/semaphore.ts +141 -0
- package/src/runtime/sensitive-paths.ts +93 -0
- package/src/runtime/session-resources.ts +25 -0
- package/src/runtime/session-snapshot.ts +59 -0
- package/src/runtime/session-usage.ts +79 -0
- package/src/runtime/settings-store.ts +155 -0
- package/src/runtime/sidechain-output.ts +35 -0
- package/src/runtime/single-agent-compose.ts +84 -0
- package/src/runtime/skill-effectiveness.ts +524 -0
- package/src/runtime/skill-instructions.ts +394 -0
- package/src/runtime/stale-reconciler.ts +680 -0
- package/src/runtime/stream-preview.ts +184 -0
- package/src/runtime/streaming-output.ts +50 -0
- package/src/runtime/subagent-manager.ts +561 -0
- package/src/runtime/subprocess-tool-registry.ts +70 -0
- package/src/runtime/supervisor-contact.ts +64 -0
- package/src/runtime/task-display.ts +52 -0
- package/src/runtime/task-graph-scheduler.ts +227 -0
- package/src/runtime/task-graph.ts +290 -0
- package/src/runtime/task-health.ts +83 -0
- package/src/runtime/task-id.ts +161 -0
- package/src/runtime/task-output-context.ts +602 -0
- package/src/runtime/task-packet.ts +268 -0
- package/src/runtime/task-quality.ts +199 -0
- package/src/runtime/task-runner/capabilities.ts +78 -0
- package/src/runtime/task-runner/child-executor.ts +790 -0
- package/src/runtime/task-runner/context-retrieval.ts +163 -0
- package/src/runtime/task-runner/live-executor.ts +227 -0
- package/src/runtime/task-runner/output-splitter.ts +152 -0
- package/src/runtime/task-runner/post-execution.ts +480 -0
- package/src/runtime/task-runner/pre-execution.ts +365 -0
- package/src/runtime/task-runner/progress.ts +157 -0
- package/src/runtime/task-runner/prompt-builder.ts +278 -0
- package/src/runtime/task-runner/prompt-pipeline.ts +88 -0
- package/src/runtime/task-runner/result-utils.ts +16 -0
- package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
- package/src/runtime/task-runner/run-projection.ts +125 -0
- package/src/runtime/task-runner/scaffold-executor.ts +25 -0
- package/src/runtime/task-runner/state-helpers.ts +176 -0
- package/src/runtime/task-runner/tail-read.ts +34 -0
- package/src/runtime/task-runner.ts +216 -0
- package/src/runtime/team-runner-artifacts.ts +13 -0
- package/src/runtime/team-runner.ts +2396 -0
- package/src/runtime/tool-output-pruner.ts +333 -0
- package/src/runtime/tool-progress.ts +278 -0
- package/src/runtime/usage-tracker.ts +73 -0
- package/src/runtime/verification-gates.ts +579 -0
- package/src/runtime/verification-integrity.ts +108 -0
- package/src/runtime/verification-worktree.ts +186 -0
- package/src/runtime/worker-heartbeat.ts +25 -0
- package/src/runtime/worker-startup.ts +83 -0
- package/src/runtime/workflow-state.ts +195 -0
- package/src/runtime/workspace-lock.ts +443 -0
- package/src/runtime/workspace-tree.ts +312 -0
- package/src/runtime/yield-handler.ts +224 -0
- package/src/runtime/zombie-scanner.ts +298 -0
- package/src/schema/config-schema.ts +327 -0
- package/src/schema/team-tool-schema.ts +492 -0
- package/src/schema/validation-types.ts +159 -0
- package/src/skills/discover-skills.ts +203 -0
- package/src/skills/skill-templates.ts +456 -0
- package/src/skills/validate.ts +285 -0
- package/src/state/active-run-registry.ts +439 -0
- package/src/state/artifact-store.ts +176 -0
- package/src/state/atomic-write.ts +970 -0
- package/src/state/blob-store.ts +308 -0
- package/src/state/contracts.ts +160 -0
- package/src/state/crew-init.ts +269 -0
- package/src/state/decision-ledger.ts +372 -0
- package/src/state/event-log-rotation.ts +399 -0
- package/src/state/event-log.ts +1465 -0
- package/src/state/event-reconstructor.ts +229 -0
- package/src/state/gitignore-manager.ts +46 -0
- package/src/state/health-store.ts +79 -0
- package/src/state/hook-instinct-bridge.ts +94 -0
- package/src/state/hook-integrations.ts +51 -0
- package/src/state/instinct-store.ts +275 -0
- package/src/state/jsonl-writer.ts +107 -0
- package/src/state/locks.ts +562 -0
- package/src/state/mailbox.ts +916 -0
- package/src/state/observation-store.ts +176 -0
- package/src/state/run-cache.ts +193 -0
- package/src/state/run-graph.ts +183 -0
- package/src/state/run-metrics.ts +165 -0
- package/src/state/schedule.ts +166 -0
- package/src/state/session-state-map.ts +51 -0
- package/src/state/state-store.ts +973 -0
- package/src/state/task-claims.ts +61 -0
- package/src/state/tiered-eval.ts +480 -0
- package/src/state/types-eval.ts +58 -0
- package/src/state/types.ts +447 -0
- package/src/state/usage.ts +138 -0
- package/src/state/worker-atomic-writer.ts +198 -0
- package/src/subagents/async-entry.ts +1 -0
- package/src/subagents/index.ts +3 -0
- package/src/subagents/live/control.ts +1 -0
- package/src/subagents/live/manager.ts +1 -0
- package/src/subagents/live/realtime.ts +1 -0
- package/src/subagents/live/session-runtime.ts +1 -0
- package/src/subagents/manager.ts +1 -0
- package/src/subagents/spawn.ts +1 -0
- package/src/teams/discover-teams.ts +187 -0
- package/src/teams/team-config.ts +27 -0
- package/src/teams/team-serializer.ts +38 -0
- package/src/tools/safe-bash-extension.ts +54 -0
- package/src/tools/safe-bash.ts +505 -0
- package/src/types/diff.d.ts +18 -0
- package/src/types/new-api-types.ts +35 -0
- package/src/ui/agent-management-overlay.ts +160 -0
- package/src/ui/card-colors.ts +139 -0
- package/src/ui/crew-footer.ts +102 -0
- package/src/ui/crew-select-list.ts +112 -0
- package/src/ui/dashboard-panes/agents-pane.ts +164 -0
- package/src/ui/dashboard-panes/cancellation-pane.ts +66 -0
- package/src/ui/dashboard-panes/capability-pane.ts +77 -0
- package/src/ui/dashboard-panes/health-pane.ts +31 -0
- package/src/ui/dashboard-panes/mailbox-pane.ts +35 -0
- package/src/ui/dashboard-panes/metrics-pane.ts +37 -0
- package/src/ui/dashboard-panes/progress-pane.ts +38 -0
- package/src/ui/dashboard-panes/transcript-pane.ts +10 -0
- package/src/ui/deploy-bundled-themes.ts +71 -0
- package/src/ui/dwf-phase-display.ts +151 -0
- package/src/ui/dynamic-border.ts +35 -0
- package/src/ui/format-helpers.ts +31 -0
- package/src/ui/heartbeat-aggregator.ts +74 -0
- package/src/ui/key-utils.ts +42 -0
- package/src/ui/keybinding-map.ts +248 -0
- package/src/ui/layout-primitives.ts +106 -0
- package/src/ui/live-conversation-overlay.ts +183 -0
- package/src/ui/live-duration.ts +55 -0
- package/src/ui/live-run-sidebar.ts +293 -0
- package/src/ui/loaders.ts +6 -0
- package/src/ui/mascot.ts +449 -0
- package/src/ui/overlays/agent-picker-overlay.ts +66 -0
- package/src/ui/overlays/confirm-overlay.ts +60 -0
- package/src/ui/overlays/help-overlay.ts +177 -0
- package/src/ui/overlays/mailbox-compose-overlay.ts +181 -0
- package/src/ui/overlays/mailbox-compose-preview.ts +78 -0
- package/src/ui/overlays/mailbox-detail-overlay.ts +155 -0
- package/src/ui/pi-ui-compat.ts +68 -0
- package/src/ui/powerbar-publisher.ts +459 -0
- package/src/ui/render-coalescer.ts +101 -0
- package/src/ui/render-diff.ts +160 -0
- package/src/ui/render-scheduler.ts +278 -0
- package/src/ui/run-action-dispatcher.ts +182 -0
- package/src/ui/run-dashboard.ts +910 -0
- package/src/ui/run-event-bus.ts +381 -0
- package/src/ui/run-snapshot-cache.ts +1057 -0
- package/src/ui/settings-overlay.ts +1067 -0
- package/src/ui/shared-overlay-scheduler.ts +96 -0
- package/src/ui/snapshot-types.ts +90 -0
- package/src/ui/spinner.ts +17 -0
- package/src/ui/status-colors.ts +143 -0
- package/src/ui/syntax-highlight.ts +136 -0
- package/src/ui/terminal-status.ts +269 -0
- package/src/ui/theme-adapter.ts +271 -0
- package/src/ui/theme-discovery.ts +199 -0
- package/src/ui/tool-progress-formatter.ts +93 -0
- package/src/ui/tool-renderers/brief-mode.ts +225 -0
- package/src/ui/tool-renderers/index.ts +726 -0
- package/src/ui/transcript-cache.ts +128 -0
- package/src/ui/transcript-entries.ts +256 -0
- package/src/ui/transcript-viewer.ts +424 -0
- package/src/ui/widget/index.ts +418 -0
- package/src/ui/widget/widget-formatters.ts +182 -0
- package/src/ui/widget/widget-model.ts +110 -0
- package/src/ui/widget/widget-renderer.ts +182 -0
- package/src/ui/widget/widget-types.ts +36 -0
- package/src/utils/bm25-search.ts +235 -0
- package/src/utils/completion-dedupe.ts +63 -0
- package/src/utils/conflict-detect.ts +668 -0
- package/src/utils/env-allowlist.ts +30 -0
- package/src/utils/env-filter.ts +155 -0
- package/src/utils/file-coalescer.ts +98 -0
- package/src/utils/fingerprint.ts +180 -0
- package/src/utils/frontmatter.ts +70 -0
- package/src/utils/fs-watch.ts +37 -0
- package/src/utils/gh-protocol.ts +556 -0
- package/src/utils/git.ts +260 -0
- package/src/utils/guards.ts +107 -0
- package/src/utils/ids.ts +24 -0
- package/src/utils/incremental-reader.ts +220 -0
- package/src/utils/internal-error.ts +10 -0
- package/src/utils/names.ts +36 -0
- package/src/utils/ndjson.ts +115 -0
- package/src/utils/paths.ts +227 -0
- package/src/utils/project-detector.ts +160 -0
- package/src/utils/redaction.ts +370 -0
- package/src/utils/resolve-shell.ts +36 -0
- package/src/utils/run-watcher-registry.ts +152 -0
- package/src/utils/safe-paths.ts +393 -0
- package/src/utils/scan-cache.ts +144 -0
- package/src/utils/session-utils.ts +110 -0
- package/src/utils/sleep.ts +54 -0
- package/src/utils/socket-path.ts +164 -0
- package/src/utils/sse-parser.ts +131 -0
- package/src/utils/task-name-generator.ts +337 -0
- package/src/utils/timings.ts +33 -0
- package/src/utils/token-counter.ts +200 -0
- package/src/utils/visual.ts +207 -0
- package/src/workflows/cost-estimator.ts +34 -0
- package/src/workflows/discover-workflows.ts +308 -0
- package/src/workflows/intermediate-store.ts +166 -0
- package/src/workflows/preflight-validator.ts +168 -0
- package/src/workflows/topology-analyzer.ts +203 -0
- package/src/workflows/validate-workflow.ts +50 -0
- package/src/workflows/workflow-config.ts +75 -0
- package/src/workflows/workflow-serializer.ts +32 -0
- package/src/worktree/branch-freshness.ts +112 -0
- package/src/worktree/cleanup.ts +341 -0
- package/src/worktree/worktree-manager.ts +1204 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import * as Diff from "diff";
|
|
2
|
+
import type { CrewTheme } from "./theme-adapter.ts";
|
|
3
|
+
import { asCrewTheme } from "./theme-adapter.ts";
|
|
4
|
+
|
|
5
|
+
interface ParsedDiffLine {
|
|
6
|
+
prefix: string;
|
|
7
|
+
lineNum: string;
|
|
8
|
+
content: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface DiffLineContent {
|
|
12
|
+
lineNum: string;
|
|
13
|
+
content: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function parseDiffLine(line: string): ParsedDiffLine | null {
|
|
17
|
+
const match = line.match(/^([+-\s])(\s*\d*)\s(.*)$/);
|
|
18
|
+
if (!match) return null;
|
|
19
|
+
return { prefix: match[1], lineNum: match[2], content: match[3] };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function replaceTabs(text: string): string {
|
|
23
|
+
return text.replace(/\t/g, " ");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Minimum similarity (fraction of unchanged chars) required before applying
|
|
28
|
+
* word-level intra-line diff highlighting. Below this, the two lines are
|
|
29
|
+
* considered unrelated and rendered plainly to avoid noisy full-line
|
|
30
|
+
* inverse-video (e.g. an empty line replaced by a large block of code).
|
|
31
|
+
*/
|
|
32
|
+
const WORD_DIFF_MIN_SIM = 0.15;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Compute the similarity between two strings as the fraction of unchanged
|
|
36
|
+
* characters relative to the longer of the two strings, using word-level
|
|
37
|
+
* diff to identify the common (unchanged) parts. Returns a value in [0, 1].
|
|
38
|
+
*/
|
|
39
|
+
function computeSimilarity(oldContent: string, newContent: string): number {
|
|
40
|
+
const wordDiff = Diff.diffWords(oldContent, newContent);
|
|
41
|
+
let commonChars = 0;
|
|
42
|
+
for (const part of wordDiff) {
|
|
43
|
+
if (!part.removed && !part.added) {
|
|
44
|
+
commonChars += part.value.length;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const maxLen = Math.max(oldContent.length, newContent.length);
|
|
48
|
+
if (maxLen === 0) return 1; // both empty -> identical
|
|
49
|
+
return commonChars / maxLen;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function renderIntraLineDiff(theme: CrewTheme, oldContent: string, newContent: string): { removedLine: string; addedLine: string } {
|
|
53
|
+
const wordDiff = Diff.diffWords(oldContent, newContent);
|
|
54
|
+
let removedLine = "";
|
|
55
|
+
let addedLine = "";
|
|
56
|
+
let isFirstRemoved = true;
|
|
57
|
+
let isFirstAdded = true;
|
|
58
|
+
|
|
59
|
+
for (const part of wordDiff) {
|
|
60
|
+
if (part.removed) {
|
|
61
|
+
let value = part.value;
|
|
62
|
+
if (isFirstRemoved) {
|
|
63
|
+
const leadingWs = value.match(/^(\s*)/)?.[1] ?? "";
|
|
64
|
+
value = value.slice(leadingWs.length);
|
|
65
|
+
removedLine += leadingWs;
|
|
66
|
+
isFirstRemoved = false;
|
|
67
|
+
}
|
|
68
|
+
if (value) removedLine += theme.inverse?.(value) ?? value;
|
|
69
|
+
} else if (part.added) {
|
|
70
|
+
let value = part.value;
|
|
71
|
+
if (isFirstAdded) {
|
|
72
|
+
const leadingWs = value.match(/^(\s*)/)?.[1] ?? "";
|
|
73
|
+
value = value.slice(leadingWs.length);
|
|
74
|
+
addedLine += leadingWs;
|
|
75
|
+
isFirstAdded = false;
|
|
76
|
+
}
|
|
77
|
+
if (value) addedLine += theme.inverse?.(value) ?? value;
|
|
78
|
+
} else {
|
|
79
|
+
removedLine += part.value;
|
|
80
|
+
addedLine += part.value;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return { removedLine, addedLine };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface RenderDiffOptions {
|
|
88
|
+
filePath?: string;
|
|
89
|
+
theme?: unknown;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function renderDiff(diffText: string, options: RenderDiffOptions = {}): string {
|
|
93
|
+
const theme = asCrewTheme(options.theme);
|
|
94
|
+
const lines = diffText.split("\n");
|
|
95
|
+
const result: string[] = [];
|
|
96
|
+
let i = 0;
|
|
97
|
+
|
|
98
|
+
while (i < lines.length) {
|
|
99
|
+
const line = lines[i] ?? "";
|
|
100
|
+
const parsed = parseDiffLine(line);
|
|
101
|
+
if (!parsed) {
|
|
102
|
+
result.push(theme.fg("toolDiffContext", line));
|
|
103
|
+
i++;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (parsed.prefix === "-") {
|
|
108
|
+
const removedLines: DiffLineContent[] = [];
|
|
109
|
+
while (i < lines.length) {
|
|
110
|
+
const nextParsed = parseDiffLine(lines[i] ?? "");
|
|
111
|
+
if (nextParsed?.prefix !== "-") break;
|
|
112
|
+
removedLines.push({
|
|
113
|
+
lineNum: nextParsed.lineNum,
|
|
114
|
+
content: nextParsed.content,
|
|
115
|
+
});
|
|
116
|
+
i++;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const addedLines: DiffLineContent[] = [];
|
|
120
|
+
while (i < lines.length) {
|
|
121
|
+
const nextParsed = parseDiffLine(lines[i] ?? "");
|
|
122
|
+
if (nextParsed?.prefix !== "+") break;
|
|
123
|
+
addedLines.push({
|
|
124
|
+
lineNum: nextParsed.lineNum,
|
|
125
|
+
content: nextParsed.content,
|
|
126
|
+
});
|
|
127
|
+
i++;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (removedLines.length === 1 && addedLines.length === 1) {
|
|
131
|
+
const oldContent = replaceTabs(removedLines[0]!.content);
|
|
132
|
+
const newContent = replaceTabs(addedLines[0]!.content);
|
|
133
|
+
const similarity = computeSimilarity(oldContent, newContent);
|
|
134
|
+
if (similarity >= WORD_DIFF_MIN_SIM) {
|
|
135
|
+
const { removedLine, addedLine } = renderIntraLineDiff(theme, oldContent, newContent);
|
|
136
|
+
result.push(theme.fg("toolDiffRemoved", `-${removedLines[0]!.lineNum} ${removedLine}`));
|
|
137
|
+
result.push(theme.fg("toolDiffAdded", `+${addedLines[0]!.lineNum} ${addedLine}`));
|
|
138
|
+
} else {
|
|
139
|
+
result.push(theme.fg("toolDiffRemoved", `-${removedLines[0]!.lineNum} ${oldContent}`));
|
|
140
|
+
result.push(theme.fg("toolDiffAdded", `+${addedLines[0]!.lineNum} ${newContent}`));
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
for (const removed of removedLines) {
|
|
144
|
+
result.push(theme.fg("toolDiffRemoved", `-${removed.lineNum} ${replaceTabs(removed.content)}`));
|
|
145
|
+
}
|
|
146
|
+
for (const added of addedLines) {
|
|
147
|
+
result.push(theme.fg("toolDiffAdded", `+${added.lineNum} ${replaceTabs(added.content)}`));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
} else if (parsed.prefix === "+") {
|
|
151
|
+
result.push(theme.fg("toolDiffAdded", `+${parsed.lineNum} ${replaceTabs(parsed.content)}`));
|
|
152
|
+
i++;
|
|
153
|
+
} else {
|
|
154
|
+
result.push(theme.fg("toolDiffContext", ` ${parsed.lineNum} ${replaceTabs(parsed.content)}`));
|
|
155
|
+
i++;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return result.join("\n");
|
|
160
|
+
}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { logInternalError } from "../utils/internal-error.ts";
|
|
2
|
+
|
|
3
|
+
export interface RenderSchedulerEventBus {
|
|
4
|
+
on?: (event: string, handler: (payload: unknown) => void) => (() => void) | void;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface RenderSchedulerOptions {
|
|
8
|
+
debounceMs?: number;
|
|
9
|
+
/**
|
|
10
|
+
* Maximum interval (ms) between auto-refreshes when no event arrives.
|
|
11
|
+
* Can be a function returning a dynamic interval — useful to tick fast
|
|
12
|
+
* while a run is active (e.g., 160ms aligned with spinner) and slow
|
|
13
|
+
* down when idle (e.g., 1000ms) without burning CPU.
|
|
14
|
+
*/
|
|
15
|
+
fallbackMs?: number | (() => number);
|
|
16
|
+
events?: string[];
|
|
17
|
+
onInvalidate?: (payload: unknown) => void;
|
|
18
|
+
/**
|
|
19
|
+
* 1.9: coalesce `onInvalidate` calls per-runId within this window so that
|
|
20
|
+
* bursts of `crew.subagent.completed`-style events touching the same run
|
|
21
|
+
* trigger a single cache invalidate instead of N. Set to 0 to disable.
|
|
22
|
+
*/
|
|
23
|
+
invalidateCoalesceMs?: number;
|
|
24
|
+
/**
|
|
25
|
+
* R1: maximum number of consecutive catch-up renders the fallback loop
|
|
26
|
+
* emits while idle (no real external event) before it stops re-arming.
|
|
27
|
+
* Prevents a hung run from rendering forever. A real event resets the
|
|
28
|
+
* count and re-arms the loop. Defaults to 8.
|
|
29
|
+
*/
|
|
30
|
+
maxIdleFallbackRenders?: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const DEFAULT_EVENTS = [
|
|
34
|
+
"crew.run.created",
|
|
35
|
+
"crew.run.completed",
|
|
36
|
+
"crew.run.failed",
|
|
37
|
+
"crew.run.cancelled",
|
|
38
|
+
"crew.subagent.completed",
|
|
39
|
+
"crew.subagent.failed",
|
|
40
|
+
"crew.mailbox.updated",
|
|
41
|
+
"crew.mailbox.message",
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
/** R4: max exponent for flush re-entrancy backoff (delay = debounceMs * 2^exp). */
|
|
45
|
+
const CAP_BACKOFF_MAX_EXP = 5;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Coordinates UI renders with debounce + fallback polling.
|
|
49
|
+
*
|
|
50
|
+
* Critical: uses recursive setTimeout instead of setInterval + a rendering
|
|
51
|
+
* guard (`rendering` / `pendingRender`) so that when render() takes longer
|
|
52
|
+
* than the fallback interval, callbacks do NOT pile up and storm the event
|
|
53
|
+
* loop. Instead, overlapping schedules are collapsed into a single deferred
|
|
54
|
+
* re-render.
|
|
55
|
+
*/
|
|
56
|
+
export class RenderScheduler {
|
|
57
|
+
private readonly render: () => void;
|
|
58
|
+
private readonly onInvalidate?: (payload: unknown) => void;
|
|
59
|
+
private readonly debounceMs: number;
|
|
60
|
+
private readonly fallbackProvider: () => number;
|
|
61
|
+
private readonly fallbackMs: number;
|
|
62
|
+
private readonly invalidateCoalesceMs: number;
|
|
63
|
+
private readonly maxIdleFallbackRenders: number;
|
|
64
|
+
private debounceTimer: ReturnType<typeof setTimeout> | undefined;
|
|
65
|
+
private fallbackTimer: ReturnType<typeof setTimeout> | undefined;
|
|
66
|
+
private invalidateTimer: ReturnType<typeof setTimeout> | undefined;
|
|
67
|
+
/** runId → most recent payload to forward when the coalesce window flushes. */
|
|
68
|
+
private invalidateBuffer = new Map<string, unknown>();
|
|
69
|
+
private disposed = false;
|
|
70
|
+
private lastEventAt = 0;
|
|
71
|
+
private rendering = false;
|
|
72
|
+
private pendingRender = false;
|
|
73
|
+
/** R1: consecutive idle fallback renders since the last real event. */
|
|
74
|
+
private idleFallbackRenders = 0;
|
|
75
|
+
/** R4: consecutive re-entrancy cap-hits — drives exponential backoff. */
|
|
76
|
+
private consecutiveCapHits = 0;
|
|
77
|
+
private readonly unsubs: Array<() => void> = [];
|
|
78
|
+
|
|
79
|
+
constructor(events: RenderSchedulerEventBus | undefined, render: () => void, options: RenderSchedulerOptions = {}) {
|
|
80
|
+
this.render = render;
|
|
81
|
+
this.onInvalidate = options.onInvalidate;
|
|
82
|
+
this.debounceMs = options.debounceMs ?? 75;
|
|
83
|
+
const fallback = options.fallbackMs ?? 750;
|
|
84
|
+
this.fallbackProvider = typeof fallback === "function" ? fallback : () => fallback;
|
|
85
|
+
this.fallbackMs = typeof fallback === "number" ? fallback : 750;
|
|
86
|
+
this.invalidateCoalesceMs = options.invalidateCoalesceMs ?? 50;
|
|
87
|
+
this.maxIdleFallbackRenders = options.maxIdleFallbackRenders ?? 8;
|
|
88
|
+
for (const event of options.events ?? DEFAULT_EVENTS) this.subscribe(events, event);
|
|
89
|
+
this.fallbackTimer = setTimeout(() => this.fallbackLoop(), this.currentFallbackMs());
|
|
90
|
+
this.fallbackTimer.unref();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private currentFallbackMs(): number {
|
|
94
|
+
try {
|
|
95
|
+
const value = this.fallbackProvider();
|
|
96
|
+
return Number.isFinite(value) && value > 0 ? value : 750;
|
|
97
|
+
} catch (error) {
|
|
98
|
+
logInternalError("render-scheduler.fallbackProvider", error);
|
|
99
|
+
return 750;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
private subscribe(events: RenderSchedulerEventBus | undefined, event: string): void {
|
|
104
|
+
if (!events?.on) return;
|
|
105
|
+
const handler = (payload: unknown): void => this.schedule(payload);
|
|
106
|
+
try {
|
|
107
|
+
const unsub = events.on(event, handler);
|
|
108
|
+
if (typeof unsub === "function") this.unsubs.push(unsub);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
logInternalError("render-scheduler.subscribe", error, event);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Recursive setTimeout — avoids setInterval timer storms. */
|
|
115
|
+
private fallbackLoop(): void {
|
|
116
|
+
if (this.disposed) return;
|
|
117
|
+
const fallbackMs = this.currentFallbackMs();
|
|
118
|
+
if (Date.now() - this.lastEventAt < fallbackMs) {
|
|
119
|
+
// A real external event arrived recently — not idle. Keep polling.
|
|
120
|
+
this.idleFallbackRenders = 0;
|
|
121
|
+
this.fallbackTimer = setTimeout(() => this.fallbackLoop(), fallbackMs);
|
|
122
|
+
this.fallbackTimer.unref();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// R1: truly idle (no real event for `fallbackMs`). `lastEventAt` is only
|
|
126
|
+
// advanced by real external events (schedule()), so this idle check stays
|
|
127
|
+
// honest — unlike the old code which called schedule() here and thereby
|
|
128
|
+
// reset lastEventAt, self-perpetuating a ~fallbackMs render loop forever.
|
|
129
|
+
if (this.idleFallbackRenders >= this.maxIdleFallbackRenders) {
|
|
130
|
+
// Sustained idleness — stop re-arming so a hung run does not render
|
|
131
|
+
// forever. A subsequent real event (schedule()) re-arms the loop.
|
|
132
|
+
this.fallbackTimer = undefined;
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
// Emit one catch-up render WITHOUT advancing lastEventAt (no real event).
|
|
136
|
+
this.armDebouncedRender();
|
|
137
|
+
this.idleFallbackRenders += 1;
|
|
138
|
+
this.fallbackTimer = setTimeout(() => this.fallbackLoop(), this.currentFallbackMs());
|
|
139
|
+
this.fallbackTimer.unref();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
schedule(payload?: unknown): void {
|
|
143
|
+
if (this.disposed) return;
|
|
144
|
+
this.lastEventAt = Date.now();
|
|
145
|
+
this.idleFallbackRenders = 0;
|
|
146
|
+
this.consecutiveCapHits = 0;
|
|
147
|
+
this.invalidate(payload);
|
|
148
|
+
this.armDebouncedRender();
|
|
149
|
+
// R1: if the fallback loop stopped itself after a sustained idle period,
|
|
150
|
+
// a real event means activity resumed — re-arm the safety-net loop.
|
|
151
|
+
if (!this.fallbackTimer) {
|
|
152
|
+
this.fallbackTimer = setTimeout(() => this.fallbackLoop(), this.currentFallbackMs());
|
|
153
|
+
this.fallbackTimer.unref();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Arm (or re-arm) the debounce timer that triggers a single flush. Shared by
|
|
159
|
+
* real-event schedules, the fallback path, and the re-entrancy cap drain.
|
|
160
|
+
* `delay` lets the cap apply exponential backoff (R4) without touching
|
|
161
|
+
* `lastEventAt` or `onInvalidate` (those belong to real external events).
|
|
162
|
+
*/
|
|
163
|
+
private armDebouncedRender(delay: number = this.debounceMs): void {
|
|
164
|
+
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
|
165
|
+
this.debounceTimer = setTimeout(() => {
|
|
166
|
+
this.debounceTimer = undefined;
|
|
167
|
+
this.flush();
|
|
168
|
+
}, delay);
|
|
169
|
+
this.debounceTimer.unref();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* 1.9: forward `onInvalidate` immediately when the payload has no `runId`
|
|
174
|
+
* (we cannot dedup it). When it carries a runId, buffer the latest payload
|
|
175
|
+
* per runId and flush on a coalesce timer so high-frequency event bursts
|
|
176
|
+
* (`crew.subagent.completed` for parallel tasks) collapse into one
|
|
177
|
+
* invalidate per affected run.
|
|
178
|
+
*/
|
|
179
|
+
private invalidate(payload: unknown): void {
|
|
180
|
+
try {
|
|
181
|
+
const runId =
|
|
182
|
+
typeof payload === "object" &&
|
|
183
|
+
payload !== null &&
|
|
184
|
+
"runId" in payload &&
|
|
185
|
+
typeof (payload as { runId: unknown }).runId === "string"
|
|
186
|
+
? (payload as { runId: string }).runId
|
|
187
|
+
: undefined;
|
|
188
|
+
if (runId === undefined || this.invalidateCoalesceMs <= 0) {
|
|
189
|
+
this.onInvalidate?.(payload);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
this.invalidateBuffer.set(runId, payload);
|
|
193
|
+
if (!this.invalidateTimer) {
|
|
194
|
+
this.invalidateTimer = setTimeout(() => this.flushInvalidate(), this.invalidateCoalesceMs);
|
|
195
|
+
this.invalidateTimer.unref();
|
|
196
|
+
}
|
|
197
|
+
} catch (error) {
|
|
198
|
+
logInternalError("render-scheduler.invalidate", error);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private flushInvalidate(): void {
|
|
203
|
+
this.invalidateTimer = undefined;
|
|
204
|
+
if (this.disposed) {
|
|
205
|
+
this.invalidateBuffer.clear();
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const buffered = this.invalidateBuffer;
|
|
209
|
+
this.invalidateBuffer = new Map();
|
|
210
|
+
for (const payload of buffered.values()) {
|
|
211
|
+
try {
|
|
212
|
+
this.onInvalidate?.(payload);
|
|
213
|
+
} catch (error) {
|
|
214
|
+
logInternalError("render-scheduler.invalidate.flush", error);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Flush a render. If a render is already in progress the request is
|
|
221
|
+
* collapsed: `pendingRender` is set and the caller that holds
|
|
222
|
+
* `rendering==true` will loop one more time after finishing.
|
|
223
|
+
*/
|
|
224
|
+
flush(): void {
|
|
225
|
+
if (this.disposed) return;
|
|
226
|
+
if (this.rendering) {
|
|
227
|
+
this.pendingRender = true;
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
this.rendering = true;
|
|
231
|
+
this.pendingRender = false;
|
|
232
|
+
let iterations = 0;
|
|
233
|
+
try {
|
|
234
|
+
do {
|
|
235
|
+
this.pendingRender = false;
|
|
236
|
+
this.render();
|
|
237
|
+
iterations += 1;
|
|
238
|
+
// Safety valve: 5 re-renders max per flush to prevent infinite loops
|
|
239
|
+
// if render() itself calls flush() synchronously.
|
|
240
|
+
} while (this.pendingRender && !this.disposed && iterations < 5);
|
|
241
|
+
} catch (error) {
|
|
242
|
+
logInternalError("render-scheduler.render", error);
|
|
243
|
+
} finally {
|
|
244
|
+
this.rendering = false;
|
|
245
|
+
// R4: if we hit the re-entrancy cap with more work pending, drain it —
|
|
246
|
+
// but with exponential backoff on consecutive cap-hits so a pathological
|
|
247
|
+
// render() cannot sustain a tight debounceMs render loop. We re-arm via
|
|
248
|
+
// armDebouncedRender (not schedule()) so lastEventAt / onInvalidate are
|
|
249
|
+
// not perturbed by a pure re-entrancy drain.
|
|
250
|
+
if (iterations >= 5 && this.pendingRender && !this.disposed) {
|
|
251
|
+
this.consecutiveCapHits += 1;
|
|
252
|
+
const exp = Math.min(this.consecutiveCapHits - 1, CAP_BACKOFF_MAX_EXP);
|
|
253
|
+
this.armDebouncedRender(this.debounceMs * 2 ** exp);
|
|
254
|
+
} else {
|
|
255
|
+
this.consecutiveCapHits = 0;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
dispose(): void {
|
|
261
|
+
if (this.disposed) return;
|
|
262
|
+
this.disposed = true;
|
|
263
|
+
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
|
264
|
+
if (this.fallbackTimer) clearTimeout(this.fallbackTimer);
|
|
265
|
+
if (this.invalidateTimer) clearTimeout(this.invalidateTimer);
|
|
266
|
+
this.debounceTimer = undefined;
|
|
267
|
+
this.fallbackTimer = undefined;
|
|
268
|
+
this.invalidateTimer = undefined;
|
|
269
|
+
this.invalidateBuffer.clear();
|
|
270
|
+
for (const unsub of this.unsubs.splice(0)) {
|
|
271
|
+
try {
|
|
272
|
+
unsub();
|
|
273
|
+
} catch (error) {
|
|
274
|
+
logInternalError("render-scheduler.unsubscribe", error);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
// Lazy-loaded: team-tool.ts pulls in entire runtime chain.
|
|
3
|
+
import type { handleTeamTool as HandleTeamToolFn } from "../extension/team-tool.ts";
|
|
4
|
+
import type { MetricRegistry } from "../observability/metric-registry.ts";
|
|
5
|
+
|
|
6
|
+
let _cachedHandleTeamTool: typeof HandleTeamToolFn | undefined;
|
|
7
|
+
async function handleTeamTool(
|
|
8
|
+
params: Parameters<typeof HandleTeamToolFn>[0],
|
|
9
|
+
ctx: Parameters<typeof HandleTeamToolFn>[1],
|
|
10
|
+
): Promise<Awaited<ReturnType<typeof HandleTeamToolFn>>> {
|
|
11
|
+
if (!_cachedHandleTeamTool) {
|
|
12
|
+
// LAZY: avoid pulling team-tool.ts (and its entire runtime chain) into module load.
|
|
13
|
+
const mod = await import("../extension/team-tool.ts");
|
|
14
|
+
_cachedHandleTeamTool = mod.handleTeamTool;
|
|
15
|
+
}
|
|
16
|
+
return _cachedHandleTeamTool(params, ctx);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
import { isToolError, textFromToolResult } from "../extension/tool-result.ts";
|
|
20
|
+
import { readCrewAgents } from "../runtime/crew-agent-records.ts";
|
|
21
|
+
import { exportDiagnostic } from "../runtime/diagnostic-export.ts";
|
|
22
|
+
import { appendEvent } from "../state/event-log.ts";
|
|
23
|
+
import type { MailboxDirection, MailboxMessage } from "../state/mailbox.ts";
|
|
24
|
+
import { loadRunManifestById, saveRunTasks } from "../state/state-store.ts";
|
|
25
|
+
|
|
26
|
+
export interface RunActionResult {
|
|
27
|
+
ok: boolean;
|
|
28
|
+
message: string;
|
|
29
|
+
data?: unknown;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function okFromTool(result: Awaited<ReturnType<typeof handleTeamTool>>): RunActionResult {
|
|
33
|
+
return {
|
|
34
|
+
ok: !isToolError(result),
|
|
35
|
+
message: textFromToolResult(result),
|
|
36
|
+
data: result,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function err(error: unknown): RunActionResult {
|
|
41
|
+
return {
|
|
42
|
+
ok: false,
|
|
43
|
+
message: error instanceof Error ? error.message : String(error),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function dispatchApi(ctx: ExtensionContext, runId: string, config: Record<string, unknown>): Promise<RunActionResult> {
|
|
48
|
+
try {
|
|
49
|
+
return okFromTool(await handleTeamTool({ action: "api", runId, config }, ctx));
|
|
50
|
+
} catch (error) {
|
|
51
|
+
return err(error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function parseMailboxMessages(text: string): MailboxMessage[] {
|
|
56
|
+
try {
|
|
57
|
+
const parsed = JSON.parse(text) as unknown;
|
|
58
|
+
if (!Array.isArray(parsed)) return [];
|
|
59
|
+
return parsed.filter(
|
|
60
|
+
(item): item is MailboxMessage =>
|
|
61
|
+
Boolean(item) && typeof item === "object" && !Array.isArray(item) && typeof (item as { id?: unknown }).id === "string",
|
|
62
|
+
);
|
|
63
|
+
} catch {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function dispatchMailboxAck(ctx: ExtensionContext, runId: string, messageId: string): Promise<RunActionResult> {
|
|
69
|
+
return dispatchApi(ctx, runId, { operation: "ack-message", messageId });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function dispatchMailboxNudge(ctx: ExtensionContext, runId: string, agentId: string, message: string): Promise<RunActionResult> {
|
|
73
|
+
return dispatchApi(ctx, runId, {
|
|
74
|
+
operation: "nudge-agent",
|
|
75
|
+
agentId,
|
|
76
|
+
message,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function dispatchMailboxCompose(
|
|
81
|
+
ctx: ExtensionContext,
|
|
82
|
+
runId: string,
|
|
83
|
+
payload: {
|
|
84
|
+
from: string;
|
|
85
|
+
to: string;
|
|
86
|
+
body: string;
|
|
87
|
+
taskId?: string;
|
|
88
|
+
direction: MailboxDirection;
|
|
89
|
+
},
|
|
90
|
+
): Promise<RunActionResult> {
|
|
91
|
+
return dispatchApi(ctx, runId, { operation: "send-message", ...payload });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export async function dispatchMailboxAckAll(ctx: ExtensionContext, runId: string): Promise<RunActionResult> {
|
|
95
|
+
const listed = await dispatchApi(ctx, runId, {
|
|
96
|
+
operation: "read-mailbox",
|
|
97
|
+
direction: "inbox",
|
|
98
|
+
});
|
|
99
|
+
if (!listed.ok) return listed;
|
|
100
|
+
const messages = parseMailboxMessages(listed.message).filter((message) => message.status !== "acknowledged");
|
|
101
|
+
let count = 0;
|
|
102
|
+
for (const message of messages) {
|
|
103
|
+
const acked = await dispatchMailboxAck(ctx, runId, message.id);
|
|
104
|
+
if (!acked.ok)
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
message: `Acknowledged ${count}/${messages.length}; failed ${message.id}: ${acked.message}`,
|
|
108
|
+
};
|
|
109
|
+
count += 1;
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
ok: true,
|
|
113
|
+
message: `Acknowledged ${count} messages.`,
|
|
114
|
+
data: { count },
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function dispatchHealthRecovery(ctx: ExtensionContext, runId: string): Promise<RunActionResult> {
|
|
119
|
+
return dispatchApi(ctx, runId, {
|
|
120
|
+
operation: "foreground-interrupt",
|
|
121
|
+
reason: "operator health recovery",
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export async function dispatchKillStaleWorkers(ctx: ExtensionContext, runId: string): Promise<RunActionResult> {
|
|
126
|
+
try {
|
|
127
|
+
const loaded = loadRunManifestById(ctx.cwd, runId); // NOTE: no withRunLock - best-effort only; concurrent writes may cause inconsistency;
|
|
128
|
+
if (!loaded) return { ok: false, message: `Run '${runId}' not found.` };
|
|
129
|
+
const currentMs = Date.now();
|
|
130
|
+
const staleMs = 60_000;
|
|
131
|
+
const now = new Date(currentMs).toISOString();
|
|
132
|
+
let count = 0;
|
|
133
|
+
const tasks = loaded.tasks.map((task) => {
|
|
134
|
+
if ((task.status !== "running" && task.status !== "queued") || !task.heartbeat || task.heartbeat.alive === false) return task;
|
|
135
|
+
const lastSeenMs = Date.parse(task.heartbeat.lastSeenAt);
|
|
136
|
+
if (!Number.isFinite(lastSeenMs) || currentMs - lastSeenMs <= staleMs) return task;
|
|
137
|
+
count += 1;
|
|
138
|
+
return {
|
|
139
|
+
...task,
|
|
140
|
+
heartbeat: { ...task.heartbeat, alive: false, lastSeenAt: now },
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
saveRunTasks(loaded.manifest, tasks);
|
|
144
|
+
appendEvent(loaded.manifest.eventsPath, {
|
|
145
|
+
type: "worker.kill_stale",
|
|
146
|
+
runId,
|
|
147
|
+
message: `Marked ${count} stale worker heartbeat(s) dead.`,
|
|
148
|
+
data: { count },
|
|
149
|
+
});
|
|
150
|
+
return {
|
|
151
|
+
ok: true,
|
|
152
|
+
message: `Marked ${count} stale worker heartbeat(s) dead.`,
|
|
153
|
+
data: { count },
|
|
154
|
+
};
|
|
155
|
+
} catch (error) {
|
|
156
|
+
return err(error);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export async function dispatchDiagnosticExport(
|
|
161
|
+
ctx: ExtensionContext,
|
|
162
|
+
runId: string,
|
|
163
|
+
options: { registry?: MetricRegistry } = {},
|
|
164
|
+
): Promise<RunActionResult> {
|
|
165
|
+
try {
|
|
166
|
+
const exported = await exportDiagnostic(ctx, runId, options);
|
|
167
|
+
return {
|
|
168
|
+
ok: true,
|
|
169
|
+
message: `Diagnostic exported to ${exported.path}`,
|
|
170
|
+
data: exported.path,
|
|
171
|
+
};
|
|
172
|
+
} catch (error) {
|
|
173
|
+
return err(error);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** @internal */
|
|
178
|
+
function defaultNudgeAgentId(ctx: Pick<ExtensionContext, "cwd">, runId: string): string | undefined {
|
|
179
|
+
const loaded = loadRunManifestById(ctx.cwd, runId); // NOTE: no withRunLock - best-effort only; concurrent writes may cause inconsistency;
|
|
180
|
+
if (!loaded) return undefined;
|
|
181
|
+
return readCrewAgents(loaded.manifest).find((agent) => agent.status === "running" || agent.status === "queued")?.taskId;
|
|
182
|
+
}
|