pi-crew 0.9.16 → 0.9.17
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 +81 -0
- package/README.md +19 -0
- package/dist/build-meta.json +21790 -0
- package/dist/index.mjs +83070 -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/patterns/command-agent-skill.md +1 -1
- 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 +166 -590
- 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 +151 -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 +27 -23
- 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 +343 -78
- 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 +186 -58
- 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 +12 -30
- 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 +8 -16
- 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 +1 -2
- package/src/runtime/retry-executor.ts +20 -4
- package/src/runtime/retry-runner.ts +26 -50
- 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 +11 -18
- 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 +29 -8
- 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 +1 -4
- 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 +105 -26
- 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 +129 -45
- 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/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 +45 -24
- 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
|
@@ -54,7 +54,9 @@ export function deliverGroupJoin(input: {
|
|
|
54
54
|
}): CrewGroupJoinDelivery | undefined {
|
|
55
55
|
if (!shouldGroupJoin(input.mode, input.batch)) return undefined;
|
|
56
56
|
const taskIds = input.batch.map((task) => task.id);
|
|
57
|
-
const latest = taskIds
|
|
57
|
+
const latest = taskIds
|
|
58
|
+
.map((id) => input.allTasks.find((task) => task.id === id))
|
|
59
|
+
.filter((task): task is TeamTaskState => Boolean(task));
|
|
58
60
|
const completed = statusList(latest, "completed");
|
|
59
61
|
const failed = statusList(latest, "failed");
|
|
60
62
|
const skipped = statusList(latest, "skipped");
|
|
@@ -64,8 +66,22 @@ export function deliverGroupJoin(input: {
|
|
|
64
66
|
const summary = aggregateTaskOutputs(latest, input.manifest);
|
|
65
67
|
const requestId = requestIdFor(input.manifest.runId, batchId, partial);
|
|
66
68
|
const existingMailbox = findMailboxMessageByRequestId(input.manifest, requestId);
|
|
67
|
-
const existingStatus = existingMailbox
|
|
68
|
-
|
|
69
|
+
const existingStatus = existingMailbox
|
|
70
|
+
? (readDeliveryState(input.manifest).messages[existingMailbox.id] ?? existingMailbox.status)
|
|
71
|
+
: undefined;
|
|
72
|
+
const delivery: CrewGroupJoinDelivery = {
|
|
73
|
+
batchId,
|
|
74
|
+
mode: input.mode,
|
|
75
|
+
partial,
|
|
76
|
+
taskIds,
|
|
77
|
+
completed,
|
|
78
|
+
failed,
|
|
79
|
+
skipped,
|
|
80
|
+
remaining,
|
|
81
|
+
requestId,
|
|
82
|
+
ackRequired: true,
|
|
83
|
+
ackStatus: existingStatus === "acknowledged" ? "acknowledged" : "pending",
|
|
84
|
+
};
|
|
69
85
|
const content = `${JSON.stringify({ ...delivery, createdAt: new Date().toISOString() }, null, 2)}\n`;
|
|
70
86
|
const artifact = writeArtifact(input.manifest.artifactsRoot, {
|
|
71
87
|
kind: "metadata",
|
|
@@ -73,35 +89,55 @@ export function deliverGroupJoin(input: {
|
|
|
73
89
|
producer: "group-join",
|
|
74
90
|
content,
|
|
75
91
|
});
|
|
76
|
-
const mailbox =
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
const mailbox =
|
|
93
|
+
existingMailbox ??
|
|
94
|
+
appendMailboxMessage(input.manifest, {
|
|
95
|
+
direction: "outbox",
|
|
96
|
+
from: "group-join",
|
|
97
|
+
to: "leader",
|
|
98
|
+
body: [
|
|
99
|
+
`Group join ${partial ? "partial" : "completed"}: ${taskIds.join(", ")}`,
|
|
100
|
+
`Request: ${requestId}`,
|
|
101
|
+
`Completed: ${completed.join(", ") || "none"}`,
|
|
102
|
+
`Failed: ${failed.join(", ") || "none"}`,
|
|
103
|
+
`Skipped: ${skipped.join(", ") || "none"}`,
|
|
104
|
+
`Remaining: ${remaining.join(", ") || "none"}`,
|
|
105
|
+
"",
|
|
106
|
+
summary,
|
|
107
|
+
].join("\n"),
|
|
108
|
+
status: "delivered",
|
|
109
|
+
data: {
|
|
110
|
+
kind: "group_join",
|
|
111
|
+
requestId,
|
|
112
|
+
batchId,
|
|
113
|
+
partial,
|
|
114
|
+
ackRequired: true,
|
|
115
|
+
taskIds,
|
|
116
|
+
completed,
|
|
117
|
+
failed,
|
|
118
|
+
skipped,
|
|
119
|
+
remaining,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
93
122
|
appendEvent(input.manifest.eventsPath, {
|
|
94
123
|
type: partial ? "agent.group_join.partial" : "agent.group_join.completed",
|
|
95
124
|
runId: input.manifest.runId,
|
|
96
125
|
message: `Group join ${partial ? "partial" : "completed"} for ${taskIds.length} task(s).`,
|
|
97
|
-
data: {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
126
|
+
data: {
|
|
127
|
+
...delivery,
|
|
128
|
+
artifactPath: artifact.path,
|
|
129
|
+
messageId: mailbox.id,
|
|
130
|
+
fallback: "mailbox-delivered",
|
|
131
|
+
reused: Boolean(existingMailbox),
|
|
132
|
+
},
|
|
104
133
|
});
|
|
134
|
+
if (existingMailbox)
|
|
135
|
+
appendEvent(input.manifest.eventsPath, {
|
|
136
|
+
type: "agent.group_join.delivery_reused",
|
|
137
|
+
runId: input.manifest.runId,
|
|
138
|
+
message: `Reused group join mailbox delivery for ${taskIds.length} task(s).`,
|
|
139
|
+
data: { requestId, messageId: mailbox.id, batchId, partial },
|
|
140
|
+
});
|
|
105
141
|
return { ...delivery, artifact, messageId: mailbox.id };
|
|
106
142
|
}
|
|
107
143
|
|
|
@@ -129,10 +165,7 @@ export class GroupJoinManager {
|
|
|
129
165
|
private deliverCb: DeliveryCallback;
|
|
130
166
|
private groupTimeout: number;
|
|
131
167
|
|
|
132
|
-
constructor(
|
|
133
|
-
deliverCb: DeliveryCallback,
|
|
134
|
-
groupTimeout = DEFAULT_TIMEOUT,
|
|
135
|
-
) {
|
|
168
|
+
constructor(deliverCb: DeliveryCallback, groupTimeout = DEFAULT_TIMEOUT) {
|
|
136
169
|
this.deliverCb = deliverCb;
|
|
137
170
|
this.groupTimeout = groupTimeout;
|
|
138
171
|
}
|
|
@@ -8,18 +8,18 @@ const MAX_PENDING_HANDOFFS = 1000; // Prevent unbounded growth
|
|
|
8
8
|
* Type guard for HandoffSummary structure validation.
|
|
9
9
|
*/
|
|
10
10
|
export function isValidHandoffSummary(value: unknown): value is HandoffSummary {
|
|
11
|
-
if (!value || typeof value !==
|
|
11
|
+
if (!value || typeof value !== "object") {
|
|
12
12
|
return false;
|
|
13
13
|
}
|
|
14
14
|
const obj = value as Record<string, unknown>;
|
|
15
15
|
|
|
16
16
|
// Check required fields
|
|
17
|
-
if (typeof obj.taskId !==
|
|
18
|
-
if (typeof obj.runId !==
|
|
19
|
-
if (typeof obj.timestamp !==
|
|
20
|
-
if (typeof obj.task !==
|
|
21
|
-
if (typeof obj.outcome !==
|
|
22
|
-
if (![
|
|
17
|
+
if (typeof obj.taskId !== "string" || !obj.taskId) return false;
|
|
18
|
+
if (typeof obj.runId !== "string" || !obj.runId) return false;
|
|
19
|
+
if (typeof obj.timestamp !== "number") return false;
|
|
20
|
+
if (typeof obj.task !== "string" || !obj.task) return false;
|
|
21
|
+
if (typeof obj.outcome !== "string") return false;
|
|
22
|
+
if (!["success", "failure", "partial"].includes(obj.outcome)) return false;
|
|
23
23
|
|
|
24
24
|
// Check arrays
|
|
25
25
|
if (!Array.isArray(obj.filesCreated)) return false;
|
|
@@ -30,27 +30,27 @@ export function isValidHandoffSummary(value: unknown): value is HandoffSummary {
|
|
|
30
30
|
if (!Array.isArray(obj.nextSteps)) return false;
|
|
31
31
|
|
|
32
32
|
// Check metrics object
|
|
33
|
-
if (!obj.metrics || typeof obj.metrics !==
|
|
33
|
+
if (!obj.metrics || typeof obj.metrics !== "object") return false;
|
|
34
34
|
const metrics = obj.metrics as Record<string, unknown>;
|
|
35
|
-
if (typeof metrics.tokensUsed !==
|
|
36
|
-
if (typeof metrics.duration !==
|
|
37
|
-
if (typeof metrics.iterations !==
|
|
35
|
+
if (typeof metrics.tokensUsed !== "number") return false;
|
|
36
|
+
if (typeof metrics.duration !== "number") return false;
|
|
37
|
+
if (typeof metrics.iterations !== "number") return false;
|
|
38
38
|
if (!Array.isArray(metrics.toolsUsed)) return false;
|
|
39
39
|
|
|
40
40
|
// Check contextSnapshot
|
|
41
|
-
if (typeof obj.contextSnapshot !==
|
|
41
|
+
if (typeof obj.contextSnapshot !== "string") return false;
|
|
42
42
|
|
|
43
43
|
return true;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* HandoffManager - Generates structured summaries for agent handoffs.
|
|
48
|
-
*
|
|
48
|
+
*
|
|
49
49
|
* Based on pi-boomerang's session_before_tree hook pattern:
|
|
50
50
|
* - Detects task completion via agent_end hook
|
|
51
51
|
* - Generates structured summaries with token metrics, artifacts, decisions
|
|
52
52
|
* - Optionally collapses context to reduce token usage
|
|
53
|
-
*
|
|
53
|
+
*
|
|
54
54
|
* @see docs/pi-boomerang-integration-plan.md
|
|
55
55
|
*/
|
|
56
56
|
|
|
@@ -172,7 +172,7 @@ export interface SummarizeDecision {
|
|
|
172
172
|
/**
|
|
173
173
|
* HandoffManager generates structured summaries when agents complete tasks,
|
|
174
174
|
* enabling efficient context passing to subsequent agents.
|
|
175
|
-
*
|
|
175
|
+
*
|
|
176
176
|
* H1: Includes memory management to prevent unbounded growth of pendingHandoffs Map.
|
|
177
177
|
*/
|
|
178
178
|
export class HandoffManager {
|
|
@@ -247,8 +247,7 @@ export class HandoffManager {
|
|
|
247
247
|
// Also enforce max handoffs limit
|
|
248
248
|
if (this.pendingHandoffs.size > MAX_PENDING_HANDOFFS) {
|
|
249
249
|
// Remove oldest entries
|
|
250
|
-
const sortedEntries = [...this.handoffTimestamps.entries()]
|
|
251
|
-
.sort((a, b) => a[1] - b[1]);
|
|
250
|
+
const sortedEntries = [...this.handoffTimestamps.entries()].sort((a, b) => a[1] - b[1]);
|
|
252
251
|
const removeCount = sortedEntries.length - MAX_PENDING_HANDOFFS;
|
|
253
252
|
for (let i = 0; i < removeCount; i++) {
|
|
254
253
|
const sessionId = sortedEntries[i][0];
|
|
@@ -261,7 +260,7 @@ export class HandoffManager {
|
|
|
261
260
|
/**
|
|
262
261
|
* Hook: agent_end
|
|
263
262
|
* Called when agent completes a task.
|
|
264
|
-
*
|
|
263
|
+
*
|
|
265
264
|
* @param packet - The task packet
|
|
266
265
|
* @param result - The task result
|
|
267
266
|
*/
|
|
@@ -294,7 +293,10 @@ export class HandoffManager {
|
|
|
294
293
|
}
|
|
295
294
|
|
|
296
295
|
// Emit handoff event
|
|
297
|
-
this.options.eventEmitter?.emit("handoff:generated", {
|
|
296
|
+
this.options.eventEmitter?.emit("handoff:generated", {
|
|
297
|
+
packet,
|
|
298
|
+
summary,
|
|
299
|
+
});
|
|
298
300
|
|
|
299
301
|
// Optionally collapse context
|
|
300
302
|
if (packet.collapseContext) {
|
|
@@ -308,7 +310,7 @@ export class HandoffManager {
|
|
|
308
310
|
* Hook: session_before_tree
|
|
309
311
|
* Called before navigating to tree view.
|
|
310
312
|
* Injects pending handoff summaries into the tree.
|
|
311
|
-
*
|
|
313
|
+
*
|
|
312
314
|
* @param sessionId - The session ID
|
|
313
315
|
* @param targetId - The target tree node ID
|
|
314
316
|
*/
|
|
@@ -335,19 +337,19 @@ export class HandoffManager {
|
|
|
335
337
|
*/
|
|
336
338
|
shouldSummarize(packet: TaskPacket, result: TaskResult): SummarizeDecision {
|
|
337
339
|
// H7: Validate packet structure
|
|
338
|
-
if (!packet || typeof packet.taskId !==
|
|
340
|
+
if (!packet || typeof packet.taskId !== "string" || !packet.taskId) {
|
|
339
341
|
return {
|
|
340
342
|
shouldSummarize: false,
|
|
341
|
-
reason:
|
|
343
|
+
reason: "Invalid task packet structure",
|
|
342
344
|
tokenCount: 0,
|
|
343
345
|
};
|
|
344
346
|
}
|
|
345
347
|
|
|
346
348
|
// H7: Validate result structure
|
|
347
|
-
if (!result || typeof result.outcome !==
|
|
349
|
+
if (!result || typeof result.outcome !== "string") {
|
|
348
350
|
return {
|
|
349
351
|
shouldSummarize: false,
|
|
350
|
-
reason:
|
|
352
|
+
reason: "Invalid task result structure",
|
|
351
353
|
tokenCount: 0,
|
|
352
354
|
};
|
|
353
355
|
}
|
|
@@ -384,8 +386,7 @@ export class HandoffManager {
|
|
|
384
386
|
}
|
|
385
387
|
|
|
386
388
|
// 4. Task has significant artifacts or decisions
|
|
387
|
-
const hasArtifacts = (result.filesCreated?.length ?? 0) > 0 ||
|
|
388
|
-
(result.filesModified?.length ?? 0) > 0;
|
|
389
|
+
const hasArtifacts = (result.filesCreated?.length ?? 0) > 0 || (result.filesModified?.length ?? 0) > 0;
|
|
389
390
|
const hasDecisions = (result.decisions?.length ?? 0) > 0;
|
|
390
391
|
|
|
391
392
|
if (hasArtifacts || hasDecisions) {
|
|
@@ -419,11 +420,7 @@ export class HandoffManager {
|
|
|
419
420
|
const artifacts = this.extractArtifacts(result);
|
|
420
421
|
// Use extractDecisionsFromResult to handle empty array and generate defaults
|
|
421
422
|
const decisions = this.extractDecisionsFromResult(result);
|
|
422
|
-
const contextSnapshot = await this.generateContextSnapshot(
|
|
423
|
-
packet.runId,
|
|
424
|
-
packet.taskId,
|
|
425
|
-
result
|
|
426
|
-
);
|
|
423
|
+
const contextSnapshot = await this.generateContextSnapshot(packet.runId, packet.taskId, result);
|
|
427
424
|
|
|
428
425
|
return {
|
|
429
426
|
taskId: packet.taskId,
|
|
@@ -531,11 +528,13 @@ export class HandoffManager {
|
|
|
531
528
|
|
|
532
529
|
// Generate a default decision for failure outcomes
|
|
533
530
|
if (result.outcome === "failure") {
|
|
534
|
-
return [
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
531
|
+
return [
|
|
532
|
+
{
|
|
533
|
+
rationale: "Task failed",
|
|
534
|
+
outcome: result.error ?? "Unknown error",
|
|
535
|
+
alternativesConsidered: [],
|
|
536
|
+
},
|
|
537
|
+
];
|
|
539
538
|
}
|
|
540
539
|
|
|
541
540
|
return [];
|
|
@@ -544,11 +543,7 @@ export class HandoffManager {
|
|
|
544
543
|
/**
|
|
545
544
|
* Generate context snapshot for handoff.
|
|
546
545
|
*/
|
|
547
|
-
private async generateContextSnapshot(
|
|
548
|
-
runId: string,
|
|
549
|
-
taskId: string,
|
|
550
|
-
result: TaskResult
|
|
551
|
-
): Promise<string> {
|
|
546
|
+
private async generateContextSnapshot(runId: string, taskId: string, result: TaskResult): Promise<string> {
|
|
552
547
|
const parts: string[] = [];
|
|
553
548
|
|
|
554
549
|
parts.push(`Task: ${taskId}`);
|
|
@@ -602,4 +597,4 @@ export class HandoffManager {
|
|
|
602
597
|
*/
|
|
603
598
|
export function createHandoffManager(options?: HandoffManagerOptions): HandoffManager {
|
|
604
599
|
return new HandoffManager(options);
|
|
605
|
-
}
|
|
600
|
+
}
|
|
@@ -8,7 +8,11 @@ export interface GradientThresholds {
|
|
|
8
8
|
deadMs: number;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export const DEFAULT_GRADIENT_THRESHOLDS: GradientThresholds = {
|
|
11
|
+
export const DEFAULT_GRADIENT_THRESHOLDS: GradientThresholds = {
|
|
12
|
+
warnMs: 30_000,
|
|
13
|
+
staleMs: 60_000,
|
|
14
|
+
deadMs: 300_000,
|
|
15
|
+
};
|
|
12
16
|
|
|
13
17
|
export function heartbeatAgeMs(heartbeat: WorkerHeartbeatState | undefined, now = Date.now()): number {
|
|
14
18
|
if (!heartbeat) return Number.POSITIVE_INFINITY;
|
|
@@ -16,7 +20,11 @@ export function heartbeatAgeMs(heartbeat: WorkerHeartbeatState | undefined, now
|
|
|
16
20
|
return Number.isFinite(lastSeen) ? Math.max(0, now - lastSeen) : Number.POSITIVE_INFINITY;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
|
-
export function classifyHeartbeat(
|
|
23
|
+
export function classifyHeartbeat(
|
|
24
|
+
heartbeat: WorkerHeartbeatState | undefined,
|
|
25
|
+
thresholds: GradientThresholds = DEFAULT_GRADIENT_THRESHOLDS,
|
|
26
|
+
now = Date.now(),
|
|
27
|
+
): HeartbeatLevel {
|
|
20
28
|
if (!heartbeat) return "dead";
|
|
21
29
|
if (heartbeat.alive === false) return "dead";
|
|
22
30
|
const elapsed = heartbeatAgeMs(heartbeat, now);
|
|
@@ -5,8 +5,8 @@ import { appendEvent } from "../state/event-log.ts";
|
|
|
5
5
|
import { loadRunManifestById } from "../state/state-store.ts";
|
|
6
6
|
import type { TeamRunManifest } from "../state/types.ts";
|
|
7
7
|
import { logInternalError } from "../utils/internal-error.ts";
|
|
8
|
+
import { DEFAULT_GRADIENT_THRESHOLDS, type GradientThresholds, type HeartbeatLevel, heartbeatAgeMs } from "./heartbeat-gradient.ts";
|
|
8
9
|
import type { ManifestCache } from "./manifest-cache.ts";
|
|
9
|
-
import { DEFAULT_GRADIENT_THRESHOLDS, heartbeatAgeMs, type GradientThresholds, type HeartbeatLevel } from "./heartbeat-gradient.ts";
|
|
10
10
|
|
|
11
11
|
export interface HeartbeatWatcherRouter {
|
|
12
12
|
enqueue(notification: NotificationDescriptor): boolean;
|
|
@@ -126,18 +126,44 @@ export class HeartbeatWatcher {
|
|
|
126
126
|
// Process is dead
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
-
let level: HeartbeatLevel =
|
|
129
|
+
let level: HeartbeatLevel =
|
|
130
|
+
elapsed > thresholds.deadMs
|
|
131
|
+
? "dead"
|
|
132
|
+
: elapsed > thresholds.staleMs
|
|
133
|
+
? "stale"
|
|
134
|
+
: elapsed > thresholds.warnMs
|
|
135
|
+
? "warn"
|
|
136
|
+
: "healthy";
|
|
130
137
|
if (level === "dead" && isProcessAlive) {
|
|
131
138
|
level = "stale";
|
|
132
139
|
}
|
|
133
|
-
this.opts.registry
|
|
134
|
-
|
|
140
|
+
this.opts.registry
|
|
141
|
+
.gauge("crew.heartbeat.staleness_ms", "Heartbeat elapsed since last seen, milliseconds")
|
|
142
|
+
.set({ runId: run.runId, taskId: task.id }, Number.isFinite(elapsed) ? elapsed : thresholds.deadMs);
|
|
143
|
+
this.opts.registry
|
|
144
|
+
.counter("crew.heartbeat.level_total", "Heartbeat classifications by level")
|
|
145
|
+
.inc({ runId: run.runId, level });
|
|
135
146
|
const previous = this.lastLevel.get(key);
|
|
136
147
|
this.lastLevel.set(key, level);
|
|
137
148
|
if (level === "dead" && previous !== "dead") {
|
|
138
149
|
this.opts.registry.counter("crew.heartbeat.dead_total", "Dead heartbeat detections").inc({ runId: run.runId });
|
|
139
|
-
appendEvent(loaded.manifest.eventsPath, {
|
|
140
|
-
|
|
150
|
+
appendEvent(loaded.manifest.eventsPath, {
|
|
151
|
+
type: "crew.task.heartbeat_dead",
|
|
152
|
+
runId: run.runId,
|
|
153
|
+
taskId: task.id,
|
|
154
|
+
message: `Task ${task.id} heartbeat dead.`,
|
|
155
|
+
data: {
|
|
156
|
+
elapsedMs: Number.isFinite(elapsed) ? elapsed : undefined,
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
this.opts.router.enqueue({
|
|
160
|
+
id: `dead_${run.runId}_${task.id}`,
|
|
161
|
+
severity: "warning",
|
|
162
|
+
source: "heartbeat-watcher",
|
|
163
|
+
runId: run.runId,
|
|
164
|
+
title: `Task ${task.id} heartbeat dead`,
|
|
165
|
+
body: "Background watcher detected a stuck worker.",
|
|
166
|
+
});
|
|
141
167
|
this.opts.onDead?.(run.runId, task.id, Number.isFinite(elapsed) ? elapsed : thresholds.deadMs);
|
|
142
168
|
}
|
|
143
169
|
if (level === "dead") {
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* HiddenHandoffService - Sends hidden boomerang-handoff messages to parent agents.
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Based on pi-boomerang's triggerHiddenOrchestratorHandoff() pattern:
|
|
5
5
|
* - Sends customType: "boomerang-handoff" messages
|
|
6
6
|
* - Full handoff details in hidden message to parent
|
|
7
7
|
* - Orchestrator immediately reads summary
|
|
8
|
-
*
|
|
8
|
+
*
|
|
9
9
|
* @see docs/pi-boomerang-integration-plan.md
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import type { HandoffSummary } from "./handoff-manager.ts";
|
|
13
12
|
import { logInternalError } from "../utils/internal-error.ts";
|
|
13
|
+
import type { HandoffSummary } from "./handoff-manager.ts";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Type of hidden handoff message.
|
|
@@ -161,14 +161,11 @@ export class HiddenHandoffService {
|
|
|
161
161
|
|
|
162
162
|
/**
|
|
163
163
|
* Send a hidden handoff to the parent agent or specified recipient.
|
|
164
|
-
*
|
|
164
|
+
*
|
|
165
165
|
* @param summary - The handoff summary to send
|
|
166
166
|
* @param options - Send options
|
|
167
167
|
*/
|
|
168
|
-
sendHandoff(
|
|
169
|
-
summary: HandoffSummary,
|
|
170
|
-
options: SendHandoffOptions = {},
|
|
171
|
-
): void {
|
|
168
|
+
sendHandoff(summary: HandoffSummary, options: SendHandoffOptions = {}): void {
|
|
172
169
|
if (!this.enabled) {
|
|
173
170
|
return;
|
|
174
171
|
}
|
|
@@ -233,10 +230,7 @@ export class HiddenHandoffService {
|
|
|
233
230
|
/**
|
|
234
231
|
* Send a hidden handoff immediately (fire and forget).
|
|
235
232
|
*/
|
|
236
|
-
sendHandoffAsync(
|
|
237
|
-
summary: HandoffSummary,
|
|
238
|
-
options?: SendHandoffOptions,
|
|
239
|
-
): void {
|
|
233
|
+
sendHandoffAsync(summary: HandoffSummary, options?: SendHandoffOptions): void {
|
|
240
234
|
// Fire and forget - no await
|
|
241
235
|
try {
|
|
242
236
|
this.sendHandoff(summary, options);
|
|
@@ -289,10 +283,7 @@ export class HiddenHandoffService {
|
|
|
289
283
|
* Build summary text from summary.
|
|
290
284
|
*/
|
|
291
285
|
private buildSummaryText(summary: HandoffSummary): string {
|
|
292
|
-
const parts: string[] = [
|
|
293
|
-
`Completed: ${summary.task}`,
|
|
294
|
-
`Outcome: ${summary.outcome}`,
|
|
295
|
-
];
|
|
286
|
+
const parts: string[] = [`Completed: ${summary.task}`, `Outcome: ${summary.outcome}`];
|
|
296
287
|
|
|
297
288
|
if (summary.filesCreated.length > 0) {
|
|
298
289
|
parts.push(`Files created: ${summary.filesCreated.join(", ")}`);
|
|
@@ -310,10 +301,7 @@ export class HiddenHandoffService {
|
|
|
310
301
|
parts.push(`Next steps: ${summary.nextSteps.join("; ")}`);
|
|
311
302
|
}
|
|
312
303
|
|
|
313
|
-
parts.push(
|
|
314
|
-
`Tokens: ${summary.metrics.tokensUsed}`,
|
|
315
|
-
`Duration: ${Math.round(summary.metrics.duration / 1000)}s`,
|
|
316
|
-
);
|
|
304
|
+
parts.push(`Tokens: ${summary.metrics.tokensUsed}`, `Duration: ${Math.round(summary.metrics.duration / 1000)}s`);
|
|
317
305
|
|
|
318
306
|
return parts.join("\n");
|
|
319
307
|
}
|
|
@@ -361,9 +349,7 @@ export class HiddenHandoffService {
|
|
|
361
349
|
const timestamps = this.sendTimestamps.get(recipient) ?? [];
|
|
362
350
|
|
|
363
351
|
// Filter out old timestamps outside the window
|
|
364
|
-
const recentTimestamps = timestamps.filter(
|
|
365
|
-
(t) => now - t < this.RATE_LIMIT_WINDOW_MS,
|
|
366
|
-
);
|
|
352
|
+
const recentTimestamps = timestamps.filter((t) => now - t < this.RATE_LIMIT_WINDOW_MS);
|
|
367
353
|
|
|
368
354
|
// HIGH-11: If no recent timestamps, remove the empty key to prevent unbounded growth
|
|
369
355
|
if (recentTimestamps.length === 0) {
|
|
@@ -384,9 +370,7 @@ export class HiddenHandoffService {
|
|
|
384
370
|
timestamps.push(now);
|
|
385
371
|
|
|
386
372
|
// MEDIUM-14: Use RATE_LIMIT_WINDOW_MS consistently for both filter and record
|
|
387
|
-
const recentTimestamps = timestamps.filter(
|
|
388
|
-
(t) => now - t < this.RATE_LIMIT_WINDOW_MS,
|
|
389
|
-
);
|
|
373
|
+
const recentTimestamps = timestamps.filter((t) => now - t < this.RATE_LIMIT_WINDOW_MS);
|
|
390
374
|
|
|
391
375
|
this.sendTimestamps.set(recipient, recentTimestamps);
|
|
392
376
|
}
|
|
@@ -418,8 +402,6 @@ export interface SendHandoffOptions {
|
|
|
418
402
|
/**
|
|
419
403
|
* Create a HiddenHandoffService with default options.
|
|
420
404
|
*/
|
|
421
|
-
export function createHiddenHandoffService(
|
|
422
|
-
options?: HiddenHandoffServiceOptions,
|
|
423
|
-
): HiddenHandoffService {
|
|
405
|
+
export function createHiddenHandoffService(options?: HiddenHandoffServiceOptions): HiddenHandoffService {
|
|
424
406
|
return new HiddenHandoffService(options);
|
|
425
|
-
}
|
|
407
|
+
}
|
|
@@ -107,7 +107,12 @@ export function splitWithImportantLines(value: string, maxChars: number, opts: S
|
|
|
107
107
|
const tail = value.slice(value.length - tailLen);
|
|
108
108
|
|
|
109
109
|
if (opts.preserveImportant === false) {
|
|
110
|
-
return {
|
|
110
|
+
return {
|
|
111
|
+
head,
|
|
112
|
+
tail,
|
|
113
|
+
importantLines: [],
|
|
114
|
+
baseDropped: value.length - maxChars,
|
|
115
|
+
};
|
|
111
116
|
}
|
|
112
117
|
|
|
113
118
|
const slackFactor = opts.slackFactor ?? 0.15;
|
|
@@ -126,5 +131,10 @@ export function splitWithImportantLines(value: string, maxChars: number, opts: S
|
|
|
126
131
|
used += addLen;
|
|
127
132
|
}
|
|
128
133
|
|
|
129
|
-
return {
|
|
134
|
+
return {
|
|
135
|
+
head,
|
|
136
|
+
tail,
|
|
137
|
+
importantLines: chosen,
|
|
138
|
+
baseDropped: value.length - maxChars,
|
|
139
|
+
};
|
|
130
140
|
}
|
|
@@ -8,9 +8,9 @@ import { spawn } from "node:child_process";
|
|
|
8
8
|
import * as fs from "node:fs";
|
|
9
9
|
import * as path from "node:path";
|
|
10
10
|
import { WINDOWS_ESSENTIAL_ENV_VARS } from "../utils/env-allowlist.ts";
|
|
11
|
-
import { HeadSnapStage } from "./compact-stages/index.ts";
|
|
12
|
-
import { resolveShellForScript } from "../utils/resolve-shell.ts";
|
|
13
11
|
import { sanitizeEnvSecrets } from "../utils/env-filter.ts";
|
|
12
|
+
import { resolveShellForScript } from "../utils/resolve-shell.ts";
|
|
13
|
+
import { HeadSnapStage } from "./compact-stages/index.ts";
|
|
14
14
|
import { DENIED_METRIC_NAMES } from "./metric-parser.ts";
|
|
15
15
|
|
|
16
16
|
/** Hook execution stage. */
|
|
@@ -139,7 +139,14 @@ export async function runIterationHook(
|
|
|
139
139
|
options?: { timeoutMs?: number },
|
|
140
140
|
): Promise<HookResult> {
|
|
141
141
|
if (!isAllowedHookPath(hookScriptPath)) {
|
|
142
|
-
return {
|
|
142
|
+
return {
|
|
143
|
+
fired: false,
|
|
144
|
+
stdout: "",
|
|
145
|
+
stderr: "hook path not allowed: " + hookScriptPath,
|
|
146
|
+
exitCode: null,
|
|
147
|
+
timedOut: false,
|
|
148
|
+
durationMs: 0,
|
|
149
|
+
};
|
|
143
150
|
}
|
|
144
151
|
// Resolve relative paths relative to cwd
|
|
145
152
|
const resolvedScript = path.isAbsolute(hookScriptPath) ? hookScriptPath : path.join(payload.cwd, hookScriptPath);
|
|
@@ -156,7 +163,12 @@ export async function runIterationHook(
|
|
|
156
163
|
const { command, args } = resolveShellForScript(resolvedScript);
|
|
157
164
|
const child = spawn(command, args, {
|
|
158
165
|
cwd: payload.cwd,
|
|
159
|
-
env: {
|
|
166
|
+
env: {
|
|
167
|
+
...sanitizeEnvSecrets(process.env, {
|
|
168
|
+
allowList: ["PATH", "HOME", "USER", ...WINDOWS_ESSENTIAL_ENV_VARS, "TMPDIR", "LANG", "LC_ALL", "PI_CREW_*"],
|
|
169
|
+
}),
|
|
170
|
+
PI_CREW_HOOK: "1",
|
|
171
|
+
},
|
|
160
172
|
stdio: ["pipe", "pipe", "pipe"],
|
|
161
173
|
});
|
|
162
174
|
|
|
@@ -184,7 +196,9 @@ export async function runIterationHook(
|
|
|
184
196
|
// then apply the byte-cap stage with newline-snap so partial lines
|
|
185
197
|
// never appear in the captured preview. HeadSnapStage is byte-cap-safe
|
|
186
198
|
// (walks back partial UTF-8 sequences at the cut boundary).
|
|
187
|
-
const stdoutText = new HeadSnapStage({
|
|
199
|
+
const stdoutText = new HeadSnapStage({
|
|
200
|
+
maxBytes: MAX_STDOUT_BYTES,
|
|
201
|
+
}).apply(rawStdout.toString("utf-8"));
|
|
188
202
|
|
|
189
203
|
const rawStderr = Buffer.concat(stderrChunks);
|
|
190
204
|
|
|
@@ -214,7 +228,9 @@ export async function runIterationHook(
|
|
|
214
228
|
// Write payload to stdin and close it.
|
|
215
229
|
// Handle EPIPE errors gracefully (occurs if the hook script exits before
|
|
216
230
|
// reading all of stdin, which is normal for some hook scripts on certain OS).
|
|
217
|
-
child.stdin.on("error", () => {
|
|
231
|
+
child.stdin.on("error", () => {
|
|
232
|
+
/* ignore EPIPE — hook exited early */
|
|
233
|
+
});
|
|
218
234
|
try {
|
|
219
235
|
child.stdin.write(stdinJson, "utf-8");
|
|
220
236
|
child.stdin.end();
|
|
@@ -232,10 +248,7 @@ export async function runIterationHook(
|
|
|
232
248
|
* - Empty stdout → null (no steer)
|
|
233
249
|
* - Otherwise → trimmed stdout content
|
|
234
250
|
*/
|
|
235
|
-
export function steerMessageFromHook(
|
|
236
|
-
stage: HookStage,
|
|
237
|
-
result: HookResult,
|
|
238
|
-
): string | null {
|
|
251
|
+
export function steerMessageFromHook(stage: HookStage, result: HookResult): string | null {
|
|
239
252
|
if (!result.fired) return null;
|
|
240
253
|
|
|
241
254
|
if (result.timedOut) {
|
|
@@ -267,10 +280,7 @@ export function steerMessageFromHook(
|
|
|
267
280
|
/**
|
|
268
281
|
* Build a log entry for recording hook execution in events.jsonl.
|
|
269
282
|
*/
|
|
270
|
-
export function hookLogEntry(
|
|
271
|
-
stage: HookStage,
|
|
272
|
-
result: HookResult,
|
|
273
|
-
): Record<string, unknown> {
|
|
283
|
+
export function hookLogEntry(stage: HookStage, result: HookResult): Record<string, unknown> {
|
|
274
284
|
const entry: Record<string, unknown> = {
|
|
275
285
|
type: "iteration-hook",
|
|
276
286
|
stage,
|
|
@@ -292,4 +302,4 @@ export function hookLogEntry(
|
|
|
292
302
|
}
|
|
293
303
|
|
|
294
304
|
return entry;
|
|
295
|
-
}
|
|
305
|
+
}
|