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,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resilient edit wrapper — uses the cascading replace() engine as a fallback
|
|
3
|
+
* when the native edit tool fails with "old_string not found".
|
|
4
|
+
*
|
|
5
|
+
* Multi-agent runs (crew workers) often provide slightly-wrong oldText:
|
|
6
|
+
* - indentation drift (tabs vs spaces)
|
|
7
|
+
* - trailing whitespace differences
|
|
8
|
+
* - line-ending normalization (\r\n vs \n)
|
|
9
|
+
*
|
|
10
|
+
* The native edit tool is exact-match only and fails hard on these. This
|
|
11
|
+
* wrapper catches that failure and retries with the cascading engine, which
|
|
12
|
+
* tries (in order): exact → escape-normalized → line-trimmed → block-anchor →
|
|
13
|
+
* whitespace-normalized → trimmed-boundary.
|
|
14
|
+
*
|
|
15
|
+
* CONFLICT AWARENESS: pi-diff (`@heyhuynhgiabuu/pi-diff`) also overrides the
|
|
16
|
+
* edit tool with its own replace() integration. To avoid double-wrapping,
|
|
17
|
+
* this module is OPT-IN via the `CREW_RESILIENT_EDIT=1` env var, and
|
|
18
|
+
* auto-disables if pi-diff is detected in the loaded extensions.
|
|
19
|
+
*
|
|
20
|
+
* Usage (in register.ts, guarded):
|
|
21
|
+
* if (process.env.CREW_RESILIENT_EDIT === "1") {
|
|
22
|
+
* wrapEditWithResilientReplace(pi);
|
|
23
|
+
* }
|
|
24
|
+
*/
|
|
25
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
26
|
+
import { type ReplaceResult, replace } from "../runtime/replace.ts";
|
|
27
|
+
|
|
28
|
+
interface EditToolLike {
|
|
29
|
+
name: string;
|
|
30
|
+
description: string;
|
|
31
|
+
parameters: unknown;
|
|
32
|
+
execute: (toolCallId: string, params: unknown, signal: unknown, onUpdate: unknown) => Promise<unknown>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface EditParams {
|
|
36
|
+
path?: string;
|
|
37
|
+
filePath?: string;
|
|
38
|
+
oldString?: string;
|
|
39
|
+
old_string?: string;
|
|
40
|
+
newString?: string;
|
|
41
|
+
new_string?: string;
|
|
42
|
+
replaceAll?: boolean;
|
|
43
|
+
replace_all?: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface EditResult {
|
|
47
|
+
content?: unknown[];
|
|
48
|
+
[key: string]: unknown;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const NOT_FOUND_PATTERNS = [/old_string not found/i, /oldstring not found/i, /no match/i, /could not find/i, /string not found/i];
|
|
52
|
+
|
|
53
|
+
function isNotFoundResult(result: unknown): boolean {
|
|
54
|
+
if (!result || typeof result !== "object") return false;
|
|
55
|
+
const r = result as EditResult;
|
|
56
|
+
const text = JSON.stringify(r.content ?? r);
|
|
57
|
+
return NOT_FOUND_PATTERNS.some((re) => re.test(text));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Detect whether pi-diff is loaded (to avoid double-wrapping edit). */
|
|
61
|
+
function isPiDiffLoaded(pi: ExtensionAPI): boolean {
|
|
62
|
+
try {
|
|
63
|
+
const piExt = pi as unknown as { extensions?: unknown[] | Record<string, unknown>; _extensions?: unknown[] };
|
|
64
|
+
const extensions = piExt?.extensions ?? piExt?._extensions ?? [];
|
|
65
|
+
const names = Array.isArray(extensions)
|
|
66
|
+
? extensions.map((e: unknown) => (typeof e === "string" ? e : ((e as { name?: string })?.name ?? "")))
|
|
67
|
+
: Object.keys(extensions as Record<string, unknown>);
|
|
68
|
+
return names.some((n: string) => typeof n === "string" && n.includes("pi-diff"));
|
|
69
|
+
} catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Wrap the native `edit` tool so that on "old_string not found" failures, it
|
|
76
|
+
* retries using the cascading replace() engine (lenient matching).
|
|
77
|
+
*
|
|
78
|
+
* @param pi the Pi extension API
|
|
79
|
+
* @param tools optional injected tool registry (for testing)
|
|
80
|
+
* @returns true if the wrapper was applied, false if skipped
|
|
81
|
+
*/
|
|
82
|
+
export function wrapEditWithResilientReplace(pi: ExtensionAPI, tools?: { edit: EditToolLike }): boolean {
|
|
83
|
+
// Auto-disable if pi-diff is present (it has its own replace integration).
|
|
84
|
+
if (isPiDiffLoaded(pi)) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const t = tools ?? (pi as unknown as { tools?: { edit?: EditToolLike } }).tools;
|
|
89
|
+
if (!t?.edit?.execute) return false;
|
|
90
|
+
|
|
91
|
+
const nativeExecute = t.edit.execute.bind(t.edit);
|
|
92
|
+
|
|
93
|
+
t.edit.execute = async function resilientExecute(
|
|
94
|
+
toolCallId: string,
|
|
95
|
+
params: unknown,
|
|
96
|
+
signal: unknown,
|
|
97
|
+
onUpdate: unknown,
|
|
98
|
+
): Promise<unknown> {
|
|
99
|
+
try {
|
|
100
|
+
const result = await nativeExecute(toolCallId, params, signal, onUpdate);
|
|
101
|
+
if (!isNotFoundResult(result)) return result;
|
|
102
|
+
// Fall through to resilient retry.
|
|
103
|
+
return await retryWithReplace(params as EditParams, toolCallId, signal, onUpdate);
|
|
104
|
+
} catch (err) {
|
|
105
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
106
|
+
if (NOT_FOUND_PATTERNS.some((re) => re.test(msg))) {
|
|
107
|
+
return await retryWithReplace(params as EditParams, toolCallId, signal, onUpdate);
|
|
108
|
+
}
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
return true;
|
|
114
|
+
|
|
115
|
+
async function retryWithReplace(params: EditParams, toolCallId: string, signal: unknown, onUpdate: unknown): Promise<unknown> {
|
|
116
|
+
const filePath = params.path ?? params.filePath;
|
|
117
|
+
const oldStr = params.oldString ?? params.old_string;
|
|
118
|
+
const newStr = params.newString ?? params.new_string;
|
|
119
|
+
const replaceAll = params.replaceAll ?? params.replace_all ?? false;
|
|
120
|
+
|
|
121
|
+
if (!filePath || typeof oldStr !== "string" || typeof newStr !== "string") {
|
|
122
|
+
// Can't retry — rethrow a not-found style error.
|
|
123
|
+
throw new Error("old_string not found (and resilient retry skipped: missing path/old/new)");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// LAZY: defer dynamic import of node:fs/promises to its call site.
|
|
127
|
+
const fs = await import("node:fs/promises");
|
|
128
|
+
let content: string;
|
|
129
|
+
try {
|
|
130
|
+
content = await fs.readFile(filePath, "utf8");
|
|
131
|
+
} catch (readErr) {
|
|
132
|
+
throw new Error(`resilient edit: could not read ${filePath}: ${readErr instanceof Error ? readErr.message : String(readErr)}`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const result: ReplaceResult = replace(content, oldStr, newStr, {
|
|
136
|
+
replaceAll,
|
|
137
|
+
});
|
|
138
|
+
if (!result.changed) {
|
|
139
|
+
throw new Error(`old_string not found (resilient cascade exhausted, strategy=${result.strategy})`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
await fs.writeFile(filePath, result.content, "utf8");
|
|
143
|
+
return {
|
|
144
|
+
content: [
|
|
145
|
+
{
|
|
146
|
+
type: "text",
|
|
147
|
+
text: `Edited ${filePath} via resilient cascade (strategy: ${result.strategy}).`,
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
_replaceStrategy: result.strategy,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured Result Extractor — attempts to extract structured data from worker output.
|
|
3
|
+
* Tries multiple extraction strategies before falling back to raw text.
|
|
4
|
+
*
|
|
5
|
+
* Round-13 P0-3: optional `schema` (TypeBox `TSchema`) — when provided, extracted
|
|
6
|
+
* data is validated against the schema via `Value.Check`. On mismatch, the result
|
|
7
|
+
* is `structured:false` with an explanatory `error`. Backward compatible: when
|
|
8
|
+
* schema is undefined, behavior is identical to the previous regex-based extractor.
|
|
9
|
+
*/
|
|
10
|
+
import type { TSchema } from "@sinclair/typebox";
|
|
11
|
+
import { Value } from "@sinclair/typebox/value";
|
|
12
|
+
|
|
13
|
+
export interface ExtractedResult {
|
|
14
|
+
/** Whether structured data was successfully extracted */
|
|
15
|
+
structured: boolean;
|
|
16
|
+
/** Parsed structured data (if structured=true AND validated against schema if provided) */
|
|
17
|
+
data: unknown;
|
|
18
|
+
/** Raw text output (always available) */
|
|
19
|
+
rawText: string;
|
|
20
|
+
/** Error message if extraction was attempted but failed */
|
|
21
|
+
error?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Extract structured result from raw worker output text.
|
|
26
|
+
* Tries strategies in order: direct JSON, fenced JSON, key-value markers, scan.
|
|
27
|
+
*
|
|
28
|
+
* @param raw - the raw text output from a worker
|
|
29
|
+
* @param schema - optional TypeBox schema. When provided, the extracted value is
|
|
30
|
+
* validated; mismatch produces `{structured:false, error:...}`.
|
|
31
|
+
*/
|
|
32
|
+
export function extractStructuredResult(raw: string, schema?: TSchema): ExtractedResult {
|
|
33
|
+
const trimmed = raw.trim();
|
|
34
|
+
if (!trimmed) {
|
|
35
|
+
return { structured: false, data: null, rawText: raw };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Strategy 1: Direct JSON parse (entire output is JSON)
|
|
39
|
+
const directResult = tryDirectJson(trimmed);
|
|
40
|
+
if (directResult !== undefined) {
|
|
41
|
+
return finalize(directResult, raw, schema);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Strategy 2: Extract from ```json ... ``` fence
|
|
45
|
+
const fencedResult = tryFencedJson(trimmed);
|
|
46
|
+
if (fencedResult !== undefined) {
|
|
47
|
+
return finalize(fencedResult, raw, schema);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Strategy 3: Extract from markers like "RESULT:" or "OUTPUT:"
|
|
51
|
+
const markerResult = tryMarkerExtraction(trimmed);
|
|
52
|
+
if (markerResult !== undefined) {
|
|
53
|
+
return finalize(markerResult, raw, schema);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Strategy 4: Scan for the first JSON object/array anywhere in text.
|
|
57
|
+
// Models often add prose preamble/epilogue ("Here's my review:", "Let me analyze...")
|
|
58
|
+
// around the JSON. This catches JSON embedded in sentences, lists, or prose.
|
|
59
|
+
const scannedResult = tryScanJson(trimmed);
|
|
60
|
+
if (scannedResult !== undefined) {
|
|
61
|
+
return finalize(scannedResult, raw, schema);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { structured: false, data: null, rawText: raw };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* After extracting a candidate object, validate it against the optional TypeBox schema.
|
|
69
|
+
* When no schema is given, behavior is the legacy "structured:true" path.
|
|
70
|
+
* When a schema is given and validation fails, return structured:false with a
|
|
71
|
+
* clear error message (caller can surface this in the AgentResult).
|
|
72
|
+
*
|
|
73
|
+
* NOTE: TypeBox 0.34.49's `Value.Check` returns a boolean and does not expose
|
|
74
|
+
* per-error paths in its public API. We use the boolean + a fallback "type mismatch"
|
|
75
|
+
* description. Scripts that need detailed diagnostics can wrap their own validator.
|
|
76
|
+
*/
|
|
77
|
+
function finalize(candidate: unknown, raw: string, schema: TSchema | undefined): ExtractedResult {
|
|
78
|
+
if (!schema) {
|
|
79
|
+
return { structured: true, data: candidate, rawText: raw };
|
|
80
|
+
}
|
|
81
|
+
const ok = Value.Check(schema, candidate);
|
|
82
|
+
if (ok) {
|
|
83
|
+
return { structured: true, data: candidate, rawText: raw };
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
structured: false,
|
|
87
|
+
data: null,
|
|
88
|
+
rawText: raw,
|
|
89
|
+
error: `structured output does not match schema: expected shape ${describeSchemaShape(schema)}, got ${describeValue(candidate)}`,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function describeValue(value: unknown): string {
|
|
94
|
+
try {
|
|
95
|
+
const json = JSON.stringify(value);
|
|
96
|
+
return json.length > 200 ? `${json.slice(0, 200)}…` : json;
|
|
97
|
+
} catch {
|
|
98
|
+
return typeof value;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function describeSchemaShape(schema: unknown): string {
|
|
103
|
+
if (!schema || typeof schema !== "object") return "any";
|
|
104
|
+
const obj = schema as Record<string, unknown>;
|
|
105
|
+
const kind = obj.kind as string | undefined;
|
|
106
|
+
const type = obj.type as string | undefined;
|
|
107
|
+
if (kind === "object" || type === "object") {
|
|
108
|
+
const properties = obj.properties;
|
|
109
|
+
if (!properties || typeof properties !== "object") return "object";
|
|
110
|
+
return `object<${Object.keys(properties as Record<string, unknown>).join(",")}>`;
|
|
111
|
+
}
|
|
112
|
+
if (kind === "array" || type === "array") return "array";
|
|
113
|
+
if (type === "string") return "string";
|
|
114
|
+
if (type === "number" || type === "integer") return "number";
|
|
115
|
+
if (type === "boolean") return "boolean";
|
|
116
|
+
if (Array.isArray(obj.anyOf) || Array.isArray(obj.oneOf)) return "union";
|
|
117
|
+
return "any";
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function tryDirectJson(text: string): unknown | undefined {
|
|
121
|
+
if (!text.startsWith("{") && !text.startsWith("[")) return undefined;
|
|
122
|
+
try {
|
|
123
|
+
return JSON.parse(text);
|
|
124
|
+
} catch {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function tryFencedJson(text: string): unknown | undefined {
|
|
130
|
+
const match = text.match(/```json\s*\n([\s\S]*?)\n\s*```/);
|
|
131
|
+
if (!match?.[1]) return undefined;
|
|
132
|
+
try {
|
|
133
|
+
return JSON.parse(match[1].trim());
|
|
134
|
+
} catch {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Strategy 4: Scan for the first balanced JSON object/array anywhere in text.
|
|
141
|
+
* Robust against prose preamble/epilogue that models add around JSON output.
|
|
142
|
+
* Returns the first valid JSON value found, or undefined.
|
|
143
|
+
*/
|
|
144
|
+
function tryScanJson(text: string): unknown | undefined {
|
|
145
|
+
// Find the first '{' or '[' in the text.
|
|
146
|
+
for (let i = 0; i < text.length; i++) {
|
|
147
|
+
const ch = text[i];
|
|
148
|
+
if (ch !== "{" && ch !== "[") continue;
|
|
149
|
+
const rest = text.slice(i);
|
|
150
|
+
const end = findMatchingBracket(rest);
|
|
151
|
+
if (end <= 0) continue;
|
|
152
|
+
const candidate = rest.slice(0, end);
|
|
153
|
+
try {
|
|
154
|
+
return JSON.parse(candidate);
|
|
155
|
+
} catch {
|
|
156
|
+
// intentional parse fallback
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function tryMarkerExtraction(text: string): unknown | undefined {
|
|
163
|
+
// Try to find JSON after common markers
|
|
164
|
+
const markers = ["RESULT:", "OUTPUT:", "ANSWER:", "### Result\n", "## Output\n"];
|
|
165
|
+
for (const marker of markers) {
|
|
166
|
+
const idx = text.indexOf(marker);
|
|
167
|
+
if (idx === -1) continue;
|
|
168
|
+
const after = text.slice(idx + marker.length).trim();
|
|
169
|
+
// Try JSON parse on text after marker
|
|
170
|
+
if (after.startsWith("{") || after.startsWith("[")) {
|
|
171
|
+
try {
|
|
172
|
+
return JSON.parse(after);
|
|
173
|
+
} catch {
|
|
174
|
+
// Try to find just the JSON object/array
|
|
175
|
+
const jsonEnd = findMatchingBracket(after);
|
|
176
|
+
if (jsonEnd > 0) {
|
|
177
|
+
try {
|
|
178
|
+
return JSON.parse(after.slice(0, jsonEnd));
|
|
179
|
+
} catch {
|
|
180
|
+
// intentional parse fallback
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function findMatchingBracket(text: string): number {
|
|
190
|
+
const openChar = text[0];
|
|
191
|
+
const closeChar = openChar === "{" ? "}" : "]";
|
|
192
|
+
let depth = 0;
|
|
193
|
+
let inString = false;
|
|
194
|
+
let escape = false;
|
|
195
|
+
for (let i = 0; i < text.length; i++) {
|
|
196
|
+
const ch = text[i];
|
|
197
|
+
if (escape) {
|
|
198
|
+
escape = false;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (ch === "\\") {
|
|
202
|
+
escape = true;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (ch === '"') {
|
|
206
|
+
inString = !inString;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (inString) continue;
|
|
210
|
+
if (ch === openChar) depth++;
|
|
211
|
+
if (ch === closeChar) {
|
|
212
|
+
depth--;
|
|
213
|
+
if (depth === 0) return i + 1;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return -1;
|
|
217
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { sleep } from "../utils/sleep.ts";
|
|
2
|
+
import { throwIfCancelled } from "./cancellation.ts";
|
|
3
|
+
|
|
4
|
+
export interface RetryPolicy {
|
|
5
|
+
maxAttempts: number;
|
|
6
|
+
backoffMs: number;
|
|
7
|
+
jitterRatio: number;
|
|
8
|
+
exponentialFactor: number;
|
|
9
|
+
retryableErrors?: string[];
|
|
10
|
+
/**
|
|
11
|
+
* CORE-3 — per-task spawn budget cap. Maximum total worker spawns
|
|
12
|
+
* (runWorker/runChildPi calls) allowed across ALL retry attempts × model
|
|
13
|
+
* fallback iterations for a single task. When omitted or 0, the budget
|
|
14
|
+
* auto-computes as `attemptModels.length × (maxAttempts + 1)` in
|
|
15
|
+
* task-runner.ts (always ≥ 1 full attempt above the theoretical max).
|
|
16
|
+
* Set explicitly to override the auto-computed value.
|
|
17
|
+
*/
|
|
18
|
+
maxTotalSpawns?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface RetryAttemptInfo {
|
|
22
|
+
attempt: number;
|
|
23
|
+
attemptId: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface RetryHooks {
|
|
27
|
+
onAttemptFailed?: (attempt: number, error: Error, nextDelayMs: number, info: RetryAttemptInfo) => void;
|
|
28
|
+
onRetryGivenUp?: (attempts: number, error: Error, info: RetryAttemptInfo) => void;
|
|
29
|
+
attemptId?: (attempt: number) => string;
|
|
30
|
+
signal?: AbortSignal;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const DEFAULT_RETRY_POLICY: RetryPolicy = {
|
|
34
|
+
maxAttempts: 3,
|
|
35
|
+
backoffMs: 1000,
|
|
36
|
+
jitterRatio: 0.3,
|
|
37
|
+
exponentialFactor: 2,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
function asError(error: unknown): Error {
|
|
41
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function globToRegex(pattern: string): RegExp {
|
|
45
|
+
const escaped = pattern
|
|
46
|
+
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
47
|
+
.replace(/\*/g, ".*")
|
|
48
|
+
.replace(/\?/g, ".");
|
|
49
|
+
return new RegExp(`^${escaped}$`, "i");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isRetryable(error: Error, policy: RetryPolicy): boolean {
|
|
53
|
+
const patterns = policy.retryableErrors ?? [];
|
|
54
|
+
if (!patterns.length) return true;
|
|
55
|
+
return patterns.some((pattern) => globToRegex(pattern).test(error.message));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function calculateRetryDelay(attempt: number, policy: RetryPolicy = DEFAULT_RETRY_POLICY, random = Math.random): number {
|
|
59
|
+
const base = policy.backoffMs * policy.exponentialFactor ** Math.max(0, attempt - 1);
|
|
60
|
+
const jitter = (random() * 2 - 1) * policy.jitterRatio * base;
|
|
61
|
+
return Math.max(0, base + jitter);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function retryAttemptInfo(attempt: number, hooks: RetryHooks): RetryAttemptInfo {
|
|
65
|
+
return {
|
|
66
|
+
attempt,
|
|
67
|
+
attemptId: hooks.attemptId?.(attempt) ?? `retry_attempt_${attempt}`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function executeWithRetry<T>(
|
|
72
|
+
fn: (attempt: number, info: RetryAttemptInfo) => Promise<T>,
|
|
73
|
+
policy: RetryPolicy = DEFAULT_RETRY_POLICY,
|
|
74
|
+
hooks: RetryHooks = {},
|
|
75
|
+
): Promise<T> {
|
|
76
|
+
const normalized: RetryPolicy = {
|
|
77
|
+
...DEFAULT_RETRY_POLICY,
|
|
78
|
+
...policy,
|
|
79
|
+
maxAttempts: Math.max(1, policy.maxAttempts ?? DEFAULT_RETRY_POLICY.maxAttempts),
|
|
80
|
+
};
|
|
81
|
+
let lastError: Error | undefined;
|
|
82
|
+
for (let attempt = 1; attempt <= normalized.maxAttempts; attempt += 1) {
|
|
83
|
+
throwIfCancelled(hooks.signal);
|
|
84
|
+
const info = retryAttemptInfo(attempt, hooks);
|
|
85
|
+
try {
|
|
86
|
+
return await fn(attempt, info);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
lastError = asError(error);
|
|
89
|
+
// Never retry if aborted — sleep() would immediately reject on every attempt.
|
|
90
|
+
if (hooks.signal?.aborted) {
|
|
91
|
+
hooks.onRetryGivenUp?.(attempt, lastError, info);
|
|
92
|
+
throw lastError;
|
|
93
|
+
}
|
|
94
|
+
if (attempt >= normalized.maxAttempts || !isRetryable(lastError, normalized)) {
|
|
95
|
+
hooks.onRetryGivenUp?.(attempt, lastError, info);
|
|
96
|
+
throw lastError;
|
|
97
|
+
}
|
|
98
|
+
const delay = calculateRetryDelay(attempt, normalized);
|
|
99
|
+
hooks.onAttemptFailed?.(attempt, lastError, delay, info);
|
|
100
|
+
try {
|
|
101
|
+
await sleep(delay, hooks.signal);
|
|
102
|
+
} catch (sleepError) {
|
|
103
|
+
if (hooks.signal?.aborted) throwIfCancelled(hooks.signal);
|
|
104
|
+
throw sleepError;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
throw lastError ?? new Error("Retry failed without error.");
|
|
109
|
+
}
|