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,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* goal-state-store.ts — Persistent outer state for the autonomous goal loop (P0/P1).
|
|
3
|
+
*
|
|
4
|
+
* Spec: research-findings/goal-workflow/00-SPEC.md §2.3
|
|
5
|
+
* Plan: research-findings/goal-workflow/07-PLAN.md v3 §0b G2 (one manifest per turn,
|
|
6
|
+
* goal loop owns OUTER state) + §0c C10 (hardening: assertSafePathId + UUID goalId).
|
|
7
|
+
*
|
|
8
|
+
* Stores GoalLoopState as atomic JSON at <crewRoot>/state/goals/<goalId>.json.
|
|
9
|
+
* Modeled on ScheduleStore (state/schedule.ts:86) but with atomicWriteJson +
|
|
10
|
+
* path-traversal defense (assertSafePathId on every public method).
|
|
11
|
+
*
|
|
12
|
+
* Per §0c C2: budget lives here (budgetUsed accumulates collectRunMetrics across turns);
|
|
13
|
+
* per-turn usage stays on each turn's TeamRunManifest/tasks.json.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { closeSync, existsSync, mkdirSync, openSync, readdirSync, readFileSync, statSync, unlinkSync } from "node:fs";
|
|
17
|
+
import { dirname } from "node:path";
|
|
18
|
+
import { atomicWriteJson } from "../state/atomic-write.ts";
|
|
19
|
+
import { appendEvent } from "../state/event-log.ts";
|
|
20
|
+
import type { GoalLoopState, GoalLoopStatus } from "../state/types.ts";
|
|
21
|
+
import { createRunId } from "../utils/ids.ts";
|
|
22
|
+
import { logInternalError } from "../utils/internal-error.ts";
|
|
23
|
+
import { projectCrewRoot, userCrewRoot } from "../utils/paths.ts";
|
|
24
|
+
import { assertSafePathId } from "../utils/safe-paths.ts";
|
|
25
|
+
|
|
26
|
+
/** Default state-root resolver: project scope if a project crew-root exists, else user scope. */
|
|
27
|
+
function resolveGoalsRoot(cwd: string): string {
|
|
28
|
+
const crewRoot = projectCrewRoot(cwd) ?? userCrewRoot();
|
|
29
|
+
return `${crewRoot}/state/goals`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Goal file path for a goalId. Asserts the id is path-safe (§0c C10). */
|
|
33
|
+
function goalFilePath(cwd: string, goalId: string): string {
|
|
34
|
+
assertSafePathId("goalId", goalId);
|
|
35
|
+
return `${resolveGoalsRoot(cwd)}/${goalId}.json`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* GoalStore — CRUD for GoalLoopState files.
|
|
40
|
+
*
|
|
41
|
+
* Concurrency: writes are atomic (temp+rename+fsync via atomicWriteJson). For
|
|
42
|
+
* read-modify-write sequences under contention, callers should coordinate via
|
|
43
|
+
* GoalLoopState.state transitions (cooperative, the goal loop is single-writer
|
|
44
|
+
* between turns). There is no file-lock here because the loop is the sole writer
|
|
45
|
+
* during its lifetime; `goal stop`/`pause`/`resume` from another session flip
|
|
46
|
+
* state fields that the loop checks between turns (cooperative, §0c C11).
|
|
47
|
+
*/
|
|
48
|
+
export class GoalStore {
|
|
49
|
+
private readonly cwd: string;
|
|
50
|
+
|
|
51
|
+
constructor(cwd: string) {
|
|
52
|
+
this.cwd = cwd;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Generate a fresh, path-safe goalId (never user-derived — §0c C10). */
|
|
56
|
+
createGoalId(): string {
|
|
57
|
+
return createRunId("goal");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Load a goal by id. Returns undefined if missing/corrupt. Throws on unsafe goalId (§0c C10). */
|
|
61
|
+
load(goalId: string): GoalLoopState | undefined {
|
|
62
|
+
// Path-safety check runs BEFORE the try/catch so traversal attempts throw (not silently return undefined).
|
|
63
|
+
const path = goalFilePath(this.cwd, goalId);
|
|
64
|
+
try {
|
|
65
|
+
if (!existsSync(path)) return undefined;
|
|
66
|
+
const raw = readFileSync(path, "utf-8");
|
|
67
|
+
const parsed = JSON.parse(raw);
|
|
68
|
+
if (!parsed || typeof parsed !== "object" || typeof parsed.goalId !== "string") return undefined;
|
|
69
|
+
return parsed as GoalLoopState;
|
|
70
|
+
} catch {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Atomically persist a goal state. Emits a goal.state_changed event if eventsPath given. */
|
|
76
|
+
save(state: GoalLoopState, eventsPath?: string): void {
|
|
77
|
+
assertSafePathId("goalId", state.goalId);
|
|
78
|
+
const path = goalFilePath(this.cwd, state.goalId);
|
|
79
|
+
const next = { ...state, updatedAt: new Date().toISOString() };
|
|
80
|
+
try {
|
|
81
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
82
|
+
atomicWriteJson(path, next);
|
|
83
|
+
if (eventsPath) {
|
|
84
|
+
appendEvent(eventsPath, {
|
|
85
|
+
type: "goal.state_changed",
|
|
86
|
+
runId: state.goalId,
|
|
87
|
+
data: { goalId: state.goalId, state: state.state },
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
} catch (error) {
|
|
91
|
+
logInternalError("goal-state-store.save", error, `goalId=${state.goalId}`);
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Patch a goal's top-level fields (e.g. state, turnsUsed, budgetUsed, currentRunId). */
|
|
97
|
+
patch(goalId: string, patch: Partial<GoalLoopState>, eventsPath?: string): GoalLoopState | undefined {
|
|
98
|
+
const current = this.load(goalId);
|
|
99
|
+
if (!current) return undefined;
|
|
100
|
+
const next: GoalLoopState = {
|
|
101
|
+
...current,
|
|
102
|
+
...patch,
|
|
103
|
+
goalId: current.goalId,
|
|
104
|
+
createdAt: current.createdAt,
|
|
105
|
+
};
|
|
106
|
+
this.save(next, eventsPath);
|
|
107
|
+
return next;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Convenience: transition state with optional event emission. */
|
|
111
|
+
setStatus(goalId: string, state: GoalLoopStatus, eventsPath?: string): GoalLoopState | undefined {
|
|
112
|
+
return this.patch(goalId, { state }, eventsPath);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Compare-And-Set status for atomic stuck↔resume transitions (P1b, RFC v0.5 §P1b).
|
|
117
|
+
*
|
|
118
|
+
* Loads current state; if `current.state === expected`, sets it to `next`,
|
|
119
|
+
* persists, and emits a `goal.state_changed` event (reusing the save()
|
|
120
|
+
* emission pattern). Otherwise returns undefined (CAS failed — no mutation,
|
|
121
|
+
* no event). This prevents lost updates when the background loop and a
|
|
122
|
+
* `goal resume`/idle-sweeper session race to flip `state`.
|
|
123
|
+
*
|
|
124
|
+
* Legal P1b transitions enforced by callers (not by this method):
|
|
125
|
+
* running → stuck, stuck → running, stuck → cancelled.
|
|
126
|
+
*/
|
|
127
|
+
/**
|
|
128
|
+
* Cross-process-safe compare-and-set (cold-review #2 HIGH #2 fix).
|
|
129
|
+
*
|
|
130
|
+
* The synchronous read-check-write is only atomic within one event loop. `goal resume` runs
|
|
131
|
+
* in a DIFFERENT process than the (exited) loop, so two concurrent resumes both see
|
|
132
|
+
* `stuck`, both pass the CAS, both spawn → double background loops, double budget burn.
|
|
133
|
+
*
|
|
134
|
+
* Fix: wrap the read-modify-write in an O_EXCL lockfile per goalId. O_EXCL is atomic at the
|
|
135
|
+
* OS level — only one process can create the lockfile. The operation is fast (ms), so stale
|
|
136
|
+
* lockfiles are rare; a 5s age guard force-clears them (crash recovery).
|
|
137
|
+
*/
|
|
138
|
+
compareAndSetStatus(goalId: string, expected: GoalLoopStatus, next: GoalLoopStatus, eventsPath?: string): GoalLoopState | undefined {
|
|
139
|
+
const lockPath = `${goalFilePath(this.cwd, goalId)}.cas.lock`;
|
|
140
|
+
if (!this.acquireCasLock(lockPath)) {
|
|
141
|
+
return undefined; // Another process holds the CAS lock — caller treats as CAS-failed.
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const current = this.load(goalId);
|
|
145
|
+
if (!current) return undefined;
|
|
146
|
+
if (current.state !== expected) return undefined; // CAS failed — state moved underneath us.
|
|
147
|
+
const updated: GoalLoopState = { ...current, state: next };
|
|
148
|
+
this.save(updated, eventsPath);
|
|
149
|
+
return updated;
|
|
150
|
+
} finally {
|
|
151
|
+
try {
|
|
152
|
+
unlinkSync(lockPath);
|
|
153
|
+
} catch {
|
|
154
|
+
/* best-effort; may already be gone */
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Acquire an O_EXCL lockfile for CAS, with stale-lock recovery (5s age guard). */
|
|
160
|
+
private acquireCasLock(lockPath: string): boolean {
|
|
161
|
+
try {
|
|
162
|
+
const fd = openSync(lockPath, "wx"); // O_EXCL — throws EEXIST if already exists.
|
|
163
|
+
closeSync(fd);
|
|
164
|
+
return true;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
167
|
+
if (code !== "EEXIST") return false;
|
|
168
|
+
// Stale recovery: if the lockfile is older than 5s, force-delete and retry once.
|
|
169
|
+
try {
|
|
170
|
+
const stat = statSync(lockPath);
|
|
171
|
+
if (Date.now() - stat.mtimeMs > 5000) {
|
|
172
|
+
unlinkSync(lockPath);
|
|
173
|
+
const fd = openSync(lockPath, "wx");
|
|
174
|
+
closeSync(fd);
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
} catch {
|
|
178
|
+
/* fall through */
|
|
179
|
+
}
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Remove a goal file (used by `goal clear`). Returns true if deleted. */
|
|
185
|
+
remove(goalId: string): boolean {
|
|
186
|
+
try {
|
|
187
|
+
const path = goalFilePath(this.cwd, goalId);
|
|
188
|
+
if (!existsSync(path)) return false;
|
|
189
|
+
unlinkSync(path);
|
|
190
|
+
return true;
|
|
191
|
+
} catch (error) {
|
|
192
|
+
logInternalError("goal-state-store.remove", error, `goalId=${goalId}`);
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** List all known goals (newest first by updatedAt). */
|
|
198
|
+
list(): GoalLoopState[] {
|
|
199
|
+
try {
|
|
200
|
+
const root = resolveGoalsRoot(this.cwd);
|
|
201
|
+
if (!existsSync(root)) return [];
|
|
202
|
+
const entries = readdirSync(root) as string[];
|
|
203
|
+
const goals: GoalLoopState[] = [];
|
|
204
|
+
for (const entry of entries) {
|
|
205
|
+
if (!entry.endsWith(".json")) continue;
|
|
206
|
+
const goalId = entry.slice(0, -".json".length);
|
|
207
|
+
// Skip entries that fail the safe-id check (defensive; createGoalId always produces safe ids).
|
|
208
|
+
if (!/^[A-Za-z0-9_-]+$/.test(goalId)) continue;
|
|
209
|
+
const g = this.load(goalId);
|
|
210
|
+
if (g) goals.push(g);
|
|
211
|
+
}
|
|
212
|
+
goals.sort((a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? ""));
|
|
213
|
+
return goals;
|
|
214
|
+
} catch (error) {
|
|
215
|
+
logInternalError("goal-state-store.list", error, `cwd=${this.cwd}`);
|
|
216
|
+
return [];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { GreenLevel, VerificationContract, VerificationEvidence } from "../state/types.ts";
|
|
2
|
+
|
|
3
|
+
const GREEN_ORDER: Record<GreenLevel, number> = {
|
|
4
|
+
none: 0,
|
|
5
|
+
targeted: 1,
|
|
6
|
+
package: 2,
|
|
7
|
+
workspace: 3,
|
|
8
|
+
merge_ready: 4,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export interface GreenContractOutcome {
|
|
12
|
+
requiredGreenLevel: GreenLevel;
|
|
13
|
+
observedGreenLevel: GreenLevel;
|
|
14
|
+
satisfied: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function greenLevelSatisfies(observed: GreenLevel, required: GreenLevel): boolean {
|
|
18
|
+
return GREEN_ORDER[observed] >= GREEN_ORDER[required];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function evaluateGreenContract(contract: VerificationContract, evidence?: VerificationEvidence): GreenContractOutcome {
|
|
22
|
+
const observedGreenLevel = evidence?.observedGreenLevel ?? "none";
|
|
23
|
+
return {
|
|
24
|
+
requiredGreenLevel: contract.requiredGreenLevel,
|
|
25
|
+
observedGreenLevel,
|
|
26
|
+
satisfied: greenLevelSatisfies(observedGreenLevel, contract.requiredGreenLevel),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function inferGreenLevelFromTask(success: boolean, contract: VerificationContract): GreenLevel {
|
|
31
|
+
if (!success) return "none";
|
|
32
|
+
if (contract.requiredGreenLevel === "none") return "none";
|
|
33
|
+
return contract.allowManualEvidence ? contract.requiredGreenLevel : "targeted";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function createVerificationEvidence(contract: VerificationContract, success: boolean, notes: string): VerificationEvidence {
|
|
37
|
+
const observedGreenLevel = inferGreenLevelFromTask(success, contract);
|
|
38
|
+
const outcome = evaluateGreenContract(contract, {
|
|
39
|
+
requiredGreenLevel: contract.requiredGreenLevel,
|
|
40
|
+
observedGreenLevel,
|
|
41
|
+
satisfied: false,
|
|
42
|
+
commands: [],
|
|
43
|
+
notes,
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
requiredGreenLevel: contract.requiredGreenLevel,
|
|
47
|
+
observedGreenLevel,
|
|
48
|
+
satisfied: outcome.satisfied,
|
|
49
|
+
commands: contract.commands.map((cmd) => ({
|
|
50
|
+
cmd,
|
|
51
|
+
status: "not_run" as const,
|
|
52
|
+
})),
|
|
53
|
+
notes,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import type { CrewRuntimeConfig } from "../config/config.ts";
|
|
2
|
+
import { writeArtifact } from "../state/artifact-store.ts";
|
|
3
|
+
import { appendEvent } from "../state/event-log.ts";
|
|
4
|
+
import { appendMailboxMessage, findMailboxMessageByRequestId, readDeliveryState } from "../state/mailbox.ts";
|
|
5
|
+
import type { ArtifactDescriptor, TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
6
|
+
import { aggregateTaskOutputs } from "./task-output-context.ts";
|
|
7
|
+
|
|
8
|
+
export type CrewGroupJoinMode = "off" | "group" | "smart";
|
|
9
|
+
|
|
10
|
+
export interface CrewGroupJoinDelivery {
|
|
11
|
+
batchId: string;
|
|
12
|
+
mode: CrewGroupJoinMode;
|
|
13
|
+
partial: boolean;
|
|
14
|
+
taskIds: string[];
|
|
15
|
+
completed: string[];
|
|
16
|
+
failed: string[];
|
|
17
|
+
skipped: string[];
|
|
18
|
+
remaining: string[];
|
|
19
|
+
artifact?: ArtifactDescriptor;
|
|
20
|
+
messageId?: string;
|
|
21
|
+
requestId?: string;
|
|
22
|
+
ackRequired?: boolean;
|
|
23
|
+
ackStatus?: "pending" | "acknowledged";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function resolveGroupJoinMode(runtime?: CrewRuntimeConfig): CrewGroupJoinMode {
|
|
27
|
+
return runtime?.groupJoin ?? "smart";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function shouldGroupJoin(mode: CrewGroupJoinMode, batch: TeamTaskState[]): boolean {
|
|
31
|
+
if (mode === "off") return false;
|
|
32
|
+
if (mode === "group") return batch.length > 0;
|
|
33
|
+
return batch.length > 1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function batchIdFor(runId: string, taskIds: string[]): string {
|
|
37
|
+
return `${runId}_${taskIds.join("+").replace(/[^a-zA-Z0-9_+-]/g, "_")}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function requestIdFor(runId: string, batchId: string, partial: boolean): string {
|
|
41
|
+
return `${runId}:group-join:${partial ? "partial" : "completed"}:${batchId}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function statusList(tasks: TeamTaskState[], status: TeamTaskState["status"]): string[] {
|
|
45
|
+
return tasks.filter((task) => task.status === status).map((task) => task.id);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function deliverGroupJoin(input: {
|
|
49
|
+
manifest: TeamRunManifest;
|
|
50
|
+
mode: CrewGroupJoinMode;
|
|
51
|
+
batch: TeamTaskState[];
|
|
52
|
+
allTasks: TeamTaskState[];
|
|
53
|
+
partial?: boolean;
|
|
54
|
+
}): CrewGroupJoinDelivery | undefined {
|
|
55
|
+
if (!shouldGroupJoin(input.mode, input.batch)) return undefined;
|
|
56
|
+
const taskIds = input.batch.map((task) => task.id);
|
|
57
|
+
const latest = taskIds
|
|
58
|
+
.map((id) => input.allTasks.find((task) => task.id === id))
|
|
59
|
+
.filter((task): task is TeamTaskState => Boolean(task));
|
|
60
|
+
const completed = statusList(latest, "completed");
|
|
61
|
+
const failed = statusList(latest, "failed");
|
|
62
|
+
const skipped = statusList(latest, "skipped");
|
|
63
|
+
const remaining = latest.filter((task) => task.status === "queued" || task.status === "running").map((task) => task.id);
|
|
64
|
+
const partial = input.partial ?? remaining.length > 0;
|
|
65
|
+
const batchId = batchIdFor(input.manifest.runId, taskIds);
|
|
66
|
+
const summary = aggregateTaskOutputs(latest, input.manifest);
|
|
67
|
+
const requestId = requestIdFor(input.manifest.runId, batchId, partial);
|
|
68
|
+
const existingMailbox = findMailboxMessageByRequestId(input.manifest, requestId);
|
|
69
|
+
const existingStatus = existingMailbox
|
|
70
|
+
? (readDeliveryState(input.manifest).messages[existingMailbox.id] ?? existingMailbox.status)
|
|
71
|
+
: undefined;
|
|
72
|
+
const delivery: CrewGroupJoinDelivery = {
|
|
73
|
+
batchId,
|
|
74
|
+
mode: input.mode,
|
|
75
|
+
partial,
|
|
76
|
+
taskIds,
|
|
77
|
+
completed,
|
|
78
|
+
failed,
|
|
79
|
+
skipped,
|
|
80
|
+
remaining,
|
|
81
|
+
requestId,
|
|
82
|
+
ackRequired: true,
|
|
83
|
+
ackStatus: existingStatus === "acknowledged" ? "acknowledged" : "pending",
|
|
84
|
+
};
|
|
85
|
+
const content = `${JSON.stringify({ ...delivery, createdAt: new Date().toISOString() }, null, 2)}\n`;
|
|
86
|
+
const artifact = writeArtifact(input.manifest.artifactsRoot, {
|
|
87
|
+
kind: "metadata",
|
|
88
|
+
relativePath: `metadata/group-joins/${batchId}.json`,
|
|
89
|
+
producer: "group-join",
|
|
90
|
+
content,
|
|
91
|
+
});
|
|
92
|
+
const mailbox =
|
|
93
|
+
existingMailbox ??
|
|
94
|
+
appendMailboxMessage(input.manifest, {
|
|
95
|
+
direction: "outbox",
|
|
96
|
+
from: "group-join",
|
|
97
|
+
to: "leader",
|
|
98
|
+
body: [
|
|
99
|
+
`Group join ${partial ? "partial" : "completed"}: ${taskIds.join(", ")}`,
|
|
100
|
+
`Request: ${requestId}`,
|
|
101
|
+
`Completed: ${completed.join(", ") || "none"}`,
|
|
102
|
+
`Failed: ${failed.join(", ") || "none"}`,
|
|
103
|
+
`Skipped: ${skipped.join(", ") || "none"}`,
|
|
104
|
+
`Remaining: ${remaining.join(", ") || "none"}`,
|
|
105
|
+
"",
|
|
106
|
+
summary,
|
|
107
|
+
].join("\n"),
|
|
108
|
+
status: "delivered",
|
|
109
|
+
data: {
|
|
110
|
+
kind: "group_join",
|
|
111
|
+
requestId,
|
|
112
|
+
batchId,
|
|
113
|
+
partial,
|
|
114
|
+
ackRequired: true,
|
|
115
|
+
taskIds,
|
|
116
|
+
completed,
|
|
117
|
+
failed,
|
|
118
|
+
skipped,
|
|
119
|
+
remaining,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
appendEvent(input.manifest.eventsPath, {
|
|
123
|
+
type: partial ? "agent.group_join.partial" : "agent.group_join.completed",
|
|
124
|
+
runId: input.manifest.runId,
|
|
125
|
+
message: `Group join ${partial ? "partial" : "completed"} for ${taskIds.length} task(s).`,
|
|
126
|
+
data: {
|
|
127
|
+
...delivery,
|
|
128
|
+
artifactPath: artifact.path,
|
|
129
|
+
messageId: mailbox.id,
|
|
130
|
+
fallback: "mailbox-delivered",
|
|
131
|
+
reused: Boolean(existingMailbox),
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
if (existingMailbox)
|
|
135
|
+
appendEvent(input.manifest.eventsPath, {
|
|
136
|
+
type: "agent.group_join.delivery_reused",
|
|
137
|
+
runId: input.manifest.runId,
|
|
138
|
+
message: `Reused group join mailbox delivery for ${taskIds.length} task(s).`,
|
|
139
|
+
data: { requestId, messageId: mailbox.id, batchId, partial },
|
|
140
|
+
});
|
|
141
|
+
return { ...delivery, artifact, messageId: mailbox.id };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
import type { CrewAgentRecord } from "./crew-agent-runtime.ts";
|
|
145
|
+
|
|
146
|
+
export type JoinMode = "async" | "group" | "smart";
|
|
147
|
+
export type DeliveryCallback = (records: CrewAgentRecord[], partial: boolean) => void;
|
|
148
|
+
|
|
149
|
+
interface AgentGroup {
|
|
150
|
+
groupId: string;
|
|
151
|
+
agentIds: Set<string>;
|
|
152
|
+
completedRecords: Map<string, CrewAgentRecord>;
|
|
153
|
+
timeoutHandle?: ReturnType<typeof setTimeout>;
|
|
154
|
+
delivered: boolean;
|
|
155
|
+
isStraggler: boolean;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const DEFAULT_TIMEOUT = 30_000;
|
|
159
|
+
const STRAGGLER_TIMEOUT = 15_000;
|
|
160
|
+
|
|
161
|
+
export class GroupJoinManager {
|
|
162
|
+
private groups = new Map<string, AgentGroup>();
|
|
163
|
+
private agentToGroup = new Map<string, string>();
|
|
164
|
+
|
|
165
|
+
private deliverCb: DeliveryCallback;
|
|
166
|
+
private groupTimeout: number;
|
|
167
|
+
|
|
168
|
+
constructor(deliverCb: DeliveryCallback, groupTimeout = DEFAULT_TIMEOUT) {
|
|
169
|
+
this.deliverCb = deliverCb;
|
|
170
|
+
this.groupTimeout = groupTimeout;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
registerGroup(groupId: string, agentIds: string[]): void {
|
|
174
|
+
const group: AgentGroup = {
|
|
175
|
+
groupId,
|
|
176
|
+
agentIds: new Set(agentIds),
|
|
177
|
+
completedRecords: new Map(),
|
|
178
|
+
delivered: false,
|
|
179
|
+
isStraggler: false,
|
|
180
|
+
};
|
|
181
|
+
this.groups.set(groupId, group);
|
|
182
|
+
for (const id of agentIds) {
|
|
183
|
+
this.agentToGroup.set(id, groupId);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
onAgentComplete(record: CrewAgentRecord): "delivered" | "held" | "pass" {
|
|
188
|
+
const groupId = this.agentToGroup.get(record.taskId);
|
|
189
|
+
if (!groupId) return "pass";
|
|
190
|
+
|
|
191
|
+
const group = this.groups.get(groupId);
|
|
192
|
+
if (!group || group.delivered) return "pass";
|
|
193
|
+
|
|
194
|
+
group.completedRecords.set(record.taskId, record);
|
|
195
|
+
|
|
196
|
+
if (group.completedRecords.size >= group.agentIds.size) {
|
|
197
|
+
this.deliver(group, false);
|
|
198
|
+
return "delivered";
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!group.timeoutHandle) {
|
|
202
|
+
const timeout = group.isStraggler ? STRAGGLER_TIMEOUT : this.groupTimeout;
|
|
203
|
+
group.timeoutHandle = setTimeout(() => {
|
|
204
|
+
this.onTimeout(group);
|
|
205
|
+
}, timeout);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return "held";
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private onTimeout(group: AgentGroup): void {
|
|
212
|
+
if (group.delivered) return;
|
|
213
|
+
group.timeoutHandle = undefined;
|
|
214
|
+
|
|
215
|
+
const remaining = new Set<string>();
|
|
216
|
+
for (const id of group.agentIds) {
|
|
217
|
+
if (!group.completedRecords.has(id)) remaining.add(id);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
for (const id of group.completedRecords.keys()) {
|
|
221
|
+
this.agentToGroup.delete(id);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
this.deliverCb([...group.completedRecords.values()], true);
|
|
225
|
+
|
|
226
|
+
group.completedRecords.clear();
|
|
227
|
+
group.agentIds = remaining;
|
|
228
|
+
group.isStraggler = true;
|
|
229
|
+
|
|
230
|
+
// Re-arm timer for remaining stragglers so they aren't silently abandoned
|
|
231
|
+
if (remaining.size > 0) {
|
|
232
|
+
group.timeoutHandle = setTimeout(() => this.onTimeout(group), STRAGGLER_TIMEOUT);
|
|
233
|
+
group.timeoutHandle.unref();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private deliver(group: AgentGroup, partial: boolean): void {
|
|
238
|
+
if (group.timeoutHandle) {
|
|
239
|
+
clearTimeout(group.timeoutHandle);
|
|
240
|
+
group.timeoutHandle = undefined;
|
|
241
|
+
}
|
|
242
|
+
group.delivered = true;
|
|
243
|
+
this.deliverCb([...group.completedRecords.values()], partial);
|
|
244
|
+
this.cleanupGroup(group.groupId);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
private cleanupGroup(groupId: string): void {
|
|
248
|
+
const group = this.groups.get(groupId);
|
|
249
|
+
if (!group) return;
|
|
250
|
+
for (const id of group.agentIds) {
|
|
251
|
+
this.agentToGroup.delete(id);
|
|
252
|
+
}
|
|
253
|
+
this.groups.delete(groupId);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
isGrouped(agentId: string): boolean {
|
|
257
|
+
return this.agentToGroup.has(agentId);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
dispose(): void {
|
|
261
|
+
for (const group of this.groups.values()) {
|
|
262
|
+
if (group.timeoutHandle) clearTimeout(group.timeoutHandle);
|
|
263
|
+
}
|
|
264
|
+
this.groups.clear();
|
|
265
|
+
this.agentToGroup.clear();
|
|
266
|
+
}
|
|
267
|
+
}
|