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
|
@@ -25,13 +25,23 @@ export function validateResources(cwd: string): ValidationReport {
|
|
|
25
25
|
const issues: ValidationIssue[] = [];
|
|
26
26
|
|
|
27
27
|
for (const agent of agents) {
|
|
28
|
-
const modelValues = [agent.model, ...(agent.fallbackModels ?? [])].filter(
|
|
28
|
+
const modelValues = [agent.model, ...(agent.fallbackModels ?? [])].filter(
|
|
29
|
+
(value): value is string => typeof value === "string" && value.length > 0,
|
|
30
|
+
);
|
|
29
31
|
for (const model of modelValues) {
|
|
30
32
|
if (/\s/.test(model)) {
|
|
31
|
-
issues.push({
|
|
33
|
+
issues.push({
|
|
34
|
+
level: "warning",
|
|
35
|
+
resource: `agent:${agent.name}`,
|
|
36
|
+
message: `Model reference '${model}' contains whitespace.`,
|
|
37
|
+
});
|
|
32
38
|
}
|
|
33
39
|
if (model.includes("/") && model.split("/").some((part) => part.trim() === "")) {
|
|
34
|
-
issues.push({
|
|
40
|
+
issues.push({
|
|
41
|
+
level: "warning",
|
|
42
|
+
resource: `agent:${agent.name}`,
|
|
43
|
+
message: `Model reference '${model}' has an empty provider/model segment.`,
|
|
44
|
+
});
|
|
35
45
|
}
|
|
36
46
|
}
|
|
37
47
|
}
|
|
@@ -39,27 +49,48 @@ export function validateResources(cwd: string): ValidationReport {
|
|
|
39
49
|
for (const team of teams) {
|
|
40
50
|
for (const role of team.roles) {
|
|
41
51
|
if (!agentNames.has(role.agent)) {
|
|
42
|
-
issues.push({
|
|
52
|
+
issues.push({
|
|
53
|
+
level: "error",
|
|
54
|
+
resource: `team:${team.name}`,
|
|
55
|
+
message: `Role '${role.name}' references unknown agent '${role.agent}'.`,
|
|
56
|
+
});
|
|
43
57
|
}
|
|
44
58
|
}
|
|
45
59
|
if (team.defaultWorkflow && !workflowNames.has(team.defaultWorkflow)) {
|
|
46
|
-
issues.push({
|
|
60
|
+
issues.push({
|
|
61
|
+
level: "error",
|
|
62
|
+
resource: `team:${team.name}`,
|
|
63
|
+
message: `defaultWorkflow references unknown workflow '${team.defaultWorkflow}'.`,
|
|
64
|
+
});
|
|
47
65
|
}
|
|
48
66
|
const workflow = workflows.find((candidate) => candidate.name === team.defaultWorkflow);
|
|
49
67
|
if (workflow) {
|
|
50
68
|
for (const error of validateWorkflowForTeam(workflow, team)) {
|
|
51
|
-
issues.push({
|
|
69
|
+
issues.push({
|
|
70
|
+
level: "error",
|
|
71
|
+
resource: `workflow:${workflow.name}`,
|
|
72
|
+
message: `Team '${team.name}': ${error}`,
|
|
73
|
+
});
|
|
52
74
|
}
|
|
53
75
|
}
|
|
54
76
|
}
|
|
55
77
|
|
|
56
78
|
for (const workflow of workflows) {
|
|
57
79
|
if (workflow.steps.length === 0) {
|
|
58
|
-
issues.push({
|
|
80
|
+
issues.push({
|
|
81
|
+
level: "warning",
|
|
82
|
+
resource: `workflow:${workflow.name}`,
|
|
83
|
+
message: "Workflow has no steps.",
|
|
84
|
+
});
|
|
59
85
|
}
|
|
60
86
|
}
|
|
61
87
|
|
|
62
|
-
return {
|
|
88
|
+
return {
|
|
89
|
+
issues,
|
|
90
|
+
agents: agents.length,
|
|
91
|
+
teams: teams.length,
|
|
92
|
+
workflows: workflows.length,
|
|
93
|
+
};
|
|
63
94
|
}
|
|
64
95
|
|
|
65
96
|
export function formatValidationReport(report: ValidationReport): string {
|
package/src/hooks/registry.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { HookDefinition, HookName, HookContext, HookResult, HookExecutionReport } from "./types.ts";
|
|
2
1
|
import { appendEvent } from "../state/event-log.ts";
|
|
3
2
|
import type { TeamRunManifest } from "../state/types.ts";
|
|
4
3
|
import { runEventBus } from "../ui/run-event-bus.ts";
|
|
4
|
+
import type { HookContext, HookDefinition, HookExecutionReport, HookName, HookResult } from "./types.ts";
|
|
5
5
|
|
|
6
6
|
const registry = new Map<HookName, HookDefinition[]>();
|
|
7
7
|
|
|
@@ -67,7 +67,22 @@ export async function executeHook(name: HookName, ctx: HookContext): Promise<Hoo
|
|
|
67
67
|
return ctx.includeGlobalHooks !== false;
|
|
68
68
|
});
|
|
69
69
|
if (scopedHooks.length === 0) return { hookName: name, outcome: "allow", durationMs: 0 };
|
|
70
|
-
const POLLUTED_KEYS = new Set(
|
|
70
|
+
const POLLUTED_KEYS = new Set(
|
|
71
|
+
[
|
|
72
|
+
"__proto__",
|
|
73
|
+
"constructor",
|
|
74
|
+
"prototype",
|
|
75
|
+
"hasOwnProperty",
|
|
76
|
+
"toString",
|
|
77
|
+
"valueOf",
|
|
78
|
+
"isPrototypeOf",
|
|
79
|
+
"propertyIsEnumerable",
|
|
80
|
+
"__defineGetter__",
|
|
81
|
+
"__defineSetter__",
|
|
82
|
+
"__lookupGetter__",
|
|
83
|
+
"__lookupSetter__",
|
|
84
|
+
].map((k) => k.toLowerCase().normalize("NFKC")),
|
|
85
|
+
);
|
|
71
86
|
function sanitizeMergeData(data: Record<string, unknown>): Record<string, unknown> {
|
|
72
87
|
const clean: Record<string, unknown> = {};
|
|
73
88
|
for (const [k, v] of Object.entries(data)) {
|
|
@@ -75,7 +90,11 @@ export async function executeHook(name: HookName, ctx: HookContext): Promise<Hoo
|
|
|
75
90
|
if (v !== null && typeof v === "object") {
|
|
76
91
|
if (Array.isArray(v)) {
|
|
77
92
|
// Sanitize array elements that are objects
|
|
78
|
-
clean[k] = v.map((item) =>
|
|
93
|
+
clean[k] = v.map((item) =>
|
|
94
|
+
item !== null && typeof item === "object" && !Array.isArray(item)
|
|
95
|
+
? sanitizeMergeData(item as Record<string, unknown>)
|
|
96
|
+
: item,
|
|
97
|
+
);
|
|
79
98
|
} else {
|
|
80
99
|
clean[k] = sanitizeMergeData(v as Record<string, unknown>);
|
|
81
100
|
}
|
|
@@ -108,14 +127,19 @@ export async function executeHook(name: HookName, ctx: HookContext): Promise<Hoo
|
|
|
108
127
|
const diagnostics: string[] = [];
|
|
109
128
|
let capturedModifications: Record<string, unknown> | undefined;
|
|
110
129
|
for (const hook of scopedHooks) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
130
|
+
try {
|
|
131
|
+
const result: HookResult = await hook.handler(sanitizeContext(ctx));
|
|
132
|
+
// SECURITY: Sanitize any direct mutations the handler may have made to ctx.
|
|
133
|
+
// This prevents hooks from injecting dangerous properties via direct ctx assignment.
|
|
134
|
+
sanitizeContext(ctx);
|
|
135
|
+
if (hook.mode === "blocking" && result.outcome === "block") {
|
|
136
|
+
return {
|
|
137
|
+
hookName: name,
|
|
138
|
+
outcome: "block",
|
|
139
|
+
durationMs: Date.now() - start,
|
|
140
|
+
reason: result.reason,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
119
143
|
if (result.outcome === "modify" && result.data) {
|
|
120
144
|
Object.assign(ctx, sanitizeMergeData(result.data));
|
|
121
145
|
capturedModifications = { ...result.data };
|
|
@@ -123,16 +147,32 @@ export async function executeHook(name: HookName, ctx: HookContext): Promise<Hoo
|
|
|
123
147
|
} catch (error) {
|
|
124
148
|
const message = sanitizeErrorMessage(error instanceof Error ? error.message : String(error));
|
|
125
149
|
if (hook.mode === "blocking") {
|
|
126
|
-
return {
|
|
150
|
+
return {
|
|
151
|
+
hookName: name,
|
|
152
|
+
outcome: "block",
|
|
153
|
+
durationMs: Date.now() - start,
|
|
154
|
+
reason: `Hook error: ${message}`,
|
|
155
|
+
};
|
|
127
156
|
}
|
|
128
157
|
// Non-blocking hook errors are accumulated as diagnostics; continue to next hook
|
|
129
158
|
diagnostics.push(message);
|
|
130
|
-
|
|
159
|
+
}
|
|
131
160
|
}
|
|
132
161
|
if (diagnostics.length > 0) {
|
|
133
|
-
return {
|
|
162
|
+
return {
|
|
163
|
+
hookName: name,
|
|
164
|
+
outcome: "diagnostic",
|
|
165
|
+
durationMs: Date.now() - start,
|
|
166
|
+
reason: diagnostics.join("; "),
|
|
167
|
+
modifiedData: capturedModifications,
|
|
168
|
+
};
|
|
134
169
|
}
|
|
135
|
-
return {
|
|
170
|
+
return {
|
|
171
|
+
hookName: name,
|
|
172
|
+
outcome: "allow",
|
|
173
|
+
durationMs: Date.now() - start,
|
|
174
|
+
modifiedData: capturedModifications,
|
|
175
|
+
};
|
|
136
176
|
}
|
|
137
177
|
|
|
138
178
|
export function appendHookEvent(manifest: TeamRunManifest, report: HookExecutionReport): void {
|
|
@@ -140,7 +180,16 @@ export function appendHookEvent(manifest: TeamRunManifest, report: HookExecution
|
|
|
140
180
|
type: "hook.executed",
|
|
141
181
|
runId: manifest.runId,
|
|
142
182
|
message: `Hook ${report.hookName} completed with outcome=${report.outcome}${report.reason ? `: ${report.reason}` : ""}`,
|
|
143
|
-
data: {
|
|
183
|
+
data: {
|
|
184
|
+
hookName: report.hookName,
|
|
185
|
+
outcome: report.outcome,
|
|
186
|
+
durationMs: report.durationMs,
|
|
187
|
+
reason: report.reason,
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
runEventBus.emit({
|
|
191
|
+
type: "effectiveness_changed",
|
|
192
|
+
runId: manifest.runId,
|
|
193
|
+
data: { hookName: report.hookName, outcome: report.outcome },
|
|
144
194
|
});
|
|
145
|
-
runEventBus.emit({ type: "effectiveness_changed", runId: manifest.runId, data: { hookName: report.hookName, outcome: report.outcome } });
|
|
146
195
|
}
|
package/src/hooks/types.ts
CHANGED
package/src/i18n.ts
CHANGED
|
@@ -41,12 +41,14 @@ const translations: Record<string, Partial<Record<Key, string>>> = {
|
|
|
41
41
|
"agent.noOutput": "Sin salida.",
|
|
42
42
|
"result.requiresAgentId": "get_subagent_result requiere agent_id.",
|
|
43
43
|
"result.notFound": "Agente no encontrado: {id}",
|
|
44
|
-
"result.unrecoverable":
|
|
44
|
+
"result.unrecoverable":
|
|
45
|
+
"El subagente fue interrumpido antes de registrar su ID de ejecución duradero; no se puede recuperar tras reiniciar.",
|
|
45
46
|
"result.waitAborted": "Se canceló la espera del resultado del subagente.",
|
|
46
47
|
"result.waitTimeout": "Se agotó el tiempo de espera del resultado del subagente.",
|
|
47
48
|
"result.stillRunning": "El agente sigue ejecutándose. Usa wait=true o vuelve a comprobar más tarde.",
|
|
48
49
|
"steer.noted": "Solicitud de dirección registrada para {id}.",
|
|
49
|
-
"steer.unavailable":
|
|
50
|
+
"steer.unavailable":
|
|
51
|
+
"El backend predeterminado actual de pi-crew es child-process, así que session.steer a mitad de turno aún no está disponible.",
|
|
50
52
|
"steer.cancelHint": "Usa team cancel runId={runId} si hay que interrumpir el agente.",
|
|
51
53
|
},
|
|
52
54
|
fr: {
|
|
@@ -60,12 +62,14 @@ const translations: Record<string, Partial<Record<Key, string>>> = {
|
|
|
60
62
|
"agent.noOutput": "Aucune sortie.",
|
|
61
63
|
"result.requiresAgentId": "get_subagent_result nécessite agent_id.",
|
|
62
64
|
"result.notFound": "Agent introuvable : {id}",
|
|
63
|
-
"result.unrecoverable":
|
|
65
|
+
"result.unrecoverable":
|
|
66
|
+
"Le sous-agent a été interrompu avant l'enregistrement de son ID d'exécution durable ; il ne peut pas être récupéré après redémarrage.",
|
|
64
67
|
"result.waitAborted": "L'attente du résultat du sous-agent a été annulée.",
|
|
65
68
|
"result.waitTimeout": "Délai d'attente du résultat du sous-agent dépassé.",
|
|
66
69
|
"result.stillRunning": "L'agent est toujours en cours d'exécution. Utilisez wait=true ou réessayez plus tard.",
|
|
67
70
|
"steer.noted": "Demande de pilotage enregistrée pour {id}.",
|
|
68
|
-
"steer.unavailable":
|
|
71
|
+
"steer.unavailable":
|
|
72
|
+
"Le backend pi-crew par défaut actuel est child-process, donc session.steer en milieu de tour n'est pas encore disponible.",
|
|
69
73
|
"steer.cancelHint": "Utilisez team cancel runId={runId} si l'agent doit être interrompu.",
|
|
70
74
|
},
|
|
71
75
|
"pt-BR": {
|
|
@@ -79,12 +83,14 @@ const translations: Record<string, Partial<Record<Key, string>>> = {
|
|
|
79
83
|
"agent.noOutput": "Sem saída.",
|
|
80
84
|
"result.requiresAgentId": "get_subagent_result requer agent_id.",
|
|
81
85
|
"result.notFound": "Agente não encontrado: {id}",
|
|
82
|
-
"result.unrecoverable":
|
|
86
|
+
"result.unrecoverable":
|
|
87
|
+
"O subagente foi interrompido antes que seu ID de execução durável fosse registrado; ele não pode ser recuperado após reiniciar.",
|
|
83
88
|
"result.waitAborted": "A espera pelo resultado do subagente foi abortada.",
|
|
84
89
|
"result.waitTimeout": "Tempo limite de espera pelo resultado do subagente esgotado.",
|
|
85
90
|
"result.stillRunning": "O agente ainda está em execução. Use wait=true ou verifique novamente mais tarde.",
|
|
86
91
|
"steer.noted": "Solicitação de orientação registrada para {id}.",
|
|
87
|
-
"steer.unavailable":
|
|
92
|
+
"steer.unavailable":
|
|
93
|
+
"O backend padrão atual do pi-crew é child-process, então session.steer no meio do turno ainda não está disponível.",
|
|
88
94
|
"steer.cancelHint": "Use team cancel runId={runId} se o agente precisar ser interrompido.",
|
|
89
95
|
},
|
|
90
96
|
};
|
|
@@ -163,7 +169,12 @@ export function listLocales(): string[] {
|
|
|
163
169
|
|
|
164
170
|
export function initI18n(pi: ExtensionAPI): () => void {
|
|
165
171
|
try {
|
|
166
|
-
pi.events?.emit?.("pi-core/i18n/registerBundle", {
|
|
172
|
+
pi.events?.emit?.("pi-core/i18n/registerBundle", {
|
|
173
|
+
namespace,
|
|
174
|
+
defaultLocale: "en",
|
|
175
|
+
fallback,
|
|
176
|
+
translations,
|
|
177
|
+
});
|
|
167
178
|
} catch {
|
|
168
179
|
// Non-critical.
|
|
169
180
|
}
|
|
@@ -173,10 +184,13 @@ export function initI18n(pi: ExtensionAPI): () => void {
|
|
|
173
184
|
currentLocale = raw && translations[raw] ? raw : undefined;
|
|
174
185
|
});
|
|
175
186
|
try {
|
|
176
|
-
pi.events?.emit?.("pi-core/i18n/requestApi", {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
187
|
+
pi.events?.emit?.("pi-core/i18n/requestApi", {
|
|
188
|
+
namespace,
|
|
189
|
+
onApi(api: { getLocale?: () => string | undefined }) {
|
|
190
|
+
const raw = api.getLocale?.()?.trim();
|
|
191
|
+
if (raw && translations[raw]) currentLocale = raw;
|
|
192
|
+
},
|
|
193
|
+
});
|
|
180
194
|
} catch {
|
|
181
195
|
// Non-critical.
|
|
182
196
|
}
|
|
@@ -25,11 +25,25 @@ export function newSpanId(runId: string, taskId = "main"): string {
|
|
|
25
25
|
export function childCorrelation(runId: string, taskId: string): CorrelationContext {
|
|
26
26
|
const parent = getCurrentContext();
|
|
27
27
|
const spanId = newSpanId(runId, taskId);
|
|
28
|
-
return {
|
|
28
|
+
return {
|
|
29
|
+
traceId: parent?.traceId ?? spanId,
|
|
30
|
+
parentSpanId: parent?.spanId,
|
|
31
|
+
spanId,
|
|
32
|
+
};
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
export function correlatedEvent<T extends { runId?: string; data?: Record<string, unknown> }>(
|
|
35
|
+
export function correlatedEvent<T extends { runId?: string; data?: Record<string, unknown> }>(
|
|
36
|
+
event: T,
|
|
37
|
+
): T & { data: Record<string, unknown> } {
|
|
32
38
|
const ctx = getCurrentContext();
|
|
33
39
|
if (!ctx) return event as T & { data: Record<string, unknown> };
|
|
34
|
-
return {
|
|
40
|
+
return {
|
|
41
|
+
...event,
|
|
42
|
+
data: {
|
|
43
|
+
...(event.data ?? {}),
|
|
44
|
+
traceId: ctx.traceId,
|
|
45
|
+
spanId: ctx.spanId,
|
|
46
|
+
parentSpanId: ctx.parentSpanId,
|
|
47
|
+
},
|
|
48
|
+
};
|
|
35
49
|
}
|
|
@@ -1,74 +1,69 @@
|
|
|
1
|
-
import { logInternalError } from "../utils/internal-error.ts";
|
|
2
1
|
import type { AgentProgress } from "../runtime/progress-tracker.ts";
|
|
2
|
+
import { logInternalError } from "../utils/internal-error.ts";
|
|
3
3
|
|
|
4
|
-
export type CrewEventType =
|
|
5
|
-
| "agent:progress"
|
|
6
|
-
| "agent:complete"
|
|
7
|
-
| "agent:error"
|
|
8
|
-
| "run:start"
|
|
9
|
-
| "run:complete";
|
|
4
|
+
export type CrewEventType = "agent:progress" | "agent:complete" | "agent:error" | "run:start" | "run:complete";
|
|
10
5
|
|
|
11
6
|
export interface CrewEvent {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
type: CrewEventType;
|
|
8
|
+
runId: string;
|
|
9
|
+
agentId?: string;
|
|
10
|
+
payload?: AgentProgress | string;
|
|
11
|
+
timestamp: number;
|
|
17
12
|
}
|
|
18
13
|
|
|
19
14
|
type CrewEventListener = (event: CrewEvent) => void;
|
|
20
15
|
|
|
21
16
|
class EventBus {
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
private listeners = new Map<CrewEventType, Set<CrewEventListener>>();
|
|
18
|
+
private static _instance?: EventBus;
|
|
24
19
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
static getInstance(): EventBus {
|
|
21
|
+
if (!EventBus._instance) {
|
|
22
|
+
EventBus._instance = new EventBus();
|
|
23
|
+
}
|
|
24
|
+
return EventBus._instance;
|
|
25
|
+
}
|
|
31
26
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Dispose of the EventBus instance and clear all listeners.
|
|
29
|
+
* Resets the singleton so a new instance can be created.
|
|
30
|
+
*/
|
|
31
|
+
dispose(): void {
|
|
32
|
+
this.listeners.clear();
|
|
33
|
+
EventBus._instance = undefined;
|
|
34
|
+
}
|
|
40
35
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
36
|
+
emit(event: CrewEvent): void {
|
|
37
|
+
const listeners = this.listeners.get(event.type);
|
|
38
|
+
if (listeners) {
|
|
39
|
+
for (const listener of listeners) {
|
|
40
|
+
try {
|
|
41
|
+
listener(event);
|
|
42
|
+
} catch (e) {
|
|
43
|
+
// FIX (Round 15, L1): Use logInternalError for consistency with
|
|
44
|
+
// the rest of the codebase. Previously console.error may not be
|
|
45
|
+
// visible in all environments (e.g. JSON-RPC mode, redirected
|
|
46
|
+
// stderr).
|
|
47
|
+
logInternalError("event-bus.listener", e, `type=${event.type} runId=${event.runId}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
57
52
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
53
|
+
on(type: CrewEventType, listener: CrewEventListener): () => void {
|
|
54
|
+
if (!this.listeners.has(type)) {
|
|
55
|
+
this.listeners.set(type, new Set());
|
|
56
|
+
}
|
|
57
|
+
this.listeners.get(type)!.add(listener);
|
|
63
58
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
return () => {
|
|
60
|
+
this.listeners.get(type)?.delete(listener);
|
|
61
|
+
};
|
|
62
|
+
}
|
|
68
63
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
off(type: CrewEventType, listener: CrewEventListener): void {
|
|
65
|
+
this.listeners.get(type)?.delete(listener);
|
|
66
|
+
}
|
|
72
67
|
}
|
|
73
68
|
|
|
74
69
|
/**
|
|
@@ -2,7 +2,7 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
2
2
|
import type { MetricRegistry } from "./metric-registry.ts";
|
|
3
3
|
|
|
4
4
|
function recordValue(value: unknown): Record<string, unknown> {
|
|
5
|
-
return value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
|
|
5
|
+
return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : {};
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
function stringValue(value: unknown, fallback: string): string {
|
|
@@ -13,7 +13,15 @@ function numberValue(value: unknown, fallback = 0): number {
|
|
|
13
13
|
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
const CANCELLATION_REASON_LABELS = new Set([
|
|
16
|
+
const CANCELLATION_REASON_LABELS = new Set([
|
|
17
|
+
"caller_cancelled",
|
|
18
|
+
"leader_interrupted",
|
|
19
|
+
"provider_timeout",
|
|
20
|
+
"worker_timeout",
|
|
21
|
+
"tool_timeout",
|
|
22
|
+
"shutdown",
|
|
23
|
+
"unknown",
|
|
24
|
+
]);
|
|
17
25
|
|
|
18
26
|
function cancellationReasonLabel(value: unknown): string {
|
|
19
27
|
const raw = stringValue(value, "unknown");
|
|
@@ -34,34 +42,128 @@ export function wireEventToMetrics(events: ExtensionAPI["events"] | undefined, r
|
|
|
34
42
|
const overflowCount = registry.counter("crew.task.overflow_phase_total", "Overflow recovery phase transitions");
|
|
35
43
|
const supervisorContactCount = registry.counter("crew.task.supervisor_contact_total", "Supervisor contact requests by reason");
|
|
36
44
|
registry.gauge("crew.heartbeat.staleness_ms", "Heartbeat elapsed since last seen, milliseconds");
|
|
37
|
-
const runDuration = registry.histogram(
|
|
38
|
-
|
|
45
|
+
const runDuration = registry.histogram(
|
|
46
|
+
"crew.run.duration_ms",
|
|
47
|
+
"Run end-to-end duration, milliseconds",
|
|
48
|
+
[1000, 5000, 15000, 30000, 60000, 300000, 600000, 1800000],
|
|
49
|
+
);
|
|
50
|
+
const taskDuration = registry.histogram(
|
|
51
|
+
"crew.task.duration_ms",
|
|
52
|
+
"Task duration, milliseconds",
|
|
53
|
+
[50, 200, 500, 1000, 5000, 30000, 120000],
|
|
54
|
+
);
|
|
39
55
|
registry.histogram("crew.task.retry_count", "Retries per task", [0, 1, 2, 3, 5, 10]);
|
|
40
56
|
const tokenUsage = registry.histogram("crew.task.tokens_total", "Token usage per task", [100, 500, 2000, 10000, 50000, 200000]);
|
|
41
57
|
|
|
42
58
|
const handlers: Array<[string, (data: unknown) => void]> = [
|
|
43
|
-
[
|
|
59
|
+
[
|
|
60
|
+
"crew.run.completed",
|
|
61
|
+
(data) => {
|
|
62
|
+
const item = recordValue(data);
|
|
63
|
+
runCount.inc({ status: "completed" });
|
|
64
|
+
runDuration.observe({ team: stringValue(item.team, "unknown") }, numberValue(item.durationMs));
|
|
65
|
+
},
|
|
66
|
+
],
|
|
44
67
|
["crew.run.failed", () => runCount.inc({ status: "failed" })],
|
|
45
|
-
[
|
|
46
|
-
|
|
68
|
+
[
|
|
69
|
+
"crew.run.cancelled",
|
|
70
|
+
(data) => {
|
|
71
|
+
const item = recordValue(data);
|
|
72
|
+
runCount.inc({
|
|
73
|
+
status: "cancelled",
|
|
74
|
+
reason: cancellationReasonLabel(item.reason),
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
[
|
|
79
|
+
"crew.task.completed",
|
|
80
|
+
(data) => {
|
|
81
|
+
const item = recordValue(data);
|
|
82
|
+
taskCount.inc({ status: "completed" });
|
|
83
|
+
taskDuration.observe({ role: stringValue(item.role, "unknown") }, numberValue(item.durationMs));
|
|
84
|
+
tokenUsage.observe({ role: stringValue(item.role, "unknown") }, numberValue(item.tokens));
|
|
85
|
+
},
|
|
86
|
+
],
|
|
47
87
|
["crew.task.failed", () => taskCount.inc({ status: "failed" })],
|
|
48
88
|
["crew.task.needs_attention", () => taskCount.inc({ status: "needs_attention" })],
|
|
49
|
-
[
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
89
|
+
[
|
|
90
|
+
"crew.task.retry_attempt",
|
|
91
|
+
(data) => {
|
|
92
|
+
const item = recordValue(data);
|
|
93
|
+
taskCount.inc({ status: "retry" });
|
|
94
|
+
retryAttemptCount.inc({
|
|
95
|
+
runId: stringValue(item.runId, "unknown"),
|
|
96
|
+
taskId: stringValue(item.taskId, "unknown"),
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
[
|
|
101
|
+
"crew.task.deadletter",
|
|
102
|
+
(data) => {
|
|
103
|
+
const item = recordValue(data);
|
|
104
|
+
deadletterCount.inc({
|
|
105
|
+
reason: stringValue(item.reason, "unknown"),
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
[
|
|
110
|
+
"crew.task.overflow",
|
|
111
|
+
(data) => {
|
|
112
|
+
const item = recordValue(data);
|
|
113
|
+
overflowCount.inc({
|
|
114
|
+
phase: stringValue(item.phase, "unknown"),
|
|
115
|
+
previous_phase: stringValue(item.previousPhase, "none"),
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
[
|
|
120
|
+
"supervisor.contact",
|
|
121
|
+
(data) => {
|
|
122
|
+
const item = recordValue(data);
|
|
123
|
+
supervisorContactCount.inc({
|
|
124
|
+
reason: stringValue(item.reason, "unknown"),
|
|
125
|
+
taskId: stringValue(item.taskId, "unknown"),
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
[
|
|
130
|
+
"crew.subagent.completed",
|
|
131
|
+
(data) => {
|
|
132
|
+
const item = recordValue(data);
|
|
133
|
+
subagentCount.inc({
|
|
134
|
+
status: stringValue(item.status, "completed"),
|
|
135
|
+
});
|
|
136
|
+
},
|
|
137
|
+
],
|
|
54
138
|
["crew.subagent.failed", () => subagentCount.inc({ status: "failed" })],
|
|
55
|
-
[
|
|
139
|
+
[
|
|
140
|
+
"crew.mailbox.message",
|
|
141
|
+
(data) => {
|
|
142
|
+
const item = recordValue(data);
|
|
143
|
+
mailboxCount.inc({
|
|
144
|
+
direction: stringValue(item.direction, "unknown"),
|
|
145
|
+
});
|
|
146
|
+
},
|
|
147
|
+
],
|
|
56
148
|
];
|
|
57
149
|
|
|
58
150
|
const unsubscribers: Array<() => void> = [];
|
|
59
151
|
for (const [event, handler] of handlers) {
|
|
60
152
|
const unsubscribe = events?.on?.(event, (data: unknown) => {
|
|
61
|
-
try {
|
|
153
|
+
try {
|
|
154
|
+
handler(data);
|
|
155
|
+
} catch {
|
|
156
|
+
/* metric handlers must never break event delivery */
|
|
157
|
+
}
|
|
62
158
|
});
|
|
63
159
|
if (typeof unsubscribe === "function") unsubscribers.push(unsubscribe);
|
|
64
160
|
}
|
|
65
161
|
let disposed = false;
|
|
66
|
-
return {
|
|
162
|
+
return {
|
|
163
|
+
dispose() {
|
|
164
|
+
if (disposed) return;
|
|
165
|
+
disposed = true;
|
|
166
|
+
for (const unsubscribe of unsubscribers.splice(0)) unsubscribe();
|
|
167
|
+
},
|
|
168
|
+
};
|
|
67
169
|
}
|