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,8 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
-
import { readEvents, type TeamEvent } from "./event-log.ts";
|
|
3
|
-
import { atomicWriteFile } from "./atomic-write.ts";
|
|
4
2
|
import { logInternalError } from "../utils/internal-error.ts";
|
|
5
|
-
import {
|
|
3
|
+
import { atomicWriteFile } from "./atomic-write.ts";
|
|
4
|
+
import { readEvents, type TeamEvent, withEventLogLockSync } from "./event-log.ts";
|
|
6
5
|
|
|
7
6
|
export interface RotationConfig {
|
|
8
7
|
maxFileSizeBytes: number;
|
|
@@ -82,12 +81,25 @@ export function compactEventLog(eventsPath: string, config?: Partial<RotationCon
|
|
|
82
81
|
/** Round 24 (BUG 1): the lock-free pre-read for compaction. Safe to run
|
|
83
82
|
* outside the lock (read-only). Returns the compacted lines + stats needed
|
|
84
83
|
* for the write phase. */
|
|
85
|
-
export function prepareCompaction(
|
|
86
|
-
|
|
84
|
+
export function prepareCompaction(
|
|
85
|
+
eventsPath: string,
|
|
86
|
+
config?: Partial<RotationConfig>,
|
|
87
|
+
):
|
|
88
|
+
| {
|
|
89
|
+
lines: string;
|
|
90
|
+
originalSize: number;
|
|
91
|
+
originalCount: number;
|
|
92
|
+
kept: TeamEvent[];
|
|
93
|
+
}
|
|
94
|
+
| undefined {
|
|
87
95
|
if (!fs.existsSync(eventsPath)) return undefined;
|
|
88
96
|
const cfg = resolveConfig(config);
|
|
89
97
|
let originalSize: number;
|
|
90
|
-
try {
|
|
98
|
+
try {
|
|
99
|
+
originalSize = fs.statSync(eventsPath).size;
|
|
100
|
+
} catch {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
91
103
|
const allEvents = readEvents(eventsPath);
|
|
92
104
|
const originalCount = allEvents.length;
|
|
93
105
|
if (originalCount <= cfg.compactToCount) return undefined;
|
|
@@ -100,7 +112,12 @@ export function prepareCompaction(eventsPath: string, config?: Partial<RotationC
|
|
|
100
112
|
* caller ALREADY holds the event-log lock (or accepts the unlocked race). */
|
|
101
113
|
export function applyCompactionUnlocked(
|
|
102
114
|
eventsPath: string,
|
|
103
|
-
prepared: {
|
|
115
|
+
prepared: {
|
|
116
|
+
lines: string;
|
|
117
|
+
originalSize: number;
|
|
118
|
+
originalCount: number;
|
|
119
|
+
kept: TeamEvent[];
|
|
120
|
+
},
|
|
104
121
|
): CompactionResult | undefined {
|
|
105
122
|
const { lines, originalSize, originalCount, kept } = prepared;
|
|
106
123
|
try {
|
|
@@ -250,16 +267,24 @@ export function getEventLogStats(eventsPath: string): EventLogStats | undefined
|
|
|
250
267
|
let searchFrom = tailStr.length;
|
|
251
268
|
for (;;) {
|
|
252
269
|
const nl = tailStr.lastIndexOf("\n", searchFrom - 1);
|
|
253
|
-
if (nl < 0) {
|
|
270
|
+
if (nl < 0) {
|
|
271
|
+
lastLine = tailStr.trim();
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
254
274
|
const candidate = tailStr.slice(nl + 1, searchFrom).trim();
|
|
255
|
-
if (candidate) {
|
|
275
|
+
if (candidate) {
|
|
276
|
+
lastLine = candidate;
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
256
279
|
searchFrom = nl;
|
|
257
280
|
}
|
|
258
281
|
try {
|
|
259
282
|
if (lastLine) {
|
|
260
283
|
newestTimestamp = (JSON.parse(lastLine) as { time: string }).time;
|
|
261
284
|
}
|
|
262
|
-
} catch {
|
|
285
|
+
} catch {
|
|
286
|
+
/* corrupt tail */
|
|
287
|
+
}
|
|
263
288
|
}
|
|
264
289
|
|
|
265
290
|
// Stream-scan to count newlines and find first line boundary.
|
|
@@ -280,9 +305,9 @@ export function getEventLogStats(eventsPath: string): EventLogStats | undefined
|
|
|
280
305
|
}
|
|
281
306
|
offset += bytesRead;
|
|
282
307
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
308
|
+
} finally {
|
|
309
|
+
fs.closeSync(scanFd);
|
|
310
|
+
}
|
|
286
311
|
eventCount = newlineCount;
|
|
287
312
|
|
|
288
313
|
// Read first line for oldest timestamp.
|
|
@@ -300,7 +325,9 @@ export function getEventLogStats(eventsPath: string): EventLogStats | undefined
|
|
|
300
325
|
if (firstLine) {
|
|
301
326
|
oldestTimestamp = (JSON.parse(firstLine) as { time: string }).time;
|
|
302
327
|
}
|
|
303
|
-
} catch {
|
|
328
|
+
} catch {
|
|
329
|
+
/* corrupt head */
|
|
330
|
+
}
|
|
304
331
|
}
|
|
305
332
|
|
|
306
333
|
return {
|
|
@@ -313,4 +340,3 @@ export function getEventLogStats(eventsPath: string): EventLogStats | undefined
|
|
|
313
340
|
return undefined;
|
|
314
341
|
}
|
|
315
342
|
}
|
|
316
|
-
|
package/src/state/event-log.ts
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import { isWorkerAtomicWriterEnabled, appendFileViaWorker } from "./worker-atomic-writer.ts";
|
|
5
4
|
import { DEFAULT_EVENT_LOG } from "../config/defaults.ts";
|
|
6
|
-
import { atomicWriteFile } from "./atomic-write.ts";
|
|
7
5
|
import { errors } from "../errors.ts";
|
|
8
6
|
import { emitFromTeamEvent } from "../ui/run-event-bus.ts";
|
|
7
|
+
import { type IncrementalReadState, readJsonlSince } from "../utils/incremental-reader.ts";
|
|
9
8
|
import { logInternalError } from "../utils/internal-error.ts";
|
|
10
|
-
import { readJsonlSince, type IncrementalReadState } from "../utils/incremental-reader.ts";
|
|
11
9
|
import { redactSecrets } from "../utils/redaction.ts";
|
|
12
10
|
import { sleepSync } from "../utils/sleep.ts";
|
|
13
|
-
import {
|
|
11
|
+
import { atomicWriteFile } from "./atomic-write.ts";
|
|
12
|
+
import {
|
|
13
|
+
applyCompactionUnlocked,
|
|
14
|
+
compactEventLog,
|
|
15
|
+
needsRotation,
|
|
16
|
+
prepareCompaction,
|
|
17
|
+
rotateEventLog,
|
|
18
|
+
rotateEventLogUnlocked,
|
|
19
|
+
} from "./event-log-rotation.ts";
|
|
20
|
+
import { appendFileViaWorker, isWorkerAtomicWriterEnabled } from "./worker-atomic-writer.ts";
|
|
14
21
|
|
|
15
22
|
export type TeamEventProvenance = "live_worker" | "test" | "healthcheck" | "replay" | "api" | "background" | "team_runner";
|
|
16
23
|
export type TeamWatcherAction = "act" | "observe" | "ignore";
|
|
@@ -54,7 +61,9 @@ export interface TeamEvent {
|
|
|
54
61
|
metadata?: TeamEventMetadata;
|
|
55
62
|
}
|
|
56
63
|
|
|
57
|
-
export type AppendTeamEvent = Omit<TeamEvent, "time" | "metadata"> & {
|
|
64
|
+
export type AppendTeamEvent = Omit<TeamEvent, "time" | "metadata"> & {
|
|
65
|
+
metadata?: Partial<TeamEventMetadata>;
|
|
66
|
+
};
|
|
58
67
|
|
|
59
68
|
const TERMINAL_EVENT_TYPES = new Set<string>(DEFAULT_EVENT_LOG.terminalEventTypes);
|
|
60
69
|
const MAX_EVENTS_BYTES = 50 * 1024 * 1024;
|
|
@@ -99,7 +108,11 @@ export function withEventLogLockSync<T>(eventsPath: string, fn: () => T, options
|
|
|
99
108
|
// the stale lock in one operation, eliminating the race. The 5s timeout
|
|
100
109
|
// (reduced from 120s) is appropriate.
|
|
101
110
|
fs.mkdirSync(lockDir);
|
|
102
|
-
try {
|
|
111
|
+
try {
|
|
112
|
+
fs.writeFileSync(pidFile, String(process.pid), "utf-8");
|
|
113
|
+
} catch {
|
|
114
|
+
/* best-effort */
|
|
115
|
+
}
|
|
103
116
|
acquired = true;
|
|
104
117
|
break;
|
|
105
118
|
} catch {
|
|
@@ -122,7 +135,9 @@ export function withEventLogLockSync<T>(eventsPath: string, fn: () => T, options
|
|
|
122
135
|
fs.rmSync(lockDir, { recursive: true, force: true });
|
|
123
136
|
continue;
|
|
124
137
|
}
|
|
125
|
-
} catch {
|
|
138
|
+
} catch {
|
|
139
|
+
/* dir vanished — let loop retry */
|
|
140
|
+
}
|
|
126
141
|
// Round 26 (BUG 4): the mtime check was previously NESTED inside
|
|
127
142
|
// `if (!alive)`, so a recycled PID (crashed holder's PID reused by an
|
|
128
143
|
// unrelated live process) kept `alive=true` and the mtime check NEVER
|
|
@@ -135,11 +150,18 @@ export function withEventLogLockSync<T>(eventsPath: string, fn: () => T, options
|
|
|
135
150
|
const ownerPid = Number.parseInt(raw, 10);
|
|
136
151
|
if (!Number.isNaN(ownerPid) && ownerPid !== process.pid) {
|
|
137
152
|
let alive = false;
|
|
138
|
-
try {
|
|
153
|
+
try {
|
|
154
|
+
process.kill(ownerPid, 0);
|
|
155
|
+
alive = true;
|
|
156
|
+
} catch {
|
|
157
|
+
/* dead */
|
|
158
|
+
}
|
|
139
159
|
// (mtime already handled above; nothing to do here for dead-but-fresh.)
|
|
140
160
|
void alive;
|
|
141
161
|
}
|
|
142
|
-
} catch {
|
|
162
|
+
} catch {
|
|
163
|
+
/* no pid file — mtime check above already handles it */
|
|
164
|
+
}
|
|
143
165
|
sleepSync(10);
|
|
144
166
|
}
|
|
145
167
|
}
|
|
@@ -159,7 +181,9 @@ export function withEventLogLockSync<T>(eventsPath: string, fn: () => T, options
|
|
|
159
181
|
if (currentPid === String(process.pid)) {
|
|
160
182
|
fs.rmSync(lockDir, { recursive: true, force: true });
|
|
161
183
|
}
|
|
162
|
-
} catch {
|
|
184
|
+
} catch {
|
|
185
|
+
/* lock stolen or already gone — do not touch */
|
|
186
|
+
}
|
|
163
187
|
}
|
|
164
188
|
}
|
|
165
189
|
}
|
|
@@ -183,7 +207,12 @@ export function __test__sequenceCacheSize(): number {
|
|
|
183
207
|
|
|
184
208
|
/** @internal — seed an entry into the sequence cache for testing. */
|
|
185
209
|
export function __test__seedSequenceCache(eventsPath: string, lastAccessMs: number): void {
|
|
186
|
-
sequenceCache.set(eventsPath, {
|
|
210
|
+
sequenceCache.set(eventsPath, {
|
|
211
|
+
size: 1,
|
|
212
|
+
mtimeMs: 0,
|
|
213
|
+
seq: 0,
|
|
214
|
+
lastAccessMs,
|
|
215
|
+
});
|
|
187
216
|
}
|
|
188
217
|
|
|
189
218
|
/** @internal — expose eviction for testing. */
|
|
@@ -248,11 +277,21 @@ function nextSequence(eventsPath: string): number {
|
|
|
248
277
|
const stored = readStoredSequence(eventsPath);
|
|
249
278
|
const fileShrunk = cached && stat.size < cached.size;
|
|
250
279
|
if (stored !== undefined && !fileShrunk) {
|
|
251
|
-
sequenceCache.set(eventsPath, {
|
|
280
|
+
sequenceCache.set(eventsPath, {
|
|
281
|
+
size: stat.size,
|
|
282
|
+
mtimeMs: stat.mtimeMs,
|
|
283
|
+
seq: stored,
|
|
284
|
+
lastAccessMs: Date.now(),
|
|
285
|
+
});
|
|
252
286
|
return stored + 1;
|
|
253
287
|
}
|
|
254
288
|
const current = scanSequence(eventsPath);
|
|
255
|
-
sequenceCache.set(eventsPath, {
|
|
289
|
+
sequenceCache.set(eventsPath, {
|
|
290
|
+
size: stat.size,
|
|
291
|
+
mtimeMs: stat.mtimeMs,
|
|
292
|
+
seq: current,
|
|
293
|
+
lastAccessMs: Date.now(),
|
|
294
|
+
});
|
|
256
295
|
persistSequence(eventsPath, current);
|
|
257
296
|
return current + 1;
|
|
258
297
|
}
|
|
@@ -266,7 +305,17 @@ function persistSequence(eventsPath: string, seq: number): void {
|
|
|
266
305
|
}
|
|
267
306
|
|
|
268
307
|
export function computeEventFingerprint(event: Pick<TeamEvent, "type" | "runId" | "taskId" | "data">): string {
|
|
269
|
-
return createHash("sha256")
|
|
308
|
+
return createHash("sha256")
|
|
309
|
+
.update(
|
|
310
|
+
JSON.stringify({
|
|
311
|
+
type: event.type,
|
|
312
|
+
runId: event.runId,
|
|
313
|
+
taskId: event.taskId,
|
|
314
|
+
data: event.data ?? null,
|
|
315
|
+
}),
|
|
316
|
+
)
|
|
317
|
+
.digest("hex")
|
|
318
|
+
.slice(0, 16);
|
|
270
319
|
}
|
|
271
320
|
|
|
272
321
|
/**
|
|
@@ -399,7 +448,10 @@ export async function appendEventAsync(eventsPath: string, event: AppendTeamEven
|
|
|
399
448
|
metadata,
|
|
400
449
|
};
|
|
401
450
|
if (baseMetadata?.fingerprint || TERMINAL_EVENT_TYPES.has(fullEvent.type)) {
|
|
402
|
-
metadata = {
|
|
451
|
+
metadata = {
|
|
452
|
+
...metadata,
|
|
453
|
+
fingerprint: baseMetadata?.fingerprint ?? computeEventFingerprint(fullEvent),
|
|
454
|
+
};
|
|
403
455
|
fullEvent.metadata = metadata;
|
|
404
456
|
}
|
|
405
457
|
|
|
@@ -409,7 +461,9 @@ export async function appendEventAsync(eventsPath: string, event: AppendTeamEven
|
|
|
409
461
|
let fileStat: fs.Stats | undefined;
|
|
410
462
|
try {
|
|
411
463
|
fileStat = await fs.promises.stat(eventsPath).catch(() => undefined);
|
|
412
|
-
} catch {
|
|
464
|
+
} catch {
|
|
465
|
+
/* file does not exist */
|
|
466
|
+
}
|
|
413
467
|
if (!isTerminal && fileStat) {
|
|
414
468
|
const stat = fileStat;
|
|
415
469
|
if (stat.size > MAX_EVENTS_BYTES) {
|
|
@@ -421,7 +475,9 @@ export async function appendEventAsync(eventsPath: string, event: AppendTeamEven
|
|
|
421
475
|
let afterCompactStat: fs.Stats | undefined;
|
|
422
476
|
try {
|
|
423
477
|
afterCompactStat = await fs.promises.stat(eventsPath).catch(() => undefined);
|
|
424
|
-
} catch {
|
|
478
|
+
} catch {
|
|
479
|
+
/* file does not exist */
|
|
480
|
+
}
|
|
425
481
|
if (afterCompactStat) {
|
|
426
482
|
if (afterCompactStat.size > MAX_EVENTS_BYTES) {
|
|
427
483
|
rotateEventLog(eventsPath);
|
|
@@ -432,10 +488,16 @@ export async function appendEventAsync(eventsPath: string, event: AppendTeamEven
|
|
|
432
488
|
let sizeCheckStat: fs.Stats | undefined;
|
|
433
489
|
try {
|
|
434
490
|
sizeCheckStat = await fs.promises.stat(eventsPath).catch(() => undefined);
|
|
435
|
-
} catch {
|
|
491
|
+
} catch {
|
|
492
|
+
/* file does not exist */
|
|
493
|
+
}
|
|
436
494
|
try {
|
|
437
495
|
if (sizeCheckStat && sizeCheckStat.size > MAX_EVENTS_BYTES) {
|
|
438
|
-
logInternalError(
|
|
496
|
+
logInternalError(
|
|
497
|
+
"event-log.size-limit",
|
|
498
|
+
new Error(`events file ${eventsPath} exceeds ${MAX_EVENTS_BYTES} bytes after compaction`),
|
|
499
|
+
`eventsPath=${eventsPath}`,
|
|
500
|
+
);
|
|
439
501
|
skippedDueToSize = true;
|
|
440
502
|
}
|
|
441
503
|
} catch (error) {
|
|
@@ -448,7 +510,10 @@ export async function appendEventAsync(eventsPath: string, event: AppendTeamEven
|
|
|
448
510
|
if (isWorkerAtomicWriterEnabled()) {
|
|
449
511
|
await appendFileViaWorker(eventsPath, line);
|
|
450
512
|
} else {
|
|
451
|
-
await fs.promises.appendFile(eventsPath, line, {
|
|
513
|
+
await fs.promises.appendFile(eventsPath, line, {
|
|
514
|
+
encoding: "utf-8",
|
|
515
|
+
flag: "a",
|
|
516
|
+
});
|
|
452
517
|
}
|
|
453
518
|
// FIX: fsync to ensure event content is flushed to disk before persisting
|
|
454
519
|
// the sequence number. This closes the crash window between appendFile and
|
|
@@ -466,9 +531,17 @@ export async function appendEventAsync(eventsPath: string, event: AppendTeamEven
|
|
|
466
531
|
persistSequence(eventsPath, seq);
|
|
467
532
|
}
|
|
468
533
|
if (appendCounter % 100 === 0 && needsRotation(eventsPath)) {
|
|
469
|
-
try {
|
|
534
|
+
try {
|
|
535
|
+
compactEventLog(eventsPath);
|
|
536
|
+
} catch (error) {
|
|
537
|
+
logInternalError("event-log.rotation", error, `eventsPath=${eventsPath}`);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
try {
|
|
541
|
+
emitFromTeamEvent(fullEvent);
|
|
542
|
+
} catch (error) {
|
|
543
|
+
logInternalError("event-log.emit", error);
|
|
470
544
|
}
|
|
471
|
-
try { emitFromTeamEvent(fullEvent); } catch (error) { logInternalError("event-log.emit", error); }
|
|
472
545
|
|
|
473
546
|
// FIX: Sequence was persisted AFTER appendFile in the append block above.
|
|
474
547
|
// Only update the cache here (the sidecar persist is already done).
|
|
@@ -477,12 +550,19 @@ export async function appendEventAsync(eventsPath: string, event: AppendTeamEven
|
|
|
477
550
|
let statResult: fs.Stats | undefined;
|
|
478
551
|
try {
|
|
479
552
|
statResult = await fs.promises.stat(eventsPath).catch(() => undefined);
|
|
480
|
-
} catch {
|
|
553
|
+
} catch {
|
|
554
|
+
/* file may not exist */
|
|
555
|
+
}
|
|
481
556
|
if (statResult) {
|
|
482
557
|
if (sequenceCache.size >= MAX_SEQUENCE_CACHE_ENTRIES) {
|
|
483
558
|
evictOldestSequenceCacheEntries();
|
|
484
559
|
}
|
|
485
|
-
sequenceCache.set(eventsPath, {
|
|
560
|
+
sequenceCache.set(eventsPath, {
|
|
561
|
+
size: statResult.size,
|
|
562
|
+
mtimeMs: statResult.mtimeMs,
|
|
563
|
+
seq: finalSeq,
|
|
564
|
+
lastAccessMs: Date.now(),
|
|
565
|
+
});
|
|
486
566
|
}
|
|
487
567
|
// Note: persistSequence is NOT called here again - it was already called
|
|
488
568
|
// after the append to ensure the sidecar is current after the event is written.
|
|
@@ -491,22 +571,27 @@ export async function appendEventAsync(eventsPath: string, event: AppendTeamEven
|
|
|
491
571
|
}
|
|
492
572
|
return fullEvent;
|
|
493
573
|
});
|
|
494
|
-
asyncQueues.set(
|
|
495
|
-
|
|
496
|
-
(
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
//
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
574
|
+
asyncQueues.set(
|
|
575
|
+
queueKey,
|
|
576
|
+
next.then(
|
|
577
|
+
() => {
|
|
578
|
+
asyncQueues.delete(queueKey);
|
|
579
|
+
},
|
|
580
|
+
(error) => {
|
|
581
|
+
// FIX: Wrap error handler in try-catch to ensure asyncQueues.delete
|
|
582
|
+
// always runs, even if logging itself throws.
|
|
583
|
+
try {
|
|
584
|
+
logInternalError("event-log.async-queue", error, eventsPath);
|
|
585
|
+
} catch {
|
|
586
|
+
// logging failed — ensure queue is still cleaned up
|
|
587
|
+
}
|
|
588
|
+
// FIX: Reset queue to a resolved state instead of deleting it.
|
|
589
|
+
// This prevents cascading failures where a single transient error
|
|
590
|
+
// (e.g., ENOSPC) causes all subsequent events on the same path to fail.
|
|
591
|
+
asyncQueues.set(queueKey, Promise.resolve());
|
|
592
|
+
},
|
|
593
|
+
),
|
|
594
|
+
);
|
|
510
595
|
return next;
|
|
511
596
|
}
|
|
512
597
|
|
|
@@ -537,7 +622,10 @@ function appendEventInsideLock(eventsPath: string, event: AppendTeamEvent): Team
|
|
|
537
622
|
metadata,
|
|
538
623
|
};
|
|
539
624
|
if (baseMetadata?.fingerprint || TERMINAL_EVENT_TYPES.has(fullEvent.type)) {
|
|
540
|
-
metadata = {
|
|
625
|
+
metadata = {
|
|
626
|
+
...metadata,
|
|
627
|
+
fingerprint: baseMetadata?.fingerprint ?? computeEventFingerprint(fullEvent),
|
|
628
|
+
};
|
|
541
629
|
fullEvent.metadata = metadata;
|
|
542
630
|
}
|
|
543
631
|
// H1 fix: handle overflow before appending.
|
|
@@ -573,7 +661,11 @@ function appendEventInsideLock(eventsPath: string, event: AppendTeamEvent): Team
|
|
|
573
661
|
if (fs.existsSync(eventsPath) && fs.statSync(eventsPath).size > MAX_EVENTS_BYTES) {
|
|
574
662
|
// Only reach here for non-terminal events that still overflow after compact+rotate.
|
|
575
663
|
// Log and mark as not appended.
|
|
576
|
-
logInternalError(
|
|
664
|
+
logInternalError(
|
|
665
|
+
"event-log.size-limit",
|
|
666
|
+
new Error(`events file ${eventsPath} exceeds ${MAX_EVENTS_BYTES} bytes after compaction`),
|
|
667
|
+
`eventsPath=${eventsPath}`,
|
|
668
|
+
);
|
|
577
669
|
skippedDueToSize = true;
|
|
578
670
|
}
|
|
579
671
|
} catch (error) {
|
|
@@ -605,7 +697,12 @@ function appendEventInsideLock(eventsPath: string, event: AppendTeamEvent): Team
|
|
|
605
697
|
if (sequenceCache.size >= MAX_SEQUENCE_CACHE_ENTRIES) {
|
|
606
698
|
evictOldestSequenceCacheEntries();
|
|
607
699
|
}
|
|
608
|
-
sequenceCache.set(eventsPath, {
|
|
700
|
+
sequenceCache.set(eventsPath, {
|
|
701
|
+
size: stat.size,
|
|
702
|
+
mtimeMs: stat.mtimeMs,
|
|
703
|
+
seq,
|
|
704
|
+
lastAccessMs: Date.now(),
|
|
705
|
+
});
|
|
609
706
|
} catch (error) {
|
|
610
707
|
logInternalError("event-log.persist-sequence", error, `eventsPath=${eventsPath}`);
|
|
611
708
|
}
|
|
@@ -620,9 +717,15 @@ function appendEventInsideLock(eventsPath: string, event: AppendTeamEvent): Team
|
|
|
620
717
|
try {
|
|
621
718
|
const prepared = prepareCompaction(eventsPath);
|
|
622
719
|
if (prepared) applyCompactionUnlocked(eventsPath, prepared);
|
|
623
|
-
} catch (error) {
|
|
720
|
+
} catch (error) {
|
|
721
|
+
logInternalError("event-log.rotation", error, `eventsPath=${eventsPath}`);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
try {
|
|
725
|
+
emitFromTeamEvent(fullEvent);
|
|
726
|
+
} catch (error) {
|
|
727
|
+
logInternalError("event-log.emit", error);
|
|
624
728
|
}
|
|
625
|
-
try { emitFromTeamEvent(fullEvent); } catch (error) { logInternalError("event-log.emit", error); }
|
|
626
729
|
return fullEvent;
|
|
627
730
|
}
|
|
628
731
|
|
|
@@ -692,13 +795,17 @@ async function flushOneEventLogBuffer(eventsPath: string): Promise<void> {
|
|
|
692
795
|
const lastDroppedMeta = dropped[dropped.length - 1]?.event.metadata;
|
|
693
796
|
logInternalError(
|
|
694
797
|
"event-log.buffer-overflow",
|
|
695
|
-
new Error(
|
|
798
|
+
new Error(
|
|
799
|
+
`Buffer overflow #${overflowCounter}: Dropped ${dropped.length} events: first seq=${firstDroppedMeta?.seq} type=${dropped[0]?.event.type}, last seq=${lastDroppedMeta?.seq} type=${dropped[dropped.length - 1]?.event.type}`,
|
|
800
|
+
),
|
|
696
801
|
`${eventsPath}: ${queue.length + dropped.length} entries > 1000 cap`,
|
|
697
802
|
);
|
|
698
803
|
for (const item of dropped) {
|
|
699
|
-
item.reject(
|
|
700
|
-
|
|
701
|
-
|
|
804
|
+
item.reject(
|
|
805
|
+
new Error(
|
|
806
|
+
`Event log buffer overflow: ${queue.length + dropped.length} entries > 1000 cap; oldest ${dropped.length} dropped to keep memory bounded; first dropped seq=${firstDroppedMeta?.seq} type=${dropped[0]?.event.type}`,
|
|
807
|
+
),
|
|
808
|
+
);
|
|
702
809
|
}
|
|
703
810
|
}
|
|
704
811
|
|
|
@@ -757,7 +864,11 @@ process.on("SIGINT", () => setImmediate(() => flushEventLogBuffer()));
|
|
|
757
864
|
// abandoned on crash; clearing the map prevents memory leaks and stale state.
|
|
758
865
|
// Note: SIGKILL (kill -9) cannot be intercepted and is not handled.
|
|
759
866
|
process.on("uncaughtException", (error) => {
|
|
760
|
-
try {
|
|
867
|
+
try {
|
|
868
|
+
flushEventLogBuffer();
|
|
869
|
+
} catch {
|
|
870
|
+
/* best-effort */
|
|
871
|
+
}
|
|
761
872
|
// FIX (Issue 1): Drain asyncQueues before clearing to minimize event loss.
|
|
762
873
|
drainAsyncQueues();
|
|
763
874
|
asyncQueues.clear();
|
|
@@ -767,13 +878,17 @@ process.on("uncaughtException", (error) => {
|
|
|
767
878
|
|
|
768
879
|
export function readEvents(eventsPath: string): TeamEvent[] {
|
|
769
880
|
if (!fs.existsSync(eventsPath)) return [];
|
|
770
|
-
return fs
|
|
881
|
+
return fs
|
|
882
|
+
.readFileSync(eventsPath, "utf-8")
|
|
771
883
|
.split("\n")
|
|
772
884
|
.map((line) => line.trim())
|
|
773
885
|
.filter(Boolean)
|
|
774
886
|
.flatMap((line) => {
|
|
775
|
-
try {
|
|
776
|
-
|
|
887
|
+
try {
|
|
888
|
+
return [JSON.parse(line) as TeamEvent];
|
|
889
|
+
} catch {
|
|
890
|
+
return [];
|
|
891
|
+
}
|
|
777
892
|
});
|
|
778
893
|
}
|
|
779
894
|
|
|
@@ -11,7 +11,17 @@ import type { TeamEvent } from "./event-log.ts";
|
|
|
11
11
|
import { readEvents } from "./event-log.ts";
|
|
12
12
|
|
|
13
13
|
/** Task status values that can be reconstructed from lifecycle events. */
|
|
14
|
-
const RECONSTRUCTABLE_STATUSES = new Set([
|
|
14
|
+
const RECONSTRUCTABLE_STATUSES = new Set([
|
|
15
|
+
"created",
|
|
16
|
+
"queued",
|
|
17
|
+
"running",
|
|
18
|
+
"completed",
|
|
19
|
+
"failed",
|
|
20
|
+
"cancelled",
|
|
21
|
+
"skipped",
|
|
22
|
+
"waiting",
|
|
23
|
+
"needs_attention",
|
|
24
|
+
]);
|
|
15
25
|
|
|
16
26
|
/** Event types that carry task lifecycle state transitions. */
|
|
17
27
|
const TASK_LIFECYCLE_EVENT_TYPES = new Set([
|
|
@@ -25,18 +25,12 @@ const CREW_GITIGNORE_ENTRIES = [
|
|
|
25
25
|
*/
|
|
26
26
|
export async function updateGitignore(gitignorePath: string): Promise<void> {
|
|
27
27
|
if (!fs.existsSync(gitignorePath)) {
|
|
28
|
-
fs.writeFileSync(
|
|
29
|
-
gitignorePath,
|
|
30
|
-
CREW_GITIGNORE_ENTRIES.join("\n") + "\n",
|
|
31
|
-
"utf-8",
|
|
32
|
-
);
|
|
28
|
+
fs.writeFileSync(gitignorePath, CREW_GITIGNORE_ENTRIES.join("\n") + "\n", "utf-8");
|
|
33
29
|
return;
|
|
34
30
|
}
|
|
35
31
|
|
|
36
32
|
const current = fs.readFileSync(gitignorePath, "utf-8");
|
|
37
|
-
const existingLines = new Set(
|
|
38
|
-
current.split("\n").map((line) => line.trim()),
|
|
39
|
-
);
|
|
33
|
+
const existingLines = new Set(current.split("\n").map((line) => line.trim()));
|
|
40
34
|
|
|
41
35
|
let appended = "";
|
|
42
36
|
for (const entry of CREW_GITIGNORE_ENTRIES) {
|