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,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* G1: Custom tool — submit_result.
|
|
3
|
+
*
|
|
4
|
+
* Registers a real `submit_result` tool in the Pi SDK session so that
|
|
5
|
+
* live-session workers can yield their result by calling a tool (instead of
|
|
6
|
+
* relying solely on prompt-based reminders).
|
|
7
|
+
*
|
|
8
|
+
* Adapted from oh-my-pi's `YieldTool` pattern. Uses Pi SDK's `defineTool()`
|
|
9
|
+
* and TypeBox schemas for validation.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { defineTool, type ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
13
|
+
import { type Static, Type } from "@sinclair/typebox";
|
|
14
|
+
import { logInternalError } from "../../utils/internal-error.ts";
|
|
15
|
+
import type { YieldResult } from "../yield-handler.ts";
|
|
16
|
+
|
|
17
|
+
const SubmitResultParams = Type.Object({
|
|
18
|
+
summary: Type.String({ description: "Summary of completed work." }),
|
|
19
|
+
artifacts: Type.Optional(
|
|
20
|
+
Type.Record(Type.String(), Type.String(), {
|
|
21
|
+
description: "Key-value map of artifact labels to file paths or content.",
|
|
22
|
+
}),
|
|
23
|
+
),
|
|
24
|
+
structuredData: Type.Optional(
|
|
25
|
+
Type.Record(Type.String(), Type.Unknown(), {
|
|
26
|
+
description: "Structured key-value data to pass back to the orchestrator.",
|
|
27
|
+
}),
|
|
28
|
+
),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
type SubmitResultParams = Static<typeof SubmitResultParams>;
|
|
32
|
+
|
|
33
|
+
interface SubmitResultDetails {
|
|
34
|
+
summary: string;
|
|
35
|
+
artifacts?: Record<string, string>;
|
|
36
|
+
structuredData?: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Create a `submit_result` tool definition that calls `onYield` when invoked.
|
|
41
|
+
*
|
|
42
|
+
* The tool is injected into the session via `createAgentSession({ customTools: [...] })`.
|
|
43
|
+
* When the model calls it, the result is captured via the `onYield` callback
|
|
44
|
+
* and the yield enforcement loop terminates.
|
|
45
|
+
*/
|
|
46
|
+
export function createSubmitResultTool(
|
|
47
|
+
onYield: (result: YieldResult) => void,
|
|
48
|
+
): ToolDefinition<typeof SubmitResultParams, SubmitResultDetails> {
|
|
49
|
+
return defineTool({
|
|
50
|
+
name: "submit_result",
|
|
51
|
+
label: "Submit Result",
|
|
52
|
+
description:
|
|
53
|
+
"Submit final task result. Call when task complete. " +
|
|
54
|
+
"Provide summary, optional artifacts (file paths/content), optional structured data.",
|
|
55
|
+
parameters: SubmitResultParams,
|
|
56
|
+
promptSnippet: "Submit your task result when done using submit_result",
|
|
57
|
+
promptGuidelines: [
|
|
58
|
+
"Always call submit_result when your task is complete, even if you were unable to finish.",
|
|
59
|
+
"Include a clear summary of what was accomplished.",
|
|
60
|
+
],
|
|
61
|
+
async execute(
|
|
62
|
+
toolCallId: string,
|
|
63
|
+
params: SubmitResultParams,
|
|
64
|
+
_signal: AbortSignal | undefined,
|
|
65
|
+
_onUpdate: unknown,
|
|
66
|
+
_ctx: unknown,
|
|
67
|
+
): Promise<{
|
|
68
|
+
content: Array<{ type: "text"; text: string }>;
|
|
69
|
+
details: SubmitResultDetails;
|
|
70
|
+
}> {
|
|
71
|
+
const result: YieldResult = {
|
|
72
|
+
summary: params.summary,
|
|
73
|
+
toolCallId,
|
|
74
|
+
...(params.artifacts ? { artifacts: params.artifacts } : {}),
|
|
75
|
+
...(params.structuredData ? { structuredData: params.structuredData } : {}),
|
|
76
|
+
};
|
|
77
|
+
// Build response first so the model always gets confirmation
|
|
78
|
+
const response: {
|
|
79
|
+
content: Array<{ type: "text"; text: string }>;
|
|
80
|
+
details: SubmitResultDetails;
|
|
81
|
+
} = {
|
|
82
|
+
content: [
|
|
83
|
+
{
|
|
84
|
+
type: "text",
|
|
85
|
+
text: "Result submitted successfully. Thank you.",
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
details: {
|
|
89
|
+
summary: params.summary,
|
|
90
|
+
artifacts: params.artifacts,
|
|
91
|
+
structuredData: params.structuredData,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
try {
|
|
95
|
+
onYield(result);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
logInternalError("submit-result-tool.yield", error, toolCallId);
|
|
98
|
+
}
|
|
99
|
+
return response;
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import type { TeamRunManifest } from "../state/types.ts";
|
|
4
|
+
|
|
5
|
+
import { logInternalError } from "../utils/internal-error.ts";
|
|
6
|
+
|
|
7
|
+
export type DeadletterReason = "max-retries" | "heartbeat-dead" | "manual";
|
|
8
|
+
|
|
9
|
+
export interface DeadletterEntry {
|
|
10
|
+
taskId: string;
|
|
11
|
+
runId: string;
|
|
12
|
+
reason: DeadletterReason;
|
|
13
|
+
attempts: number;
|
|
14
|
+
lastError?: string;
|
|
15
|
+
attemptId?: string;
|
|
16
|
+
timestamp: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function deadletterPath(manifest: TeamRunManifest): string {
|
|
20
|
+
return path.join(manifest.stateRoot, "deadletter.jsonl");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function appendDeadletter(manifest: TeamRunManifest, entry: DeadletterEntry): void {
|
|
24
|
+
try {
|
|
25
|
+
fs.mkdirSync(manifest.stateRoot, { recursive: true });
|
|
26
|
+
fs.appendFileSync(deadletterPath(manifest), `${JSON.stringify(entry)}\n`, "utf-8");
|
|
27
|
+
} catch (error) {
|
|
28
|
+
logInternalError("deadletter.append", error, `taskId=${entry.taskId}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function readDeadletter(manifest: TeamRunManifest, maxEntries = 1000): DeadletterEntry[] {
|
|
33
|
+
const filePath = deadletterPath(manifest);
|
|
34
|
+
if (!fs.existsSync(filePath)) return [];
|
|
35
|
+
// Read last maxEntries lines only to limit memory.
|
|
36
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
37
|
+
const lines = raw.split(/\r?\n/).filter(Boolean);
|
|
38
|
+
const tail = lines.slice(-maxEntries);
|
|
39
|
+
return tail.flatMap((line) => {
|
|
40
|
+
try {
|
|
41
|
+
const parsed = JSON.parse(line) as DeadletterEntry;
|
|
42
|
+
return parsed && typeof parsed.taskId === "string" && typeof parsed.runId === "string" ? [parsed] : [];
|
|
43
|
+
} catch {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import type { NotificationDescriptor } from "../extension/notification-router.ts";
|
|
2
|
+
import { logInternalError } from "../utils/internal-error.ts";
|
|
3
|
+
|
|
4
|
+
export interface PendingDelivery {
|
|
5
|
+
runId: string;
|
|
6
|
+
payload: unknown;
|
|
7
|
+
timestamp: number;
|
|
8
|
+
type: "result" | "notification" | "steer";
|
|
9
|
+
generation?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface DeliveryCoordinatorDeps {
|
|
13
|
+
/** Emit an event to the active Pi event bus. */
|
|
14
|
+
emit?: (event: string, data: unknown) => void;
|
|
15
|
+
/** Send a follow-up message to the active session (for notifications). */
|
|
16
|
+
sendFollowUp?: (title: string, body: string) => void;
|
|
17
|
+
/** Send a wake-up message to the active session (for async results). */
|
|
18
|
+
sendWakeUp?: (message: string) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const PENDING_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
22
|
+
|
|
23
|
+
export class DeliveryCoordinator {
|
|
24
|
+
private active = false;
|
|
25
|
+
private generation = 0;
|
|
26
|
+
private pending: PendingDelivery[] = [];
|
|
27
|
+
private flushing = false;
|
|
28
|
+
private readonly deps: DeliveryCoordinatorDeps;
|
|
29
|
+
private ttlTimer: ReturnType<typeof setInterval> | undefined;
|
|
30
|
+
private timerStarted = false;
|
|
31
|
+
private ownerSessionId: string | undefined = undefined;
|
|
32
|
+
|
|
33
|
+
constructor(deps: DeliveryCoordinatorDeps) {
|
|
34
|
+
this.deps = deps;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
activate(sessionId: string): void {
|
|
38
|
+
this.ownerSessionId = sessionId;
|
|
39
|
+
this.active = true;
|
|
40
|
+
this.flushQueuedResults();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
deactivate(): void {
|
|
44
|
+
this.active = false;
|
|
45
|
+
this.ownerSessionId = undefined;
|
|
46
|
+
this.generation += 1;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
isActive(): boolean {
|
|
50
|
+
return this.active;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getPendingCount(): number {
|
|
54
|
+
return this.pending.length;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
deliverResult(runId: string, result: unknown): void {
|
|
58
|
+
if (this.active && this.deps.emit) {
|
|
59
|
+
try {
|
|
60
|
+
this.deps.emit("pi-crew:run-result", result);
|
|
61
|
+
return;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
logInternalError("delivery-coordinator.deliverResult", error, `runId=${runId}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (!this.flushing)
|
|
67
|
+
this.enqueue({
|
|
68
|
+
runId,
|
|
69
|
+
payload: result,
|
|
70
|
+
timestamp: Date.now(),
|
|
71
|
+
type: "result",
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
deliverNotification(notification: NotificationDescriptor): void {
|
|
76
|
+
let delivered = false;
|
|
77
|
+
if (this.active && this.deps.sendFollowUp) {
|
|
78
|
+
try {
|
|
79
|
+
this.deps.sendFollowUp(notification.title, notification.body ?? "");
|
|
80
|
+
delivered = true;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
logInternalError("delivery-coordinator.deliverNotification", error, `id=${notification.id}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (delivered) {
|
|
86
|
+
if (this.deps.emit) {
|
|
87
|
+
try {
|
|
88
|
+
this.deps.emit("pi-crew:notification", notification);
|
|
89
|
+
} catch {
|
|
90
|
+
/* secondary delivery, ignore errors */
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (!this.flushing)
|
|
96
|
+
this.enqueue({
|
|
97
|
+
runId: notification.runId ?? "",
|
|
98
|
+
payload: notification,
|
|
99
|
+
timestamp: Date.now(),
|
|
100
|
+
type: "notification",
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
deliverSteer(runId: string, message: string): void {
|
|
105
|
+
if (this.active && this.deps.sendWakeUp) {
|
|
106
|
+
try {
|
|
107
|
+
this.deps.sendWakeUp(message);
|
|
108
|
+
return;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
logInternalError("delivery-coordinator.deliverSteer", error, `runId=${runId}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (!this.flushing)
|
|
114
|
+
this.enqueue({
|
|
115
|
+
runId,
|
|
116
|
+
payload: message,
|
|
117
|
+
timestamp: Date.now(),
|
|
118
|
+
type: "steer",
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
flushQueuedResults(): void {
|
|
123
|
+
if (!this.active || this.pending.length === 0) return;
|
|
124
|
+
// HIGH-16/ MEDIUM-16: Set flushing BEFORE splice to prevent re-entrancy
|
|
125
|
+
if (this.flushing) return;
|
|
126
|
+
this.flushing = true;
|
|
127
|
+
// Note: this.flushing is now set, so concurrent calls will exit early due to the check above
|
|
128
|
+
// This serves as a simple lock to prevent race conditions
|
|
129
|
+
const batch = this.pending.splice(0);
|
|
130
|
+
try {
|
|
131
|
+
const retryLater: PendingDelivery[] = [];
|
|
132
|
+
for (const delivery of batch) {
|
|
133
|
+
if (delivery.type === "steer" && delivery.generation !== undefined && delivery.generation !== this.generation) {
|
|
134
|
+
logInternalError("delivery-coordinator.flush.stale", undefined, `runId=${delivery.runId} type=${delivery.type}`);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
if (!this.deliverQueued(delivery))
|
|
139
|
+
retryLater.push({
|
|
140
|
+
...delivery,
|
|
141
|
+
generation: this.generation,
|
|
142
|
+
});
|
|
143
|
+
} catch (error) {
|
|
144
|
+
logInternalError("delivery-coordinator.flush", error, `runId=${delivery.runId} type=${delivery.type}`);
|
|
145
|
+
retryLater.push({
|
|
146
|
+
...delivery,
|
|
147
|
+
generation: this.generation,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
this.pending.unshift(...retryLater);
|
|
152
|
+
} finally {
|
|
153
|
+
this.flushing = false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
dispose(): void {
|
|
158
|
+
this.deactivate();
|
|
159
|
+
this.pending.length = 0;
|
|
160
|
+
if (this.ttlTimer) {
|
|
161
|
+
clearInterval(this.ttlTimer);
|
|
162
|
+
this.ttlTimer = undefined;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private deliverQueued(delivery: PendingDelivery): boolean {
|
|
167
|
+
switch (delivery.type) {
|
|
168
|
+
case "result":
|
|
169
|
+
if (!this.deps.emit) return false;
|
|
170
|
+
this.deps.emit("pi-crew:run-result", delivery.payload);
|
|
171
|
+
return true;
|
|
172
|
+
case "notification": {
|
|
173
|
+
const notification = delivery.payload as NotificationDescriptor;
|
|
174
|
+
if (!this.deps.sendFollowUp) return false;
|
|
175
|
+
this.deps.sendFollowUp(notification.title, notification.body ?? "");
|
|
176
|
+
try {
|
|
177
|
+
this.deps.emit?.("pi-crew:notification", notification);
|
|
178
|
+
} catch {
|
|
179
|
+
// Secondary event delivery must not consume the user-facing notification.
|
|
180
|
+
}
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
case "steer": {
|
|
184
|
+
if (!this.deps.sendWakeUp) return false;
|
|
185
|
+
const message = typeof delivery.payload === "string" ? delivery.payload : String(delivery.payload);
|
|
186
|
+
this.deps.sendWakeUp(message);
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private enqueue(delivery: PendingDelivery): void {
|
|
193
|
+
// Lazily start the TTL timer on first enqueue (only if never started)
|
|
194
|
+
if (!this.timerStarted) {
|
|
195
|
+
this.timerStarted = true;
|
|
196
|
+
this.ttlTimer = setInterval(() => this.evictExpired(), 60_000);
|
|
197
|
+
this.ttlTimer.unref();
|
|
198
|
+
}
|
|
199
|
+
this.pending.push({ ...delivery, generation: this.generation });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private evictExpired(): void {
|
|
203
|
+
const cutoff = Date.now() - PENDING_TTL_MS;
|
|
204
|
+
const before = this.pending.length;
|
|
205
|
+
this.pending = this.pending.filter((d) => d.timestamp > cutoff);
|
|
206
|
+
const evicted = before - this.pending.length;
|
|
207
|
+
if (evicted > 0) {
|
|
208
|
+
logInternalError("delivery-coordinator.evict", undefined, `evicted=${evicted} remaining=${this.pending.length}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delta conflict detection for pi-crew import and resume operations.
|
|
3
|
+
*
|
|
4
|
+
* Compares incoming bundles against existing state to surface conflicts
|
|
5
|
+
* (file overwrites, status mismatches, schema drift, deleted resources)
|
|
6
|
+
* without blocking the operation — only reporting for user awareness.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ── Types ──────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
export type ConflictKind = "file_overwrite" | "state_mismatch" | "schema_drift" | "resource_deleted";
|
|
12
|
+
|
|
13
|
+
export interface Conflict {
|
|
14
|
+
kind: ConflictKind;
|
|
15
|
+
/** File or resource path that is in conflict. */
|
|
16
|
+
path: string;
|
|
17
|
+
/** Current value or summary (optional). */
|
|
18
|
+
existing?: string;
|
|
19
|
+
/** Incoming value or summary (optional). */
|
|
20
|
+
incoming?: string;
|
|
21
|
+
severity: "error" | "warning";
|
|
22
|
+
autoResolvable: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ConflictReport {
|
|
26
|
+
hasConflicts: boolean;
|
|
27
|
+
conflicts: Conflict[];
|
|
28
|
+
summary: { errors: number; warnings: number; autoResolvable: number };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type ConflictStrategy = "skip" | "overwrite" | "merge";
|
|
32
|
+
|
|
33
|
+
export interface ConflictResolution {
|
|
34
|
+
resolved: boolean;
|
|
35
|
+
action: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ── Helpers ────────────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
interface TaskLike {
|
|
41
|
+
id: string;
|
|
42
|
+
status: string;
|
|
43
|
+
role?: string;
|
|
44
|
+
agent?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function buildReport(conflicts: Conflict[]): ConflictReport {
|
|
48
|
+
const errors = conflicts.filter((c) => c.severity === "error").length;
|
|
49
|
+
const warnings = conflicts.filter((c) => c.severity === "warning").length;
|
|
50
|
+
const autoResolvable = conflicts.filter((c) => c.autoResolvable).length;
|
|
51
|
+
return {
|
|
52
|
+
hasConflicts: conflicts.length > 0,
|
|
53
|
+
conflicts,
|
|
54
|
+
summary: { errors, warnings, autoResolvable },
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
59
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Extract task-like objects from an unknown array, filtering out non-records.
|
|
64
|
+
*/
|
|
65
|
+
function extractTaskLikes(tasks: unknown[]): TaskLike[] {
|
|
66
|
+
return tasks
|
|
67
|
+
.filter(isRecord)
|
|
68
|
+
.map((t) => ({
|
|
69
|
+
id: typeof t.id === "string" ? t.id : "",
|
|
70
|
+
status: typeof t.status === "string" ? t.status : "",
|
|
71
|
+
role: typeof t.role === "string" ? t.role : undefined,
|
|
72
|
+
agent: typeof t.agent === "string" ? t.agent : undefined,
|
|
73
|
+
}))
|
|
74
|
+
.filter((t) => t.id !== "");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ── Import Conflict Detection ──────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
export interface ImportBundle {
|
|
80
|
+
manifest: Record<string, unknown>;
|
|
81
|
+
tasks: unknown[];
|
|
82
|
+
events?: unknown[];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ExistingState {
|
|
86
|
+
manifest?: Record<string, unknown>;
|
|
87
|
+
tasks?: unknown[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Detect conflicts between an import bundle and existing run state.
|
|
92
|
+
*
|
|
93
|
+
* Checks:
|
|
94
|
+
* - **schema_drift**: manifest `schemaVersion` differs between import and existing.
|
|
95
|
+
* - **file_overwrite**: artifact paths in the import bundle that already exist
|
|
96
|
+
* in the current manifest's artifact list.
|
|
97
|
+
* - **state_mismatch**: task statuses differ between import and existing tasks.
|
|
98
|
+
* - **resource_deleted**: referenced agent/team/workflow in import does not exist
|
|
99
|
+
* in the current state.
|
|
100
|
+
*/
|
|
101
|
+
export function detectImportConflicts(importBundle: ImportBundle, existingState: ExistingState): ConflictReport {
|
|
102
|
+
const conflicts: Conflict[] = [];
|
|
103
|
+
const { manifest: incoming, tasks: incomingTasks } = importBundle;
|
|
104
|
+
const { manifest: current, tasks: currentTasks } = existingState;
|
|
105
|
+
|
|
106
|
+
// ── Schema drift ─────────────────────────────────────────────────
|
|
107
|
+
if (current) {
|
|
108
|
+
const incomingVersion = incoming.schemaVersion;
|
|
109
|
+
const currentVersion = current.schemaVersion;
|
|
110
|
+
if (typeof incomingVersion !== "undefined" && typeof currentVersion !== "undefined" && incomingVersion !== currentVersion) {
|
|
111
|
+
conflicts.push({
|
|
112
|
+
kind: "schema_drift",
|
|
113
|
+
path: "manifest.schemaVersion",
|
|
114
|
+
existing: String(currentVersion),
|
|
115
|
+
incoming: String(incomingVersion),
|
|
116
|
+
severity: "warning",
|
|
117
|
+
autoResolvable: true,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ── File overwrite (artifact collision) ──────────────────────────
|
|
123
|
+
const incomingArtifacts = Array.isArray(incoming.artifacts) ? incoming.artifacts : [];
|
|
124
|
+
const currentArtifacts = Array.isArray(current?.artifacts) ? current?.artifacts : [];
|
|
125
|
+
|
|
126
|
+
if (current && currentArtifacts.length > 0) {
|
|
127
|
+
const currentPaths = new Set(
|
|
128
|
+
currentArtifacts
|
|
129
|
+
.filter(isRecord)
|
|
130
|
+
.map((a) => (typeof a.path === "string" ? a.path : ""))
|
|
131
|
+
.filter((p) => p !== ""),
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
for (const artifact of incomingArtifacts) {
|
|
135
|
+
if (!isRecord(artifact)) continue;
|
|
136
|
+
const artifactPath = typeof artifact.path === "string" ? artifact.path : "";
|
|
137
|
+
if (artifactPath !== "" && currentPaths.has(artifactPath)) {
|
|
138
|
+
conflicts.push({
|
|
139
|
+
kind: "file_overwrite",
|
|
140
|
+
path: artifactPath,
|
|
141
|
+
existing: "present in current run",
|
|
142
|
+
incoming: "present in import bundle",
|
|
143
|
+
severity: "warning",
|
|
144
|
+
autoResolvable: true,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ── State mismatch (task status differences) ─────────────────────
|
|
151
|
+
if (currentTasks && currentTasks.length > 0) {
|
|
152
|
+
const currentTaskMap = new Map(extractTaskLikes(currentTasks).map((t) => [t.id, t]));
|
|
153
|
+
const incomingTaskList = extractTaskLikes(incomingTasks);
|
|
154
|
+
|
|
155
|
+
for (const inTask of incomingTaskList) {
|
|
156
|
+
const curTask = currentTaskMap.get(inTask.id);
|
|
157
|
+
if (curTask && curTask.status !== inTask.status) {
|
|
158
|
+
conflicts.push({
|
|
159
|
+
kind: "state_mismatch",
|
|
160
|
+
path: `tasks/${inTask.id}`,
|
|
161
|
+
existing: curTask.status,
|
|
162
|
+
incoming: inTask.status,
|
|
163
|
+
severity: "error",
|
|
164
|
+
autoResolvable: false,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ── Resource deleted (agent/team/workflow no longer in current) ──
|
|
171
|
+
if (current) {
|
|
172
|
+
const currentTeam = typeof current.team === "string" ? current.team : undefined;
|
|
173
|
+
const currentWorkflow = typeof current.workflow === "string" ? current.workflow : undefined;
|
|
174
|
+
const incomingTeam = typeof incoming.team === "string" ? incoming.team : undefined;
|
|
175
|
+
const incomingWorkflow = typeof incoming.workflow === "string" ? incoming.workflow : undefined;
|
|
176
|
+
|
|
177
|
+
if (incomingTeam && currentTeam && incomingTeam !== currentTeam) {
|
|
178
|
+
conflicts.push({
|
|
179
|
+
kind: "resource_deleted",
|
|
180
|
+
path: `team/${incomingTeam}`,
|
|
181
|
+
existing: currentTeam,
|
|
182
|
+
incoming: incomingTeam,
|
|
183
|
+
severity: "warning",
|
|
184
|
+
autoResolvable: true,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (incomingWorkflow && currentWorkflow && incomingWorkflow !== currentWorkflow) {
|
|
189
|
+
conflicts.push({
|
|
190
|
+
kind: "resource_deleted",
|
|
191
|
+
path: `workflow/${incomingWorkflow}`,
|
|
192
|
+
existing: currentWorkflow,
|
|
193
|
+
incoming: incomingWorkflow,
|
|
194
|
+
severity: "warning",
|
|
195
|
+
autoResolvable: true,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Check agent references from tasks vs current tasks
|
|
200
|
+
if (currentTasks && currentTasks.length > 0) {
|
|
201
|
+
const currentAgents = new Set(
|
|
202
|
+
extractTaskLikes(currentTasks)
|
|
203
|
+
.map((t) => t.agent)
|
|
204
|
+
.filter((a): a is string => a !== undefined),
|
|
205
|
+
);
|
|
206
|
+
const incomingTaskList = extractTaskLikes(incomingTasks);
|
|
207
|
+
for (const inTask of incomingTaskList) {
|
|
208
|
+
if (
|
|
209
|
+
inTask.agent &&
|
|
210
|
+
inTask.id !== "" &&
|
|
211
|
+
currentAgents.size > 0 &&
|
|
212
|
+
!currentAgents.has(inTask.agent) &&
|
|
213
|
+
// Only flag if there's a matching task id (agent was reassigned)
|
|
214
|
+
extractTaskLikes(currentTasks).some((ct) => ct.id === inTask.id)
|
|
215
|
+
) {
|
|
216
|
+
conflicts.push({
|
|
217
|
+
kind: "resource_deleted",
|
|
218
|
+
path: `agent/${inTask.agent}`,
|
|
219
|
+
existing: "not in current run",
|
|
220
|
+
incoming: inTask.agent,
|
|
221
|
+
severity: "warning",
|
|
222
|
+
autoResolvable: true,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return buildReport(conflicts);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ── Resume Conflict Detection ──────────────────────────────────────────
|
|
233
|
+
|
|
234
|
+
export interface SuspendedState {
|
|
235
|
+
tasks: unknown[];
|
|
236
|
+
artifacts: string[];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export interface CurrentState {
|
|
240
|
+
changedFiles: string[];
|
|
241
|
+
taskStatuses: Record<string, string>;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Detect conflicts when resuming a suspended run against current filesystem state.
|
|
246
|
+
*
|
|
247
|
+
* Checks:
|
|
248
|
+
* - **file_overwrite**: files changed since suspension.
|
|
249
|
+
* - **state_mismatch**: task statuses changed externally.
|
|
250
|
+
*/
|
|
251
|
+
export function detectResumeConflicts(suspendedState: SuspendedState, currentState: CurrentState): ConflictReport {
|
|
252
|
+
const conflicts: Conflict[] = [];
|
|
253
|
+
const { tasks: suspendedTasks, artifacts: suspendedArtifacts } = suspendedState;
|
|
254
|
+
const { changedFiles, taskStatuses } = currentState;
|
|
255
|
+
|
|
256
|
+
// ── File overwrite ───────────────────────────────────────────────
|
|
257
|
+
const changedSet = new Set(changedFiles);
|
|
258
|
+
for (const artifactPath of suspendedArtifacts) {
|
|
259
|
+
if (changedSet.has(artifactPath)) {
|
|
260
|
+
conflicts.push({
|
|
261
|
+
kind: "file_overwrite",
|
|
262
|
+
path: artifactPath,
|
|
263
|
+
existing: "modified since suspension",
|
|
264
|
+
incoming: "expected unchanged",
|
|
265
|
+
severity: "error",
|
|
266
|
+
autoResolvable: false,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// ── State mismatch ───────────────────────────────────────────────
|
|
272
|
+
const suspendedTaskList = extractTaskLikes(suspendedTasks);
|
|
273
|
+
for (const task of suspendedTaskList) {
|
|
274
|
+
const currentStatus = taskStatuses[task.id];
|
|
275
|
+
if (currentStatus !== undefined && currentStatus !== task.status) {
|
|
276
|
+
conflicts.push({
|
|
277
|
+
kind: "state_mismatch",
|
|
278
|
+
path: `tasks/${task.id}`,
|
|
279
|
+
existing: currentStatus,
|
|
280
|
+
incoming: task.status,
|
|
281
|
+
severity: "error",
|
|
282
|
+
autoResolvable: false,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return buildReport(conflicts);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// ── Conflict Resolution ────────────────────────────────────────────────
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Apply a resolution strategy to a conflict.
|
|
294
|
+
*
|
|
295
|
+
* - **skip**: skip the conflicting item (always resolves).
|
|
296
|
+
* - **overwrite**: replace existing with incoming (resolves for `file_overwrite`
|
|
297
|
+
* and `schema_drift`; does not resolve `state_mismatch` or `resource_deleted`).
|
|
298
|
+
* - **merge**: attempt merge — resolves `state_mismatch` with merged status;
|
|
299
|
+
* resolves others conditionally.
|
|
300
|
+
*/
|
|
301
|
+
export function resolveConflict(conflict: Conflict, strategy: ConflictStrategy): ConflictResolution {
|
|
302
|
+
switch (strategy) {
|
|
303
|
+
case "skip":
|
|
304
|
+
return { resolved: true, action: `Skipped ${conflict.path}` };
|
|
305
|
+
|
|
306
|
+
case "overwrite":
|
|
307
|
+
if (conflict.kind === "file_overwrite" || conflict.kind === "schema_drift") {
|
|
308
|
+
return {
|
|
309
|
+
resolved: true,
|
|
310
|
+
action: `Overwritten ${conflict.path} with incoming value`,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
resolved: false,
|
|
315
|
+
action: `Cannot overwrite ${conflict.kind} at ${conflict.path}; manual resolution required`,
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
case "merge":
|
|
319
|
+
if (conflict.kind === "state_mismatch") {
|
|
320
|
+
return {
|
|
321
|
+
resolved: true,
|
|
322
|
+
action: `Merged ${conflict.path}: kept existing=${conflict.existing ?? "?"}, incoming=${conflict.incoming ?? "?"}`,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
if (conflict.kind === "resource_deleted") {
|
|
326
|
+
return {
|
|
327
|
+
resolved: true,
|
|
328
|
+
action: `Merged ${conflict.path}: using incoming resource reference`,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
if (conflict.kind === "file_overwrite") {
|
|
332
|
+
return {
|
|
333
|
+
resolved: true,
|
|
334
|
+
action: `Merged ${conflict.path}: kept both versions`,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
if (conflict.kind === "schema_drift") {
|
|
338
|
+
return {
|
|
339
|
+
resolved: true,
|
|
340
|
+
action: `Merged ${conflict.path}: using incoming schema version`,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
return {
|
|
344
|
+
resolved: false,
|
|
345
|
+
action: `Cannot merge ${conflict.kind} at ${conflict.path}`,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
}
|