pi-crew 0.9.16 → 0.9.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +130 -0
- package/README.md +19 -0
- package/dist/build-meta.json +21784 -0
- package/dist/index.mjs +83169 -0
- package/dist/index.mjs.map +7 -0
- package/docs/A +358 -0
- package/docs/M-A +357 -0
- package/docs/REVIEW-FINDINGS-2026-06 +357 -0
- package/docs/Y +357 -0
- package/docs/a +357 -0
- package/docs/aA +358 -0
- package/docs/archive/README.md +91 -0
- package/docs/migration/atomic-write-v2-migration.md +297 -0
- package/docs/patterns/command-agent-skill.md +1 -1
- package/docs/perf/performance-review-2026-07.md +287 -0
- package/docs/skills/REFERENCE.md +0 -17
- package/docs//303/242mmaAAA/303/242 +357 -0
- package/index.bundle.ts +25 -0
- package/index.ts +95 -4
- package/package.json +15 -4
- package/skills/iterative-audit/SKILL.md +0 -1
- package/skills/widget-rendering/SKILL.md +17 -0
- package/src/adapters/claude-adapter.ts +1 -3
- package/src/adapters/export-util.ts +16 -16
- package/src/adapters/index.ts +5 -5
- package/src/agents/agent-config.ts +20 -14
- package/src/agents/agent-serializer.ts +1 -1
- package/src/agents/discover-agents.ts +83 -39
- package/src/benchmark/benchmark-runner.ts +51 -51
- package/src/benchmark/feedback-loop.ts +1 -1
- package/src/config/config.ts +303 -591
- package/src/config/defaults.ts +28 -2
- package/src/config/drift-detector.ts +11 -14
- package/src/config/markers.ts +201 -203
- package/src/config/resilient-parser.ts +14 -6
- package/src/config/role-tools.ts +3 -3
- package/src/config/suggestions.ts +5 -12
- package/src/config/types.ts +9 -25
- package/src/errors.ts +151 -157
- package/src/extension/action-suggestions.ts +54 -9
- package/src/extension/async-notifier.ts +43 -13
- package/src/extension/autonomous-policy.ts +77 -37
- package/src/extension/command-completions.ts +9 -8
- package/src/extension/context-status-injection.ts +14 -12
- package/src/extension/crew-autocomplete.ts +8 -16
- package/src/extension/crew-cleanup.ts +2 -1
- package/src/extension/crew-shortcuts.ts +9 -6
- package/src/extension/cross-extension-rpc.ts +136 -42
- package/src/extension/help.ts +1 -1
- package/src/extension/import-index.ts +10 -5
- package/src/extension/knowledge-injection.ts +88 -15
- package/src/extension/management.ts +89 -349
- package/src/extension/message-renderers.ts +3 -4
- package/src/extension/notification-router.ts +22 -5
- package/src/extension/notification-sink.ts +6 -3
- package/src/extension/pi-api.ts +17 -8
- package/src/extension/plan-orchestrate.ts +8 -27
- package/src/extension/project-init.ts +21 -6
- package/src/extension/register.ts +329 -975
- package/src/extension/registration/artifact-cleanup.ts +9 -4
- package/src/extension/registration/command-utils.ts +5 -1
- package/src/extension/registration/commands.ts +806 -314
- package/src/extension/registration/compaction-guard.ts +15 -15
- package/src/extension/registration/lifecycle.ts +259 -0
- package/src/extension/registration/observability.ts +293 -0
- package/src/extension/registration/subagent-helpers.ts +29 -7
- package/src/extension/registration/subagent-tools.ts +253 -44
- package/src/extension/registration/team-tool.ts +19 -84
- package/src/extension/registration/ui.ts +168 -0
- package/src/extension/registration/viewers.ts +64 -24
- package/src/extension/result-watcher.ts +18 -7
- package/src/extension/run-bundle-schema.ts +21 -6
- package/src/extension/run-export.ts +15 -6
- package/src/extension/run-import.ts +64 -29
- package/src/extension/run-index.ts +28 -9
- package/src/extension/run-maintenance.ts +32 -12
- package/src/extension/session-summary.ts +1 -1
- package/src/extension/team-manager-command.ts +64 -7
- package/src/extension/team-onboard.ts +149 -150
- package/src/extension/team-recommendation.ts +121 -21
- package/src/extension/team-tool/anchor.ts +31 -64
- package/src/extension/team-tool/api.ts +929 -141
- package/src/extension/team-tool/auto-summarize.ts +14 -26
- package/src/extension/team-tool/cache-control.ts +1 -5
- package/src/extension/team-tool/cancel.ts +138 -38
- package/src/extension/team-tool/chain-dispatch.ts +5 -13
- package/src/extension/team-tool/chain-executor.ts +29 -51
- package/src/extension/team-tool/config-patch.ts +1 -1
- package/src/extension/team-tool/context.ts +40 -20
- package/src/extension/team-tool/destructive-gate.ts +10 -5
- package/src/extension/team-tool/doctor.ts +170 -51
- package/src/extension/team-tool/explain.ts +228 -214
- package/src/extension/team-tool/failure-patterns.ts +3 -9
- package/src/extension/team-tool/goal-wrap.ts +71 -29
- package/src/extension/team-tool/goal.ts +185 -35
- package/src/extension/team-tool/handle-schedule.ts +34 -15
- package/src/extension/team-tool/handle-settings.ts +94 -56
- package/src/extension/team-tool/health-monitor.ts +27 -108
- package/src/extension/team-tool/inspect.ts +42 -9
- package/src/extension/team-tool/intent-policy.ts +4 -1
- package/src/extension/team-tool/lifecycle-actions.ts +244 -48
- package/src/extension/team-tool/orchestrate.ts +7 -18
- package/src/extension/team-tool/parallel-dispatch.ts +51 -29
- package/src/extension/team-tool/plan.ts +29 -5
- package/src/extension/team-tool/respond.ts +49 -16
- package/src/extension/team-tool/run-not-found.ts +3 -8
- package/src/extension/team-tool/run.ts +80 -317
- package/src/extension/team-tool/status.ts +135 -43
- package/src/extension/team-tool/workflow-manage.ts +92 -25
- package/src/extension/team-tool-types.ts +8 -1
- package/src/extension/team-tool.ts +142 -551
- package/src/extension/validate-resources.ts +39 -8
- package/src/hooks/registry.ts +66 -17
- package/src/hooks/types.ts +1 -1
- package/src/i18n.ts +25 -11
- package/src/observability/correlation.ts +17 -3
- package/src/observability/event-bus.ts +51 -56
- package/src/observability/event-to-metric.ts +117 -15
- package/src/observability/exporters/otlp-exporter.ts +56 -17
- package/src/observability/exporters/prometheus-exporter.ts +1 -1
- package/src/observability/metric-registry.ts +16 -4
- package/src/observability/metric-retention.ts +8 -2
- package/src/observability/metric-sink.ts +11 -3
- package/src/observability/metrics-primitives.ts +42 -9
- package/src/plugins/plugin-define.ts +2 -2
- package/src/plugins/plugin-registry.ts +25 -25
- package/src/plugins/plugins/index.ts +1 -1
- package/src/plugins/plugins/nextjs.ts +16 -16
- package/src/plugins/plugins/vite.ts +7 -11
- package/src/plugins/plugins/vitest.ts +6 -11
- package/src/runtime/adaptive-plan.ts +225 -40
- package/src/runtime/agent-control.ts +58 -15
- package/src/runtime/agent-memory.ts +15 -9
- package/src/runtime/agent-observability.ts +10 -2
- package/src/runtime/anchor-manager.ts +27 -25
- package/src/runtime/async-marker.ts +7 -1
- package/src/runtime/async-runner.ts +39 -24
- package/src/runtime/auto-summarize.ts +7 -13
- package/src/runtime/background-runner.ts +189 -251
- package/src/runtime/batch-barrier.ts +18 -16
- package/src/runtime/cancellation-token.ts +16 -6
- package/src/runtime/cancellation.ts +46 -8
- package/src/runtime/capability-inventory.ts +6 -4
- package/src/runtime/chain-parser.ts +44 -11
- package/src/runtime/chain-runner.ts +63 -69
- package/src/runtime/checkpoint.ts +12 -56
- package/src/runtime/child-pi.ts +344 -79
- package/src/runtime/coalesce-tasks.ts +268 -0
- package/src/runtime/code-summary.ts +71 -26
- package/src/runtime/compact-stages/index.ts +15 -5
- package/src/runtime/compact-stages/tail-capture-stage.ts +7 -2
- package/src/runtime/compact-stages/truncation-stage.ts +4 -1
- package/src/runtime/compaction-summary.ts +17 -10
- package/src/runtime/completion-guard.ts +33 -16
- package/src/runtime/crash-classification.ts +28 -7
- package/src/runtime/crash-recovery.ts +177 -37
- package/src/runtime/crew-agent-records.ts +145 -47
- package/src/runtime/crew-hooks.ts +9 -24
- package/src/runtime/cross-extension-rpc.ts +25 -23
- package/src/runtime/custom-tools/irc-tool.ts +43 -15
- package/src/runtime/custom-tools/submit-result-tool.ts +16 -5
- package/src/runtime/delivery-coordinator.ts +34 -7
- package/src/runtime/delta-conflict.ts +9 -21
- package/src/runtime/deterministic-ast.ts +1 -1
- package/src/runtime/diagnostic-export.ts +53 -20
- package/src/runtime/direct-run.ts +12 -2
- package/src/runtime/dwf-state-store.ts +1 -1
- package/src/runtime/dynamic-workflow-context.ts +187 -59
- package/src/runtime/dynamic-workflow-runner.ts +42 -20
- package/src/runtime/effectiveness.ts +16 -8
- package/src/runtime/errors/crew-errors.ts +7 -11
- package/src/runtime/event-stream-bridge.ts +9 -3
- package/src/runtime/foreground-control.ts +54 -14
- package/src/runtime/foreground-watchdog.ts +9 -6
- package/src/runtime/goal-achievement.ts +27 -10
- package/src/runtime/goal-evaluator.ts +54 -19
- package/src/runtime/goal-loop-runner.ts +280 -99
- package/src/runtime/goal-state-store.ts +27 -17
- package/src/runtime/green-contract.ts +11 -2
- package/src/runtime/group-join.ts +64 -31
- package/src/runtime/handoff-manager.ts +37 -42
- package/src/runtime/heartbeat-gradient.ts +10 -2
- package/src/runtime/heartbeat-watcher.ts +32 -6
- package/src/runtime/hidden-handoff.ts +13 -31
- package/src/runtime/important-line-classifier.ts +12 -2
- package/src/runtime/iteration-hooks.ts +25 -15
- package/src/runtime/live-agent-control.ts +55 -7
- package/src/runtime/live-agent-manager.ts +130 -27
- package/src/runtime/live-control-realtime.ts +23 -3
- package/src/runtime/live-irc.ts +4 -1
- package/src/runtime/live-session-health.ts +12 -4
- package/src/runtime/live-session-runtime.ts +379 -117
- package/src/runtime/manifest-cache.ts +28 -12
- package/src/runtime/mcp-proxy.ts +10 -18
- package/src/runtime/metric-parser.ts +1 -5
- package/src/runtime/model-fallback.ts +55 -18
- package/src/runtime/model-resolver.ts +2 -6
- package/src/runtime/model-scope.ts +19 -3
- package/src/runtime/notebook-helpers.ts +60 -62
- package/src/runtime/orphan-worker-registry.ts +37 -26
- package/src/runtime/output-validator.ts +18 -3
- package/src/runtime/overflow-recovery.ts +5 -4
- package/src/runtime/parallel-research.ts +29 -5
- package/src/runtime/parallel-utils.ts +9 -11
- package/src/runtime/path-overlap.ts +150 -0
- package/src/runtime/peer-dep.ts +12 -17
- package/src/runtime/per-write-validator.ts +1 -3
- package/src/runtime/phase-tracker.ts +342 -330
- package/src/runtime/pi-args.ts +26 -8
- package/src/runtime/pi-json-output.ts +1 -1
- package/src/runtime/pi-spawn.ts +30 -19
- package/src/runtime/pipeline-runner.ts +56 -55
- package/src/runtime/plan-templates.ts +5 -6
- package/src/runtime/policy-engine.ts +43 -7
- package/src/runtime/post-checks.ts +12 -4
- package/src/runtime/post-exit-stdio-guard.ts +2 -2
- package/src/runtime/process-lifecycle.ts +20 -10
- package/src/runtime/process-status.ts +16 -5
- package/src/runtime/progress-event-coalescer.ts +2 -1
- package/src/runtime/progress-tracker.ts +103 -103
- package/src/runtime/prose-compressor.ts +9 -11
- package/src/runtime/recovery-recipes.ts +118 -24
- package/src/runtime/replace.ts +25 -10
- package/src/runtime/resilient-edit.ts +10 -25
- package/src/runtime/result-extractor.ts +2 -6
- package/src/runtime/retry-executor.ts +20 -4
- package/src/runtime/retry-runner.ts +27 -51
- package/src/runtime/role-permission.ts +6 -1
- package/src/runtime/run-coalesced-task-group.ts +256 -0
- package/src/runtime/run-drift.ts +14 -15
- package/src/runtime/run-tracker.ts +6 -28
- package/src/runtime/runtime-policy.ts +1 -6
- package/src/runtime/runtime-resolver.ts +80 -15
- package/src/runtime/scheduler.ts +47 -18
- package/src/runtime/semaphore.ts +5 -7
- package/src/runtime/sensitive-paths.ts +2 -1
- package/src/runtime/session-usage.ts +1 -1
- package/src/runtime/settings-store.ts +16 -12
- package/src/runtime/sidechain-output.ts +6 -2
- package/src/runtime/single-agent-compose.ts +1 -4
- package/src/runtime/skill-effectiveness.ts +29 -97
- package/src/runtime/skill-instructions.ts +40 -83
- package/src/runtime/stale-reconciler.ts +73 -197
- package/src/runtime/stream-preview.ts +1 -1
- package/src/runtime/streaming-output.ts +1 -1
- package/src/runtime/subagent-manager.ts +44 -169
- package/src/runtime/subprocess-tool-registry.ts +4 -1
- package/src/runtime/supervisor-contact.ts +10 -5
- package/src/runtime/task-display.ts +19 -5
- package/src/runtime/task-graph-scheduler.ts +95 -21
- package/src/runtime/task-graph.ts +5 -11
- package/src/runtime/task-health.ts +54 -47
- package/src/runtime/task-id.ts +12 -19
- package/src/runtime/task-output-context.ts +265 -81
- package/src/runtime/task-packet.ts +12 -20
- package/src/runtime/task-quality.ts +6 -14
- package/src/runtime/task-runner/context-retrieval.ts +4 -13
- package/src/runtime/task-runner/live-executor.ts +114 -36
- package/src/runtime/task-runner/output-splitter.ts +152 -0
- package/src/runtime/task-runner/progress.ts +50 -12
- package/src/runtime/task-runner/prompt-builder.ts +31 -7
- package/src/runtime/task-runner/prompt-pipeline.ts +31 -7
- package/src/runtime/task-runner/result-utils.ts +3 -1
- package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
- package/src/runtime/task-runner/run-projection.ts +27 -8
- package/src/runtime/task-runner/state-helpers.ts +50 -14
- package/src/runtime/task-runner/tail-read.ts +2 -6
- package/src/runtime/task-runner.ts +180 -326
- package/src/runtime/team-runner-artifacts.ts +13 -0
- package/src/runtime/team-runner.ts +456 -974
- package/src/runtime/tool-output-pruner.ts +6 -9
- package/src/runtime/tool-progress.ts +11 -21
- package/src/runtime/verification-gates.ts +33 -24
- package/src/runtime/verification-integrity.ts +2 -7
- package/src/runtime/verification-worktree.ts +62 -12
- package/src/runtime/worker-heartbeat.ts +5 -1
- package/src/runtime/worker-startup.ts +29 -5
- package/src/runtime/workflow-state.ts +17 -9
- package/src/runtime/workspace-lock.ts +30 -35
- package/src/runtime/workspace-tree.ts +20 -32
- package/src/runtime/yield-handler.ts +43 -9
- package/src/runtime/zombie-scanner.ts +5 -4
- package/src/schema/config-schema.ts +266 -172
- package/src/schema/team-tool-schema.ts +29 -74
- package/src/schema/validation-types.ts +25 -17
- package/src/skills/discover-skills.ts +35 -10
- package/src/skills/skill-templates.ts +109 -27
- package/src/skills/validate.ts +26 -8
- package/src/state/active-run-registry.ts +87 -28
- package/src/state/artifact-store.ts +9 -5
- package/src/state/atomic-write-v2.ts +85 -63
- package/src/state/atomic-write.ts +106 -27
- package/src/state/blob-store.ts +47 -20
- package/src/state/contracts.ts +20 -5
- package/src/state/crew-init.ts +5 -22
- package/src/state/decision-ledger.ts +19 -73
- package/src/state/event-log-rotation.ts +41 -15
- package/src/state/event-log.ts +168 -53
- package/src/state/event-reconstructor.ts +11 -1
- package/src/state/gitignore-manager.ts +2 -8
- package/src/state/health-store.ts +57 -57
- package/src/state/hook-instinct-bridge.ts +1 -1
- package/src/state/hook-integrations.ts +1 -1
- package/src/state/instinct-store.ts +17 -5
- package/src/state/jsonl-writer.ts +1 -1
- package/src/state/locks.ts +23 -9
- package/src/state/mailbox.ts +151 -28
- package/src/state/observation-store.ts +20 -14
- package/src/state/run-cache.ts +142 -135
- package/src/state/run-graph.ts +6 -15
- package/src/state/run-metrics.ts +5 -18
- package/src/state/schedule.ts +15 -9
- package/src/state/state-store.ts +137 -47
- package/src/state/task-claims.ts +12 -2
- package/src/state/tiered-eval.ts +52 -43
- package/src/state/types-eval.ts +2 -2
- package/src/state/types.ts +24 -20
- package/src/state/usage.ts +20 -4
- package/src/state/worker-atomic-writer.ts +6 -3
- package/src/subagents/index.ts +2 -2
- package/src/teams/discover-teams.ts +21 -6
- package/src/tools/safe-bash-extension.ts +5 -6
- package/src/tools/safe-bash.ts +10 -11
- package/src/types/new-api-types.ts +6 -10
- package/src/ui/agent-management-overlay.ts +52 -40
- package/src/ui/card-colors.ts +13 -4
- package/src/ui/crew-footer.ts +8 -7
- package/src/ui/crew-select-list.ts +1 -1
- package/src/ui/dashboard-panes/agents-pane.ts +41 -25
- package/src/ui/dashboard-panes/cancellation-pane.ts +1 -1
- package/src/ui/dashboard-panes/capability-pane.ts +32 -15
- package/src/ui/dashboard-panes/health-pane.ts +2 -1
- package/src/ui/dashboard-panes/metrics-pane.ts +4 -1
- package/src/ui/dashboard-panes/progress-pane.ts +12 -9
- package/src/ui/heartbeat-aggregator.ts +15 -4
- package/src/ui/keybinding-map.ts +40 -7
- package/src/ui/live-conversation-overlay.ts +36 -20
- package/src/ui/live-duration.ts +1 -4
- package/src/ui/live-run-sidebar.ts +88 -25
- package/src/ui/loaders.ts +2 -8
- package/src/ui/mascot.ts +20 -36
- package/src/ui/overlays/agent-picker-overlay.ts +11 -3
- package/src/ui/overlays/confirm-overlay.ts +4 -2
- package/src/ui/overlays/help-overlay.ts +25 -14
- package/src/ui/overlays/mailbox-compose-overlay.ts +48 -11
- package/src/ui/overlays/mailbox-compose-preview.ts +20 -5
- package/src/ui/overlays/mailbox-detail-overlay.ts +27 -6
- package/src/ui/pi-ui-compat.ts +7 -7
- package/src/ui/powerbar-publisher.ts +120 -42
- package/src/ui/render-diff.ts +10 -3
- package/src/ui/render-scheduler.ts +12 -4
- package/src/ui/run-action-dispatcher.ts +81 -18
- package/src/ui/run-dashboard.ts +172 -76
- package/src/ui/run-event-bus.ts +59 -37
- package/src/ui/run-snapshot-cache.ts +276 -86
- package/src/ui/settings-overlay.ts +361 -74
- package/src/ui/status-colors.ts +12 -1
- package/src/ui/syntax-highlight.ts +1 -1
- package/src/ui/terminal-status.ts +1 -3
- package/src/ui/theme-adapter.ts +16 -14
- package/src/ui/theme-discovery.ts +13 -2
- package/src/ui/tool-progress-formatter.ts +7 -7
- package/src/ui/tool-render.ts +128 -56
- package/src/ui/tool-renderers/brief-mode.ts +45 -27
- package/src/ui/tool-renderers/index.ts +109 -43
- package/src/ui/transcript-cache.ts +32 -6
- package/src/ui/transcript-entries.ts +25 -23
- package/src/ui/transcript-viewer.ts +78 -29
- package/src/ui/widget/index.ts +99 -40
- package/src/ui/widget/widget-formatters.ts +13 -6
- package/src/ui/widget/widget-model.ts +19 -8
- package/src/ui/widget/widget-renderer.ts +23 -13
- package/src/ui/widget/widget-types.ts +1 -1
- package/src/utils/bm25-search.ts +199 -199
- package/src/utils/conflict-detect.ts +22 -21
- package/src/utils/env-filter.ts +14 -7
- package/src/utils/file-coalescer.ts +5 -1
- package/src/utils/fingerprint.ts +3 -6
- package/src/utils/frontmatter.ts +3 -1
- package/src/utils/fs-watch.ts +2 -6
- package/src/utils/gh-protocol.ts +119 -44
- package/src/utils/git.ts +13 -15
- package/src/utils/guards.ts +2 -5
- package/src/utils/ids.ts +9 -2
- package/src/utils/incremental-reader.ts +14 -3
- package/src/utils/internal-error.ts +2 -1
- package/src/utils/names.ts +12 -3
- package/src/utils/paths.ts +49 -5
- package/src/utils/project-detector.ts +2 -2
- package/src/utils/redaction.ts +29 -19
- package/src/utils/resolve-shell.ts +9 -7
- package/src/utils/run-watcher-registry.ts +19 -31
- package/src/utils/safe-paths.ts +46 -29
- package/src/utils/scan-cache.ts +9 -2
- package/src/utils/session-utils.ts +2 -4
- package/src/utils/sleep.ts +12 -5
- package/src/utils/sse-parser.ts +5 -17
- package/src/utils/visual.ts +52 -32
- package/src/workflows/discover-workflows.ts +41 -109
- package/src/workflows/intermediate-store.ts +5 -21
- package/src/workflows/preflight-validator.ts +6 -33
- package/src/workflows/topology-analyzer.ts +5 -21
- package/src/workflows/workflow-config.ts +7 -6
- package/src/worktree/branch-freshness.ts +66 -8
- package/src/worktree/cleanup.ts +130 -20
- package/src/worktree/worktree-manager.ts +212 -69
- package/skills/artifact-analysis-loop/SKILL.md +0 -303
- package/skills/detection-pipeline-design/SKILL.md +0 -286
- package/skills/hunting-investigation-loop/SKILL.md +0 -402
- package/skills/incident-playbook-construction/SKILL.md +0 -384
- package/skills/security-review/SKILL.md +0 -561
- package/skills/threat-hypothesis-framework/SKILL.md +0 -176
- package/skills/ui-render-performance/SKILL.md +0 -58
- /package/docs/{followup-review-round3-2026-05-12.md → archive/followup-review-round3-2026-05-12.md} +0 -0
- /package/docs/{followup-review-round4-2026-05-13.md → archive/followup-review-round4-2026-05-13.md} +0 -0
- /package/docs/{pi-crew-bugs.md → archive/pi-crew-bugs.md} +0 -0
- /package/docs/{pi-crew-test-final.md → archive/pi-crew-test-final.md} +0 -0
- /package/docs/{pi-crew-test-results.md → archive/pi-crew-test-results.md} +0 -0
- /package/docs/{pi-crew-test-round2.md → archive/pi-crew-test-round2.md} +0 -0
- /package/docs/{pi-crew-test-round4.md → archive/pi-crew-test-round4.md} +0 -0
- /package/docs/{pi-crew-test-round5.md → archive/pi-crew-test-round5.md} +0 -0
- /package/docs/{pi-crew-test-round6.md → archive/pi-crew-test-round6.md} +0 -0
- /package/docs/{pi-crew-v0.5.10-audit-fix-plan.md → archive/pi-crew-v0.5.10-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.11-audit-fix-plan.md → archive/pi-crew-v0.5.11-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.12-audit-fix-plan.md → archive/pi-crew-v0.5.12-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.13-audit-fix-plan.md → archive/pi-crew-v0.5.13-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.14-audit-fix-plan.md → archive/pi-crew-v0.5.14-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.16-audit-fix-plan.md → archive/pi-crew-v0.5.16-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.17-audit-fix-plan.md → archive/pi-crew-v0.5.17-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.5-audit-fix-plan.md → archive/pi-crew-v0.5.5-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.9-audit-fix-plan.md → archive/pi-crew-v0.5.9-audit-fix-plan.md} +0 -0
- /package/docs/{pi-mono-opportunities.md → archive/pi-mono-opportunities.md} +0 -0
- /package/docs/{pi-mono-review.md → archive/pi-mono-review.md} +0 -0
- /package/docs/{pi-subagent4-comparison.md → archive/pi-subagent4-comparison.md} +0 -0
- /package/docs/{pi-subagents3-deep-analysis.md → archive/pi-subagents3-deep-analysis.md} +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
4
3
|
import { atomicWriteJson, atomicWriteJsonCoalesced, flushPendingAtomicWrites, readJsonFile } from "../state/atomic-write.ts";
|
|
4
|
+
import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
|
|
5
5
|
import { readJsonFileCoalesced } from "../utils/file-coalescer.ts";
|
|
6
|
-
import type { CrewAgentProgress, CrewAgentRecord, CrewRuntimeKind } from "./crew-agent-runtime.ts";
|
|
7
|
-
import { taskStatusToAgentStatus } from "./crew-agent-runtime.ts";
|
|
8
6
|
import { logInternalError } from "../utils/internal-error.ts";
|
|
9
|
-
import { assertSafePathId, resolveRealContainedPath } from "../utils/safe-paths.ts";
|
|
10
7
|
import { redactSecretString, redactSecrets } from "../utils/redaction.ts";
|
|
8
|
+
import { assertSafePathId, resolveRealContainedPath } from "../utils/safe-paths.ts";
|
|
11
9
|
import { sleepSync } from "../utils/sleep.ts";
|
|
10
|
+
import type { CrewAgentProgress, CrewAgentRecord, CrewRuntimeKind } from "./crew-agent-runtime.ts";
|
|
11
|
+
import { taskStatusToAgentStatus } from "./crew-agent-runtime.ts";
|
|
12
12
|
|
|
13
13
|
export function agentsPath(manifest: TeamRunManifest): string {
|
|
14
14
|
return path.join(manifest.stateRoot, "agents.json");
|
|
@@ -65,7 +65,14 @@ const AGENT_READER_TTL_MS = 200;
|
|
|
65
65
|
const ASYNC_AGENT_READER_CACHE_MAX_ENTRIES = 128;
|
|
66
66
|
const AGENTS_LOCK_STALE_MS = 30_000;
|
|
67
67
|
|
|
68
|
-
const asyncAgentReaderCache = new Map<
|
|
68
|
+
const asyncAgentReaderCache = new Map<
|
|
69
|
+
string,
|
|
70
|
+
{
|
|
71
|
+
expiresAt: number;
|
|
72
|
+
records: CrewAgentRecord[];
|
|
73
|
+
inFlight?: Promise<CrewAgentRecord[]>;
|
|
74
|
+
}
|
|
75
|
+
>();
|
|
69
76
|
|
|
70
77
|
function agentsLockPath(manifest: TeamRunManifest): string {
|
|
71
78
|
return `${agentsPath(manifest)}.lock`;
|
|
@@ -76,12 +83,20 @@ function removeStaleAgentsLock(lockPath: string, staleMs: number): boolean {
|
|
|
76
83
|
const stat = fs.statSync(lockPath);
|
|
77
84
|
if (stat.size > 1024) return false;
|
|
78
85
|
const raw = fs.readFileSync(lockPath, "utf-8");
|
|
79
|
-
const parsed = JSON.parse(raw) as {
|
|
86
|
+
const parsed = JSON.parse(raw) as {
|
|
87
|
+
createdAt?: unknown;
|
|
88
|
+
pid?: unknown;
|
|
89
|
+
};
|
|
80
90
|
const createdAt = typeof parsed.createdAt === "string" ? Date.parse(parsed.createdAt) : NaN;
|
|
81
91
|
if (Number.isFinite(createdAt) && Date.now() - createdAt <= staleMs) return false;
|
|
82
92
|
const pid = typeof parsed.pid === "number" ? parsed.pid : undefined;
|
|
83
93
|
if (pid && pid !== process.pid) {
|
|
84
|
-
try {
|
|
94
|
+
try {
|
|
95
|
+
process.kill(pid, 0);
|
|
96
|
+
return false;
|
|
97
|
+
} catch {
|
|
98
|
+
/* owner dead */
|
|
99
|
+
}
|
|
85
100
|
}
|
|
86
101
|
fs.rmSync(lockPath, { force: true });
|
|
87
102
|
return true;
|
|
@@ -99,7 +114,13 @@ function withAgentsLock<T>(manifest: TeamRunManifest, fn: () => T): T {
|
|
|
99
114
|
try {
|
|
100
115
|
const fd = fs.openSync(filePath, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL, 0o644);
|
|
101
116
|
try {
|
|
102
|
-
fs.writeSync(
|
|
117
|
+
fs.writeSync(
|
|
118
|
+
fd,
|
|
119
|
+
JSON.stringify({
|
|
120
|
+
pid: process.pid,
|
|
121
|
+
createdAt: new Date().toISOString(),
|
|
122
|
+
}),
|
|
123
|
+
);
|
|
103
124
|
} finally {
|
|
104
125
|
fs.closeSync(fd);
|
|
105
126
|
}
|
|
@@ -108,10 +129,15 @@ function withAgentsLock<T>(manifest: TeamRunManifest, fn: () => T): T {
|
|
|
108
129
|
const code = (error as NodeJS.ErrnoException).code;
|
|
109
130
|
if (code !== "EEXIST" && code !== "EISDIR") throw error;
|
|
110
131
|
if (code === "EISDIR") {
|
|
111
|
-
try {
|
|
132
|
+
try {
|
|
133
|
+
fs.rmSync(filePath, { recursive: true, force: true });
|
|
134
|
+
} catch {
|
|
135
|
+
/* ignore */
|
|
136
|
+
}
|
|
112
137
|
continue;
|
|
113
138
|
}
|
|
114
|
-
if (!removeStaleAgentsLock(filePath, AGENTS_LOCK_STALE_MS) && Date.now() > deadline)
|
|
139
|
+
if (!removeStaleAgentsLock(filePath, AGENTS_LOCK_STALE_MS) && Date.now() > deadline)
|
|
140
|
+
throw new Error(`Crew agents file is locked by another operation: ${agentsPath(manifest)}`);
|
|
115
141
|
sleepSync(Math.min(250, 25 * 2 ** attempt));
|
|
116
142
|
attempt += 1;
|
|
117
143
|
}
|
|
@@ -119,11 +145,22 @@ function withAgentsLock<T>(manifest: TeamRunManifest, fn: () => T): T {
|
|
|
119
145
|
try {
|
|
120
146
|
return fn();
|
|
121
147
|
} finally {
|
|
122
|
-
try {
|
|
148
|
+
try {
|
|
149
|
+
fs.rmSync(filePath, { force: true });
|
|
150
|
+
} catch {
|
|
151
|
+
/* best-effort */
|
|
152
|
+
}
|
|
123
153
|
}
|
|
124
154
|
}
|
|
125
155
|
|
|
126
|
-
function setAsyncAgentReaderCache(
|
|
156
|
+
function setAsyncAgentReaderCache(
|
|
157
|
+
filePath: string,
|
|
158
|
+
entry: {
|
|
159
|
+
expiresAt: number;
|
|
160
|
+
records: CrewAgentRecord[];
|
|
161
|
+
inFlight?: Promise<CrewAgentRecord[]>;
|
|
162
|
+
},
|
|
163
|
+
): void {
|
|
127
164
|
const now = Date.now();
|
|
128
165
|
for (const [key, cached] of asyncAgentReaderCache) {
|
|
129
166
|
if (cached.expiresAt <= now && !cached.inFlight) asyncAgentReaderCache.delete(key);
|
|
@@ -143,7 +180,11 @@ export function readCrewAgents(manifest: TeamRunManifest): CrewAgentRecord[] {
|
|
|
143
180
|
// after at most one coalesce window (250 ms).
|
|
144
181
|
flushPendingAtomicWrites();
|
|
145
182
|
try {
|
|
146
|
-
const records = readJsonFileCoalesced(
|
|
183
|
+
const records = readJsonFileCoalesced(
|
|
184
|
+
agentsPath(manifest),
|
|
185
|
+
AGENT_READER_TTL_MS,
|
|
186
|
+
() => readJsonFile<CrewAgentRecord[]>(agentsPath(manifest)) ?? [],
|
|
187
|
+
);
|
|
147
188
|
// Validate schema and deduplicate by id to handle concurrent write conflicts
|
|
148
189
|
const seen = new Set<string>();
|
|
149
190
|
const deduped = records.filter((r) => {
|
|
@@ -171,7 +212,7 @@ export async function readCrewAgentsAsync(manifest: TeamRunManifest): Promise<Cr
|
|
|
171
212
|
const inFlight = (async (): Promise<CrewAgentRecord[]> => {
|
|
172
213
|
try {
|
|
173
214
|
const parsed = JSON.parse(await fs.promises.readFile(filePath, "utf-8")) as unknown;
|
|
174
|
-
const raw = Array.isArray(parsed) ? redactSecrets(parsed) as CrewAgentRecord[] : [];
|
|
215
|
+
const raw = Array.isArray(parsed) ? (redactSecrets(parsed) as CrewAgentRecord[]) : [];
|
|
175
216
|
// Deduplicate by id to handle concurrent write conflicts
|
|
176
217
|
const seen = new Set<string>();
|
|
177
218
|
const deduped = raw.filter((r) => {
|
|
@@ -181,16 +222,30 @@ export async function readCrewAgentsAsync(manifest: TeamRunManifest): Promise<Cr
|
|
|
181
222
|
return true;
|
|
182
223
|
});
|
|
183
224
|
if (deduped.length !== raw.length) {
|
|
184
|
-
try {
|
|
225
|
+
try {
|
|
226
|
+
saveCrewAgents(manifest, deduped);
|
|
227
|
+
} catch {
|
|
228
|
+
/* best-effort */
|
|
229
|
+
}
|
|
185
230
|
}
|
|
186
|
-
setAsyncAgentReaderCache(filePath, {
|
|
231
|
+
setAsyncAgentReaderCache(filePath, {
|
|
232
|
+
expiresAt: Date.now() + AGENT_READER_TTL_MS,
|
|
233
|
+
records: deduped,
|
|
234
|
+
});
|
|
187
235
|
return deduped;
|
|
188
236
|
} catch {
|
|
189
|
-
setAsyncAgentReaderCache(filePath, {
|
|
237
|
+
setAsyncAgentReaderCache(filePath, {
|
|
238
|
+
expiresAt: Date.now() + AGENT_READER_TTL_MS,
|
|
239
|
+
records: [],
|
|
240
|
+
});
|
|
190
241
|
return [];
|
|
191
242
|
}
|
|
192
243
|
})();
|
|
193
|
-
setAsyncAgentReaderCache(filePath, {
|
|
244
|
+
setAsyncAgentReaderCache(filePath, {
|
|
245
|
+
expiresAt: now + AGENT_READER_TTL_MS,
|
|
246
|
+
records: cached?.records ?? [],
|
|
247
|
+
inFlight,
|
|
248
|
+
});
|
|
194
249
|
return inFlight;
|
|
195
250
|
}
|
|
196
251
|
|
|
@@ -210,9 +265,7 @@ const TERMINAL_AGENT_STATUSES = new Set(["completed", "failed", "cancelled", "bl
|
|
|
210
265
|
* User policy (v0.9.16): cancelled / stopped crew-agent records leave NO trace.
|
|
211
266
|
* `failed` records keep their audit trail (different from cancel — agent errored).
|
|
212
267
|
*/
|
|
213
|
-
export function shouldDeleteCrewAgentOnTerminalStatus(
|
|
214
|
-
record: Pick<CrewAgentRecord, "status">,
|
|
215
|
-
): boolean {
|
|
268
|
+
export function shouldDeleteCrewAgentOnTerminalStatus(record: Pick<CrewAgentRecord, "status">): boolean {
|
|
216
269
|
const s = record.status;
|
|
217
270
|
return s === "cancelled" || s === "stopped";
|
|
218
271
|
}
|
|
@@ -222,10 +275,7 @@ export function shouldDeleteCrewAgentOnTerminalStatus(
|
|
|
222
275
|
* and the per-task `status.json`. Called on cancellation to wipe the trace.
|
|
223
276
|
* Safe-fail: missing files are treated as success (already removed).
|
|
224
277
|
*/
|
|
225
|
-
export function removeCrewAgent(
|
|
226
|
-
manifest: TeamRunManifest,
|
|
227
|
-
taskId: string,
|
|
228
|
-
): { removedIndex: boolean; removedStatus: boolean } {
|
|
278
|
+
export function removeCrewAgent(manifest: TeamRunManifest, taskId: string): { removedIndex: boolean; removedStatus: boolean } {
|
|
229
279
|
let removedIndex = false;
|
|
230
280
|
let removedStatus = false;
|
|
231
281
|
// 1. Remove from agents.json index
|
|
@@ -254,7 +304,11 @@ export function removeCrewAgent(
|
|
|
254
304
|
|
|
255
305
|
export function upsertCrewAgent(manifest: TeamRunManifest, record: CrewAgentRecord): void {
|
|
256
306
|
// Guard: skip if run state has been deleted (prune/forget/cleanup)
|
|
257
|
-
try {
|
|
307
|
+
try {
|
|
308
|
+
fs.statSync(manifest.stateRoot);
|
|
309
|
+
} catch {
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
258
312
|
// User policy (v0.9.16): cancelled / stopped crew agents leave NO trace.
|
|
259
313
|
// We delete BOTH the index entry (agents.json) and the per-task status.json
|
|
260
314
|
// immediately so the UI dashboard/widget never see the cancelled agent again.
|
|
@@ -268,7 +322,7 @@ export function upsertCrewAgent(manifest: TeamRunManifest, record: CrewAgentReco
|
|
|
268
322
|
const existing = readCrewAgents(manifest);
|
|
269
323
|
// Deduplicate by id: keep newer record when same id appears
|
|
270
324
|
const idIndex = new Map(existing.map((item, i) => [item.id, i]));
|
|
271
|
-
const merged: CrewAgentRecord[] = existing.map((item) => item.id === record.id ? record : item);
|
|
325
|
+
const merged: CrewAgentRecord[] = existing.map((item) => (item.id === record.id ? record : item));
|
|
272
326
|
if (!idIndex.has(record.id)) merged.push(record);
|
|
273
327
|
// 2.5 caller migration: coalesce non-terminal progress writes; flush
|
|
274
328
|
// terminal statuses (completed/failed/cancelled/blocked) durably so
|
|
@@ -363,7 +417,9 @@ function writeSeqToSidecar(filePath: string, seq: number): void {
|
|
|
363
417
|
function nextAgentEventSeq(filePath: string): number {
|
|
364
418
|
if (!fs.existsSync(filePath)) {
|
|
365
419
|
// Clean up stale sidecar when main file is gone.
|
|
366
|
-
try {
|
|
420
|
+
try {
|
|
421
|
+
fs.unlinkSync(`${filePath}.${AGENT_EVENT_SEQ_SIDECAR}`);
|
|
422
|
+
} catch {}
|
|
367
423
|
return 1;
|
|
368
424
|
}
|
|
369
425
|
const stat = fs.statSync(filePath);
|
|
@@ -372,7 +428,11 @@ function nextAgentEventSeq(filePath: string): number {
|
|
|
372
428
|
// FIX: Try sidecar file for O(1) lookup before falling back to O(n) scan.
|
|
373
429
|
const sidecarSeq = readSeqFromSidecar(filePath);
|
|
374
430
|
if (sidecarSeq !== undefined) {
|
|
375
|
-
setAgentEventSeqCache(filePath, {
|
|
431
|
+
setAgentEventSeqCache(filePath, {
|
|
432
|
+
size: stat.size,
|
|
433
|
+
mtimeMs: stat.mtimeMs,
|
|
434
|
+
seq: sidecarSeq,
|
|
435
|
+
});
|
|
376
436
|
return sidecarSeq + 1;
|
|
377
437
|
}
|
|
378
438
|
let max = 0;
|
|
@@ -386,7 +446,11 @@ function nextAgentEventSeq(filePath: string): number {
|
|
|
386
446
|
max += 1;
|
|
387
447
|
}
|
|
388
448
|
}
|
|
389
|
-
setAgentEventSeqCache(filePath, {
|
|
449
|
+
setAgentEventSeqCache(filePath, {
|
|
450
|
+
size: stat.size,
|
|
451
|
+
mtimeMs: stat.mtimeMs,
|
|
452
|
+
seq: max,
|
|
453
|
+
});
|
|
390
454
|
writeSeqToSidecar(filePath, max);
|
|
391
455
|
return max + 1;
|
|
392
456
|
}
|
|
@@ -398,7 +462,11 @@ export function appendCrewAgentEvent(manifest: TeamRunManifest, taskId: string,
|
|
|
398
462
|
fs.appendFileSync(filePath, `${JSON.stringify(redactSecrets({ seq, time: new Date().toISOString(), event }))}\n`, "utf-8");
|
|
399
463
|
try {
|
|
400
464
|
const stat = fs.statSync(filePath);
|
|
401
|
-
setAgentEventSeqCache(filePath, {
|
|
465
|
+
setAgentEventSeqCache(filePath, {
|
|
466
|
+
size: stat.size,
|
|
467
|
+
mtimeMs: stat.mtimeMs,
|
|
468
|
+
seq,
|
|
469
|
+
});
|
|
402
470
|
writeSeqToSidecar(filePath, seq);
|
|
403
471
|
} catch (error) {
|
|
404
472
|
logInternalError("crew-agent-records.stat", error, `filePath=${filePath}`);
|
|
@@ -415,34 +483,64 @@ function readCrewAgentEvents(manifest: TeamRunManifest, taskId: string): unknown
|
|
|
415
483
|
return readCrewAgentEventsCursor(manifest, taskId).events;
|
|
416
484
|
}
|
|
417
485
|
|
|
418
|
-
export function readCrewAgentEventsCursor(
|
|
486
|
+
export function readCrewAgentEventsCursor(
|
|
487
|
+
manifest: TeamRunManifest,
|
|
488
|
+
taskId: string,
|
|
489
|
+
options: CrewAgentEventCursorOptions = {},
|
|
490
|
+
): { path: string; events: unknown[]; nextSeq: number; total: number } {
|
|
419
491
|
let filePath: string;
|
|
420
492
|
try {
|
|
421
493
|
filePath = agentEventsPath(manifest, taskId);
|
|
422
494
|
} catch {
|
|
423
|
-
return {
|
|
495
|
+
return {
|
|
496
|
+
path: "",
|
|
497
|
+
events: [],
|
|
498
|
+
nextSeq: options.sinceSeq ?? 0,
|
|
499
|
+
total: 0,
|
|
500
|
+
};
|
|
424
501
|
}
|
|
425
|
-
if (!fs.existsSync(filePath))
|
|
502
|
+
if (!fs.existsSync(filePath))
|
|
503
|
+
return {
|
|
504
|
+
path: filePath,
|
|
505
|
+
events: [],
|
|
506
|
+
nextSeq: options.sinceSeq ?? 0,
|
|
507
|
+
total: 0,
|
|
508
|
+
};
|
|
426
509
|
try {
|
|
427
510
|
filePath = safeExistingAgentFile(manifest, taskId, "events.jsonl");
|
|
428
511
|
} catch {
|
|
429
|
-
return {
|
|
512
|
+
return {
|
|
513
|
+
path: "",
|
|
514
|
+
events: [],
|
|
515
|
+
nextSeq: options.sinceSeq ?? 0,
|
|
516
|
+
total: 0,
|
|
517
|
+
};
|
|
430
518
|
}
|
|
431
|
-
const sinceSeq =
|
|
519
|
+
const sinceSeq =
|
|
520
|
+
typeof options.sinceSeq === "number" && Number.isInteger(options.sinceSeq) && options.sinceSeq >= 0 ? options.sinceSeq : 0;
|
|
432
521
|
const limit = typeof options.limit === "number" && Number.isInteger(options.limit) && options.limit >= 0 ? options.limit : undefined;
|
|
433
|
-
const parsed = fs
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
522
|
+
const parsed = fs
|
|
523
|
+
.readFileSync(filePath, "utf-8")
|
|
524
|
+
.split(/\r?\n/)
|
|
525
|
+
.filter(Boolean)
|
|
526
|
+
.map((line, index) => {
|
|
527
|
+
try {
|
|
528
|
+
const event = JSON.parse(line) as Record<string, unknown>;
|
|
529
|
+
if (typeof event.seq !== "number") event.seq = index + 1;
|
|
530
|
+
return event;
|
|
531
|
+
} catch {
|
|
532
|
+
return { seq: index + 1, raw: line };
|
|
533
|
+
}
|
|
534
|
+
});
|
|
442
535
|
const filtered = parsed.filter((event) => typeof event.seq === "number" && event.seq > sinceSeq);
|
|
443
536
|
const events = limit !== undefined ? filtered.slice(0, limit) : filtered;
|
|
444
|
-
const returnedMaxSeq = events.reduce((max, event) => typeof event.seq === "number" ? Math.max(max, event.seq) : max, sinceSeq);
|
|
445
|
-
return {
|
|
537
|
+
const returnedMaxSeq = events.reduce((max, event) => (typeof event.seq === "number" ? Math.max(max, event.seq) : max), sinceSeq);
|
|
538
|
+
return {
|
|
539
|
+
path: filePath,
|
|
540
|
+
events,
|
|
541
|
+
nextSeq: returnedMaxSeq,
|
|
542
|
+
total: filtered.length,
|
|
543
|
+
};
|
|
446
544
|
}
|
|
447
545
|
|
|
448
546
|
export function appendCrewAgentOutput(manifest: TeamRunManifest, taskId: string, text: string): void {
|
|
@@ -25,12 +25,7 @@
|
|
|
25
25
|
import { logInternalError } from "../utils/internal-error.ts";
|
|
26
26
|
|
|
27
27
|
/** Valid hook event types in the crew lifecycle. */
|
|
28
|
-
export type CrewHookEventType =
|
|
29
|
-
| 'task_started'
|
|
30
|
-
| 'task_completed'
|
|
31
|
-
| 'task_failed'
|
|
32
|
-
| 'run_completed'
|
|
33
|
-
| 'run_failed';
|
|
28
|
+
export type CrewHookEventType = "task_started" | "task_completed" | "task_failed" | "run_completed" | "run_failed";
|
|
34
29
|
|
|
35
30
|
/**
|
|
36
31
|
* A hook event emitted by the crew runtime.
|
|
@@ -59,11 +54,7 @@ export type CrewHook = (event: CrewHookEvent) => void | Promise<void>;
|
|
|
59
54
|
*/
|
|
60
55
|
export function isValidEventType(type: string): type is CrewHookEventType {
|
|
61
56
|
return (
|
|
62
|
-
type ===
|
|
63
|
-
type === 'task_completed' ||
|
|
64
|
-
type === 'task_failed' ||
|
|
65
|
-
type === 'run_completed' ||
|
|
66
|
-
type === 'run_failed'
|
|
57
|
+
type === "task_started" || type === "task_completed" || type === "task_failed" || type === "run_completed" || type === "run_failed"
|
|
67
58
|
);
|
|
68
59
|
}
|
|
69
60
|
|
|
@@ -71,15 +62,15 @@ export function isValidEventType(type: string): type is CrewHookEventType {
|
|
|
71
62
|
* Type guard to check if an object is a valid CrewHookEvent.
|
|
72
63
|
*/
|
|
73
64
|
export function isHookEvent(obj: unknown): obj is CrewHookEvent {
|
|
74
|
-
if (typeof obj !==
|
|
65
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
75
66
|
const event = obj as Record<string, unknown>;
|
|
76
67
|
return (
|
|
77
|
-
typeof event.type ===
|
|
68
|
+
typeof event.type === "string" &&
|
|
78
69
|
isValidEventType(event.type) &&
|
|
79
|
-
typeof event.timestamp ===
|
|
80
|
-
typeof event.runId ===
|
|
81
|
-
(event.taskId === undefined || typeof event.taskId ===
|
|
82
|
-
(event.data === undefined || typeof event.data ===
|
|
70
|
+
typeof event.timestamp === "string" &&
|
|
71
|
+
typeof event.runId === "string" &&
|
|
72
|
+
(event.taskId === undefined || typeof event.taskId === "string") &&
|
|
73
|
+
(event.data === undefined || typeof event.data === "object")
|
|
83
74
|
);
|
|
84
75
|
}
|
|
85
76
|
|
|
@@ -96,13 +87,7 @@ export class HookRegistry {
|
|
|
96
87
|
constructor() {
|
|
97
88
|
this.hooks = new Map();
|
|
98
89
|
// Initialize with empty Sets for all event types
|
|
99
|
-
const eventTypes: CrewHookEventType[] = [
|
|
100
|
-
'task_started',
|
|
101
|
-
'task_completed',
|
|
102
|
-
'task_failed',
|
|
103
|
-
'run_completed',
|
|
104
|
-
'run_failed',
|
|
105
|
-
];
|
|
90
|
+
const eventTypes: CrewHookEventType[] = ["task_started", "task_completed", "task_failed", "run_completed", "run_failed"];
|
|
106
91
|
for (const type of eventTypes) {
|
|
107
92
|
this.hooks.set(type, new Set());
|
|
108
93
|
}
|
|
@@ -5,9 +5,7 @@ export interface EventBus {
|
|
|
5
5
|
emit(event: string, data: unknown): void;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export type RpcReply<T = void> =
|
|
9
|
-
| { success: true; data?: T }
|
|
10
|
-
| { success: false; error: string };
|
|
8
|
+
export type RpcReply<T = void> = { success: true; data?: T } | { success: false; error: string };
|
|
11
9
|
|
|
12
10
|
export const PROTOCOL_VERSION = 1;
|
|
13
11
|
|
|
@@ -84,39 +82,43 @@ export function registerCrewRpcHandlers(deps: RpcDeps): RpcHandle {
|
|
|
84
82
|
if (params.token !== EXPECTED_TOKEN) {
|
|
85
83
|
console.warn(
|
|
86
84
|
`[pi-crew SECURITY] RPC invocation rejected: missing/invalid token (source=${params.source ?? "(none)"}). ` +
|
|
87
|
-
|
|
85
|
+
`Privileged RPC requires the in-process token. Request may be from an untrusted extension.`,
|
|
88
86
|
);
|
|
89
87
|
return false;
|
|
90
88
|
}
|
|
91
89
|
if (!params.source || params.source !== CREW_RPC_SOURCE) {
|
|
92
90
|
console.warn(
|
|
93
91
|
`[pi-crew SECURITY] RPC invocation from unexpected source: ${params.source ?? "(none)"}. ` +
|
|
94
|
-
|
|
92
|
+
`Expected '${CREW_RPC_SOURCE}'. Request may be from an untrusted extension.`,
|
|
95
93
|
);
|
|
96
94
|
return false;
|
|
97
95
|
}
|
|
98
96
|
return true;
|
|
99
97
|
}
|
|
100
98
|
|
|
101
|
-
const unsubSpawn = handleRpc<{
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
99
|
+
const unsubSpawn = handleRpc<{
|
|
100
|
+
requestId: string;
|
|
101
|
+
type: string;
|
|
102
|
+
prompt: string;
|
|
103
|
+
options?: Record<string, unknown>;
|
|
104
|
+
source?: string;
|
|
105
|
+
token?: string;
|
|
106
|
+
}>(events, "crew:rpc:spawn", (params) => {
|
|
107
|
+
if (!validateRpcSource(params)) throw new Error("Unauthorized: RPC spawn requires valid token and source='pi-crew'");
|
|
108
|
+
const ctx = getCtx();
|
|
109
|
+
if (!ctx) throw new Error("No active session");
|
|
110
|
+
return { id: spawn(params.type, params.prompt, params.options ?? {}) };
|
|
111
|
+
});
|
|
111
112
|
|
|
112
|
-
const unsubStop = handleRpc<{
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
113
|
+
const unsubStop = handleRpc<{
|
|
114
|
+
requestId: string;
|
|
115
|
+
agentId: string;
|
|
116
|
+
source?: string;
|
|
117
|
+
token?: string;
|
|
118
|
+
}>(events, "crew:rpc:stop", (params) => {
|
|
119
|
+
if (!validateRpcSource(params)) throw new Error("Unauthorized: RPC stop requires valid token and source='pi-crew'");
|
|
120
|
+
if (!abort(params.agentId)) throw new Error("Agent not found");
|
|
121
|
+
});
|
|
120
122
|
|
|
121
123
|
return { unsubPing, unsubSpawn, unsubStop };
|
|
122
124
|
}
|
|
@@ -13,15 +13,19 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { defineTool, type ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
16
|
+
import { type Static, Type } from "@sinclair/typebox";
|
|
17
|
+
import { broadcastIrcMessage, listLiveAgents, respondAsBackground, sendIrcMessage } from "../live-agent-manager.ts";
|
|
18
18
|
import type { IrcMessage } from "../live-irc.ts";
|
|
19
19
|
|
|
20
20
|
const IrcParams = Type.Object({
|
|
21
21
|
op: Type.Union(
|
|
22
22
|
[
|
|
23
|
-
Type.Literal("send", {
|
|
24
|
-
|
|
23
|
+
Type.Literal("send", {
|
|
24
|
+
description: "Send a message to one peer or to all peers.",
|
|
25
|
+
}),
|
|
26
|
+
Type.Literal("list", {
|
|
27
|
+
description: "List currently visible peers.",
|
|
28
|
+
}),
|
|
25
29
|
],
|
|
26
30
|
{ description: "IRC operation." },
|
|
27
31
|
),
|
|
@@ -37,7 +41,8 @@ const IrcParams = Type.Object({
|
|
|
37
41
|
),
|
|
38
42
|
awaitReply: Type.Optional(
|
|
39
43
|
Type.Boolean({
|
|
40
|
-
description:
|
|
44
|
+
description:
|
|
45
|
+
"Wait for a prose reply (default: true for DM, false for broadcast). For DMs the recipient receives the message as a non-blocking background turn and its reply is returned to the caller. Broadcast always ignores this flag.",
|
|
41
46
|
}),
|
|
42
47
|
),
|
|
43
48
|
});
|
|
@@ -74,9 +79,7 @@ interface IrcDetails {
|
|
|
74
79
|
*
|
|
75
80
|
* @param selfId — This agent's ID (runId:taskId format)
|
|
76
81
|
*/
|
|
77
|
-
export function createIrcTool(
|
|
78
|
-
selfId: string,
|
|
79
|
-
): ToolDefinition<typeof IrcParams, IrcDetails> {
|
|
82
|
+
export function createIrcTool(selfId: string): ToolDefinition<typeof IrcParams, IrcDetails> {
|
|
80
83
|
return defineTool({
|
|
81
84
|
name: "irc",
|
|
82
85
|
label: "IRC",
|
|
@@ -95,7 +98,10 @@ export function createIrcTool(
|
|
|
95
98
|
_signal: AbortSignal | undefined,
|
|
96
99
|
_onUpdate: unknown,
|
|
97
100
|
_ctx: unknown,
|
|
98
|
-
): Promise<{
|
|
101
|
+
): Promise<{
|
|
102
|
+
content: Array<{ type: "text"; text: string }>;
|
|
103
|
+
details: IrcDetails;
|
|
104
|
+
}> {
|
|
99
105
|
if (params.op === "list") {
|
|
100
106
|
return executeList(selfId);
|
|
101
107
|
}
|
|
@@ -104,13 +110,20 @@ export function createIrcTool(
|
|
|
104
110
|
}
|
|
105
111
|
return {
|
|
106
112
|
content: [{ type: "text", text: "Unknown irc op." }],
|
|
107
|
-
details: {
|
|
113
|
+
details: {
|
|
114
|
+
op: params.op,
|
|
115
|
+
from: selfId,
|
|
116
|
+
error: "Unknown operation.",
|
|
117
|
+
},
|
|
108
118
|
};
|
|
109
119
|
},
|
|
110
120
|
});
|
|
111
121
|
}
|
|
112
122
|
|
|
113
|
-
function executeList(selfId: string): {
|
|
123
|
+
function executeList(selfId: string): {
|
|
124
|
+
content: Array<{ type: "text"; text: string }>;
|
|
125
|
+
details: IrcDetails;
|
|
126
|
+
} {
|
|
114
127
|
const agents = listLiveAgents();
|
|
115
128
|
const peers = agents
|
|
116
129
|
.filter((a) => a.agentId !== selfId && (a.status === "running" || a.status === "queued"))
|
|
@@ -135,7 +148,10 @@ function executeList(selfId: string): { content: Array<{ type: "text"; text: str
|
|
|
135
148
|
async function executeSend(
|
|
136
149
|
selfId: string,
|
|
137
150
|
params: IrcParams,
|
|
138
|
-
): Promise<{
|
|
151
|
+
): Promise<{
|
|
152
|
+
content: Array<{ type: "text"; text: string }>;
|
|
153
|
+
details: IrcDetails;
|
|
154
|
+
}> {
|
|
139
155
|
const to = params.to?.trim();
|
|
140
156
|
const message = params.message?.trim();
|
|
141
157
|
|
|
@@ -148,13 +164,23 @@ async function executeSend(
|
|
|
148
164
|
if (!message) {
|
|
149
165
|
return {
|
|
150
166
|
content: [{ type: "text", text: '`message` is required for op="send".' }],
|
|
151
|
-
details: {
|
|
167
|
+
details: {
|
|
168
|
+
op: "send",
|
|
169
|
+
from: selfId,
|
|
170
|
+
to,
|
|
171
|
+
error: "Missing 'message' field.",
|
|
172
|
+
},
|
|
152
173
|
};
|
|
153
174
|
}
|
|
154
175
|
if (to === selfId) {
|
|
155
176
|
return {
|
|
156
177
|
content: [{ type: "text", text: "Cannot send a message to yourself." }],
|
|
157
|
-
details: {
|
|
178
|
+
details: {
|
|
179
|
+
op: "send",
|
|
180
|
+
from: selfId,
|
|
181
|
+
to,
|
|
182
|
+
error: "Self-message not allowed.",
|
|
183
|
+
},
|
|
158
184
|
};
|
|
159
185
|
}
|
|
160
186
|
|
|
@@ -188,7 +214,9 @@ async function executeSend(
|
|
|
188
214
|
if (!target || (target.status !== "running" && target.status !== "queued")) {
|
|
189
215
|
notFound.push(to);
|
|
190
216
|
} else {
|
|
191
|
-
const result = await respondAsBackground(to, selfId, message, {
|
|
217
|
+
const result = await respondAsBackground(to, selfId, message, {
|
|
218
|
+
awaitReply: true,
|
|
219
|
+
});
|
|
192
220
|
if (result.ok) {
|
|
193
221
|
delivered.push(to);
|
|
194
222
|
if (result.replyContent) replies.push({ from: to, text: result.replyContent });
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { defineTool, type ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
13
|
-
import {
|
|
14
|
-
import type { YieldResult } from "../yield-handler.ts";
|
|
13
|
+
import { type Static, Type } from "@sinclair/typebox";
|
|
15
14
|
import { logInternalError } from "../../utils/internal-error.ts";
|
|
15
|
+
import type { YieldResult } from "../yield-handler.ts";
|
|
16
16
|
|
|
17
17
|
const SubmitResultParams = Type.Object({
|
|
18
18
|
summary: Type.String({ description: "Summary of completed work." }),
|
|
@@ -64,7 +64,10 @@ export function createSubmitResultTool(
|
|
|
64
64
|
_signal: AbortSignal | undefined,
|
|
65
65
|
_onUpdate: unknown,
|
|
66
66
|
_ctx: unknown,
|
|
67
|
-
): Promise<{
|
|
67
|
+
): Promise<{
|
|
68
|
+
content: Array<{ type: "text"; text: string }>;
|
|
69
|
+
details: SubmitResultDetails;
|
|
70
|
+
}> {
|
|
68
71
|
const result: YieldResult = {
|
|
69
72
|
summary: params.summary,
|
|
70
73
|
toolCallId,
|
|
@@ -72,8 +75,16 @@ export function createSubmitResultTool(
|
|
|
72
75
|
...(params.structuredData ? { structuredData: params.structuredData } : {}),
|
|
73
76
|
};
|
|
74
77
|
// Build response first so the model always gets confirmation
|
|
75
|
-
const response: {
|
|
76
|
-
content:
|
|
78
|
+
const response: {
|
|
79
|
+
content: Array<{ type: "text"; text: string }>;
|
|
80
|
+
details: SubmitResultDetails;
|
|
81
|
+
} = {
|
|
82
|
+
content: [
|
|
83
|
+
{
|
|
84
|
+
type: "text",
|
|
85
|
+
text: "Result submitted successfully. Thank you.",
|
|
86
|
+
},
|
|
87
|
+
],
|
|
77
88
|
details: {
|
|
78
89
|
summary: params.summary,
|
|
79
90
|
artifacts: params.artifacts,
|