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,704 @@
|
|
|
1
|
+
import type { appendEvent } from "../state/event-log.ts";
|
|
2
|
+
import { logInternalError } from "../utils/internal-error.ts";
|
|
3
|
+
import type { CrewAgentRecord } from "./crew-agent-runtime.ts";
|
|
4
|
+
import type { IrcMessage } from "./live-irc.ts";
|
|
5
|
+
import { checkProcessLiveness } from "./process-status.ts";
|
|
6
|
+
|
|
7
|
+
const MAX_PENDING_MESSAGES = 1000;
|
|
8
|
+
|
|
9
|
+
type LiveSessionHandle = {
|
|
10
|
+
steer?: (text: string) => Promise<void>;
|
|
11
|
+
prompt?: (text: string, options?: Record<string, unknown>) => Promise<void>;
|
|
12
|
+
abort?: () => Promise<void> | void;
|
|
13
|
+
dispose?: () => void;
|
|
14
|
+
/** Upstream session stats (input/output/cacheWrite tokens, context %). */
|
|
15
|
+
getSessionStats?: () => {
|
|
16
|
+
tokens?: {
|
|
17
|
+
input?: number;
|
|
18
|
+
output?: number;
|
|
19
|
+
cacheWrite?: number;
|
|
20
|
+
cacheRead?: number;
|
|
21
|
+
};
|
|
22
|
+
contextUsage?: { percent?: number | null; window?: number };
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/** Real-time activity state for a live-session agent. */
|
|
27
|
+
export interface LiveAgentActivity {
|
|
28
|
+
/** Currently active tools (toolName → description). */
|
|
29
|
+
activeTools: Map<string, string>;
|
|
30
|
+
/** Total tool invocations. */
|
|
31
|
+
toolUses: number;
|
|
32
|
+
/** Current turn count. */
|
|
33
|
+
turnCount: number;
|
|
34
|
+
/** Effective max turns (undefined = unlimited). */
|
|
35
|
+
maxTurns?: number;
|
|
36
|
+
/** Latest assistant text snippet. */
|
|
37
|
+
responseText: string;
|
|
38
|
+
/** Number of context compactions survived. */
|
|
39
|
+
compactionCount: number;
|
|
40
|
+
/** Started-at timestamp (ms epoch). */
|
|
41
|
+
startedAtMs: number;
|
|
42
|
+
/** Completed-at timestamp (ms epoch, 0 = still running). */
|
|
43
|
+
completedAtMs: number;
|
|
44
|
+
/** Model name used for this agent (e.g. "sonnet", "haiku"). */
|
|
45
|
+
modelName?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface LiveAgentHandle {
|
|
49
|
+
agentId: string;
|
|
50
|
+
taskId: string;
|
|
51
|
+
runId: string;
|
|
52
|
+
/** Workspace where this agent was spawned — used for session-scoped visibility. */
|
|
53
|
+
workspaceId: string;
|
|
54
|
+
role?: string;
|
|
55
|
+
agent?: string;
|
|
56
|
+
description?: string;
|
|
57
|
+
/** Model name used for this agent (e.g. "sonnet", "haiku"). */
|
|
58
|
+
modelName?: string;
|
|
59
|
+
session: LiveSessionHandle;
|
|
60
|
+
createdAt: string;
|
|
61
|
+
updatedAt: string;
|
|
62
|
+
status: CrewAgentRecord["status"];
|
|
63
|
+
pendingSteers: string[];
|
|
64
|
+
pendingFollowUps: string[];
|
|
65
|
+
/** Phase 7: Pending IRC messages for this agent. */
|
|
66
|
+
pendingMessages: IrcMessage[];
|
|
67
|
+
/** G1-G6: Real-time activity tracking (in-memory only). */
|
|
68
|
+
activity: LiveAgentActivity;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const liveAgents = new Map<string, LiveAgentHandle>();
|
|
72
|
+
// FIX (Round 15): Cap the number of tracked live agents to prevent unbounded
|
|
73
|
+
// growth if a caller spawns agents but fails to unregister them. When the
|
|
74
|
+
// cap is reached, the oldest completed agent is evicted first; if no
|
|
75
|
+
// completed agents are present, the oldest running one is evicted (with a
|
|
76
|
+
// warning) to keep memory bounded.
|
|
77
|
+
const MAX_LIVE_AGENTS = 5_000;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* List all live agents for a specific workspace.
|
|
81
|
+
* Only agents belonging to the given workspaceId are returned.
|
|
82
|
+
*/
|
|
83
|
+
export function listLiveAgentsByWorkspace(workspaceId: string): LiveAgentHandle[] {
|
|
84
|
+
return listLiveAgents().filter((a) => a.workspaceId === workspaceId);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function registerLiveAgent(
|
|
88
|
+
input: Omit<LiveAgentHandle, "createdAt" | "updatedAt" | "pendingSteers" | "pendingFollowUps" | "pendingMessages" | "activity"> & {
|
|
89
|
+
workspaceId: string;
|
|
90
|
+
},
|
|
91
|
+
eventLogFn?: typeof appendEvent,
|
|
92
|
+
eventsPath?: string,
|
|
93
|
+
): LiveAgentHandle {
|
|
94
|
+
const now = new Date().toISOString();
|
|
95
|
+
const existing = liveAgents.get(input.agentId);
|
|
96
|
+
const handle: LiveAgentHandle = {
|
|
97
|
+
...input,
|
|
98
|
+
createdAt: existing?.createdAt ?? now,
|
|
99
|
+
updatedAt: now,
|
|
100
|
+
pendingSteers: existing?.pendingSteers ?? [],
|
|
101
|
+
pendingFollowUps: existing?.pendingFollowUps ?? [],
|
|
102
|
+
pendingMessages: existing?.pendingMessages ?? [],
|
|
103
|
+
activity: existing?.activity ?? {
|
|
104
|
+
activeTools: new Map(),
|
|
105
|
+
toolUses: 0,
|
|
106
|
+
turnCount: 0,
|
|
107
|
+
responseText: "",
|
|
108
|
+
compactionCount: 0,
|
|
109
|
+
startedAtMs: Date.now(),
|
|
110
|
+
completedAtMs: 0,
|
|
111
|
+
modelName: undefined,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
// FIX (Round 15): Enforce the live-agent cap before adding. Prefer to
|
|
115
|
+
// evict the oldest completed agent (already finished, so caller no
|
|
116
|
+
// longer needs it). If none exist, evict the oldest running one with
|
|
117
|
+
// a warning so memory stays bounded.
|
|
118
|
+
if (liveAgents.size >= MAX_LIVE_AGENTS) {
|
|
119
|
+
const completed = [...liveAgents.entries()].find(([, h]) => h.activity.completedAtMs > 0);
|
|
120
|
+
if (completed) {
|
|
121
|
+
liveAgents.delete(completed[0]);
|
|
122
|
+
} else {
|
|
123
|
+
const oldestKey = liveAgents.keys().next().value;
|
|
124
|
+
if (oldestKey !== undefined) {
|
|
125
|
+
logInternalError("live-agent-manager.cap", new Error(`liveAgents at cap ${MAX_LIVE_AGENTS}; evicting oldest ${oldestKey}`));
|
|
126
|
+
liveAgents.delete(oldestKey);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
liveAgents.set(input.agentId, handle);
|
|
131
|
+
try {
|
|
132
|
+
if (eventLogFn && eventsPath)
|
|
133
|
+
eventLogFn(eventsPath, {
|
|
134
|
+
type: "live_agent.registered",
|
|
135
|
+
runId: input.runId,
|
|
136
|
+
taskId: input.taskId,
|
|
137
|
+
message: `Live agent registered: ${input.agent} (${input.role})`,
|
|
138
|
+
data: {
|
|
139
|
+
agentId: input.agentId,
|
|
140
|
+
role: input.role,
|
|
141
|
+
agent: input.agent,
|
|
142
|
+
workspaceId: input.workspaceId,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
} catch {
|
|
146
|
+
/* non-critical */
|
|
147
|
+
}
|
|
148
|
+
if (handle.pendingSteers.length && typeof handle.session.steer === "function") {
|
|
149
|
+
const pending = [...handle.pendingSteers];
|
|
150
|
+
handle.pendingSteers.length = 0;
|
|
151
|
+
for (const message of pending)
|
|
152
|
+
void handle.session
|
|
153
|
+
.steer(message)
|
|
154
|
+
.catch((error) => logInternalError("live-agent-manager.steer", error, `agentId=${handle.agentId}`));
|
|
155
|
+
}
|
|
156
|
+
if (handle.pendingFollowUps.length && typeof handle.session.prompt === "function") {
|
|
157
|
+
const pending = [...handle.pendingFollowUps];
|
|
158
|
+
handle.pendingFollowUps.length = 0;
|
|
159
|
+
for (const message of pending)
|
|
160
|
+
void handle.session
|
|
161
|
+
.prompt(message, {
|
|
162
|
+
source: "api",
|
|
163
|
+
expandPromptTemplates: false,
|
|
164
|
+
})
|
|
165
|
+
.catch((error) => logInternalError("live-agent-manager.prompt", error, `agentId=${handle.agentId}`));
|
|
166
|
+
}
|
|
167
|
+
return handle;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function updateLiveAgentStatus(agentId: string, status: CrewAgentRecord["status"]): void {
|
|
171
|
+
const handle = liveAgents.get(agentId);
|
|
172
|
+
if (!handle) return;
|
|
173
|
+
handle.status = status;
|
|
174
|
+
handle.updatedAt = new Date().toISOString();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function safeDisposeLiveSession(handle: LiveAgentHandle): void {
|
|
178
|
+
try {
|
|
179
|
+
handle.session.dispose?.();
|
|
180
|
+
} catch (error) {
|
|
181
|
+
logInternalError("live-agent-manager.dispose", error, `agentId=${handle.agentId}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function disposeLiveAgentSession(agentIdOrTaskId: string): void {
|
|
186
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
187
|
+
if (!handle) return;
|
|
188
|
+
safeDisposeLiveSession(handle);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export async function terminateLiveAgent(
|
|
192
|
+
agentIdOrTaskId: string,
|
|
193
|
+
status: CrewAgentRecord["status"] = "stopped",
|
|
194
|
+
eventLogFn?: typeof appendEvent,
|
|
195
|
+
eventsPath?: string,
|
|
196
|
+
): Promise<LiveAgentHandle | undefined> {
|
|
197
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
198
|
+
if (!handle) return undefined;
|
|
199
|
+
handle.status = status;
|
|
200
|
+
handle.updatedAt = new Date().toISOString();
|
|
201
|
+
try {
|
|
202
|
+
if (eventLogFn && eventsPath)
|
|
203
|
+
eventLogFn(eventsPath, {
|
|
204
|
+
type: "live_agent.terminated",
|
|
205
|
+
runId: handle.runId,
|
|
206
|
+
taskId: handle.taskId,
|
|
207
|
+
message: `Live agent terminated: ${handle.agent} status=${status}`,
|
|
208
|
+
data: {
|
|
209
|
+
agentId: handle.agentId,
|
|
210
|
+
status,
|
|
211
|
+
role: handle.role,
|
|
212
|
+
workspaceId: handle.workspaceId,
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
} catch {
|
|
216
|
+
/* non-critical */
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
await handle.session.abort?.();
|
|
220
|
+
} finally {
|
|
221
|
+
safeDisposeLiveSession(handle);
|
|
222
|
+
liveAgents.delete(handle.agentId); // Move AFTER abort completes to prevent race
|
|
223
|
+
}
|
|
224
|
+
return handle;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export async function terminateLiveAgentsForRun(
|
|
228
|
+
runId: string,
|
|
229
|
+
status: CrewAgentRecord["status"] = "failed",
|
|
230
|
+
eventLogFn?: typeof appendEvent,
|
|
231
|
+
eventsPath?: string,
|
|
232
|
+
): Promise<number> {
|
|
233
|
+
const agents = [...liveAgents.values()].filter((agent) => agent.runId === runId);
|
|
234
|
+
await Promise.all(agents.map((agent) => terminateLiveAgent(agent.agentId, status, eventLogFn, eventsPath)));
|
|
235
|
+
return agents.length;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function getLiveAgent(agentIdOrTaskId: string): LiveAgentHandle | undefined {
|
|
239
|
+
return liveAgents.get(agentIdOrTaskId) ?? [...liveAgents.values()].find((entry) => entry.taskId === agentIdOrTaskId);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** Maximum time a terminal live agent handle stays in memory (10 minutes). */
|
|
243
|
+
const STALE_HANDLE_MS = 10 * 60 * 1000;
|
|
244
|
+
/** Maximum time a running/queued live agent handle stays without any update (30 minutes).
|
|
245
|
+
* After this, the agent is presumed dead — the real process would have updated the handle. */
|
|
246
|
+
const STALE_RUNNING_HANDLE_MS = 30 * 60 * 1000;
|
|
247
|
+
|
|
248
|
+
/** Remove dead live agent handles.
|
|
249
|
+
* Evicts: (1) terminal-status handles older than STALE_HANDLE_MS, and
|
|
250
|
+
* (2) running/queued handles with no update for STALE_RUNNING_HANDLE_MS.
|
|
251
|
+
* Called periodically by the widget refresh cycle.
|
|
252
|
+
* Returns the number of handles evicted.
|
|
253
|
+
*/
|
|
254
|
+
export function evictStaleLiveAgentHandles(now = Date.now()): number {
|
|
255
|
+
let evicted = 0;
|
|
256
|
+
for (const [agentId, handle] of liveAgents) {
|
|
257
|
+
const age = now - new Date(handle.updatedAt).getTime();
|
|
258
|
+
const isActive = handle.status === "running" || handle.status === "queued" || handle.status === "waiting";
|
|
259
|
+
if (!isActive) {
|
|
260
|
+
// Terminal handle — evict after grace period
|
|
261
|
+
if (age > STALE_HANDLE_MS) {
|
|
262
|
+
liveAgents.delete(agentId);
|
|
263
|
+
safeDisposeLiveSession(handle);
|
|
264
|
+
evicted++;
|
|
265
|
+
}
|
|
266
|
+
} else if (age > STALE_RUNNING_HANDLE_MS) {
|
|
267
|
+
// Active-status handle with no update for 30min — check process liveness
|
|
268
|
+
// before evicting. Only evict if the process is dead, to avoid evicting
|
|
269
|
+
// slow-but-alive agents.
|
|
270
|
+
const sessionPid = (handle.session as Record<string, unknown>)?.pid as number | undefined;
|
|
271
|
+
if (sessionPid === undefined) {
|
|
272
|
+
// In-process live-session agents have no pid — the Pi SDK returns
|
|
273
|
+
// in-process objects, not child processes. checkProcessLiveness(undefined)
|
|
274
|
+
// returns {alive:false} which would wrongly evict a running agent.
|
|
275
|
+
// Check session-level activity signals first. If the session is
|
|
276
|
+
// actively streaming or has pending messages, it is alive — skip.
|
|
277
|
+
// Otherwise, rely on the idle timeout alone: do NOT dispose just
|
|
278
|
+
// because pid is undefined.
|
|
279
|
+
const session = handle.session as Record<string, unknown>;
|
|
280
|
+
const isStreaming = session?.isStreaming === true;
|
|
281
|
+
const pendingCount = (session?.pendingMessageCount as number | undefined) ?? 0;
|
|
282
|
+
if (isStreaming || pendingCount > 0) {
|
|
283
|
+
// Session is active — skip eviction.
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
// No activity signals and no pid — do NOT evict based on PID liveness.
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
const liveness = checkProcessLiveness(sessionPid);
|
|
290
|
+
if (!liveness.alive) {
|
|
291
|
+
liveAgents.delete(agentId);
|
|
292
|
+
safeDisposeLiveSession(handle);
|
|
293
|
+
evicted++;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return evicted;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export function listLiveAgents(): LiveAgentHandle[] {
|
|
301
|
+
return [...liveAgents.values()].sort((a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? ""));
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export function listActiveLiveAgents(): LiveAgentHandle[] {
|
|
305
|
+
return listLiveAgents().filter((agent) => agent.status === "running" || agent.status === "queued" || agent.status === "waiting");
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function getLiveAgentContextPercent(agentIdOrTaskId: string): number | null {
|
|
309
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
310
|
+
if (handle?.status !== "running") return null;
|
|
311
|
+
try {
|
|
312
|
+
return handle.session.getSessionStats?.().contextUsage?.percent ?? null;
|
|
313
|
+
} catch {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export async function steerLiveAgent(agentIdOrTaskId: string, message: string): Promise<LiveAgentHandle> {
|
|
319
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
320
|
+
if (!handle) throw new Error(`Live agent '${agentIdOrTaskId}' is not registered in this process.`);
|
|
321
|
+
if (typeof handle.session.steer !== "function") {
|
|
322
|
+
handle.pendingSteers.push(message);
|
|
323
|
+
return handle;
|
|
324
|
+
}
|
|
325
|
+
await handle.session.steer(message);
|
|
326
|
+
handle.updatedAt = new Date().toISOString();
|
|
327
|
+
return handle;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export async function followUpLiveAgent(agentIdOrTaskId: string, prompt: string): Promise<LiveAgentHandle> {
|
|
331
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
332
|
+
if (!handle) throw new Error(`Live agent '${agentIdOrTaskId}' is not registered in this process.`);
|
|
333
|
+
if (typeof handle.session.prompt !== "function") {
|
|
334
|
+
handle.pendingFollowUps.push(prompt);
|
|
335
|
+
return handle;
|
|
336
|
+
}
|
|
337
|
+
await handle.session.prompt(prompt, {
|
|
338
|
+
source: "api",
|
|
339
|
+
expandPromptTemplates: false,
|
|
340
|
+
});
|
|
341
|
+
handle.updatedAt = new Date().toISOString();
|
|
342
|
+
return handle;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export async function stopLiveAgent(agentIdOrTaskId: string): Promise<LiveAgentHandle> {
|
|
346
|
+
const stopped = await terminateLiveAgent(agentIdOrTaskId, "stopped");
|
|
347
|
+
if (!stopped) throw new Error(`Live agent '${agentIdOrTaskId}' is not registered in this process.`);
|
|
348
|
+
return stopped;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export async function resumeLiveAgent(agentIdOrTaskId: string, prompt: string): Promise<LiveAgentHandle> {
|
|
352
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
353
|
+
if (!handle) throw new Error(`Live agent '${agentIdOrTaskId}' is not registered in this process.`);
|
|
354
|
+
if (typeof handle.session.prompt !== "function") throw new Error(`Live agent '${agentIdOrTaskId}' does not expose prompt().`);
|
|
355
|
+
handle.status = "running";
|
|
356
|
+
await handle.session.prompt(prompt, {
|
|
357
|
+
source: "api",
|
|
358
|
+
expandPromptTemplates: false,
|
|
359
|
+
});
|
|
360
|
+
handle.status = "completed";
|
|
361
|
+
handle.updatedAt = new Date().toISOString();
|
|
362
|
+
return handle;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/** G2: Track tool start for a live agent. */
|
|
366
|
+
export function trackLiveAgentToolStart(agentIdOrTaskId: string, toolName: string): void {
|
|
367
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
368
|
+
if (!handle) return;
|
|
369
|
+
// Evict oldest entries if at capacity
|
|
370
|
+
const MAX_TRACKED_TOOLS = 1000;
|
|
371
|
+
if (handle.activity.activeTools.size >= MAX_TRACKED_TOOLS) {
|
|
372
|
+
const firstKey = handle.activity.activeTools.keys().next().value;
|
|
373
|
+
if (firstKey !== undefined) {
|
|
374
|
+
handle.activity.activeTools.delete(firstKey);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
handle.activity.activeTools.set(toolName, toolName);
|
|
378
|
+
handle.activity.toolUses++;
|
|
379
|
+
handle.updatedAt = new Date().toISOString();
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/** G2: Track tool end for a live agent. */
|
|
383
|
+
export function trackLiveAgentToolEnd(agentIdOrTaskId: string, toolName: string): void {
|
|
384
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
385
|
+
if (!handle) return;
|
|
386
|
+
handle.activity.activeTools.delete(toolName);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/** G3/G6: Track turn end and compaction. */
|
|
390
|
+
export function trackLiveAgentTurnEnd(agentIdOrTaskId: string, compaction = false): void {
|
|
391
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
392
|
+
if (!handle) return;
|
|
393
|
+
handle.activity.turnCount++;
|
|
394
|
+
if (compaction) handle.activity.compactionCount++;
|
|
395
|
+
handle.activity.activeTools.clear();
|
|
396
|
+
handle.updatedAt = new Date().toISOString();
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/** G2: Track assistant response text. */
|
|
400
|
+
export function trackLiveAgentResponseText(agentIdOrTaskId: string, text: string): void {
|
|
401
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
402
|
+
if (!handle) return;
|
|
403
|
+
handle.activity.responseText = text.slice(-200);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/** Mark live agent completed with timestamp. */
|
|
407
|
+
export function markLiveAgentCompleted(agentIdOrTaskId: string): void {
|
|
408
|
+
const handle = getLiveAgent(agentIdOrTaskId);
|
|
409
|
+
if (!handle) return;
|
|
410
|
+
handle.activity.completedAtMs = Date.now();
|
|
411
|
+
handle.activity.activeTools.clear();
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export function clearLiveAgentsForTest(): void {
|
|
415
|
+
liveAgents.clear();
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/** Phase 7/G4: Send an IRC message to a specific live agent (DM).
|
|
419
|
+
* Uses non-blocking delivery via sendCustomMessage when available.
|
|
420
|
+
* Falls back to session.prompt (blocking) when not.
|
|
421
|
+
*/
|
|
422
|
+
export function sendIrcMessage(targetAgentId: string, message: IrcMessage): void {
|
|
423
|
+
const handle = getLiveAgent(targetAgentId);
|
|
424
|
+
if (!handle) return;
|
|
425
|
+
if (handle.pendingMessages.length >= MAX_PENDING_MESSAGES) {
|
|
426
|
+
handle.pendingMessages.shift();
|
|
427
|
+
}
|
|
428
|
+
handle.pendingMessages.push(message);
|
|
429
|
+
handle.updatedAt = new Date().toISOString();
|
|
430
|
+
// G4: Try non-blocking delivery via sendCustomMessage
|
|
431
|
+
const session = handle.session as Record<string, unknown>;
|
|
432
|
+
if (typeof session.sendCustomMessage === "function") {
|
|
433
|
+
try {
|
|
434
|
+
(session.sendCustomMessage as (msg: unknown, opts?: unknown) => void)(
|
|
435
|
+
{
|
|
436
|
+
customType: "irc",
|
|
437
|
+
content: `[DM from ${message.from}] ${message.content}`,
|
|
438
|
+
display: "collapsed",
|
|
439
|
+
},
|
|
440
|
+
{ deliverAs: "followUp", triggerTurn: false },
|
|
441
|
+
);
|
|
442
|
+
return;
|
|
443
|
+
} catch {
|
|
444
|
+
// Fall through to prompt-based delivery
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
// Fallback: inject as prompt (blocking)
|
|
448
|
+
if (typeof handle.session.prompt === "function") {
|
|
449
|
+
const ircPrompt = `[Message from ${message.from}] ${message.content}`;
|
|
450
|
+
void handle.session
|
|
451
|
+
.prompt(ircPrompt, { source: "api", expandPromptTemplates: false })
|
|
452
|
+
.catch((error) => logInternalError("live-agent-manager.irc-deliver", error, `agentId=${handle.agentId}`));
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/** Phase 7/G4: Broadcast an IRC message to all live agents except the sender.
|
|
457
|
+
* Uses non-blocking delivery via sendCustomMessage when available.
|
|
458
|
+
* Returns recipient IDs.
|
|
459
|
+
*/
|
|
460
|
+
export function broadcastIrcMessage(fromAgentId: string, message: IrcMessage): string[] {
|
|
461
|
+
const recipients: string[] = [];
|
|
462
|
+
for (const handle of liveAgents.values()) {
|
|
463
|
+
if (handle.agentId === fromAgentId) continue;
|
|
464
|
+
if (handle.status !== "running" && handle.status !== "queued") continue;
|
|
465
|
+
if (handle.pendingMessages.length >= MAX_PENDING_MESSAGES) {
|
|
466
|
+
handle.pendingMessages.shift();
|
|
467
|
+
}
|
|
468
|
+
handle.pendingMessages.push(message);
|
|
469
|
+
handle.updatedAt = new Date().toISOString();
|
|
470
|
+
// G4: Try non-blocking delivery
|
|
471
|
+
const session = handle.session as Record<string, unknown>;
|
|
472
|
+
if (typeof session.sendCustomMessage === "function") {
|
|
473
|
+
try {
|
|
474
|
+
(session.sendCustomMessage as (msg: unknown, opts?: unknown) => void)(
|
|
475
|
+
{
|
|
476
|
+
customType: "irc",
|
|
477
|
+
content: `[Broadcast from ${message.from}] ${message.content}`,
|
|
478
|
+
display: "collapsed",
|
|
479
|
+
},
|
|
480
|
+
{ deliverAs: "followUp", triggerTurn: false },
|
|
481
|
+
);
|
|
482
|
+
recipients.push(handle.agentId);
|
|
483
|
+
continue;
|
|
484
|
+
} catch {
|
|
485
|
+
// Fall through to prompt-based delivery
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
// Fallback: inject as prompt
|
|
489
|
+
if (typeof handle.session.prompt === "function") {
|
|
490
|
+
const ircPrompt = `[Broadcast from ${message.from}] ${message.content}`;
|
|
491
|
+
void handle.session
|
|
492
|
+
.prompt(ircPrompt, {
|
|
493
|
+
source: "api",
|
|
494
|
+
expandPromptTemplates: false,
|
|
495
|
+
})
|
|
496
|
+
.catch((error) => logInternalError("live-agent-manager.irc-broadcast", error, `agentId=${handle.agentId}`));
|
|
497
|
+
}
|
|
498
|
+
recipients.push(handle.agentId);
|
|
499
|
+
}
|
|
500
|
+
return recipients;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/* ── IRC reply support (side-channel Q&A) ─────────────────────────── */
|
|
504
|
+
|
|
505
|
+
/** Default timeout for awaiting a side-channel reply (60s). */
|
|
506
|
+
const DEFAULT_REPLY_TIMEOUT_MS = 60_000;
|
|
507
|
+
|
|
508
|
+
/** Result of a background reply attempt. */
|
|
509
|
+
export interface BackgroundReplyResult {
|
|
510
|
+
ok: boolean;
|
|
511
|
+
/** Correlation id for the pending reply (present once registered). */
|
|
512
|
+
corrId?: string;
|
|
513
|
+
/** Reply prose content (present on success when awaitReply was set). */
|
|
514
|
+
replyContent?: string;
|
|
515
|
+
/** Human-readable error description. */
|
|
516
|
+
error?: string;
|
|
517
|
+
/** True when the reply did not arrive before the timeout. */
|
|
518
|
+
timedOut?: boolean;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
interface PendingReply {
|
|
522
|
+
corrId: string;
|
|
523
|
+
targetAgentId: string;
|
|
524
|
+
fromId: string;
|
|
525
|
+
deadline: number;
|
|
526
|
+
resolve: (result: BackgroundReplyResult) => void;
|
|
527
|
+
timer?: ReturnType<typeof setTimeout>;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/** In-process pending replies keyed by correlation id. */
|
|
531
|
+
const pendingReplies = new Map<string, PendingReply>();
|
|
532
|
+
/** Reverse index: targetAgentId → set of corrIds awaiting a reply from it. */
|
|
533
|
+
const pendingRepliesByTarget = new Map<string, Set<string>>();
|
|
534
|
+
|
|
535
|
+
function makeCorrelationId(): string {
|
|
536
|
+
return `irc_reply_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Deliver a message to a live agent's session as a *background* turn —
|
|
541
|
+
* without blocking the recipient's main agent loop — and (optionally)
|
|
542
|
+
* await a prose reply via a side-channel.
|
|
543
|
+
*
|
|
544
|
+
* Non-blocking invariant (mirrors gajae-code's `respondAsBackground`):
|
|
545
|
+
* the message is injected via `sendCustomMessage` (triggerTurn:false) or a
|
|
546
|
+
* fire-and-forget `session.prompt`; we NEVER await the recipient's full
|
|
547
|
+
* main-loop turn. When `awaitReply` is set we instead await an event-driven
|
|
548
|
+
* reply resolution (see {@link resolveIrcReply}) bounded by a timeout.
|
|
549
|
+
*
|
|
550
|
+
* Note on mailbox.ts reply fields: those file-based fields
|
|
551
|
+
* (`replyTo`/`replyContent`/`replyDeadline`/`updateMailboxMessageReply`)
|
|
552
|
+
* serve cross-process workers that communicate via on-disk mailbox files.
|
|
553
|
+
* Live-session agents share a single process, so an in-memory event-driven
|
|
554
|
+
* registry is used here — it is lower-latency and trivially non-blocking.
|
|
555
|
+
* Both mechanisms coexist; file-based workers keep using mailbox.ts.
|
|
556
|
+
*/
|
|
557
|
+
export async function respondAsBackground(
|
|
558
|
+
targetAgentId: string,
|
|
559
|
+
fromId: string,
|
|
560
|
+
message: string,
|
|
561
|
+
opts?: { awaitReply?: boolean; timeoutMs?: number; signal?: AbortSignal },
|
|
562
|
+
): Promise<BackgroundReplyResult> {
|
|
563
|
+
const handle = getLiveAgent(targetAgentId);
|
|
564
|
+
if (!handle) return { ok: false, error: `Live agent '${targetAgentId}' not found.` };
|
|
565
|
+
|
|
566
|
+
const awaitReply = opts?.awaitReply ?? false;
|
|
567
|
+
const timeoutMs = opts?.timeoutMs ?? DEFAULT_REPLY_TIMEOUT_MS;
|
|
568
|
+
const corrId = makeCorrelationId();
|
|
569
|
+
|
|
570
|
+
// --- Non-blocking delivery -------------------------------------------
|
|
571
|
+
const session = handle.session as Record<string, unknown>;
|
|
572
|
+
const deliveredTag = `[DM from ${fromId}] ${message}`;
|
|
573
|
+
let delivered = false;
|
|
574
|
+
if (typeof session.sendCustomMessage === "function") {
|
|
575
|
+
try {
|
|
576
|
+
(session.sendCustomMessage as (msg: unknown, o?: unknown) => void)(
|
|
577
|
+
{
|
|
578
|
+
customType: "irc",
|
|
579
|
+
content: deliveredTag,
|
|
580
|
+
display: "collapsed",
|
|
581
|
+
corrId,
|
|
582
|
+
},
|
|
583
|
+
{ deliverAs: "followUp", triggerTurn: false },
|
|
584
|
+
);
|
|
585
|
+
delivered = true;
|
|
586
|
+
} catch {
|
|
587
|
+
// fall through to prompt-based delivery
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (!delivered && typeof handle.session.prompt === "function") {
|
|
591
|
+
const promptText = `${deliveredTag}${awaitReply ? ` (reply correlation: ${corrId})` : ""}`;
|
|
592
|
+
void handle.session
|
|
593
|
+
.prompt(promptText, { source: "api", expandPromptTemplates: false })
|
|
594
|
+
.catch((error) => logInternalError("live-agent-manager.respondAsBackground", error, `agentId=${handle.agentId}`));
|
|
595
|
+
delivered = true;
|
|
596
|
+
}
|
|
597
|
+
if (!delivered)
|
|
598
|
+
return {
|
|
599
|
+
ok: false,
|
|
600
|
+
error: `Target '${targetAgentId}' has no message channel.`,
|
|
601
|
+
};
|
|
602
|
+
handle.updatedAt = new Date().toISOString();
|
|
603
|
+
|
|
604
|
+
if (!awaitReply) return { ok: true, corrId };
|
|
605
|
+
|
|
606
|
+
// --- Await reply (event-driven, bounded by timeout) ------------------
|
|
607
|
+
return awaitPendingReply(corrId, targetAgentId, fromId, timeoutMs, opts?.signal);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Register a pending reply and resolve it when the reply arrives, the
|
|
612
|
+
* timeout elapses, or the caller's abort signal fires.
|
|
613
|
+
*
|
|
614
|
+
* @internal exported for testing
|
|
615
|
+
*/
|
|
616
|
+
export function awaitPendingReply(
|
|
617
|
+
corrId: string,
|
|
618
|
+
targetAgentId: string,
|
|
619
|
+
fromId: string,
|
|
620
|
+
timeoutMs: number,
|
|
621
|
+
signal?: AbortSignal,
|
|
622
|
+
): Promise<BackgroundReplyResult> {
|
|
623
|
+
return new Promise((resolve) => {
|
|
624
|
+
const deadline = Date.now() + timeoutMs;
|
|
625
|
+
let settled = false;
|
|
626
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
627
|
+
let signalListener: (() => void) | undefined;
|
|
628
|
+
|
|
629
|
+
const finish = (result: BackgroundReplyResult) => {
|
|
630
|
+
if (settled) return;
|
|
631
|
+
settled = true;
|
|
632
|
+
if (timer) clearTimeout(timer);
|
|
633
|
+
if (signalListener && signal) signal.removeEventListener("abort", signalListener);
|
|
634
|
+
pendingReplies.delete(corrId);
|
|
635
|
+
const set = pendingRepliesByTarget.get(targetAgentId);
|
|
636
|
+
set?.delete(corrId);
|
|
637
|
+
if (set && set.size === 0) pendingRepliesByTarget.delete(targetAgentId);
|
|
638
|
+
resolve(result);
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
timer = setTimeout(() => finish({ ok: false, corrId, timedOut: true }), timeoutMs);
|
|
642
|
+
|
|
643
|
+
if (signal) {
|
|
644
|
+
if (signal.aborted) {
|
|
645
|
+
finish({ ok: false, corrId, error: "cancelled" });
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
signalListener = () => finish({ ok: false, corrId, error: "cancelled" });
|
|
649
|
+
signal.addEventListener("abort", signalListener, { once: true });
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
pendingReplies.set(corrId, {
|
|
653
|
+
corrId,
|
|
654
|
+
targetAgentId,
|
|
655
|
+
fromId,
|
|
656
|
+
deadline,
|
|
657
|
+
resolve: finish,
|
|
658
|
+
timer,
|
|
659
|
+
});
|
|
660
|
+
const set = pendingRepliesByTarget.get(targetAgentId) ?? new Set<string>();
|
|
661
|
+
set.add(corrId);
|
|
662
|
+
pendingRepliesByTarget.set(targetAgentId, set);
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Resolve a pending side-channel reply. Called by the reply-routing layer
|
|
668
|
+
* (e.g. irc-tool when the recipient sends a message back referencing the
|
|
669
|
+
* correlation id, or by tests simulating a recipient response).
|
|
670
|
+
*
|
|
671
|
+
* Returns true if a pending reply was resolved, false if none matched
|
|
672
|
+
* (already timed out / cancelled / unknown correlation id).
|
|
673
|
+
*/
|
|
674
|
+
export function resolveIrcReply(corrId: string, replyContent: string): boolean {
|
|
675
|
+
const pending = pendingReplies.get(corrId);
|
|
676
|
+
if (!pending) return false;
|
|
677
|
+
pending.resolve({ ok: true, corrId, replyContent });
|
|
678
|
+
return true;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Cancel a pending side-channel reply (e.g. sender gave up).
|
|
683
|
+
* Returns true if a pending reply was cancelled, false if none matched.
|
|
684
|
+
*/
|
|
685
|
+
export function cancelIrcReply(corrId: string, reason = "cancelled"): boolean {
|
|
686
|
+
const pending = pendingReplies.get(corrId);
|
|
687
|
+
if (!pending) return false;
|
|
688
|
+
pending.resolve({ ok: false, corrId, error: reason });
|
|
689
|
+
return true;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/** Correlation ids currently awaiting a reply from the given target agent. */
|
|
693
|
+
export function pendingReplyCorrIdsForTarget(targetAgentId: string): string[] {
|
|
694
|
+
return [...(pendingRepliesByTarget.get(targetAgentId) ?? [])];
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/** Clear all pending replies (test helper). */
|
|
698
|
+
export function clearPendingRepliesForTest(): void {
|
|
699
|
+
for (const pending of pendingReplies.values()) {
|
|
700
|
+
if (pending.timer) clearTimeout(pending.timer);
|
|
701
|
+
}
|
|
702
|
+
pendingReplies.clear();
|
|
703
|
+
pendingRepliesByTarget.clear();
|
|
704
|
+
}
|