pi-crew 0.9.16 → 0.9.18
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 +130 -0
- package/README.md +19 -0
- package/dist/build-meta.json +21784 -0
- package/dist/index.mjs +83169 -0
- package/dist/index.mjs.map +7 -0
- package/docs/A +358 -0
- package/docs/M-A +357 -0
- package/docs/REVIEW-FINDINGS-2026-06 +357 -0
- package/docs/Y +357 -0
- package/docs/a +357 -0
- package/docs/aA +358 -0
- package/docs/archive/README.md +91 -0
- package/docs/migration/atomic-write-v2-migration.md +297 -0
- package/docs/patterns/command-agent-skill.md +1 -1
- package/docs/perf/performance-review-2026-07.md +287 -0
- package/docs/skills/REFERENCE.md +0 -17
- package/docs//303/242mmaAAA/303/242 +357 -0
- package/index.bundle.ts +25 -0
- package/index.ts +95 -4
- package/package.json +15 -4
- package/skills/iterative-audit/SKILL.md +0 -1
- package/skills/widget-rendering/SKILL.md +17 -0
- package/src/adapters/claude-adapter.ts +1 -3
- package/src/adapters/export-util.ts +16 -16
- package/src/adapters/index.ts +5 -5
- package/src/agents/agent-config.ts +20 -14
- package/src/agents/agent-serializer.ts +1 -1
- package/src/agents/discover-agents.ts +83 -39
- package/src/benchmark/benchmark-runner.ts +51 -51
- package/src/benchmark/feedback-loop.ts +1 -1
- package/src/config/config.ts +303 -591
- package/src/config/defaults.ts +28 -2
- package/src/config/drift-detector.ts +11 -14
- package/src/config/markers.ts +201 -203
- package/src/config/resilient-parser.ts +14 -6
- package/src/config/role-tools.ts +3 -3
- package/src/config/suggestions.ts +5 -12
- package/src/config/types.ts +9 -25
- package/src/errors.ts +151 -157
- package/src/extension/action-suggestions.ts +54 -9
- package/src/extension/async-notifier.ts +43 -13
- package/src/extension/autonomous-policy.ts +77 -37
- package/src/extension/command-completions.ts +9 -8
- package/src/extension/context-status-injection.ts +14 -12
- package/src/extension/crew-autocomplete.ts +8 -16
- package/src/extension/crew-cleanup.ts +2 -1
- package/src/extension/crew-shortcuts.ts +9 -6
- package/src/extension/cross-extension-rpc.ts +136 -42
- package/src/extension/help.ts +1 -1
- package/src/extension/import-index.ts +10 -5
- package/src/extension/knowledge-injection.ts +88 -15
- package/src/extension/management.ts +89 -349
- package/src/extension/message-renderers.ts +3 -4
- package/src/extension/notification-router.ts +22 -5
- package/src/extension/notification-sink.ts +6 -3
- package/src/extension/pi-api.ts +17 -8
- package/src/extension/plan-orchestrate.ts +8 -27
- package/src/extension/project-init.ts +21 -6
- package/src/extension/register.ts +329 -975
- package/src/extension/registration/artifact-cleanup.ts +9 -4
- package/src/extension/registration/command-utils.ts +5 -1
- package/src/extension/registration/commands.ts +806 -314
- package/src/extension/registration/compaction-guard.ts +15 -15
- package/src/extension/registration/lifecycle.ts +259 -0
- package/src/extension/registration/observability.ts +293 -0
- package/src/extension/registration/subagent-helpers.ts +29 -7
- package/src/extension/registration/subagent-tools.ts +253 -44
- package/src/extension/registration/team-tool.ts +19 -84
- package/src/extension/registration/ui.ts +168 -0
- package/src/extension/registration/viewers.ts +64 -24
- package/src/extension/result-watcher.ts +18 -7
- package/src/extension/run-bundle-schema.ts +21 -6
- package/src/extension/run-export.ts +15 -6
- package/src/extension/run-import.ts +64 -29
- package/src/extension/run-index.ts +28 -9
- package/src/extension/run-maintenance.ts +32 -12
- package/src/extension/session-summary.ts +1 -1
- package/src/extension/team-manager-command.ts +64 -7
- package/src/extension/team-onboard.ts +149 -150
- package/src/extension/team-recommendation.ts +121 -21
- package/src/extension/team-tool/anchor.ts +31 -64
- package/src/extension/team-tool/api.ts +929 -141
- package/src/extension/team-tool/auto-summarize.ts +14 -26
- package/src/extension/team-tool/cache-control.ts +1 -5
- package/src/extension/team-tool/cancel.ts +138 -38
- package/src/extension/team-tool/chain-dispatch.ts +5 -13
- package/src/extension/team-tool/chain-executor.ts +29 -51
- package/src/extension/team-tool/config-patch.ts +1 -1
- package/src/extension/team-tool/context.ts +40 -20
- package/src/extension/team-tool/destructive-gate.ts +10 -5
- package/src/extension/team-tool/doctor.ts +170 -51
- package/src/extension/team-tool/explain.ts +228 -214
- package/src/extension/team-tool/failure-patterns.ts +3 -9
- package/src/extension/team-tool/goal-wrap.ts +71 -29
- package/src/extension/team-tool/goal.ts +185 -35
- package/src/extension/team-tool/handle-schedule.ts +34 -15
- package/src/extension/team-tool/handle-settings.ts +94 -56
- package/src/extension/team-tool/health-monitor.ts +27 -108
- package/src/extension/team-tool/inspect.ts +42 -9
- package/src/extension/team-tool/intent-policy.ts +4 -1
- package/src/extension/team-tool/lifecycle-actions.ts +244 -48
- package/src/extension/team-tool/orchestrate.ts +7 -18
- package/src/extension/team-tool/parallel-dispatch.ts +51 -29
- package/src/extension/team-tool/plan.ts +29 -5
- package/src/extension/team-tool/respond.ts +49 -16
- package/src/extension/team-tool/run-not-found.ts +3 -8
- package/src/extension/team-tool/run.ts +80 -317
- package/src/extension/team-tool/status.ts +135 -43
- package/src/extension/team-tool/workflow-manage.ts +92 -25
- package/src/extension/team-tool-types.ts +8 -1
- package/src/extension/team-tool.ts +142 -551
- package/src/extension/validate-resources.ts +39 -8
- package/src/hooks/registry.ts +66 -17
- package/src/hooks/types.ts +1 -1
- package/src/i18n.ts +25 -11
- package/src/observability/correlation.ts +17 -3
- package/src/observability/event-bus.ts +51 -56
- package/src/observability/event-to-metric.ts +117 -15
- package/src/observability/exporters/otlp-exporter.ts +56 -17
- package/src/observability/exporters/prometheus-exporter.ts +1 -1
- package/src/observability/metric-registry.ts +16 -4
- package/src/observability/metric-retention.ts +8 -2
- package/src/observability/metric-sink.ts +11 -3
- package/src/observability/metrics-primitives.ts +42 -9
- package/src/plugins/plugin-define.ts +2 -2
- package/src/plugins/plugin-registry.ts +25 -25
- package/src/plugins/plugins/index.ts +1 -1
- package/src/plugins/plugins/nextjs.ts +16 -16
- package/src/plugins/plugins/vite.ts +7 -11
- package/src/plugins/plugins/vitest.ts +6 -11
- package/src/runtime/adaptive-plan.ts +225 -40
- package/src/runtime/agent-control.ts +58 -15
- package/src/runtime/agent-memory.ts +15 -9
- package/src/runtime/agent-observability.ts +10 -2
- package/src/runtime/anchor-manager.ts +27 -25
- package/src/runtime/async-marker.ts +7 -1
- package/src/runtime/async-runner.ts +39 -24
- package/src/runtime/auto-summarize.ts +7 -13
- package/src/runtime/background-runner.ts +189 -251
- package/src/runtime/batch-barrier.ts +18 -16
- package/src/runtime/cancellation-token.ts +16 -6
- package/src/runtime/cancellation.ts +46 -8
- package/src/runtime/capability-inventory.ts +6 -4
- package/src/runtime/chain-parser.ts +44 -11
- package/src/runtime/chain-runner.ts +63 -69
- package/src/runtime/checkpoint.ts +12 -56
- package/src/runtime/child-pi.ts +344 -79
- package/src/runtime/coalesce-tasks.ts +268 -0
- package/src/runtime/code-summary.ts +71 -26
- package/src/runtime/compact-stages/index.ts +15 -5
- package/src/runtime/compact-stages/tail-capture-stage.ts +7 -2
- package/src/runtime/compact-stages/truncation-stage.ts +4 -1
- package/src/runtime/compaction-summary.ts +17 -10
- package/src/runtime/completion-guard.ts +33 -16
- package/src/runtime/crash-classification.ts +28 -7
- package/src/runtime/crash-recovery.ts +177 -37
- package/src/runtime/crew-agent-records.ts +145 -47
- package/src/runtime/crew-hooks.ts +9 -24
- package/src/runtime/cross-extension-rpc.ts +25 -23
- package/src/runtime/custom-tools/irc-tool.ts +43 -15
- package/src/runtime/custom-tools/submit-result-tool.ts +16 -5
- package/src/runtime/delivery-coordinator.ts +34 -7
- package/src/runtime/delta-conflict.ts +9 -21
- package/src/runtime/deterministic-ast.ts +1 -1
- package/src/runtime/diagnostic-export.ts +53 -20
- package/src/runtime/direct-run.ts +12 -2
- package/src/runtime/dwf-state-store.ts +1 -1
- package/src/runtime/dynamic-workflow-context.ts +187 -59
- package/src/runtime/dynamic-workflow-runner.ts +42 -20
- package/src/runtime/effectiveness.ts +16 -8
- package/src/runtime/errors/crew-errors.ts +7 -11
- package/src/runtime/event-stream-bridge.ts +9 -3
- package/src/runtime/foreground-control.ts +54 -14
- package/src/runtime/foreground-watchdog.ts +9 -6
- package/src/runtime/goal-achievement.ts +27 -10
- package/src/runtime/goal-evaluator.ts +54 -19
- package/src/runtime/goal-loop-runner.ts +280 -99
- package/src/runtime/goal-state-store.ts +27 -17
- package/src/runtime/green-contract.ts +11 -2
- package/src/runtime/group-join.ts +64 -31
- package/src/runtime/handoff-manager.ts +37 -42
- package/src/runtime/heartbeat-gradient.ts +10 -2
- package/src/runtime/heartbeat-watcher.ts +32 -6
- package/src/runtime/hidden-handoff.ts +13 -31
- package/src/runtime/important-line-classifier.ts +12 -2
- package/src/runtime/iteration-hooks.ts +25 -15
- package/src/runtime/live-agent-control.ts +55 -7
- package/src/runtime/live-agent-manager.ts +130 -27
- package/src/runtime/live-control-realtime.ts +23 -3
- package/src/runtime/live-irc.ts +4 -1
- package/src/runtime/live-session-health.ts +12 -4
- package/src/runtime/live-session-runtime.ts +379 -117
- package/src/runtime/manifest-cache.ts +28 -12
- package/src/runtime/mcp-proxy.ts +10 -18
- package/src/runtime/metric-parser.ts +1 -5
- package/src/runtime/model-fallback.ts +55 -18
- package/src/runtime/model-resolver.ts +2 -6
- package/src/runtime/model-scope.ts +19 -3
- package/src/runtime/notebook-helpers.ts +60 -62
- package/src/runtime/orphan-worker-registry.ts +37 -26
- package/src/runtime/output-validator.ts +18 -3
- package/src/runtime/overflow-recovery.ts +5 -4
- package/src/runtime/parallel-research.ts +29 -5
- package/src/runtime/parallel-utils.ts +9 -11
- package/src/runtime/path-overlap.ts +150 -0
- package/src/runtime/peer-dep.ts +12 -17
- package/src/runtime/per-write-validator.ts +1 -3
- package/src/runtime/phase-tracker.ts +342 -330
- package/src/runtime/pi-args.ts +26 -8
- package/src/runtime/pi-json-output.ts +1 -1
- package/src/runtime/pi-spawn.ts +30 -19
- package/src/runtime/pipeline-runner.ts +56 -55
- package/src/runtime/plan-templates.ts +5 -6
- package/src/runtime/policy-engine.ts +43 -7
- package/src/runtime/post-checks.ts +12 -4
- package/src/runtime/post-exit-stdio-guard.ts +2 -2
- package/src/runtime/process-lifecycle.ts +20 -10
- package/src/runtime/process-status.ts +16 -5
- package/src/runtime/progress-event-coalescer.ts +2 -1
- package/src/runtime/progress-tracker.ts +103 -103
- package/src/runtime/prose-compressor.ts +9 -11
- package/src/runtime/recovery-recipes.ts +118 -24
- package/src/runtime/replace.ts +25 -10
- package/src/runtime/resilient-edit.ts +10 -25
- package/src/runtime/result-extractor.ts +2 -6
- package/src/runtime/retry-executor.ts +20 -4
- package/src/runtime/retry-runner.ts +27 -51
- package/src/runtime/role-permission.ts +6 -1
- package/src/runtime/run-coalesced-task-group.ts +256 -0
- package/src/runtime/run-drift.ts +14 -15
- package/src/runtime/run-tracker.ts +6 -28
- package/src/runtime/runtime-policy.ts +1 -6
- package/src/runtime/runtime-resolver.ts +80 -15
- package/src/runtime/scheduler.ts +47 -18
- package/src/runtime/semaphore.ts +5 -7
- package/src/runtime/sensitive-paths.ts +2 -1
- package/src/runtime/session-usage.ts +1 -1
- package/src/runtime/settings-store.ts +16 -12
- package/src/runtime/sidechain-output.ts +6 -2
- package/src/runtime/single-agent-compose.ts +1 -4
- package/src/runtime/skill-effectiveness.ts +29 -97
- package/src/runtime/skill-instructions.ts +40 -83
- package/src/runtime/stale-reconciler.ts +73 -197
- package/src/runtime/stream-preview.ts +1 -1
- package/src/runtime/streaming-output.ts +1 -1
- package/src/runtime/subagent-manager.ts +44 -169
- package/src/runtime/subprocess-tool-registry.ts +4 -1
- package/src/runtime/supervisor-contact.ts +10 -5
- package/src/runtime/task-display.ts +19 -5
- package/src/runtime/task-graph-scheduler.ts +95 -21
- package/src/runtime/task-graph.ts +5 -11
- package/src/runtime/task-health.ts +54 -47
- package/src/runtime/task-id.ts +12 -19
- package/src/runtime/task-output-context.ts +265 -81
- package/src/runtime/task-packet.ts +12 -20
- package/src/runtime/task-quality.ts +6 -14
- package/src/runtime/task-runner/context-retrieval.ts +4 -13
- package/src/runtime/task-runner/live-executor.ts +114 -36
- package/src/runtime/task-runner/output-splitter.ts +152 -0
- package/src/runtime/task-runner/progress.ts +50 -12
- package/src/runtime/task-runner/prompt-builder.ts +31 -7
- package/src/runtime/task-runner/prompt-pipeline.ts +31 -7
- package/src/runtime/task-runner/result-utils.ts +3 -1
- package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
- package/src/runtime/task-runner/run-projection.ts +27 -8
- package/src/runtime/task-runner/state-helpers.ts +50 -14
- package/src/runtime/task-runner/tail-read.ts +2 -6
- package/src/runtime/task-runner.ts +180 -326
- package/src/runtime/team-runner-artifacts.ts +13 -0
- package/src/runtime/team-runner.ts +456 -974
- package/src/runtime/tool-output-pruner.ts +6 -9
- package/src/runtime/tool-progress.ts +11 -21
- package/src/runtime/verification-gates.ts +33 -24
- package/src/runtime/verification-integrity.ts +2 -7
- package/src/runtime/verification-worktree.ts +62 -12
- package/src/runtime/worker-heartbeat.ts +5 -1
- package/src/runtime/worker-startup.ts +29 -5
- package/src/runtime/workflow-state.ts +17 -9
- package/src/runtime/workspace-lock.ts +30 -35
- package/src/runtime/workspace-tree.ts +20 -32
- package/src/runtime/yield-handler.ts +43 -9
- package/src/runtime/zombie-scanner.ts +5 -4
- package/src/schema/config-schema.ts +266 -172
- package/src/schema/team-tool-schema.ts +29 -74
- package/src/schema/validation-types.ts +25 -17
- package/src/skills/discover-skills.ts +35 -10
- package/src/skills/skill-templates.ts +109 -27
- package/src/skills/validate.ts +26 -8
- package/src/state/active-run-registry.ts +87 -28
- package/src/state/artifact-store.ts +9 -5
- package/src/state/atomic-write-v2.ts +85 -63
- package/src/state/atomic-write.ts +106 -27
- package/src/state/blob-store.ts +47 -20
- package/src/state/contracts.ts +20 -5
- package/src/state/crew-init.ts +5 -22
- package/src/state/decision-ledger.ts +19 -73
- package/src/state/event-log-rotation.ts +41 -15
- package/src/state/event-log.ts +168 -53
- package/src/state/event-reconstructor.ts +11 -1
- package/src/state/gitignore-manager.ts +2 -8
- package/src/state/health-store.ts +57 -57
- package/src/state/hook-instinct-bridge.ts +1 -1
- package/src/state/hook-integrations.ts +1 -1
- package/src/state/instinct-store.ts +17 -5
- package/src/state/jsonl-writer.ts +1 -1
- package/src/state/locks.ts +23 -9
- package/src/state/mailbox.ts +151 -28
- package/src/state/observation-store.ts +20 -14
- package/src/state/run-cache.ts +142 -135
- package/src/state/run-graph.ts +6 -15
- package/src/state/run-metrics.ts +5 -18
- package/src/state/schedule.ts +15 -9
- package/src/state/state-store.ts +137 -47
- package/src/state/task-claims.ts +12 -2
- package/src/state/tiered-eval.ts +52 -43
- package/src/state/types-eval.ts +2 -2
- package/src/state/types.ts +24 -20
- package/src/state/usage.ts +20 -4
- package/src/state/worker-atomic-writer.ts +6 -3
- package/src/subagents/index.ts +2 -2
- package/src/teams/discover-teams.ts +21 -6
- package/src/tools/safe-bash-extension.ts +5 -6
- package/src/tools/safe-bash.ts +10 -11
- package/src/types/new-api-types.ts +6 -10
- package/src/ui/agent-management-overlay.ts +52 -40
- package/src/ui/card-colors.ts +13 -4
- package/src/ui/crew-footer.ts +8 -7
- package/src/ui/crew-select-list.ts +1 -1
- package/src/ui/dashboard-panes/agents-pane.ts +41 -25
- package/src/ui/dashboard-panes/cancellation-pane.ts +1 -1
- package/src/ui/dashboard-panes/capability-pane.ts +32 -15
- package/src/ui/dashboard-panes/health-pane.ts +2 -1
- package/src/ui/dashboard-panes/metrics-pane.ts +4 -1
- package/src/ui/dashboard-panes/progress-pane.ts +12 -9
- package/src/ui/heartbeat-aggregator.ts +15 -4
- package/src/ui/keybinding-map.ts +40 -7
- package/src/ui/live-conversation-overlay.ts +36 -20
- package/src/ui/live-duration.ts +1 -4
- package/src/ui/live-run-sidebar.ts +88 -25
- package/src/ui/loaders.ts +2 -8
- package/src/ui/mascot.ts +20 -36
- package/src/ui/overlays/agent-picker-overlay.ts +11 -3
- package/src/ui/overlays/confirm-overlay.ts +4 -2
- package/src/ui/overlays/help-overlay.ts +25 -14
- package/src/ui/overlays/mailbox-compose-overlay.ts +48 -11
- package/src/ui/overlays/mailbox-compose-preview.ts +20 -5
- package/src/ui/overlays/mailbox-detail-overlay.ts +27 -6
- package/src/ui/pi-ui-compat.ts +7 -7
- package/src/ui/powerbar-publisher.ts +120 -42
- package/src/ui/render-diff.ts +10 -3
- package/src/ui/render-scheduler.ts +12 -4
- package/src/ui/run-action-dispatcher.ts +81 -18
- package/src/ui/run-dashboard.ts +172 -76
- package/src/ui/run-event-bus.ts +59 -37
- package/src/ui/run-snapshot-cache.ts +276 -86
- package/src/ui/settings-overlay.ts +361 -74
- package/src/ui/status-colors.ts +12 -1
- package/src/ui/syntax-highlight.ts +1 -1
- package/src/ui/terminal-status.ts +1 -3
- package/src/ui/theme-adapter.ts +16 -14
- package/src/ui/theme-discovery.ts +13 -2
- package/src/ui/tool-progress-formatter.ts +7 -7
- package/src/ui/tool-render.ts +128 -56
- package/src/ui/tool-renderers/brief-mode.ts +45 -27
- package/src/ui/tool-renderers/index.ts +109 -43
- package/src/ui/transcript-cache.ts +32 -6
- package/src/ui/transcript-entries.ts +25 -23
- package/src/ui/transcript-viewer.ts +78 -29
- package/src/ui/widget/index.ts +99 -40
- package/src/ui/widget/widget-formatters.ts +13 -6
- package/src/ui/widget/widget-model.ts +19 -8
- package/src/ui/widget/widget-renderer.ts +23 -13
- package/src/ui/widget/widget-types.ts +1 -1
- package/src/utils/bm25-search.ts +199 -199
- package/src/utils/conflict-detect.ts +22 -21
- package/src/utils/env-filter.ts +14 -7
- package/src/utils/file-coalescer.ts +5 -1
- package/src/utils/fingerprint.ts +3 -6
- package/src/utils/frontmatter.ts +3 -1
- package/src/utils/fs-watch.ts +2 -6
- package/src/utils/gh-protocol.ts +119 -44
- package/src/utils/git.ts +13 -15
- package/src/utils/guards.ts +2 -5
- package/src/utils/ids.ts +9 -2
- package/src/utils/incremental-reader.ts +14 -3
- package/src/utils/internal-error.ts +2 -1
- package/src/utils/names.ts +12 -3
- package/src/utils/paths.ts +49 -5
- package/src/utils/project-detector.ts +2 -2
- package/src/utils/redaction.ts +29 -19
- package/src/utils/resolve-shell.ts +9 -7
- package/src/utils/run-watcher-registry.ts +19 -31
- package/src/utils/safe-paths.ts +46 -29
- package/src/utils/scan-cache.ts +9 -2
- package/src/utils/session-utils.ts +2 -4
- package/src/utils/sleep.ts +12 -5
- package/src/utils/sse-parser.ts +5 -17
- package/src/utils/visual.ts +52 -32
- package/src/workflows/discover-workflows.ts +41 -109
- package/src/workflows/intermediate-store.ts +5 -21
- package/src/workflows/preflight-validator.ts +6 -33
- package/src/workflows/topology-analyzer.ts +5 -21
- package/src/workflows/workflow-config.ts +7 -6
- package/src/worktree/branch-freshness.ts +66 -8
- package/src/worktree/cleanup.ts +130 -20
- package/src/worktree/worktree-manager.ts +212 -69
- package/skills/artifact-analysis-loop/SKILL.md +0 -303
- package/skills/detection-pipeline-design/SKILL.md +0 -286
- package/skills/hunting-investigation-loop/SKILL.md +0 -402
- package/skills/incident-playbook-construction/SKILL.md +0 -384
- package/skills/security-review/SKILL.md +0 -561
- package/skills/threat-hypothesis-framework/SKILL.md +0 -176
- package/skills/ui-render-performance/SKILL.md +0 -58
- /package/docs/{followup-review-round3-2026-05-12.md → archive/followup-review-round3-2026-05-12.md} +0 -0
- /package/docs/{followup-review-round4-2026-05-13.md → archive/followup-review-round4-2026-05-13.md} +0 -0
- /package/docs/{pi-crew-bugs.md → archive/pi-crew-bugs.md} +0 -0
- /package/docs/{pi-crew-test-final.md → archive/pi-crew-test-final.md} +0 -0
- /package/docs/{pi-crew-test-results.md → archive/pi-crew-test-results.md} +0 -0
- /package/docs/{pi-crew-test-round2.md → archive/pi-crew-test-round2.md} +0 -0
- /package/docs/{pi-crew-test-round4.md → archive/pi-crew-test-round4.md} +0 -0
- /package/docs/{pi-crew-test-round5.md → archive/pi-crew-test-round5.md} +0 -0
- /package/docs/{pi-crew-test-round6.md → archive/pi-crew-test-round6.md} +0 -0
- /package/docs/{pi-crew-v0.5.10-audit-fix-plan.md → archive/pi-crew-v0.5.10-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.11-audit-fix-plan.md → archive/pi-crew-v0.5.11-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.12-audit-fix-plan.md → archive/pi-crew-v0.5.12-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.13-audit-fix-plan.md → archive/pi-crew-v0.5.13-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.14-audit-fix-plan.md → archive/pi-crew-v0.5.14-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.16-audit-fix-plan.md → archive/pi-crew-v0.5.16-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.17-audit-fix-plan.md → archive/pi-crew-v0.5.17-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.5-audit-fix-plan.md → archive/pi-crew-v0.5.5-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.9-audit-fix-plan.md → archive/pi-crew-v0.5.9-audit-fix-plan.md} +0 -0
- /package/docs/{pi-mono-opportunities.md → archive/pi-mono-opportunities.md} +0 -0
- /package/docs/{pi-mono-review.md → archive/pi-mono-review.md} +0 -0
- /package/docs/{pi-subagent4-comparison.md → archive/pi-subagent4-comparison.md} +0 -0
- /package/docs/{pi-subagents3-deep-analysis.md → archive/pi-subagents3-deep-analysis.md} +0 -0
|
@@ -14,32 +14,26 @@
|
|
|
14
14
|
* Each turn spawns one executeTeamRun; the loop is the outer coordinator.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { createRunManifest, saveRunTasks } from "../state/state-store.ts";
|
|
18
|
-
import { appendEvent } from "../state/event-log.ts";
|
|
19
|
-
import { collectRunMetrics } from "../state/run-metrics.ts";
|
|
20
|
-
import { registerActiveRun, unregisterActiveRun } from "../state/active-run-registry.ts";
|
|
21
|
-
import { executeTeamRun } from "./team-runner.ts";
|
|
22
|
-
import { GoalStore } from "./goal-state-store.ts";
|
|
23
|
-
import { evaluateGoal, bundleEvidence } from "./goal-evaluator.ts";
|
|
24
|
-
import { withWorkerSlot } from "./global-worker-cap.ts";
|
|
25
|
-
import { acquireWorkspaceLock, type WorkspaceLockHandle } from "./workspace-lock.ts";
|
|
26
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
27
17
|
import { randomBytes } from "node:crypto";
|
|
28
|
-
import {
|
|
18
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
19
|
+
import type { AgentConfig } from "../agents/agent-config.ts";
|
|
29
20
|
import { loadConfig } from "../config/config.ts";
|
|
30
21
|
import { effectiveRunConfig } from "../extension/team-tool/config-patch.ts";
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
GoalVerdict,
|
|
37
|
-
TeamRunManifest,
|
|
38
|
-
TeamTaskState,
|
|
39
|
-
} from "../state/types.ts";
|
|
22
|
+
import { registerActiveRun, unregisterActiveRun } from "../state/active-run-registry.ts";
|
|
23
|
+
import { appendEvent } from "../state/event-log.ts";
|
|
24
|
+
import { collectRunMetrics } from "../state/run-metrics.ts";
|
|
25
|
+
import { createRunManifest, saveRunTasks } from "../state/state-store.ts";
|
|
26
|
+
import type { GoalLoopState, GoalLoopStatus, GoalVerdict, TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
40
27
|
import type { TeamConfig } from "../teams/team-config.ts";
|
|
28
|
+
import { logInternalError } from "../utils/internal-error.ts";
|
|
41
29
|
import type { WorkflowConfig } from "../workflows/workflow-config.ts";
|
|
42
|
-
import
|
|
30
|
+
import { withWorkerSlot } from "./global-worker-cap.ts";
|
|
31
|
+
import { bundleEvidence, evaluateGoal } from "./goal-evaluator.ts";
|
|
32
|
+
import { GoalStore } from "./goal-state-store.ts";
|
|
33
|
+
import { resolveCrewRuntime } from "./runtime-resolver.ts";
|
|
34
|
+
import { executeTeamRun } from "./team-runner.ts";
|
|
35
|
+
import { compareSnapshot, snapshotManifests } from "./verification-integrity.ts";
|
|
36
|
+
import { acquireWorkspaceLock, type WorkspaceLockHandle } from "./workspace-lock.ts";
|
|
43
37
|
|
|
44
38
|
/** Required minimal shape for the worker + agents discovery (P0 uses the goal's workerAgent). */
|
|
45
39
|
export interface GoalLoopRuntimeDeps {
|
|
@@ -65,7 +59,13 @@ export interface RunGoalLoopResult {
|
|
|
65
59
|
* Kept for unit tests of the loop's max_turns exit path. The production loop
|
|
66
60
|
* uses `realGoalEvaluator` (P1) which calls the LLM judge.
|
|
67
61
|
*/
|
|
68
|
-
export const stubGoalEvaluator = async (
|
|
62
|
+
export const stubGoalEvaluator = async (
|
|
63
|
+
goal: GoalLoopState,
|
|
64
|
+
_turnRunId: string,
|
|
65
|
+
_m?: import("../state/types.ts").TeamRunManifest,
|
|
66
|
+
_t?: import("../state/types.ts").TeamTaskState[],
|
|
67
|
+
_s?: AbortSignal,
|
|
68
|
+
): Promise<GoalVerdict> => ({
|
|
69
69
|
turn: goal.turnsUsed,
|
|
70
70
|
achieved: false,
|
|
71
71
|
reason: `not-achieved: stub evaluator (P0). Turn ${goal.turnsUsed}/${goal.maxTurns} completed; P1 will judge against objective + verification.`,
|
|
@@ -113,7 +113,15 @@ export const realGoalEvaluator = async (
|
|
|
113
113
|
const drift = compareSnapshot(snapshot.snapshot, current);
|
|
114
114
|
if (drift.length > 0) {
|
|
115
115
|
verificationCompromised = drift;
|
|
116
|
-
appendEvent(turnManifest.eventsPath, {
|
|
116
|
+
appendEvent(turnManifest.eventsPath, {
|
|
117
|
+
type: "goal.verification_compromised",
|
|
118
|
+
runId: turnRunId,
|
|
119
|
+
data: {
|
|
120
|
+
goalId: goal.goalId,
|
|
121
|
+
driftedFiles: drift,
|
|
122
|
+
phase: "T_snap",
|
|
123
|
+
},
|
|
124
|
+
});
|
|
117
125
|
}
|
|
118
126
|
} catch (error) {
|
|
119
127
|
logInternalError("goal-loop.integritySnap", error, `goalId=${goal.goalId} phase=T_snap`);
|
|
@@ -121,9 +129,13 @@ export const realGoalEvaluator = async (
|
|
|
121
129
|
}
|
|
122
130
|
if (!verificationCompromised) {
|
|
123
131
|
try {
|
|
124
|
-
|
|
132
|
+
// LAZY: defer dynamic import of ./verification-gates.ts to its call site.
|
|
125
133
|
const { executeVerificationCommands } = await import("./verification-gates.ts");
|
|
126
|
-
const contract = {
|
|
134
|
+
const contract = {
|
|
135
|
+
requiredGreenLevel: "none" as const,
|
|
136
|
+
commands: goal.verification.commands,
|
|
137
|
+
allowManualEvidence: goal.verification.allowManualEvidence ?? false,
|
|
138
|
+
};
|
|
127
139
|
// Phase 1.5 #2 (RFC 16): run verification in a pristine git worktree at
|
|
128
140
|
// T_snap commit SHA when opt-in + clean git repo. Closes the round-trip
|
|
129
141
|
// manifest tamper residual (MAJ#2) and invoked-script tampering residual
|
|
@@ -132,7 +144,8 @@ export const realGoalEvaluator = async (
|
|
|
132
144
|
let worktreeCwd: string | undefined;
|
|
133
145
|
let worktreeCleanup: (() => void) | undefined;
|
|
134
146
|
try {
|
|
135
|
-
|
|
147
|
+
// LAZY: defer dynamic import of ./verification-worktree.ts to its call site.
|
|
148
|
+
// LAZY: defer dynamic import of ./verification-worktree.ts to its call site. Multi-line form breaks scripts/check-lazy-imports.mjs (which does `lines[lineNum - 2]`), so keep destructuring + await import on one line.
|
|
136
149
|
const { checkWorktreeSandboxAvailable, prepareVerificationWorktree } = await import("./verification-worktree.ts");
|
|
137
150
|
const availability = checkWorktreeSandboxAvailable(goal.cwd);
|
|
138
151
|
if (availability.available) {
|
|
@@ -141,33 +154,57 @@ export const realGoalEvaluator = async (
|
|
|
141
154
|
worktreeCleanup = wt.cleanup;
|
|
142
155
|
} else {
|
|
143
156
|
// Graceful fallback — log reason so the bypass is traceable.
|
|
144
|
-
logInternalError(
|
|
157
|
+
logInternalError(
|
|
158
|
+
"goal-loop.worktreeSandboxBypassed",
|
|
159
|
+
new Error(availability.reason),
|
|
160
|
+
`goalId=${goal.goalId} cwd=${goal.cwd}`,
|
|
161
|
+
);
|
|
145
162
|
}
|
|
146
163
|
} catch (error) {
|
|
147
164
|
logInternalError("goal-loop.worktreeSandboxPrep", error, `goalId=${goal.goalId}`);
|
|
148
165
|
}
|
|
149
166
|
try {
|
|
150
|
-
const cmdResults = await executeVerificationCommands(
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
167
|
+
const cmdResults = await executeVerificationCommands(
|
|
168
|
+
contract,
|
|
169
|
+
goal.cwd,
|
|
170
|
+
turnRunId,
|
|
171
|
+
"goal-verify",
|
|
172
|
+
turnManifest.artifactsRoot,
|
|
173
|
+
signal,
|
|
174
|
+
worktreeCwd,
|
|
175
|
+
);
|
|
176
|
+
verificationResults = cmdResults.map((r) => ({
|
|
177
|
+
command: r.cmd,
|
|
178
|
+
exitCode: r.exitCode ?? null,
|
|
179
|
+
passed: r.status === "passed",
|
|
180
|
+
}));
|
|
181
|
+
// T_verify_done: re-hash AFTER the commands ran. If the manifest changed between
|
|
182
|
+
// command-launch and command-exit (worker edited it mid-run, then the test "passed"
|
|
183
|
+
// against the modified graph), treat the result as compromised even if exit was 0.
|
|
184
|
+
// NOTE (RFC v0.5 §P1a residual): this catches PERSISTENT edits only — a round-trip
|
|
185
|
+
// (edit -> run -> revert) leaves the hash unchanged and is NOT caught here.
|
|
186
|
+
// Phase 1.5 #2 (RFC 16) closes this residual when worktree sandbox is active:
|
|
187
|
+
// verification ran in a pristine checkout, so worker edits are invisible.
|
|
188
|
+
if (snapshot && snapshot !== "none-text-only") {
|
|
189
|
+
try {
|
|
190
|
+
const post = snapshotManifests(goal.cwd);
|
|
191
|
+
const postDrift = compareSnapshot(snapshot.snapshot, post);
|
|
192
|
+
if (postDrift.length > 0) {
|
|
193
|
+
verificationCompromised = postDrift;
|
|
194
|
+
appendEvent(turnManifest.eventsPath, {
|
|
195
|
+
type: "goal.verification_compromised",
|
|
196
|
+
runId: turnRunId,
|
|
197
|
+
data: {
|
|
198
|
+
goalId: goal.goalId,
|
|
199
|
+
driftedFiles: postDrift,
|
|
200
|
+
phase: "T_verify_done",
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
} catch (error) {
|
|
205
|
+
logInternalError("goal-loop.integritySnap", error, `goalId=${goal.goalId} phase=T_verify_done`);
|
|
166
206
|
}
|
|
167
|
-
} catch (error) {
|
|
168
|
-
logInternalError("goal-loop.integritySnap", error, `goalId=${goal.goalId} phase=T_verify_done`);
|
|
169
207
|
}
|
|
170
|
-
}
|
|
171
208
|
} catch (error) {
|
|
172
209
|
logInternalError("goal-loop.verification", error, `goalId=${goal.goalId}`);
|
|
173
210
|
verificationResults = [];
|
|
@@ -223,10 +260,15 @@ function resolveGoalTurnWorkflow(goal: GoalLoopState): WorkflowConfig {
|
|
|
223
260
|
const wrapName = (goal as GoalLoopState & { goalWrapWorkflow?: string }).goalWrapWorkflow;
|
|
224
261
|
if (!wrapName) return buildTurnWorkflow();
|
|
225
262
|
try {
|
|
226
|
-
const { discoverWorkflows, allWorkflows } =
|
|
263
|
+
const { discoverWorkflows, allWorkflows } =
|
|
264
|
+
require("../workflows/discover-workflows.ts") as typeof import("../workflows/discover-workflows.ts");
|
|
227
265
|
const found = allWorkflows(discoverWorkflows(goal.cwd)).find((w) => w.name === wrapName && w.source === "builtin");
|
|
228
266
|
if (found) return found;
|
|
229
|
-
logInternalError(
|
|
267
|
+
logInternalError(
|
|
268
|
+
"goal-loop.goalWrapWorkflow",
|
|
269
|
+
new Error(`builtin workflow '${wrapName}' not found; falling back to goal-turn`),
|
|
270
|
+
`goalId=${goal.goalId}`,
|
|
271
|
+
);
|
|
230
272
|
} catch (error) {
|
|
231
273
|
logInternalError("goal-loop.goalWrapWorkflow", error, `goalId=${goal.goalId} wrapName=${wrapName}`);
|
|
232
274
|
}
|
|
@@ -253,7 +295,13 @@ export function buildGoalTeam(goal: GoalLoopState): TeamConfig {
|
|
|
253
295
|
description: `Synthetic team for goal loop ${goal.goalId} (worker=${workerAgent}).`,
|
|
254
296
|
source: "dynamic",
|
|
255
297
|
filePath: "<goal-loop>",
|
|
256
|
-
roles: [
|
|
298
|
+
roles: [
|
|
299
|
+
{
|
|
300
|
+
name: workerAgent,
|
|
301
|
+
agent: workerAgent,
|
|
302
|
+
description: `Worker for goal ${goal.goalId}`,
|
|
303
|
+
},
|
|
304
|
+
],
|
|
257
305
|
workspaceMode: "single",
|
|
258
306
|
};
|
|
259
307
|
}
|
|
@@ -302,9 +350,10 @@ function composeGoalPrompt(goal: GoalLoopState): string {
|
|
|
302
350
|
if (reasons[i] === currentReason) priorMatches++;
|
|
303
351
|
else break; // count consecutive tail matches only (oscillation = exact repeat)
|
|
304
352
|
}
|
|
305
|
-
const recurrenceNote =
|
|
306
|
-
|
|
307
|
-
|
|
353
|
+
const recurrenceNote =
|
|
354
|
+
priorMatches >= 1
|
|
355
|
+
? `\n_Note: this same issue has now been raised ${priorMatches + 1} time(s). If you genuinely cannot resolve it, stop attempting the same fix and explain the blocker instead._`
|
|
356
|
+
: "";
|
|
308
357
|
// P1e: per-turn unpredictable nonce. randomBytes(6) -> 12 hex chars (48 bits of entropy),
|
|
309
358
|
// comfortably unguessable. The worker is told the contents are DATA only.
|
|
310
359
|
const nonce = randomBytes(6).toString("hex");
|
|
@@ -343,13 +392,24 @@ async function yieldBetweenTurns(goal: GoalLoopState, signal: AbortSignal, ms =
|
|
|
343
392
|
|
|
344
393
|
/** Fix round-7: re-read disk before applying terminal state. If an external actor
|
|
345
394
|
* (goal stop/pause) already changed the state, don't overwrite — external cancel wins. */
|
|
346
|
-
function safeSetStatus(
|
|
395
|
+
function safeSetStatus(
|
|
396
|
+
store: GoalStore,
|
|
397
|
+
goalId: string,
|
|
398
|
+
proposed: GoalLoopStatus,
|
|
399
|
+
fallback: GoalLoopState,
|
|
400
|
+
eventsPath: string,
|
|
401
|
+
): GoalLoopState {
|
|
347
402
|
const current = store.load(goalId);
|
|
348
403
|
if (current && current.state !== "running") {
|
|
349
404
|
// External actor already set a terminal/paused state — respect it.
|
|
350
405
|
return current;
|
|
351
406
|
}
|
|
352
|
-
return
|
|
407
|
+
return (
|
|
408
|
+
store.setStatus(goalId, proposed, eventsPath) ?? {
|
|
409
|
+
...fallback,
|
|
410
|
+
state: proposed,
|
|
411
|
+
}
|
|
412
|
+
);
|
|
353
413
|
}
|
|
354
414
|
|
|
355
415
|
/**
|
|
@@ -361,10 +421,7 @@ function safeSetStatus(store: GoalStore, goalId: string, proposed: GoalLoopStatu
|
|
|
361
421
|
*
|
|
362
422
|
* Exported for unit testing.
|
|
363
423
|
*/
|
|
364
|
-
export function detectOscillation(
|
|
365
|
-
verdicts: Array<{ reason: string }>,
|
|
366
|
-
opts?: { window?: number; threshold?: number },
|
|
367
|
-
): boolean {
|
|
424
|
+
export function detectOscillation(verdicts: Array<{ reason: string }>, opts?: { window?: number; threshold?: number }): boolean {
|
|
368
425
|
const window = Math.max(2, opts?.window ?? 3);
|
|
369
426
|
const threshold = opts?.threshold ?? 0.8;
|
|
370
427
|
if (verdicts.length < window) return false;
|
|
@@ -380,7 +437,11 @@ export function detectOscillation(
|
|
|
380
437
|
|
|
381
438
|
function normalizeForSimilarity(s: string): Set<string> {
|
|
382
439
|
// Lowercase, split into word 3-shingles (trigrams of words). Skip non-word tokens.
|
|
383
|
-
const words = s
|
|
440
|
+
const words = s
|
|
441
|
+
.toLowerCase()
|
|
442
|
+
.replace(/[^\w\s]/g, " ")
|
|
443
|
+
.split(/\s+/)
|
|
444
|
+
.filter(Boolean);
|
|
384
445
|
if (words.length < 3) return new Set(words);
|
|
385
446
|
const shingles = new Set<string>();
|
|
386
447
|
for (let i = 0; i <= words.length - 3; i++) {
|
|
@@ -413,7 +474,9 @@ export function deriveTranscriptPath(artifactsRoot: string, tasks: import("../st
|
|
|
413
474
|
try {
|
|
414
475
|
const matches = readdirSync(transcriptsDir).filter((f) => f.startsWith(`${firstTask.id}.attempt-`));
|
|
415
476
|
if (matches.length) return `${transcriptsDir}/${matches.sort().pop()}`;
|
|
416
|
-
} catch {
|
|
477
|
+
} catch {
|
|
478
|
+
/* dir missing — fall through */
|
|
479
|
+
}
|
|
417
480
|
}
|
|
418
481
|
// Fallback: any transcript in the dir (newest).
|
|
419
482
|
try {
|
|
@@ -445,7 +508,15 @@ export async function runGoalLoop(input: RunGoalLoopInput): Promise<RunGoalLoopR
|
|
|
445
508
|
const workflow = resolveGoalTurnWorkflow(goal);
|
|
446
509
|
const agents = input.deps.discoverAgents(goal.cwd);
|
|
447
510
|
|
|
448
|
-
appendEvent(eventsPath, {
|
|
511
|
+
appendEvent(eventsPath, {
|
|
512
|
+
type: "goal.loop_start",
|
|
513
|
+
runId: manifest.runId,
|
|
514
|
+
data: {
|
|
515
|
+
goalId: goal.goalId,
|
|
516
|
+
objective: goal.objective,
|
|
517
|
+
maxTurns: goal.maxTurns,
|
|
518
|
+
},
|
|
519
|
+
});
|
|
449
520
|
|
|
450
521
|
// P1g (RFC v0.5 §P1g, cold-review #2 BLOCKING fix): acquire the workspace lock for the
|
|
451
522
|
// goal's lifetime. This serializes concurrent goals targeting the same cwd (workspaceMode:
|
|
@@ -455,11 +526,20 @@ export async function runGoalLoop(input: RunGoalLoopInput): Promise<RunGoalLoopR
|
|
|
455
526
|
// for a good error message; this acquisition is the authoritative claim.
|
|
456
527
|
let workspaceLock: WorkspaceLockHandle | undefined;
|
|
457
528
|
try {
|
|
458
|
-
workspaceLock = await acquireWorkspaceLock(goal.cwd, goal.goalId, {
|
|
529
|
+
workspaceLock = await acquireWorkspaceLock(goal.cwd, goal.goalId, {
|
|
530
|
+
signal,
|
|
531
|
+
});
|
|
459
532
|
} catch (error) {
|
|
460
533
|
logInternalError("goal-loop.workspaceLock", error, `goalId=${goal.goalId} cwd=${goal.cwd}`);
|
|
461
534
|
goal = safeSetStatus(store, goal.goalId, "blocked", goal, eventsPath);
|
|
462
|
-
appendEvent(eventsPath, {
|
|
535
|
+
appendEvent(eventsPath, {
|
|
536
|
+
type: "goal.workspace_lock_failed",
|
|
537
|
+
runId: manifest.runId,
|
|
538
|
+
data: {
|
|
539
|
+
goalId: goal.goalId,
|
|
540
|
+
error: error instanceof Error ? error.message : String(error),
|
|
541
|
+
},
|
|
542
|
+
});
|
|
463
543
|
return { manifest, tasks: [], goalState: goal };
|
|
464
544
|
}
|
|
465
545
|
|
|
@@ -477,16 +557,42 @@ export async function runGoalLoop(input: RunGoalLoopInput): Promise<RunGoalLoopR
|
|
|
477
557
|
if (goal.budgetUnlimited !== true && goal.budgetTotal !== undefined && goal.budgetTotal > 0 && goal.budgetAbort !== undefined) {
|
|
478
558
|
if (goal.budgetUsed >= goal.budgetAbort * goal.budgetTotal) {
|
|
479
559
|
goal = safeSetStatus(store, goal.goalId, "budget_exceeded", goal, eventsPath);
|
|
480
|
-
appendEvent(eventsPath, {
|
|
560
|
+
appendEvent(eventsPath, {
|
|
561
|
+
type: "goal.budget_warning",
|
|
562
|
+
runId: manifest.runId,
|
|
563
|
+
data: {
|
|
564
|
+
goalId: goal.goalId,
|
|
565
|
+
budgetUsed: goal.budgetUsed,
|
|
566
|
+
budgetTotal: goal.budgetTotal,
|
|
567
|
+
threshold: "abort",
|
|
568
|
+
},
|
|
569
|
+
});
|
|
481
570
|
break;
|
|
482
571
|
}
|
|
483
572
|
if (goal.budgetUsed >= (goal.budgetWarning ?? 0.8) * goal.budgetTotal) {
|
|
484
|
-
appendEvent(eventsPath, {
|
|
573
|
+
appendEvent(eventsPath, {
|
|
574
|
+
type: "goal.budget_warning",
|
|
575
|
+
runId: manifest.runId,
|
|
576
|
+
data: {
|
|
577
|
+
goalId: goal.goalId,
|
|
578
|
+
budgetUsed: goal.budgetUsed,
|
|
579
|
+
budgetTotal: goal.budgetTotal,
|
|
580
|
+
threshold: "warning",
|
|
581
|
+
},
|
|
582
|
+
});
|
|
485
583
|
}
|
|
486
584
|
}
|
|
487
585
|
|
|
488
586
|
const turnIndex = goal.turnsUsed + 1;
|
|
489
|
-
appendEvent(eventsPath, {
|
|
587
|
+
appendEvent(eventsPath, {
|
|
588
|
+
type: "goal.turn_start",
|
|
589
|
+
runId: manifest.runId,
|
|
590
|
+
data: {
|
|
591
|
+
goalId: goal.goalId,
|
|
592
|
+
turn: turnIndex,
|
|
593
|
+
maxTurns: goal.maxTurns,
|
|
594
|
+
},
|
|
595
|
+
});
|
|
490
596
|
|
|
491
597
|
// ── TURN: fresh manifest per turn (G2) + executeTeamRun ──────────────────
|
|
492
598
|
const turnGoalText = composeGoalPrompt(goal);
|
|
@@ -499,15 +605,34 @@ export async function runGoalLoop(input: RunGoalLoopInput): Promise<RunGoalLoopR
|
|
|
499
605
|
ownerSessionId: goal.ownerSessionId,
|
|
500
606
|
runKind: "team-run", // §0a v2 note: turns are normal team-runs; the OUTER loop is goal-loop
|
|
501
607
|
});
|
|
502
|
-
goal =
|
|
608
|
+
goal =
|
|
609
|
+
store.patch(
|
|
610
|
+
goal.goalId,
|
|
611
|
+
{
|
|
612
|
+
currentRunId: created.manifest.runId,
|
|
613
|
+
turnsUsed: turnIndex,
|
|
614
|
+
},
|
|
615
|
+
eventsPath,
|
|
616
|
+
) ?? goal;
|
|
503
617
|
// Fix round-6: re-check state AFTER patching (user may have paused/stopped in the inter-turn gap).
|
|
504
618
|
// Without this, a pause that lands between store.patch and executeTeamRun lets one extra turn run.
|
|
505
619
|
if (goal.state !== "running") {
|
|
506
|
-
appendEvent(eventsPath, {
|
|
620
|
+
appendEvent(eventsPath, {
|
|
621
|
+
type: "goal.loop_end",
|
|
622
|
+
runId: manifest.runId,
|
|
623
|
+
data: {
|
|
624
|
+
goalId: goal.goalId,
|
|
625
|
+
state: goal.state,
|
|
626
|
+
reason: "state changed before turn spawn",
|
|
627
|
+
},
|
|
628
|
+
});
|
|
507
629
|
break;
|
|
508
630
|
}
|
|
509
631
|
registerActiveRun(created.manifest);
|
|
510
|
-
let turnResult: {
|
|
632
|
+
let turnResult: {
|
|
633
|
+
manifest: TeamRunManifest;
|
|
634
|
+
tasks: TeamTaskState[];
|
|
635
|
+
};
|
|
511
636
|
try {
|
|
512
637
|
// P1g (RFC v0.5 §P1g): route the worker turn through the GLOBAL worker cap so that
|
|
513
638
|
// many concurrent goals / dynamic-workflows / fanOuts cannot fork-storm. The JUDGE is
|
|
@@ -520,20 +645,22 @@ export async function runGoalLoop(input: RunGoalLoopInput): Promise<RunGoalLoopR
|
|
|
520
645
|
const turnConfig = loadConfig(goal.cwd);
|
|
521
646
|
const turnExecutedConfig = effectiveRunConfig(turnConfig.config, {});
|
|
522
647
|
const turnRuntime = await resolveCrewRuntime(turnExecutedConfig);
|
|
523
|
-
turnResult = await withWorkerSlot(() =>
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
648
|
+
turnResult = await withWorkerSlot(() =>
|
|
649
|
+
executeTeamRun({
|
|
650
|
+
manifest: created.manifest,
|
|
651
|
+
tasks: created.tasks,
|
|
652
|
+
team,
|
|
653
|
+
workflow,
|
|
654
|
+
agents,
|
|
655
|
+
executeWorkers: true,
|
|
656
|
+
limits: turnExecutedConfig.limits,
|
|
657
|
+
runtime: turnRuntime,
|
|
658
|
+
runtimeConfig: turnExecutedConfig.runtime,
|
|
659
|
+
reliability: turnExecutedConfig.reliability,
|
|
660
|
+
workspaceId: goal.ownerSessionId ?? goal.cwd,
|
|
661
|
+
signal,
|
|
662
|
+
}),
|
|
663
|
+
);
|
|
537
664
|
} finally {
|
|
538
665
|
unregisterActiveRun(created.manifest.runId);
|
|
539
666
|
}
|
|
@@ -549,20 +676,53 @@ export async function runGoalLoop(input: RunGoalLoopInput): Promise<RunGoalLoopR
|
|
|
549
676
|
const updatedBudget = accumulateBudget(goal, created.manifest.runId);
|
|
550
677
|
|
|
551
678
|
// ── EVALUATE (P1: real LLM judge; pass turn manifest + tasks for transcript lookup) ──
|
|
552
|
-
const verdict = await evaluator(
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
679
|
+
const verdict = await evaluator(
|
|
680
|
+
{ ...goal, budgetUsed: updatedBudget },
|
|
681
|
+
created.manifest.runId,
|
|
682
|
+
turnResult.manifest,
|
|
683
|
+
turnResult.tasks,
|
|
684
|
+
signal,
|
|
685
|
+
);
|
|
686
|
+
const historyEntry = {
|
|
687
|
+
runId: created.manifest.runId,
|
|
688
|
+
outcome: verdict.achieved ? "achieved" : "not-achieved",
|
|
689
|
+
learnedAt: new Date().toISOString(),
|
|
690
|
+
turn: turnIndex,
|
|
691
|
+
};
|
|
692
|
+
goal =
|
|
693
|
+
store.patch(
|
|
694
|
+
goal.goalId,
|
|
695
|
+
{
|
|
696
|
+
budgetUsed: updatedBudget,
|
|
697
|
+
verdicts: [...goal.verdicts, verdict],
|
|
698
|
+
history: [...goal.history, historyEntry],
|
|
699
|
+
currentRunId: undefined,
|
|
700
|
+
// G1/A3: feedback feeds turn N+1's prompt via manifest.goal (NOT session.steer)
|
|
701
|
+
nextTurnFeedback: verdict.achieved ? undefined : verdict.reason,
|
|
702
|
+
},
|
|
703
|
+
eventsPath,
|
|
704
|
+
) ?? goal;
|
|
705
|
+
|
|
706
|
+
appendEvent(eventsPath, {
|
|
707
|
+
type: "goal.turn_evaluated",
|
|
708
|
+
runId: manifest.runId,
|
|
709
|
+
data: {
|
|
710
|
+
goalId: goal.goalId,
|
|
711
|
+
turn: turnIndex,
|
|
712
|
+
achieved: verdict.achieved,
|
|
713
|
+
reason: verdict.reason,
|
|
714
|
+
},
|
|
715
|
+
});
|
|
564
716
|
if (!verdict.achieved && goal.nextTurnFeedback) {
|
|
565
|
-
appendEvent(eventsPath, {
|
|
717
|
+
appendEvent(eventsPath, {
|
|
718
|
+
type: "goal.feedback_steered",
|
|
719
|
+
runId: manifest.runId,
|
|
720
|
+
data: {
|
|
721
|
+
goalId: goal.goalId,
|
|
722
|
+
turn: turnIndex,
|
|
723
|
+
feedback: goal.nextTurnFeedback,
|
|
724
|
+
},
|
|
725
|
+
});
|
|
566
726
|
}
|
|
567
727
|
|
|
568
728
|
// ── STOP CONDITIONS (round-7: re-read disk before applying — external cancel/pause wins) ─
|
|
@@ -589,7 +749,15 @@ export async function runGoalLoop(input: RunGoalLoopInput): Promise<RunGoalLoopR
|
|
|
589
749
|
const stuck = store.compareAndSetStatus(goal.goalId, "running", "stuck", eventsPath);
|
|
590
750
|
if (stuck) {
|
|
591
751
|
goal = stuck;
|
|
592
|
-
appendEvent(eventsPath, {
|
|
752
|
+
appendEvent(eventsPath, {
|
|
753
|
+
type: "goal.stuck",
|
|
754
|
+
runId: manifest.runId,
|
|
755
|
+
data: {
|
|
756
|
+
goalId: goal.goalId,
|
|
757
|
+
turn: goal.turnsUsed,
|
|
758
|
+
lastReasons: goal.verdicts.slice(-3).map((v) => v.reason.slice(0, 200)),
|
|
759
|
+
},
|
|
760
|
+
});
|
|
593
761
|
break;
|
|
594
762
|
}
|
|
595
763
|
}
|
|
@@ -606,8 +774,21 @@ export async function runGoalLoop(input: RunGoalLoopInput): Promise<RunGoalLoopR
|
|
|
606
774
|
goal = safeSetStatus(store, goal.goalId, "blocked", goal, eventsPath);
|
|
607
775
|
} finally {
|
|
608
776
|
// P1g: release the workspace lock (held since loop start).
|
|
609
|
-
try {
|
|
610
|
-
|
|
777
|
+
try {
|
|
778
|
+
workspaceLock?.release();
|
|
779
|
+
} catch {
|
|
780
|
+
/* best-effort */
|
|
781
|
+
}
|
|
782
|
+
appendEvent(eventsPath, {
|
|
783
|
+
type: "goal.loop_end",
|
|
784
|
+
runId: manifest.runId,
|
|
785
|
+
data: {
|
|
786
|
+
goalId: goal.goalId,
|
|
787
|
+
state: goal.state,
|
|
788
|
+
turnsUsed: goal.turnsUsed,
|
|
789
|
+
budgetUsed: goal.budgetUsed,
|
|
790
|
+
},
|
|
791
|
+
});
|
|
611
792
|
}
|
|
612
793
|
|
|
613
794
|
return { manifest, tasks: [], goalState: goal };
|
|
@@ -13,15 +13,15 @@
|
|
|
13
13
|
* per-turn usage stays on each turn's TeamRunManifest/tasks.json.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import { closeSync, existsSync, mkdirSync, openSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
17
17
|
import { dirname } from "node:path";
|
|
18
18
|
import { atomicWriteJson } from "../state/atomic-write.ts";
|
|
19
19
|
import { appendEvent } from "../state/event-log.ts";
|
|
20
|
-
import {
|
|
20
|
+
import type { GoalLoopState, GoalLoopStatus } from "../state/types.ts";
|
|
21
21
|
import { createRunId } from "../utils/ids.ts";
|
|
22
|
-
import { projectCrewRoot, userCrewRoot } from "../utils/paths.ts";
|
|
23
22
|
import { logInternalError } from "../utils/internal-error.ts";
|
|
24
|
-
import
|
|
23
|
+
import { projectCrewRoot, userCrewRoot } from "../utils/paths.ts";
|
|
24
|
+
import { assertSafePathId } from "../utils/safe-paths.ts";
|
|
25
25
|
|
|
26
26
|
/** Default state-root resolver: project scope if a project crew-root exists, else user scope. */
|
|
27
27
|
function resolveGoalsRoot(cwd: string): string {
|
|
@@ -81,7 +81,11 @@ export class GoalStore {
|
|
|
81
81
|
mkdirSync(dirname(path), { recursive: true });
|
|
82
82
|
atomicWriteJson(path, next);
|
|
83
83
|
if (eventsPath) {
|
|
84
|
-
appendEvent(eventsPath, {
|
|
84
|
+
appendEvent(eventsPath, {
|
|
85
|
+
type: "goal.state_changed",
|
|
86
|
+
runId: state.goalId,
|
|
87
|
+
data: { goalId: state.goalId, state: state.state },
|
|
88
|
+
});
|
|
85
89
|
}
|
|
86
90
|
} catch (error) {
|
|
87
91
|
logInternalError("goal-state-store.save", error, `goalId=${state.goalId}`);
|
|
@@ -93,7 +97,12 @@ export class GoalStore {
|
|
|
93
97
|
patch(goalId: string, patch: Partial<GoalLoopState>, eventsPath?: string): GoalLoopState | undefined {
|
|
94
98
|
const current = this.load(goalId);
|
|
95
99
|
if (!current) return undefined;
|
|
96
|
-
const next: GoalLoopState = {
|
|
100
|
+
const next: GoalLoopState = {
|
|
101
|
+
...current,
|
|
102
|
+
...patch,
|
|
103
|
+
goalId: current.goalId,
|
|
104
|
+
createdAt: current.createdAt,
|
|
105
|
+
};
|
|
97
106
|
this.save(next, eventsPath);
|
|
98
107
|
return next;
|
|
99
108
|
}
|
|
@@ -126,12 +135,7 @@ export class GoalStore {
|
|
|
126
135
|
* OS level — only one process can create the lockfile. The operation is fast (ms), so stale
|
|
127
136
|
* lockfiles are rare; a 5s age guard force-clears them (crash recovery).
|
|
128
137
|
*/
|
|
129
|
-
compareAndSetStatus(
|
|
130
|
-
goalId: string,
|
|
131
|
-
expected: GoalLoopStatus,
|
|
132
|
-
next: GoalLoopStatus,
|
|
133
|
-
eventsPath?: string,
|
|
134
|
-
): GoalLoopState | undefined {
|
|
138
|
+
compareAndSetStatus(goalId: string, expected: GoalLoopStatus, next: GoalLoopStatus, eventsPath?: string): GoalLoopState | undefined {
|
|
135
139
|
const lockPath = `${goalFilePath(this.cwd, goalId)}.cas.lock`;
|
|
136
140
|
if (!this.acquireCasLock(lockPath)) {
|
|
137
141
|
return undefined; // Another process holds the CAS lock — caller treats as CAS-failed.
|
|
@@ -144,7 +148,11 @@ export class GoalStore {
|
|
|
144
148
|
this.save(updated, eventsPath);
|
|
145
149
|
return updated;
|
|
146
150
|
} finally {
|
|
147
|
-
try {
|
|
151
|
+
try {
|
|
152
|
+
unlinkSync(lockPath);
|
|
153
|
+
} catch {
|
|
154
|
+
/* best-effort; may already be gone */
|
|
155
|
+
}
|
|
148
156
|
}
|
|
149
157
|
}
|
|
150
158
|
|
|
@@ -162,11 +170,13 @@ export class GoalStore {
|
|
|
162
170
|
const stat = statSync(lockPath);
|
|
163
171
|
if (Date.now() - stat.mtimeMs > 5000) {
|
|
164
172
|
unlinkSync(lockPath);
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
173
|
+
const fd = openSync(lockPath, "wx");
|
|
174
|
+
closeSync(fd);
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
} catch {
|
|
178
|
+
/* fall through */
|
|
168
179
|
}
|
|
169
|
-
} catch { /* fall through */ }
|
|
170
180
|
return false;
|
|
171
181
|
}
|
|
172
182
|
}
|
|
@@ -35,12 +35,21 @@ export function inferGreenLevelFromTask(success: boolean, contract: Verification
|
|
|
35
35
|
|
|
36
36
|
export function createVerificationEvidence(contract: VerificationContract, success: boolean, notes: string): VerificationEvidence {
|
|
37
37
|
const observedGreenLevel = inferGreenLevelFromTask(success, contract);
|
|
38
|
-
const outcome = evaluateGreenContract(contract, {
|
|
38
|
+
const outcome = evaluateGreenContract(contract, {
|
|
39
|
+
requiredGreenLevel: contract.requiredGreenLevel,
|
|
40
|
+
observedGreenLevel,
|
|
41
|
+
satisfied: false,
|
|
42
|
+
commands: [],
|
|
43
|
+
notes,
|
|
44
|
+
});
|
|
39
45
|
return {
|
|
40
46
|
requiredGreenLevel: contract.requiredGreenLevel,
|
|
41
47
|
observedGreenLevel,
|
|
42
48
|
satisfied: outcome.satisfied,
|
|
43
|
-
commands: contract.commands.map((cmd) => ({
|
|
49
|
+
commands: contract.commands.map((cmd) => ({
|
|
50
|
+
cmd,
|
|
51
|
+
status: "not_run" as const,
|
|
52
|
+
})),
|
|
44
53
|
notes,
|
|
45
54
|
};
|
|
46
55
|
}
|