pi-crew 0.9.16 → 0.9.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +81 -0
- package/README.md +19 -0
- package/dist/build-meta.json +21790 -0
- package/dist/index.mjs +83070 -0
- package/dist/index.mjs.map +7 -0
- package/docs/A +358 -0
- package/docs/M-A +357 -0
- package/docs/REVIEW-FINDINGS-2026-06 +357 -0
- package/docs/Y +357 -0
- package/docs/a +357 -0
- package/docs/aA +358 -0
- package/docs/archive/README.md +91 -0
- package/docs/patterns/command-agent-skill.md +1 -1
- package/docs/skills/REFERENCE.md +0 -17
- package/docs//303/242mmaAAA/303/242 +357 -0
- package/index.bundle.ts +25 -0
- package/index.ts +95 -4
- package/package.json +15 -4
- package/skills/iterative-audit/SKILL.md +0 -1
- package/skills/widget-rendering/SKILL.md +17 -0
- package/src/adapters/claude-adapter.ts +1 -3
- package/src/adapters/export-util.ts +16 -16
- package/src/adapters/index.ts +5 -5
- package/src/agents/agent-config.ts +20 -14
- package/src/agents/agent-serializer.ts +1 -1
- package/src/agents/discover-agents.ts +83 -39
- package/src/benchmark/benchmark-runner.ts +51 -51
- package/src/benchmark/feedback-loop.ts +1 -1
- package/src/config/config.ts +166 -590
- package/src/config/defaults.ts +28 -2
- package/src/config/drift-detector.ts +11 -14
- package/src/config/markers.ts +201 -203
- package/src/config/resilient-parser.ts +14 -6
- package/src/config/role-tools.ts +3 -3
- package/src/config/suggestions.ts +5 -12
- package/src/config/types.ts +9 -25
- package/src/errors.ts +151 -157
- package/src/extension/action-suggestions.ts +54 -9
- package/src/extension/async-notifier.ts +43 -13
- package/src/extension/autonomous-policy.ts +77 -37
- package/src/extension/command-completions.ts +9 -8
- package/src/extension/context-status-injection.ts +14 -12
- package/src/extension/crew-autocomplete.ts +8 -16
- package/src/extension/crew-cleanup.ts +2 -1
- package/src/extension/crew-shortcuts.ts +9 -6
- package/src/extension/cross-extension-rpc.ts +136 -42
- package/src/extension/help.ts +1 -1
- package/src/extension/import-index.ts +10 -5
- package/src/extension/knowledge-injection.ts +88 -15
- package/src/extension/management.ts +89 -349
- package/src/extension/message-renderers.ts +3 -4
- package/src/extension/notification-router.ts +22 -5
- package/src/extension/notification-sink.ts +6 -3
- package/src/extension/pi-api.ts +17 -8
- package/src/extension/plan-orchestrate.ts +8 -27
- package/src/extension/project-init.ts +21 -6
- package/src/extension/register.ts +329 -975
- package/src/extension/registration/artifact-cleanup.ts +9 -4
- package/src/extension/registration/command-utils.ts +5 -1
- package/src/extension/registration/commands.ts +806 -314
- package/src/extension/registration/compaction-guard.ts +15 -15
- package/src/extension/registration/lifecycle.ts +259 -0
- package/src/extension/registration/observability.ts +293 -0
- package/src/extension/registration/subagent-helpers.ts +29 -7
- package/src/extension/registration/subagent-tools.ts +253 -44
- package/src/extension/registration/team-tool.ts +19 -84
- package/src/extension/registration/ui.ts +168 -0
- package/src/extension/registration/viewers.ts +64 -24
- package/src/extension/result-watcher.ts +18 -7
- package/src/extension/run-bundle-schema.ts +21 -6
- package/src/extension/run-export.ts +15 -6
- package/src/extension/run-import.ts +64 -29
- package/src/extension/run-index.ts +28 -9
- package/src/extension/run-maintenance.ts +32 -12
- package/src/extension/session-summary.ts +1 -1
- package/src/extension/team-manager-command.ts +64 -7
- package/src/extension/team-onboard.ts +151 -150
- package/src/extension/team-recommendation.ts +121 -21
- package/src/extension/team-tool/anchor.ts +31 -64
- package/src/extension/team-tool/api.ts +929 -141
- package/src/extension/team-tool/auto-summarize.ts +14 -26
- package/src/extension/team-tool/cache-control.ts +1 -5
- package/src/extension/team-tool/cancel.ts +138 -38
- package/src/extension/team-tool/chain-dispatch.ts +5 -13
- package/src/extension/team-tool/chain-executor.ts +29 -51
- package/src/extension/team-tool/config-patch.ts +1 -1
- package/src/extension/team-tool/context.ts +40 -20
- package/src/extension/team-tool/destructive-gate.ts +10 -5
- package/src/extension/team-tool/doctor.ts +170 -51
- package/src/extension/team-tool/explain.ts +228 -214
- package/src/extension/team-tool/failure-patterns.ts +3 -9
- package/src/extension/team-tool/goal-wrap.ts +71 -29
- package/src/extension/team-tool/goal.ts +185 -35
- package/src/extension/team-tool/handle-schedule.ts +34 -15
- package/src/extension/team-tool/handle-settings.ts +94 -56
- package/src/extension/team-tool/health-monitor.ts +27 -108
- package/src/extension/team-tool/inspect.ts +42 -9
- package/src/extension/team-tool/intent-policy.ts +4 -1
- package/src/extension/team-tool/lifecycle-actions.ts +244 -48
- package/src/extension/team-tool/orchestrate.ts +7 -18
- package/src/extension/team-tool/parallel-dispatch.ts +51 -29
- package/src/extension/team-tool/plan.ts +29 -5
- package/src/extension/team-tool/respond.ts +49 -16
- package/src/extension/team-tool/run-not-found.ts +3 -8
- package/src/extension/team-tool/run.ts +80 -317
- package/src/extension/team-tool/status.ts +135 -43
- package/src/extension/team-tool/workflow-manage.ts +92 -25
- package/src/extension/team-tool-types.ts +8 -1
- package/src/extension/team-tool.ts +142 -551
- package/src/extension/validate-resources.ts +39 -8
- package/src/hooks/registry.ts +66 -17
- package/src/hooks/types.ts +1 -1
- package/src/i18n.ts +25 -11
- package/src/observability/correlation.ts +17 -3
- package/src/observability/event-bus.ts +51 -56
- package/src/observability/event-to-metric.ts +117 -15
- package/src/observability/exporters/otlp-exporter.ts +56 -17
- package/src/observability/exporters/prometheus-exporter.ts +1 -1
- package/src/observability/metric-registry.ts +16 -4
- package/src/observability/metric-retention.ts +8 -2
- package/src/observability/metric-sink.ts +11 -3
- package/src/observability/metrics-primitives.ts +42 -9
- package/src/plugins/plugin-define.ts +2 -2
- package/src/plugins/plugin-registry.ts +25 -25
- package/src/plugins/plugins/index.ts +1 -1
- package/src/plugins/plugins/nextjs.ts +16 -16
- package/src/plugins/plugins/vite.ts +7 -11
- package/src/plugins/plugins/vitest.ts +6 -11
- package/src/runtime/adaptive-plan.ts +225 -40
- package/src/runtime/agent-control.ts +58 -15
- package/src/runtime/agent-memory.ts +15 -9
- package/src/runtime/agent-observability.ts +10 -2
- package/src/runtime/anchor-manager.ts +27 -25
- package/src/runtime/async-marker.ts +7 -1
- package/src/runtime/async-runner.ts +27 -23
- package/src/runtime/auto-summarize.ts +7 -13
- package/src/runtime/background-runner.ts +189 -251
- package/src/runtime/batch-barrier.ts +18 -16
- package/src/runtime/cancellation-token.ts +16 -6
- package/src/runtime/cancellation.ts +46 -8
- package/src/runtime/capability-inventory.ts +6 -4
- package/src/runtime/chain-parser.ts +44 -11
- package/src/runtime/chain-runner.ts +63 -69
- package/src/runtime/checkpoint.ts +12 -56
- package/src/runtime/child-pi.ts +343 -78
- package/src/runtime/coalesce-tasks.ts +268 -0
- package/src/runtime/code-summary.ts +71 -26
- package/src/runtime/compact-stages/index.ts +15 -5
- package/src/runtime/compact-stages/tail-capture-stage.ts +7 -2
- package/src/runtime/compact-stages/truncation-stage.ts +4 -1
- package/src/runtime/compaction-summary.ts +17 -10
- package/src/runtime/completion-guard.ts +33 -16
- package/src/runtime/crash-classification.ts +28 -7
- package/src/runtime/crash-recovery.ts +177 -37
- package/src/runtime/crew-agent-records.ts +145 -47
- package/src/runtime/crew-hooks.ts +9 -24
- package/src/runtime/cross-extension-rpc.ts +25 -23
- package/src/runtime/custom-tools/irc-tool.ts +43 -15
- package/src/runtime/custom-tools/submit-result-tool.ts +16 -5
- package/src/runtime/delivery-coordinator.ts +34 -7
- package/src/runtime/delta-conflict.ts +9 -21
- package/src/runtime/deterministic-ast.ts +1 -1
- package/src/runtime/diagnostic-export.ts +53 -20
- package/src/runtime/direct-run.ts +12 -2
- package/src/runtime/dwf-state-store.ts +1 -1
- package/src/runtime/dynamic-workflow-context.ts +186 -58
- package/src/runtime/dynamic-workflow-runner.ts +42 -20
- package/src/runtime/effectiveness.ts +16 -8
- package/src/runtime/errors/crew-errors.ts +7 -11
- package/src/runtime/event-stream-bridge.ts +9 -3
- package/src/runtime/foreground-control.ts +54 -14
- package/src/runtime/foreground-watchdog.ts +9 -6
- package/src/runtime/goal-achievement.ts +27 -10
- package/src/runtime/goal-evaluator.ts +54 -19
- package/src/runtime/goal-loop-runner.ts +280 -99
- package/src/runtime/goal-state-store.ts +27 -17
- package/src/runtime/green-contract.ts +11 -2
- package/src/runtime/group-join.ts +64 -31
- package/src/runtime/handoff-manager.ts +37 -42
- package/src/runtime/heartbeat-gradient.ts +10 -2
- package/src/runtime/heartbeat-watcher.ts +32 -6
- package/src/runtime/hidden-handoff.ts +12 -30
- package/src/runtime/important-line-classifier.ts +12 -2
- package/src/runtime/iteration-hooks.ts +25 -15
- package/src/runtime/live-agent-control.ts +55 -7
- package/src/runtime/live-agent-manager.ts +130 -27
- package/src/runtime/live-control-realtime.ts +23 -3
- package/src/runtime/live-irc.ts +4 -1
- package/src/runtime/live-session-health.ts +12 -4
- package/src/runtime/live-session-runtime.ts +379 -117
- package/src/runtime/manifest-cache.ts +28 -12
- package/src/runtime/mcp-proxy.ts +8 -16
- package/src/runtime/metric-parser.ts +1 -5
- package/src/runtime/model-fallback.ts +55 -18
- package/src/runtime/model-resolver.ts +2 -6
- package/src/runtime/model-scope.ts +19 -3
- package/src/runtime/notebook-helpers.ts +60 -62
- package/src/runtime/orphan-worker-registry.ts +37 -26
- package/src/runtime/output-validator.ts +18 -3
- package/src/runtime/overflow-recovery.ts +5 -4
- package/src/runtime/parallel-research.ts +29 -5
- package/src/runtime/parallel-utils.ts +9 -11
- package/src/runtime/path-overlap.ts +150 -0
- package/src/runtime/peer-dep.ts +12 -17
- package/src/runtime/per-write-validator.ts +1 -3
- package/src/runtime/phase-tracker.ts +342 -330
- package/src/runtime/pi-args.ts +26 -8
- package/src/runtime/pi-json-output.ts +1 -1
- package/src/runtime/pi-spawn.ts +30 -19
- package/src/runtime/pipeline-runner.ts +56 -55
- package/src/runtime/plan-templates.ts +5 -6
- package/src/runtime/policy-engine.ts +43 -7
- package/src/runtime/post-checks.ts +12 -4
- package/src/runtime/post-exit-stdio-guard.ts +2 -2
- package/src/runtime/process-lifecycle.ts +20 -10
- package/src/runtime/process-status.ts +16 -5
- package/src/runtime/progress-event-coalescer.ts +2 -1
- package/src/runtime/progress-tracker.ts +103 -103
- package/src/runtime/prose-compressor.ts +9 -11
- package/src/runtime/recovery-recipes.ts +118 -24
- package/src/runtime/replace.ts +25 -10
- package/src/runtime/resilient-edit.ts +10 -25
- package/src/runtime/result-extractor.ts +1 -2
- package/src/runtime/retry-executor.ts +20 -4
- package/src/runtime/retry-runner.ts +26 -50
- package/src/runtime/role-permission.ts +6 -1
- package/src/runtime/run-coalesced-task-group.ts +256 -0
- package/src/runtime/run-drift.ts +14 -15
- package/src/runtime/run-tracker.ts +6 -28
- package/src/runtime/runtime-policy.ts +1 -6
- package/src/runtime/runtime-resolver.ts +80 -15
- package/src/runtime/scheduler.ts +47 -18
- package/src/runtime/semaphore.ts +5 -7
- package/src/runtime/sensitive-paths.ts +2 -1
- package/src/runtime/session-usage.ts +1 -1
- package/src/runtime/settings-store.ts +16 -12
- package/src/runtime/sidechain-output.ts +6 -2
- package/src/runtime/single-agent-compose.ts +1 -4
- package/src/runtime/skill-effectiveness.ts +29 -97
- package/src/runtime/skill-instructions.ts +40 -83
- package/src/runtime/stale-reconciler.ts +73 -197
- package/src/runtime/stream-preview.ts +1 -1
- package/src/runtime/streaming-output.ts +1 -1
- package/src/runtime/subagent-manager.ts +44 -169
- package/src/runtime/subprocess-tool-registry.ts +4 -1
- package/src/runtime/supervisor-contact.ts +10 -5
- package/src/runtime/task-display.ts +19 -5
- package/src/runtime/task-graph-scheduler.ts +95 -21
- package/src/runtime/task-graph.ts +5 -11
- package/src/runtime/task-health.ts +54 -47
- package/src/runtime/task-id.ts +11 -18
- package/src/runtime/task-output-context.ts +265 -81
- package/src/runtime/task-packet.ts +12 -20
- package/src/runtime/task-quality.ts +6 -14
- package/src/runtime/task-runner/context-retrieval.ts +4 -13
- package/src/runtime/task-runner/live-executor.ts +114 -36
- package/src/runtime/task-runner/output-splitter.ts +152 -0
- package/src/runtime/task-runner/progress.ts +50 -12
- package/src/runtime/task-runner/prompt-builder.ts +31 -7
- package/src/runtime/task-runner/prompt-pipeline.ts +31 -7
- package/src/runtime/task-runner/result-utils.ts +3 -1
- package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
- package/src/runtime/task-runner/run-projection.ts +27 -8
- package/src/runtime/task-runner/state-helpers.ts +29 -8
- package/src/runtime/task-runner/tail-read.ts +2 -6
- package/src/runtime/task-runner.ts +180 -326
- package/src/runtime/team-runner-artifacts.ts +13 -0
- package/src/runtime/team-runner.ts +456 -974
- package/src/runtime/tool-output-pruner.ts +6 -9
- package/src/runtime/tool-progress.ts +11 -21
- package/src/runtime/verification-gates.ts +33 -24
- package/src/runtime/verification-integrity.ts +1 -4
- package/src/runtime/verification-worktree.ts +62 -12
- package/src/runtime/worker-heartbeat.ts +5 -1
- package/src/runtime/worker-startup.ts +29 -5
- package/src/runtime/workflow-state.ts +17 -9
- package/src/runtime/workspace-lock.ts +30 -35
- package/src/runtime/workspace-tree.ts +20 -32
- package/src/runtime/yield-handler.ts +43 -9
- package/src/runtime/zombie-scanner.ts +5 -4
- package/src/schema/config-schema.ts +266 -172
- package/src/schema/team-tool-schema.ts +29 -74
- package/src/schema/validation-types.ts +25 -17
- package/src/skills/discover-skills.ts +35 -10
- package/src/skills/skill-templates.ts +109 -27
- package/src/skills/validate.ts +26 -8
- package/src/state/active-run-registry.ts +87 -28
- package/src/state/artifact-store.ts +9 -5
- package/src/state/atomic-write-v2.ts +85 -63
- package/src/state/atomic-write.ts +105 -26
- package/src/state/blob-store.ts +47 -20
- package/src/state/contracts.ts +20 -5
- package/src/state/crew-init.ts +5 -22
- package/src/state/decision-ledger.ts +19 -73
- package/src/state/event-log-rotation.ts +41 -15
- package/src/state/event-log.ts +168 -53
- package/src/state/event-reconstructor.ts +11 -1
- package/src/state/gitignore-manager.ts +2 -8
- package/src/state/health-store.ts +57 -57
- package/src/state/hook-instinct-bridge.ts +1 -1
- package/src/state/hook-integrations.ts +1 -1
- package/src/state/instinct-store.ts +17 -5
- package/src/state/jsonl-writer.ts +1 -1
- package/src/state/locks.ts +23 -9
- package/src/state/mailbox.ts +151 -28
- package/src/state/observation-store.ts +20 -14
- package/src/state/run-cache.ts +142 -135
- package/src/state/run-graph.ts +6 -15
- package/src/state/run-metrics.ts +5 -18
- package/src/state/schedule.ts +15 -9
- package/src/state/state-store.ts +129 -45
- package/src/state/task-claims.ts +12 -2
- package/src/state/tiered-eval.ts +52 -43
- package/src/state/types-eval.ts +2 -2
- package/src/state/types.ts +24 -20
- package/src/state/usage.ts +20 -4
- package/src/state/worker-atomic-writer.ts +6 -3
- package/src/subagents/index.ts +2 -2
- package/src/teams/discover-teams.ts +21 -6
- package/src/tools/safe-bash-extension.ts +5 -6
- package/src/tools/safe-bash.ts +10 -11
- package/src/types/new-api-types.ts +6 -10
- package/src/ui/agent-management-overlay.ts +52 -40
- package/src/ui/card-colors.ts +13 -4
- package/src/ui/crew-footer.ts +8 -7
- package/src/ui/crew-select-list.ts +1 -1
- package/src/ui/dashboard-panes/agents-pane.ts +41 -25
- package/src/ui/dashboard-panes/cancellation-pane.ts +1 -1
- package/src/ui/dashboard-panes/capability-pane.ts +32 -15
- package/src/ui/dashboard-panes/health-pane.ts +2 -1
- package/src/ui/dashboard-panes/metrics-pane.ts +4 -1
- package/src/ui/dashboard-panes/progress-pane.ts +12 -9
- package/src/ui/heartbeat-aggregator.ts +15 -4
- package/src/ui/keybinding-map.ts +40 -7
- package/src/ui/live-conversation-overlay.ts +36 -20
- package/src/ui/live-duration.ts +1 -4
- package/src/ui/live-run-sidebar.ts +88 -25
- package/src/ui/loaders.ts +2 -8
- package/src/ui/mascot.ts +20 -36
- package/src/ui/overlays/agent-picker-overlay.ts +11 -3
- package/src/ui/overlays/confirm-overlay.ts +4 -2
- package/src/ui/overlays/help-overlay.ts +25 -14
- package/src/ui/overlays/mailbox-compose-overlay.ts +48 -11
- package/src/ui/overlays/mailbox-compose-preview.ts +20 -5
- package/src/ui/overlays/mailbox-detail-overlay.ts +27 -6
- package/src/ui/pi-ui-compat.ts +7 -7
- package/src/ui/powerbar-publisher.ts +120 -42
- package/src/ui/render-diff.ts +10 -3
- package/src/ui/render-scheduler.ts +12 -4
- package/src/ui/run-action-dispatcher.ts +81 -18
- package/src/ui/run-dashboard.ts +172 -76
- package/src/ui/run-event-bus.ts +59 -37
- package/src/ui/run-snapshot-cache.ts +276 -86
- package/src/ui/settings-overlay.ts +361 -74
- package/src/ui/status-colors.ts +12 -1
- package/src/ui/syntax-highlight.ts +1 -1
- package/src/ui/theme-adapter.ts +16 -14
- package/src/ui/theme-discovery.ts +13 -2
- package/src/ui/tool-progress-formatter.ts +7 -7
- package/src/ui/tool-render.ts +128 -56
- package/src/ui/tool-renderers/brief-mode.ts +45 -27
- package/src/ui/tool-renderers/index.ts +109 -43
- package/src/ui/transcript-cache.ts +32 -6
- package/src/ui/transcript-entries.ts +25 -23
- package/src/ui/transcript-viewer.ts +78 -29
- package/src/ui/widget/index.ts +99 -40
- package/src/ui/widget/widget-formatters.ts +13 -6
- package/src/ui/widget/widget-model.ts +19 -8
- package/src/ui/widget/widget-renderer.ts +23 -13
- package/src/ui/widget/widget-types.ts +1 -1
- package/src/utils/bm25-search.ts +199 -199
- package/src/utils/conflict-detect.ts +22 -21
- package/src/utils/env-filter.ts +14 -7
- package/src/utils/file-coalescer.ts +5 -1
- package/src/utils/fingerprint.ts +3 -6
- package/src/utils/frontmatter.ts +3 -1
- package/src/utils/fs-watch.ts +2 -6
- package/src/utils/gh-protocol.ts +119 -44
- package/src/utils/git.ts +13 -15
- package/src/utils/guards.ts +2 -5
- package/src/utils/ids.ts +9 -2
- package/src/utils/incremental-reader.ts +14 -3
- package/src/utils/internal-error.ts +2 -1
- package/src/utils/names.ts +12 -3
- package/src/utils/paths.ts +49 -5
- package/src/utils/project-detector.ts +2 -2
- package/src/utils/redaction.ts +29 -19
- package/src/utils/resolve-shell.ts +9 -7
- package/src/utils/run-watcher-registry.ts +19 -31
- package/src/utils/safe-paths.ts +45 -24
- package/src/utils/scan-cache.ts +9 -2
- package/src/utils/session-utils.ts +2 -4
- package/src/utils/sleep.ts +12 -5
- package/src/utils/sse-parser.ts +5 -17
- package/src/utils/visual.ts +52 -32
- package/src/workflows/discover-workflows.ts +41 -109
- package/src/workflows/intermediate-store.ts +5 -21
- package/src/workflows/preflight-validator.ts +6 -33
- package/src/workflows/topology-analyzer.ts +5 -21
- package/src/workflows/workflow-config.ts +7 -6
- package/src/worktree/branch-freshness.ts +66 -8
- package/src/worktree/cleanup.ts +130 -20
- package/src/worktree/worktree-manager.ts +212 -69
- package/skills/artifact-analysis-loop/SKILL.md +0 -303
- package/skills/detection-pipeline-design/SKILL.md +0 -286
- package/skills/hunting-investigation-loop/SKILL.md +0 -402
- package/skills/incident-playbook-construction/SKILL.md +0 -384
- package/skills/security-review/SKILL.md +0 -561
- package/skills/threat-hypothesis-framework/SKILL.md +0 -176
- package/skills/ui-render-performance/SKILL.md +0 -58
- /package/docs/{followup-review-round3-2026-05-12.md → archive/followup-review-round3-2026-05-12.md} +0 -0
- /package/docs/{followup-review-round4-2026-05-13.md → archive/followup-review-round4-2026-05-13.md} +0 -0
- /package/docs/{pi-crew-bugs.md → archive/pi-crew-bugs.md} +0 -0
- /package/docs/{pi-crew-test-final.md → archive/pi-crew-test-final.md} +0 -0
- /package/docs/{pi-crew-test-results.md → archive/pi-crew-test-results.md} +0 -0
- /package/docs/{pi-crew-test-round2.md → archive/pi-crew-test-round2.md} +0 -0
- /package/docs/{pi-crew-test-round4.md → archive/pi-crew-test-round4.md} +0 -0
- /package/docs/{pi-crew-test-round5.md → archive/pi-crew-test-round5.md} +0 -0
- /package/docs/{pi-crew-test-round6.md → archive/pi-crew-test-round6.md} +0 -0
- /package/docs/{pi-crew-v0.5.10-audit-fix-plan.md → archive/pi-crew-v0.5.10-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.11-audit-fix-plan.md → archive/pi-crew-v0.5.11-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.12-audit-fix-plan.md → archive/pi-crew-v0.5.12-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.13-audit-fix-plan.md → archive/pi-crew-v0.5.13-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.14-audit-fix-plan.md → archive/pi-crew-v0.5.14-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.16-audit-fix-plan.md → archive/pi-crew-v0.5.16-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.17-audit-fix-plan.md → archive/pi-crew-v0.5.17-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.5-audit-fix-plan.md → archive/pi-crew-v0.5.5-audit-fix-plan.md} +0 -0
- /package/docs/{pi-crew-v0.5.9-audit-fix-plan.md → archive/pi-crew-v0.5.9-audit-fix-plan.md} +0 -0
- /package/docs/{pi-mono-opportunities.md → archive/pi-mono-opportunities.md} +0 -0
- /package/docs/{pi-mono-review.md → archive/pi-mono-review.md} +0 -0
- /package/docs/{pi-subagent4-comparison.md → archive/pi-subagent4-comparison.md} +0 -0
- /package/docs/{pi-subagents3-deep-analysis.md → archive/pi-subagents3-deep-analysis.md} +0 -0
package/src/config/config.ts
CHANGED
|
@@ -3,12 +3,9 @@ import * as os from "node:os";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { type Static, type TSchema, Type } from "@sinclair/typebox";
|
|
5
5
|
import { Value } from "@sinclair/typebox/value";
|
|
6
|
-
import {
|
|
7
|
-
PiTeamsAutonomyProfileSchema,
|
|
8
|
-
PiTeamsConfigSchema,
|
|
9
|
-
} from "../schema/config-schema.ts";
|
|
10
|
-
import { withFileLockSync } from "../state/locks.ts";
|
|
6
|
+
import { PiTeamsAutonomyProfileSchema, PiTeamsConfigSchema } from "../schema/config-schema.ts";
|
|
11
7
|
import { atomicWriteFile } from "../state/atomic-write.ts";
|
|
8
|
+
import { withFileLockSync } from "../state/locks.ts";
|
|
12
9
|
import { logInternalError } from "../utils/internal-error.ts";
|
|
13
10
|
import { projectCrewRoot, projectPiRoot } from "../utils/paths.ts";
|
|
14
11
|
import { suggestConfigKey } from "./suggestions.ts";
|
|
@@ -112,14 +109,7 @@ export function configPath(): string {
|
|
|
112
109
|
}
|
|
113
110
|
|
|
114
111
|
export function legacyConfigPath(): string {
|
|
115
|
-
return path.join(
|
|
116
|
-
resolveHomeDir(),
|
|
117
|
-
".pi",
|
|
118
|
-
"agent",
|
|
119
|
-
"extensions",
|
|
120
|
-
"pi-crew",
|
|
121
|
-
"config.json",
|
|
122
|
-
);
|
|
112
|
+
return path.join(resolveHomeDir(), ".pi", "agent", "extensions", "pi-crew", "config.json");
|
|
123
113
|
}
|
|
124
114
|
|
|
125
115
|
export function projectConfigPath(cwd: string): string {
|
|
@@ -134,23 +124,14 @@ export function projectPiCrewJsonPath(cwd: string): string {
|
|
|
134
124
|
return path.join(projectPiRoot(cwd), "pi-crew.json");
|
|
135
125
|
}
|
|
136
126
|
|
|
137
|
-
function withoutUndefined<T extends Record<string, unknown>>(
|
|
138
|
-
value
|
|
139
|
-
): Partial<T> {
|
|
140
|
-
return Object.fromEntries(
|
|
141
|
-
Object.entries(value).filter(([, entry]) => entry !== undefined),
|
|
142
|
-
) as Partial<T>;
|
|
127
|
+
function withoutUndefined<T extends Record<string, unknown>>(value: T): Partial<T> {
|
|
128
|
+
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined)) as Partial<T>;
|
|
143
129
|
}
|
|
144
130
|
|
|
145
131
|
function errorPathFromValidation(error: unknown): string {
|
|
146
132
|
if (error && typeof error === "object") {
|
|
147
|
-
if (typeof (error as { path?: unknown }).path === "string")
|
|
148
|
-
|
|
149
|
-
if (
|
|
150
|
-
typeof (error as { instancePath?: unknown }).instancePath ===
|
|
151
|
-
"string"
|
|
152
|
-
)
|
|
153
|
-
return (error as { instancePath: string }).instancePath;
|
|
133
|
+
if (typeof (error as { path?: unknown }).path === "string") return (error as { path: string }).path;
|
|
134
|
+
if (typeof (error as { instancePath?: unknown }).instancePath === "string") return (error as { instancePath: string }).instancePath;
|
|
154
135
|
if (
|
|
155
136
|
typeof (error as { keyword?: unknown }).keyword === "string" &&
|
|
156
137
|
typeof (error as { schemaPath?: unknown }).schemaPath === "string"
|
|
@@ -161,28 +142,18 @@ function errorPathFromValidation(error: unknown): string {
|
|
|
161
142
|
}
|
|
162
143
|
|
|
163
144
|
/** Known top-level config keys from the schema — used for fuzzy suggestions. */
|
|
164
|
-
const KNOWN_TOP_LEVEL_KEYS = Object.keys(
|
|
165
|
-
PiTeamsConfigSchema.properties ?? {},
|
|
166
|
-
) as string[];
|
|
145
|
+
const KNOWN_TOP_LEVEL_KEYS = Object.keys(PiTeamsConfigSchema.properties ?? {}) as string[];
|
|
167
146
|
|
|
168
147
|
function validateConfigWithWarnings(raw: unknown): string[] {
|
|
169
148
|
if (!Value.Check(PiTeamsConfigSchema, raw)) {
|
|
170
149
|
return [...Value.Errors(PiTeamsConfigSchema, raw)].map((error) => {
|
|
171
150
|
const path = errorPathFromValidation(error);
|
|
172
|
-
const message =
|
|
173
|
-
(error as { message?: unknown }).message ?? "invalid value";
|
|
151
|
+
const message = (error as { message?: unknown }).message ?? "invalid value";
|
|
174
152
|
// Enhance "additionalProperties" errors with fuzzy suggestions
|
|
175
|
-
if (
|
|
176
|
-
(error as { keyword?: unknown }).keyword ===
|
|
177
|
-
"additionalProperties"
|
|
178
|
-
) {
|
|
153
|
+
if ((error as { keyword?: unknown }).keyword === "additionalProperties") {
|
|
179
154
|
const offendingKey = path.split("/").pop() ?? path;
|
|
180
|
-
const suggestion = suggestConfigKey(
|
|
181
|
-
|
|
182
|
-
KNOWN_TOP_LEVEL_KEYS,
|
|
183
|
-
);
|
|
184
|
-
if (suggestion)
|
|
185
|
-
return `${path}: ${message} (did you mean '${suggestion}'?)`;
|
|
155
|
+
const suggestion = suggestConfigKey(offendingKey, KNOWN_TOP_LEVEL_KEYS);
|
|
156
|
+
if (suggestion) return `${path}: ${message} (did you mean '${suggestion}'?)`;
|
|
186
157
|
}
|
|
187
158
|
return `${path}: ${message}`;
|
|
188
159
|
});
|
|
@@ -190,18 +161,11 @@ function validateConfigWithWarnings(raw: unknown): string[] {
|
|
|
190
161
|
return [];
|
|
191
162
|
}
|
|
192
163
|
|
|
193
|
-
function projectOverrideWarning(
|
|
194
|
-
projectPath: string,
|
|
195
|
-
dottedPath: string,
|
|
196
|
-
): string {
|
|
164
|
+
function projectOverrideWarning(projectPath: string, dottedPath: string): string {
|
|
197
165
|
return `${projectPath}: project-level sensitive config '${dottedPath}' is ignored; set it in user config to trust it explicitly`;
|
|
198
166
|
}
|
|
199
167
|
|
|
200
|
-
function sanitizeProjectConfig(
|
|
201
|
-
projectPath: string,
|
|
202
|
-
userConfig: PiTeamsConfig,
|
|
203
|
-
config: PiTeamsConfig,
|
|
204
|
-
): ConfigValidationResult {
|
|
168
|
+
function sanitizeProjectConfig(projectPath: string, userConfig: PiTeamsConfig, config: PiTeamsConfig): ConfigValidationResult {
|
|
205
169
|
const sanitized: PiTeamsConfig = { ...config };
|
|
206
170
|
const warnings: string[] = [];
|
|
207
171
|
const dropTopLevel = (key: keyof PiTeamsConfig): void => {
|
|
@@ -214,73 +178,36 @@ function sanitizeProjectConfig(
|
|
|
214
178
|
dropTopLevel("requireCleanWorktreeLeader");
|
|
215
179
|
if (config.runtime) {
|
|
216
180
|
const runtime = { ...config.runtime };
|
|
217
|
-
for (const key of [
|
|
218
|
-
"mode",
|
|
219
|
-
"preferLiveSession",
|
|
220
|
-
"allowChildProcessFallback",
|
|
221
|
-
"inheritContext",
|
|
222
|
-
"isolationPolicy",
|
|
223
|
-
] as const) {
|
|
181
|
+
for (const key of ["mode", "preferLiveSession", "allowChildProcessFallback", "inheritContext", "isolationPolicy"] as const) {
|
|
224
182
|
if (runtime[key] !== undefined) {
|
|
225
183
|
delete runtime[key];
|
|
226
|
-
warnings.push(
|
|
227
|
-
projectOverrideWarning(projectPath, `runtime.${key}`),
|
|
228
|
-
);
|
|
184
|
+
warnings.push(projectOverrideWarning(projectPath, `runtime.${key}`));
|
|
229
185
|
}
|
|
230
186
|
}
|
|
231
187
|
if (runtime.requirePlanApproval === false) {
|
|
232
188
|
delete runtime.requirePlanApproval;
|
|
233
|
-
warnings.push(
|
|
234
|
-
projectOverrideWarning(
|
|
235
|
-
projectPath,
|
|
236
|
-
"runtime.requirePlanApproval",
|
|
237
|
-
),
|
|
238
|
-
);
|
|
189
|
+
warnings.push(projectOverrideWarning(projectPath, "runtime.requirePlanApproval"));
|
|
239
190
|
}
|
|
240
|
-
sanitized.runtime = Object.values(runtime).some(
|
|
241
|
-
(entry) => entry !== undefined,
|
|
242
|
-
)
|
|
243
|
-
? runtime
|
|
244
|
-
: undefined;
|
|
191
|
+
sanitized.runtime = Object.values(runtime).some((entry) => entry !== undefined) ? runtime : undefined;
|
|
245
192
|
}
|
|
246
193
|
if (config.autonomous) {
|
|
247
194
|
const autonomous = { ...config.autonomous };
|
|
248
|
-
for (const key of [
|
|
249
|
-
"profile",
|
|
250
|
-
"enabled",
|
|
251
|
-
"injectPolicy",
|
|
252
|
-
"preferAsyncForLongTasks",
|
|
253
|
-
"allowWorktreeSuggestion",
|
|
254
|
-
] as const) {
|
|
195
|
+
for (const key of ["profile", "enabled", "injectPolicy", "preferAsyncForLongTasks", "allowWorktreeSuggestion"] as const) {
|
|
255
196
|
if (autonomous[key] !== undefined) {
|
|
256
197
|
delete autonomous[key];
|
|
257
|
-
warnings.push(
|
|
258
|
-
projectOverrideWarning(projectPath, `autonomous.${key}`),
|
|
259
|
-
);
|
|
198
|
+
warnings.push(projectOverrideWarning(projectPath, `autonomous.${key}`));
|
|
260
199
|
}
|
|
261
200
|
}
|
|
262
|
-
sanitized.autonomous = Object.values(autonomous).some(
|
|
263
|
-
(entry) => entry !== undefined,
|
|
264
|
-
)
|
|
265
|
-
? autonomous
|
|
266
|
-
: undefined;
|
|
201
|
+
sanitized.autonomous = Object.values(autonomous).some((entry) => entry !== undefined) ? autonomous : undefined;
|
|
267
202
|
}
|
|
268
203
|
if (config.worktree?.setupHook !== undefined) {
|
|
269
204
|
sanitized.worktree = { ...config.worktree, setupHook: undefined };
|
|
270
|
-
if (
|
|
271
|
-
|
|
272
|
-
(entry) => entry !== undefined,
|
|
273
|
-
)
|
|
274
|
-
)
|
|
275
|
-
sanitized.worktree = undefined;
|
|
276
|
-
warnings.push(
|
|
277
|
-
projectOverrideWarning(projectPath, "worktree.setupHook"),
|
|
278
|
-
);
|
|
205
|
+
if (!Object.values(sanitized.worktree).some((entry) => entry !== undefined)) sanitized.worktree = undefined;
|
|
206
|
+
warnings.push(projectOverrideWarning(projectPath, "worktree.setupHook"));
|
|
279
207
|
}
|
|
280
208
|
if (config.otlp?.headers !== undefined) {
|
|
281
209
|
sanitized.otlp = { ...config.otlp, headers: undefined };
|
|
282
|
-
if (!Object.values(sanitized.otlp).some((entry) => entry !== undefined))
|
|
283
|
-
sanitized.otlp = undefined;
|
|
210
|
+
if (!Object.values(sanitized.otlp).some((entry) => entry !== undefined)) sanitized.otlp = undefined;
|
|
284
211
|
warnings.push(projectOverrideWarning(projectPath, "otlp.headers"));
|
|
285
212
|
}
|
|
286
213
|
// FIX: Block project config from setting otlp.endpoint — it controls where
|
|
@@ -288,66 +215,37 @@ function sanitizeProjectConfig(
|
|
|
288
215
|
if (config.otlp?.endpoint !== undefined) {
|
|
289
216
|
if (!sanitized.otlp) sanitized.otlp = { ...config.otlp, endpoint: undefined };
|
|
290
217
|
else sanitized.otlp = { ...sanitized.otlp, endpoint: undefined };
|
|
291
|
-
if (!Object.values(sanitized.otlp).some((entry) => entry !== undefined))
|
|
292
|
-
sanitized.otlp = undefined;
|
|
218
|
+
if (!Object.values(sanitized.otlp).some((entry) => entry !== undefined)) sanitized.otlp = undefined;
|
|
293
219
|
warnings.push(projectOverrideWarning(projectPath, "otlp.endpoint"));
|
|
294
220
|
}
|
|
295
|
-
if (
|
|
296
|
-
config.agents?.disableBuiltins !== undefined ||
|
|
297
|
-
config.agents?.overrides !== undefined
|
|
298
|
-
) {
|
|
221
|
+
if (config.agents?.disableBuiltins !== undefined || config.agents?.overrides !== undefined) {
|
|
299
222
|
const agents = { ...config.agents };
|
|
300
223
|
if (agents.disableBuiltins !== undefined) {
|
|
301
224
|
delete agents.disableBuiltins;
|
|
302
|
-
warnings.push(
|
|
303
|
-
projectOverrideWarning(projectPath, "agents.disableBuiltins"),
|
|
304
|
-
);
|
|
225
|
+
warnings.push(projectOverrideWarning(projectPath, "agents.disableBuiltins"));
|
|
305
226
|
}
|
|
306
227
|
if (agents.overrides !== undefined) {
|
|
307
228
|
delete agents.overrides;
|
|
308
|
-
warnings.push(
|
|
309
|
-
projectOverrideWarning(projectPath, "agents.overrides"),
|
|
310
|
-
);
|
|
229
|
+
warnings.push(projectOverrideWarning(projectPath, "agents.overrides"));
|
|
311
230
|
}
|
|
312
|
-
sanitized.agents = Object.values(agents).some(
|
|
313
|
-
(entry) => entry !== undefined,
|
|
314
|
-
)
|
|
315
|
-
? agents
|
|
316
|
-
: undefined;
|
|
231
|
+
sanitized.agents = Object.values(agents).some((entry) => entry !== undefined) ? agents : undefined;
|
|
317
232
|
}
|
|
318
|
-
if (
|
|
319
|
-
config.tools?.enableSteer !== undefined ||
|
|
320
|
-
config.tools?.terminateOnForeground !== undefined
|
|
321
|
-
) {
|
|
233
|
+
if (config.tools?.enableSteer !== undefined || config.tools?.terminateOnForeground !== undefined) {
|
|
322
234
|
const tools = { ...config.tools };
|
|
323
235
|
if (tools.enableSteer !== undefined) {
|
|
324
236
|
delete tools.enableSteer;
|
|
325
|
-
warnings.push(
|
|
326
|
-
projectOverrideWarning(projectPath, "tools.enableSteer"),
|
|
327
|
-
);
|
|
237
|
+
warnings.push(projectOverrideWarning(projectPath, "tools.enableSteer"));
|
|
328
238
|
}
|
|
329
239
|
if (tools.terminateOnForeground !== undefined) {
|
|
330
240
|
delete tools.terminateOnForeground;
|
|
331
|
-
warnings.push(
|
|
332
|
-
projectOverrideWarning(
|
|
333
|
-
projectPath,
|
|
334
|
-
"tools.terminateOnForeground",
|
|
335
|
-
),
|
|
336
|
-
);
|
|
241
|
+
warnings.push(projectOverrideWarning(projectPath, "tools.terminateOnForeground"));
|
|
337
242
|
}
|
|
338
|
-
sanitized.tools = Object.values(tools).some(
|
|
339
|
-
(entry) => entry !== undefined,
|
|
340
|
-
)
|
|
341
|
-
? tools
|
|
342
|
-
: undefined;
|
|
243
|
+
sanitized.tools = Object.values(tools).some((entry) => entry !== undefined) ? tools : undefined;
|
|
343
244
|
}
|
|
344
245
|
return { config: sanitized, warnings };
|
|
345
246
|
}
|
|
346
247
|
|
|
347
|
-
function mergeConfig(
|
|
348
|
-
base: PiTeamsConfig,
|
|
349
|
-
override: PiTeamsConfig,
|
|
350
|
-
): PiTeamsConfig {
|
|
248
|
+
function mergeConfig(base: PiTeamsConfig, override: PiTeamsConfig): PiTeamsConfig {
|
|
351
249
|
const warnings: string[] = [];
|
|
352
250
|
const merged: PiTeamsConfig = {
|
|
353
251
|
...base,
|
|
@@ -356,41 +254,31 @@ function mergeConfig(
|
|
|
356
254
|
if (base.autonomous || override.autonomous) {
|
|
357
255
|
merged.autonomous = {
|
|
358
256
|
...(base.autonomous ?? {}),
|
|
359
|
-
...withoutUndefined(
|
|
360
|
-
(override.autonomous ?? {}) as Record<string, unknown>,
|
|
361
|
-
),
|
|
257
|
+
...withoutUndefined((override.autonomous ?? {}) as Record<string, unknown>),
|
|
362
258
|
};
|
|
363
259
|
}
|
|
364
260
|
if (base.limits || override.limits) {
|
|
365
261
|
merged.limits = {
|
|
366
262
|
...(base.limits ?? {}),
|
|
367
|
-
...withoutUndefined(
|
|
368
|
-
(override.limits ?? {}) as Record<string, unknown>,
|
|
369
|
-
),
|
|
263
|
+
...withoutUndefined((override.limits ?? {}) as Record<string, unknown>),
|
|
370
264
|
};
|
|
371
265
|
}
|
|
372
266
|
if (base.runtime || override.runtime) {
|
|
373
267
|
merged.runtime = {
|
|
374
268
|
...(base.runtime ?? {}),
|
|
375
|
-
...withoutUndefined(
|
|
376
|
-
(override.runtime ?? {}) as Record<string, unknown>,
|
|
377
|
-
),
|
|
269
|
+
...withoutUndefined((override.runtime ?? {}) as Record<string, unknown>),
|
|
378
270
|
};
|
|
379
271
|
}
|
|
380
272
|
if (base.control || override.control) {
|
|
381
273
|
merged.control = {
|
|
382
274
|
...(base.control ?? {}),
|
|
383
|
-
...withoutUndefined(
|
|
384
|
-
(override.control ?? {}) as Record<string, unknown>,
|
|
385
|
-
),
|
|
275
|
+
...withoutUndefined((override.control ?? {}) as Record<string, unknown>),
|
|
386
276
|
};
|
|
387
277
|
}
|
|
388
278
|
if (base.worktree || override.worktree) {
|
|
389
279
|
merged.worktree = {
|
|
390
280
|
...(base.worktree ?? {}),
|
|
391
|
-
...withoutUndefined(
|
|
392
|
-
(override.worktree ?? {}) as Record<string, unknown>,
|
|
393
|
-
),
|
|
281
|
+
...withoutUndefined((override.worktree ?? {}) as Record<string, unknown>),
|
|
394
282
|
};
|
|
395
283
|
}
|
|
396
284
|
if (base.ui || override.ui) {
|
|
@@ -402,75 +290,52 @@ function mergeConfig(
|
|
|
402
290
|
if (base.agents || override.agents) {
|
|
403
291
|
merged.agents = {
|
|
404
292
|
...(base.agents ?? {}),
|
|
405
|
-
...withoutUndefined(
|
|
406
|
-
(override.agents ?? {}) as Record<string, unknown>,
|
|
407
|
-
),
|
|
293
|
+
...withoutUndefined((override.agents ?? {}) as Record<string, unknown>),
|
|
408
294
|
overrides: {
|
|
409
295
|
...(base.agents?.overrides ?? {}),
|
|
410
|
-
...(withoutUndefined(
|
|
411
|
-
(override.agents?.overrides ?? {}) as Record<
|
|
412
|
-
string,
|
|
413
|
-
unknown
|
|
414
|
-
>,
|
|
415
|
-
) as Record<string, AgentOverrideConfig>),
|
|
296
|
+
...(withoutUndefined((override.agents?.overrides ?? {}) as Record<string, unknown>) as Record<string, AgentOverrideConfig>),
|
|
416
297
|
},
|
|
417
298
|
};
|
|
418
299
|
}
|
|
419
300
|
if (base.tools || override.tools) {
|
|
420
301
|
merged.tools = {
|
|
421
302
|
...(base.tools ?? {}),
|
|
422
|
-
...withoutUndefined(
|
|
423
|
-
(override.tools ?? {}) as Record<string, unknown>,
|
|
424
|
-
),
|
|
303
|
+
...withoutUndefined((override.tools ?? {}) as Record<string, unknown>),
|
|
425
304
|
};
|
|
426
305
|
}
|
|
427
306
|
if (base.telemetry || override.telemetry) {
|
|
428
307
|
merged.telemetry = {
|
|
429
308
|
...(base.telemetry ?? {}),
|
|
430
|
-
...withoutUndefined(
|
|
431
|
-
(override.telemetry ?? {}) as Record<string, unknown>,
|
|
432
|
-
),
|
|
309
|
+
...withoutUndefined((override.telemetry ?? {}) as Record<string, unknown>),
|
|
433
310
|
};
|
|
434
311
|
}
|
|
435
312
|
if (base.policy || override.policy) {
|
|
436
313
|
merged.policy = {
|
|
437
314
|
...(base.policy ?? {}),
|
|
438
|
-
...withoutUndefined(
|
|
439
|
-
(override.policy ?? {}) as Record<string, unknown>,
|
|
440
|
-
),
|
|
315
|
+
...withoutUndefined((override.policy ?? {}) as Record<string, unknown>),
|
|
441
316
|
};
|
|
442
317
|
}
|
|
443
318
|
if (base.notifications || override.notifications) {
|
|
444
319
|
merged.notifications = {
|
|
445
320
|
...(base.notifications ?? {}),
|
|
446
|
-
...withoutUndefined(
|
|
447
|
-
(override.notifications ?? {}) as Record<string, unknown>,
|
|
448
|
-
),
|
|
321
|
+
...withoutUndefined((override.notifications ?? {}) as Record<string, unknown>),
|
|
449
322
|
};
|
|
450
323
|
}
|
|
451
324
|
if (base.observability || override.observability) {
|
|
452
325
|
merged.observability = {
|
|
453
326
|
...(base.observability ?? {}),
|
|
454
|
-
...withoutUndefined(
|
|
455
|
-
(override.observability ?? {}) as Record<string, unknown>,
|
|
456
|
-
),
|
|
327
|
+
...withoutUndefined((override.observability ?? {}) as Record<string, unknown>),
|
|
457
328
|
};
|
|
458
329
|
}
|
|
459
330
|
if (base.reliability || override.reliability) {
|
|
460
331
|
merged.reliability = {
|
|
461
332
|
...(base.reliability ?? {}),
|
|
462
|
-
...withoutUndefined(
|
|
463
|
-
(override.reliability ?? {}) as Record<string, unknown>,
|
|
464
|
-
),
|
|
333
|
+
...withoutUndefined((override.reliability ?? {}) as Record<string, unknown>),
|
|
465
334
|
retryPolicy:
|
|
466
|
-
base.reliability?.retryPolicy ||
|
|
467
|
-
override.reliability?.retryPolicy
|
|
335
|
+
base.reliability?.retryPolicy || override.reliability?.retryPolicy
|
|
468
336
|
? {
|
|
469
337
|
...(base.reliability?.retryPolicy ?? {}),
|
|
470
|
-
...withoutUndefined(
|
|
471
|
-
(override.reliability?.retryPolicy ??
|
|
472
|
-
{}) as Record<string, unknown>,
|
|
473
|
-
),
|
|
338
|
+
...withoutUndefined((override.reliability?.retryPolicy ?? {}) as Record<string, unknown>),
|
|
474
339
|
}
|
|
475
340
|
: undefined,
|
|
476
341
|
};
|
|
@@ -478,16 +343,13 @@ function mergeConfig(
|
|
|
478
343
|
if (base.otlp || override.otlp) {
|
|
479
344
|
merged.otlp = {
|
|
480
345
|
...(base.otlp ?? {}),
|
|
481
|
-
...withoutUndefined(
|
|
482
|
-
(override.otlp ?? {}) as Record<string, unknown>,
|
|
483
|
-
),
|
|
346
|
+
...withoutUndefined((override.otlp ?? {}) as Record<string, unknown>),
|
|
484
347
|
headers: {
|
|
485
348
|
...(base.otlp?.headers ?? {}),
|
|
486
349
|
...(override.otlp?.headers ?? {}),
|
|
487
350
|
},
|
|
488
351
|
};
|
|
489
|
-
if (Object.keys(merged.otlp.headers ?? {}).length === 0)
|
|
490
|
-
delete merged.otlp.headers;
|
|
352
|
+
if (Object.keys(merged.otlp.headers ?? {}).length === 0) delete merged.otlp.headers;
|
|
491
353
|
// Validate OTLP headers for injection attacks:
|
|
492
354
|
// - Check top-level keys for dangerous prototype pollution patterns
|
|
493
355
|
// - Block ALL control characters except tab (0x09) to prevent header
|
|
@@ -506,22 +368,23 @@ function mergeConfig(
|
|
|
506
368
|
if (DANGEROUS_OBJECT_KEYS.has(lowerKey)) return true;
|
|
507
369
|
return false;
|
|
508
370
|
};
|
|
509
|
-
if (checkKey(k)) {
|
|
371
|
+
if (checkKey(k)) {
|
|
372
|
+
invalidHeaders.push(k);
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
510
375
|
// Block any control characters except tab (0x09) in values.
|
|
511
376
|
// Round 28 fix: /[\x00-\x08\x0a-\x1f]/ blocks LF (0x0A) and CR (0x0D) too.
|
|
512
377
|
const valStr = String(v);
|
|
513
|
-
if (/[\x00-\x08\x0a-\x1f]/.test(valStr)) {
|
|
378
|
+
if (/[\x00-\x08\x0a-\x1f]/.test(valStr)) {
|
|
379
|
+
invalidHeaders.push(k);
|
|
380
|
+
}
|
|
514
381
|
}
|
|
515
382
|
if (invalidHeaders.length > 0) {
|
|
516
383
|
delete merged.otlp.headers;
|
|
517
384
|
warnings.push(`OTLP headers blocked due to invalid characters: ${invalidHeaders.join(", ")}`);
|
|
518
385
|
}
|
|
519
386
|
}
|
|
520
|
-
if (
|
|
521
|
-
merged.agents?.overrides &&
|
|
522
|
-
Object.keys(merged.agents.overrides).length === 0
|
|
523
|
-
)
|
|
524
|
-
delete merged.agents.overrides;
|
|
387
|
+
if (merged.agents?.overrides && Object.keys(merged.agents.overrides).length === 0) delete merged.agents.overrides;
|
|
525
388
|
return merged;
|
|
526
389
|
}
|
|
527
390
|
|
|
@@ -580,8 +443,7 @@ function sanitizeObject(obj: Record<string, unknown>): Record<string, unknown> {
|
|
|
580
443
|
}
|
|
581
444
|
|
|
582
445
|
export function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
583
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
584
|
-
return undefined;
|
|
446
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
585
447
|
// Defensive: create a sanitized copy to prevent prototype pollution.
|
|
586
448
|
// Uses Object.create(null) so the result has no prototype chain.
|
|
587
449
|
// WARNING: The returned object has no prototype methods (no hasOwnProperty,
|
|
@@ -590,11 +452,7 @@ export function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
|
590
452
|
return sanitizeObject(value as Record<string, unknown>);
|
|
591
453
|
}
|
|
592
454
|
|
|
593
|
-
function parseWithSchema<T extends TSchema>(
|
|
594
|
-
schema: T,
|
|
595
|
-
value: unknown,
|
|
596
|
-
context?: string,
|
|
597
|
-
): Static<T> | undefined {
|
|
455
|
+
function parseWithSchema<T extends TSchema>(schema: T, value: unknown, context?: string): Static<T> | undefined {
|
|
598
456
|
if (!Value.Check(schema, value)) {
|
|
599
457
|
if (context) {
|
|
600
458
|
logInternalError("config.parseWithSchema", undefined, `${context}: schema validation failed`);
|
|
@@ -604,18 +462,11 @@ function parseWithSchema<T extends TSchema>(
|
|
|
604
462
|
return Value.Decode(schema, value);
|
|
605
463
|
}
|
|
606
464
|
|
|
607
|
-
function parseIntegerInRange(
|
|
608
|
-
value: unknown,
|
|
609
|
-
minimum = 1,
|
|
610
|
-
maximum = Number.MAX_SAFE_INTEGER,
|
|
611
|
-
): number | undefined {
|
|
465
|
+
function parseIntegerInRange(value: unknown, minimum = 1, maximum = Number.MAX_SAFE_INTEGER): number | undefined {
|
|
612
466
|
return parseWithSchema(Type.Integer({ minimum, maximum }), value);
|
|
613
467
|
}
|
|
614
468
|
|
|
615
|
-
function parsePositiveInteger(
|
|
616
|
-
value: unknown,
|
|
617
|
-
max = Number.MAX_SAFE_INTEGER,
|
|
618
|
-
): number | undefined {
|
|
469
|
+
function parsePositiveInteger(value: unknown, max = Number.MAX_SAFE_INTEGER): number | undefined {
|
|
619
470
|
return parseIntegerInRange(value, 1, max);
|
|
620
471
|
}
|
|
621
472
|
|
|
@@ -626,34 +477,21 @@ function parseProfile(value: unknown): PiTeamsAutonomyProfile | undefined {
|
|
|
626
477
|
function parseStringList(value: unknown): string[] | undefined {
|
|
627
478
|
const items = parseWithSchema(Type.Array(Type.String()), value);
|
|
628
479
|
if (!items || items.length === 0) return undefined;
|
|
629
|
-
const normalized = items
|
|
630
|
-
.map((entry) => entry.trim())
|
|
631
|
-
.filter((entry) => entry.length > 0);
|
|
480
|
+
const normalized = items.map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
632
481
|
return normalized.length > 0 ? normalized : undefined;
|
|
633
482
|
}
|
|
634
483
|
|
|
635
484
|
function parseStringArrayOrFalse(value: unknown): string[] | false | undefined {
|
|
636
485
|
if (value === false) return false;
|
|
637
|
-
if (typeof value === "string")
|
|
638
|
-
return value.trim() === "" ? [] : parseStringList(value.split(","));
|
|
486
|
+
if (typeof value === "string") return value.trim() === "" ? [] : parseStringList(value.split(","));
|
|
639
487
|
return parseStringList(value);
|
|
640
488
|
}
|
|
641
489
|
|
|
642
490
|
export function effectiveAutonomousConfig(
|
|
643
491
|
config: PiTeamsAutonomousConfig | undefined,
|
|
644
|
-
): Required<
|
|
645
|
-
Pick<
|
|
646
|
-
PiTeamsAutonomousConfig,
|
|
647
|
-
| "profile"
|
|
648
|
-
| "enabled"
|
|
649
|
-
| "injectPolicy"
|
|
650
|
-
| "preferAsyncForLongTasks"
|
|
651
|
-
| "allowWorktreeSuggestion"
|
|
652
|
-
>
|
|
653
|
-
> &
|
|
492
|
+
): Required<Pick<PiTeamsAutonomousConfig, "profile" | "enabled" | "injectPolicy" | "preferAsyncForLongTasks" | "allowWorktreeSuggestion">> &
|
|
654
493
|
Pick<PiTeamsAutonomousConfig, "magicKeywords"> {
|
|
655
|
-
const profile =
|
|
656
|
-
config?.enabled === false ? "manual" : (config?.profile ?? "suggested");
|
|
494
|
+
const profile = config?.enabled === false ? "manual" : (config?.profile ?? "suggested");
|
|
657
495
|
const profileDefaults: Record<
|
|
658
496
|
PiTeamsAutonomyProfile,
|
|
659
497
|
{
|
|
@@ -693,21 +531,14 @@ export function effectiveAutonomousConfig(
|
|
|
693
531
|
profile,
|
|
694
532
|
enabled: config?.enabled ?? defaults.enabled,
|
|
695
533
|
injectPolicy: config?.injectPolicy ?? defaults.injectPolicy,
|
|
696
|
-
preferAsyncForLongTasks:
|
|
697
|
-
|
|
698
|
-
allowWorktreeSuggestion:
|
|
699
|
-
config?.allowWorktreeSuggestion ?? defaults.allowWorktreeSuggestion,
|
|
534
|
+
preferAsyncForLongTasks: config?.preferAsyncForLongTasks ?? defaults.preferAsyncForLongTasks,
|
|
535
|
+
allowWorktreeSuggestion: config?.allowWorktreeSuggestion ?? defaults.allowWorktreeSuggestion,
|
|
700
536
|
magicKeywords: config?.magicKeywords,
|
|
701
537
|
};
|
|
702
538
|
}
|
|
703
539
|
|
|
704
|
-
function parseStringArrayRecord(
|
|
705
|
-
|
|
706
|
-
): Record<string, string[]> | undefined {
|
|
707
|
-
const record = parseWithSchema(
|
|
708
|
-
Type.Record(Type.String({ minLength: 1 }), Type.Array(Type.String())),
|
|
709
|
-
value,
|
|
710
|
-
);
|
|
540
|
+
function parseStringArrayRecord(value: unknown): Record<string, string[]> | undefined {
|
|
541
|
+
const record = parseWithSchema(Type.Record(Type.String({ minLength: 1 }), Type.Array(Type.String())), value);
|
|
711
542
|
if (!record) return undefined;
|
|
712
543
|
const result: Record<string, string[]> = {};
|
|
713
544
|
for (const [key, rawValues] of Object.entries(record)) {
|
|
@@ -717,87 +548,42 @@ function parseStringArrayRecord(
|
|
|
717
548
|
return Object.keys(result).length > 0 ? result : undefined;
|
|
718
549
|
}
|
|
719
550
|
|
|
720
|
-
function parseAutonomousConfig(
|
|
721
|
-
value: unknown,
|
|
722
|
-
): PiTeamsAutonomousConfig | undefined {
|
|
551
|
+
function parseAutonomousConfig(value: unknown): PiTeamsAutonomousConfig | undefined {
|
|
723
552
|
const obj = asRecord(value);
|
|
724
553
|
if (!obj) return undefined;
|
|
725
554
|
const config: PiTeamsAutonomousConfig = {
|
|
726
555
|
profile: parseProfile(obj.profile),
|
|
727
556
|
enabled: parseWithSchema(Type.Boolean(), obj.enabled),
|
|
728
557
|
injectPolicy: parseWithSchema(Type.Boolean(), obj.injectPolicy),
|
|
729
|
-
preferAsyncForLongTasks: parseWithSchema(
|
|
730
|
-
|
|
731
|
-
obj.preferAsyncForLongTasks,
|
|
732
|
-
),
|
|
733
|
-
allowWorktreeSuggestion: parseWithSchema(
|
|
734
|
-
Type.Boolean(),
|
|
735
|
-
obj.allowWorktreeSuggestion,
|
|
736
|
-
),
|
|
558
|
+
preferAsyncForLongTasks: parseWithSchema(Type.Boolean(), obj.preferAsyncForLongTasks),
|
|
559
|
+
allowWorktreeSuggestion: parseWithSchema(Type.Boolean(), obj.allowWorktreeSuggestion),
|
|
737
560
|
magicKeywords: parseStringArrayRecord(obj.magicKeywords),
|
|
738
561
|
};
|
|
739
|
-
return Object.values(config).some((entry) => entry !== undefined)
|
|
740
|
-
? config
|
|
741
|
-
: undefined;
|
|
562
|
+
return Object.values(config).some((entry) => entry !== undefined) ? config : undefined;
|
|
742
563
|
}
|
|
743
564
|
|
|
744
565
|
function parseLimitsConfig(value: unknown): CrewLimitsConfig | undefined {
|
|
745
566
|
const obj = asRecord(value);
|
|
746
567
|
if (!obj) return undefined;
|
|
747
568
|
const limits: CrewLimitsConfig = {
|
|
748
|
-
maxConcurrentWorkers: parsePositiveInteger(
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
),
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
),
|
|
756
|
-
maxTaskDepth: parsePositiveInteger(
|
|
757
|
-
obj.maxTaskDepth,
|
|
758
|
-
LIMIT_CEILINGS.maxTaskDepth,
|
|
759
|
-
),
|
|
760
|
-
maxChildrenPerTask: parsePositiveInteger(
|
|
761
|
-
obj.maxChildrenPerTask,
|
|
762
|
-
LIMIT_CEILINGS.maxChildrenPerTask,
|
|
763
|
-
),
|
|
764
|
-
maxRunMinutes: parsePositiveInteger(
|
|
765
|
-
obj.maxRunMinutes,
|
|
766
|
-
LIMIT_CEILINGS.maxRunMinutes,
|
|
767
|
-
),
|
|
768
|
-
maxRetriesPerTask: parsePositiveInteger(
|
|
769
|
-
obj.maxRetriesPerTask,
|
|
770
|
-
LIMIT_CEILINGS.maxRetriesPerTask,
|
|
771
|
-
),
|
|
772
|
-
maxTasksPerRun: parsePositiveInteger(
|
|
773
|
-
obj.maxTasksPerRun,
|
|
774
|
-
LIMIT_CEILINGS.maxTasksPerRun,
|
|
775
|
-
),
|
|
776
|
-
heartbeatStaleMs: parsePositiveInteger(
|
|
777
|
-
obj.heartbeatStaleMs,
|
|
778
|
-
LIMIT_CEILINGS.heartbeatStaleMs,
|
|
779
|
-
),
|
|
569
|
+
maxConcurrentWorkers: parsePositiveInteger(obj.maxConcurrentWorkers, LIMIT_CEILINGS.maxConcurrentWorkers),
|
|
570
|
+
allowUnboundedConcurrency: parseWithSchema(Type.Boolean(), obj.allowUnboundedConcurrency),
|
|
571
|
+
maxTaskDepth: parsePositiveInteger(obj.maxTaskDepth, LIMIT_CEILINGS.maxTaskDepth),
|
|
572
|
+
maxChildrenPerTask: parsePositiveInteger(obj.maxChildrenPerTask, LIMIT_CEILINGS.maxChildrenPerTask),
|
|
573
|
+
maxRunMinutes: parsePositiveInteger(obj.maxRunMinutes, LIMIT_CEILINGS.maxRunMinutes),
|
|
574
|
+
maxRetriesPerTask: parsePositiveInteger(obj.maxRetriesPerTask, LIMIT_CEILINGS.maxRetriesPerTask),
|
|
575
|
+
maxTasksPerRun: parsePositiveInteger(obj.maxTasksPerRun, LIMIT_CEILINGS.maxTasksPerRun),
|
|
576
|
+
heartbeatStaleMs: parsePositiveInteger(obj.heartbeatStaleMs, LIMIT_CEILINGS.heartbeatStaleMs),
|
|
780
577
|
};
|
|
781
|
-
return Object.values(limits).some((entry) => entry !== undefined)
|
|
782
|
-
? limits
|
|
783
|
-
: undefined;
|
|
578
|
+
return Object.values(limits).some((entry) => entry !== undefined) ? limits : undefined;
|
|
784
579
|
}
|
|
785
580
|
|
|
786
|
-
function parseIsolationPolicy(
|
|
787
|
-
value: unknown,
|
|
788
|
-
): CrewRuntimeConfig["isolationPolicy"] | undefined {
|
|
581
|
+
function parseIsolationPolicy(value: unknown): CrewRuntimeConfig["isolationPolicy"] | undefined {
|
|
789
582
|
const obj = asRecord(value);
|
|
790
583
|
if (!obj) return undefined;
|
|
791
584
|
const isolatedRoles = parseStringList(obj.isolatedRoles);
|
|
792
|
-
const defaultRuntime = parseWithSchema(
|
|
793
|
-
|
|
794
|
-
Type.Literal("live-session"),
|
|
795
|
-
Type.Literal("child-process"),
|
|
796
|
-
]),
|
|
797
|
-
obj.defaultRuntime,
|
|
798
|
-
);
|
|
799
|
-
if (isolatedRoles === undefined && defaultRuntime === undefined)
|
|
800
|
-
return undefined;
|
|
585
|
+
const defaultRuntime = parseWithSchema(Type.Union([Type.Literal("live-session"), Type.Literal("child-process")]), obj.defaultRuntime);
|
|
586
|
+
if (isolatedRoles === undefined && defaultRuntime === undefined) return undefined;
|
|
801
587
|
return {
|
|
802
588
|
...(isolatedRoles !== undefined ? { isolatedRoles } : {}),
|
|
803
589
|
...(defaultRuntime !== undefined ? { defaultRuntime } : {}),
|
|
@@ -809,73 +595,29 @@ function parseRuntimeConfig(value: unknown): CrewRuntimeConfig | undefined {
|
|
|
809
595
|
if (!obj) return undefined;
|
|
810
596
|
const runtime: CrewRuntimeConfig = {
|
|
811
597
|
mode: parseWithSchema(
|
|
812
|
-
Type.Union([
|
|
813
|
-
Type.Literal("auto"),
|
|
814
|
-
Type.Literal("scaffold"),
|
|
815
|
-
Type.Literal("child-process"),
|
|
816
|
-
Type.Literal("live-session"),
|
|
817
|
-
]),
|
|
598
|
+
Type.Union([Type.Literal("auto"), Type.Literal("scaffold"), Type.Literal("child-process"), Type.Literal("live-session")]),
|
|
818
599
|
obj.mode,
|
|
819
600
|
),
|
|
820
|
-
preferLiveSession: parseWithSchema(
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
),
|
|
824
|
-
allowChildProcessFallback: parseWithSchema(
|
|
825
|
-
Type.Boolean(),
|
|
826
|
-
obj.allowChildProcessFallback,
|
|
827
|
-
),
|
|
828
|
-
maxTurns: parsePositiveInteger(
|
|
829
|
-
obj.maxTurns,
|
|
830
|
-
LIMIT_CEILINGS.runtimeMaxTurns,
|
|
831
|
-
),
|
|
832
|
-
graceTurns: parsePositiveInteger(
|
|
833
|
-
obj.graceTurns,
|
|
834
|
-
LIMIT_CEILINGS.runtimeGraceTurns,
|
|
835
|
-
),
|
|
601
|
+
preferLiveSession: parseWithSchema(Type.Boolean(), obj.preferLiveSession),
|
|
602
|
+
allowChildProcessFallback: parseWithSchema(Type.Boolean(), obj.allowChildProcessFallback),
|
|
603
|
+
maxTurns: parsePositiveInteger(obj.maxTurns, LIMIT_CEILINGS.runtimeMaxTurns),
|
|
604
|
+
graceTurns: parsePositiveInteger(obj.graceTurns, LIMIT_CEILINGS.runtimeGraceTurns),
|
|
836
605
|
inheritContext: parseWithSchema(Type.Boolean(), obj.inheritContext),
|
|
837
|
-
promptMode: parseWithSchema(
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
),
|
|
841
|
-
groupJoin: parseWithSchema(
|
|
842
|
-
Type.Union([
|
|
843
|
-
Type.Literal("off"),
|
|
844
|
-
Type.Literal("group"),
|
|
845
|
-
Type.Literal("smart"),
|
|
846
|
-
]),
|
|
847
|
-
obj.groupJoin,
|
|
848
|
-
),
|
|
849
|
-
groupJoinAckTimeoutMs: parsePositiveInteger(
|
|
850
|
-
obj.groupJoinAckTimeoutMs,
|
|
851
|
-
86_400_000,
|
|
852
|
-
),
|
|
853
|
-
requirePlanApproval: parseWithSchema(
|
|
854
|
-
Type.Boolean(),
|
|
855
|
-
obj.requirePlanApproval,
|
|
856
|
-
),
|
|
606
|
+
promptMode: parseWithSchema(Type.Union([Type.Literal("replace"), Type.Literal("append")]), obj.promptMode),
|
|
607
|
+
groupJoin: parseWithSchema(Type.Union([Type.Literal("off"), Type.Literal("group"), Type.Literal("smart")]), obj.groupJoin),
|
|
608
|
+
groupJoinAckTimeoutMs: parsePositiveInteger(obj.groupJoinAckTimeoutMs, 86_400_000),
|
|
609
|
+
requirePlanApproval: parseWithSchema(Type.Boolean(), obj.requirePlanApproval),
|
|
857
610
|
completionMutationGuard: parseWithSchema(
|
|
858
|
-
Type.Union([
|
|
859
|
-
Type.Literal("off"),
|
|
860
|
-
Type.Literal("warn"),
|
|
861
|
-
Type.Literal("fail"),
|
|
862
|
-
]),
|
|
611
|
+
Type.Union([Type.Literal("off"), Type.Literal("warn"), Type.Literal("fail")]),
|
|
863
612
|
obj.completionMutationGuard,
|
|
864
613
|
),
|
|
865
614
|
effectivenessGuard: parseWithSchema(
|
|
866
|
-
Type.Union([
|
|
867
|
-
Type.Literal("off"),
|
|
868
|
-
Type.Literal("warn"),
|
|
869
|
-
Type.Literal("block"),
|
|
870
|
-
Type.Literal("fail"),
|
|
871
|
-
]),
|
|
615
|
+
Type.Union([Type.Literal("off"), Type.Literal("warn"), Type.Literal("block"), Type.Literal("fail")]),
|
|
872
616
|
obj.effectivenessGuard,
|
|
873
617
|
),
|
|
874
618
|
isolationPolicy: parseIsolationPolicy(obj.isolationPolicy),
|
|
875
619
|
};
|
|
876
|
-
return Object.values(runtime).some((entry) => entry !== undefined)
|
|
877
|
-
? runtime
|
|
878
|
-
: undefined;
|
|
620
|
+
return Object.values(runtime).some((entry) => entry !== undefined) ? runtime : undefined;
|
|
879
621
|
}
|
|
880
622
|
|
|
881
623
|
function parseControlConfig(value: unknown): CrewControlConfig | undefined {
|
|
@@ -885,9 +627,7 @@ function parseControlConfig(value: unknown): CrewControlConfig | undefined {
|
|
|
885
627
|
enabled: parseWithSchema(Type.Boolean(), obj.enabled),
|
|
886
628
|
needsAttentionAfterMs: parsePositiveInteger(obj.needsAttentionAfterMs),
|
|
887
629
|
};
|
|
888
|
-
return Object.values(control).some((entry) => entry !== undefined)
|
|
889
|
-
? control
|
|
890
|
-
: undefined;
|
|
630
|
+
return Object.values(control).some((entry) => entry !== undefined) ? control : undefined;
|
|
891
631
|
}
|
|
892
632
|
|
|
893
633
|
function parseWorktreeConfig(value: unknown): CrewWorktreeConfig | undefined {
|
|
@@ -897,21 +637,14 @@ function parseWorktreeConfig(value: unknown): CrewWorktreeConfig | undefined {
|
|
|
897
637
|
const setupHook = rawSetupHook?.trim();
|
|
898
638
|
const worktree: CrewWorktreeConfig = {
|
|
899
639
|
setupHook: setupHook ? setupHook : undefined,
|
|
900
|
-
setupHookTimeoutMs: parsePositiveInteger(
|
|
901
|
-
obj.setupHookTimeoutMs,
|
|
902
|
-
300_000,
|
|
903
|
-
),
|
|
640
|
+
setupHookTimeoutMs: parsePositiveInteger(obj.setupHookTimeoutMs, 300_000),
|
|
904
641
|
linkNodeModules: parseWithSchema(Type.Boolean(), obj.linkNodeModules),
|
|
905
642
|
};
|
|
906
|
-
return Object.values(worktree).some((entry) => entry !== undefined)
|
|
907
|
-
? worktree
|
|
908
|
-
: undefined;
|
|
643
|
+
return Object.values(worktree).some((entry) => entry !== undefined) ? worktree : undefined;
|
|
909
644
|
}
|
|
910
645
|
|
|
911
646
|
/** Parse goalWrap config (RFC v0.5 vision: apply goal completion-guarantee to builtins). */
|
|
912
|
-
function parseGoalWrapConfig(
|
|
913
|
-
value: unknown,
|
|
914
|
-
): Record<string, GoalWrapWorkflowConfig> | undefined {
|
|
647
|
+
function parseGoalWrapConfig(value: unknown): Record<string, GoalWrapWorkflowConfig> | undefined {
|
|
915
648
|
const obj = asRecord(value);
|
|
916
649
|
if (!obj) return undefined;
|
|
917
650
|
const result: Record<string, GoalWrapWorkflowConfig> = {};
|
|
@@ -932,9 +665,12 @@ function parseGoalWrapConfig(
|
|
|
932
665
|
const commands = Array.isArray(verObj.commands)
|
|
933
666
|
? verObj.commands.filter((c): c is string => typeof c === "string" && c.length > 0)
|
|
934
667
|
: undefined;
|
|
935
|
-
const mode = verObj.mode === "text-only" ? "text-only" as const : undefined;
|
|
668
|
+
const mode = verObj.mode === "text-only" ? ("text-only" as const) : undefined;
|
|
936
669
|
if (commands || mode) {
|
|
937
|
-
parsed.verification = {
|
|
670
|
+
parsed.verification = {
|
|
671
|
+
...(commands ? { commands } : { commands: [] }),
|
|
672
|
+
...(mode ? { mode } : {}),
|
|
673
|
+
};
|
|
938
674
|
}
|
|
939
675
|
}
|
|
940
676
|
if (Object.values(parsed).some((v) => v !== undefined)) {
|
|
@@ -950,65 +686,34 @@ function parseAgentOverride(value: unknown): AgentOverrideConfig | undefined {
|
|
|
950
686
|
if (!obj) return undefined;
|
|
951
687
|
const override: AgentOverrideConfig = {
|
|
952
688
|
disabled: parseWithSchema(Type.Boolean(), obj.disabled),
|
|
953
|
-
model: parseWithSchema(
|
|
954
|
-
Type.Union([Type.String(), Type.Literal(false)]),
|
|
955
|
-
obj.model,
|
|
956
|
-
),
|
|
689
|
+
model: parseWithSchema(Type.Union([Type.String(), Type.Literal(false)]), obj.model),
|
|
957
690
|
fallbackModels: parseStringArrayOrFalse(obj.fallbackModels),
|
|
958
|
-
thinking: parseWithSchema(
|
|
959
|
-
Type.Union([Type.String(), Type.Literal(false)]),
|
|
960
|
-
obj.thinking,
|
|
961
|
-
),
|
|
691
|
+
thinking: parseWithSchema(Type.Union([Type.String(), Type.Literal(false)]), obj.thinking),
|
|
962
692
|
tools: parseStringArrayOrFalse(obj.tools),
|
|
963
693
|
skills: parseStringArrayOrFalse(obj.skills),
|
|
964
694
|
};
|
|
965
|
-
return Object.values(override).some((entry) => entry !== undefined)
|
|
966
|
-
? override
|
|
967
|
-
: undefined;
|
|
695
|
+
return Object.values(override).some((entry) => entry !== undefined) ? override : undefined;
|
|
968
696
|
}
|
|
969
697
|
|
|
970
698
|
function parseUiConfig(value: unknown): CrewUiConfig | undefined {
|
|
971
699
|
const obj = asRecord(value);
|
|
972
700
|
if (!obj) return undefined;
|
|
973
|
-
const rawWidgetPlacement = parseWithSchema(
|
|
974
|
-
|
|
975
|
-
obj.widgetPlacement,
|
|
976
|
-
);
|
|
977
|
-
const rawDashboardPlacement = parseWithSchema(
|
|
978
|
-
Type.Union([Type.Literal("center"), Type.Literal("right")]),
|
|
979
|
-
obj.dashboardPlacement,
|
|
980
|
-
);
|
|
701
|
+
const rawWidgetPlacement = parseWithSchema(Type.Union([Type.Literal("aboveEditor"), Type.Literal("belowEditor")]), obj.widgetPlacement);
|
|
702
|
+
const rawDashboardPlacement = parseWithSchema(Type.Union([Type.Literal("center"), Type.Literal("right")]), obj.dashboardPlacement);
|
|
981
703
|
const ui: CrewUiConfig = {
|
|
982
704
|
widgetPlacement: rawWidgetPlacement,
|
|
983
705
|
widgetMaxLines: parsePositiveInteger(obj.widgetMaxLines, 50),
|
|
984
706
|
powerbar: parseWithSchema(Type.Boolean(), obj.powerbar),
|
|
985
707
|
dashboardPlacement: rawDashboardPlacement,
|
|
986
708
|
dashboardWidth: parseIntegerInRange(obj.dashboardWidth, 32, 120),
|
|
987
|
-
dashboardLiveRefreshMs: parseIntegerInRange(
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
60_000,
|
|
991
|
-
),
|
|
992
|
-
autoOpenDashboard: parseWithSchema(
|
|
993
|
-
Type.Boolean(),
|
|
994
|
-
obj.autoOpenDashboard,
|
|
995
|
-
),
|
|
996
|
-
autoOpenDashboardForForegroundRuns: parseWithSchema(
|
|
997
|
-
Type.Boolean(),
|
|
998
|
-
obj.autoOpenDashboardForForegroundRuns,
|
|
999
|
-
),
|
|
709
|
+
dashboardLiveRefreshMs: parseIntegerInRange(obj.dashboardLiveRefreshMs, 250, 60_000),
|
|
710
|
+
autoOpenDashboard: parseWithSchema(Type.Boolean(), obj.autoOpenDashboard),
|
|
711
|
+
autoOpenDashboardForForegroundRuns: parseWithSchema(Type.Boolean(), obj.autoOpenDashboardForForegroundRuns),
|
|
1000
712
|
showModel: parseWithSchema(Type.Boolean(), obj.showModel),
|
|
1001
713
|
showTokens: parseWithSchema(Type.Boolean(), obj.showTokens),
|
|
1002
714
|
showTools: parseWithSchema(Type.Boolean(), obj.showTools),
|
|
1003
|
-
transcriptTailBytes: parseIntegerInRange(
|
|
1004
|
-
|
|
1005
|
-
1024,
|
|
1006
|
-
50 * 1024 * 1024,
|
|
1007
|
-
),
|
|
1008
|
-
mascotStyle: parseWithSchema(
|
|
1009
|
-
Type.Union([Type.Literal("cat"), Type.Literal("armin")]),
|
|
1010
|
-
obj.mascotStyle,
|
|
1011
|
-
),
|
|
715
|
+
transcriptTailBytes: parseIntegerInRange(obj.transcriptTailBytes, 1024, 50 * 1024 * 1024),
|
|
716
|
+
mascotStyle: parseWithSchema(Type.Union([Type.Literal("cat"), Type.Literal("armin")]), obj.mascotStyle),
|
|
1012
717
|
mascotEffect: parseWithSchema(
|
|
1013
718
|
Type.Union([
|
|
1014
719
|
Type.Literal("random"),
|
|
@@ -1024,23 +729,15 @@ function parseUiConfig(value: unknown): CrewUiConfig | undefined {
|
|
|
1024
729
|
obj.mascotEffect,
|
|
1025
730
|
),
|
|
1026
731
|
};
|
|
1027
|
-
return Object.values(ui).some((entry) => entry !== undefined)
|
|
1028
|
-
? ui
|
|
1029
|
-
: undefined;
|
|
732
|
+
return Object.values(ui).some((entry) => entry !== undefined) ? ui : undefined;
|
|
1030
733
|
}
|
|
1031
734
|
|
|
1032
735
|
function parseAgentsConfig(value: unknown): CrewAgentsConfig | undefined {
|
|
1033
736
|
const obj = asRecord(value);
|
|
1034
737
|
if (!obj) return undefined;
|
|
1035
738
|
const overrides: Record<string, AgentOverrideConfig> = {};
|
|
1036
|
-
if (
|
|
1037
|
-
obj.overrides
|
|
1038
|
-
typeof obj.overrides === "object" &&
|
|
1039
|
-
!Array.isArray(obj.overrides)
|
|
1040
|
-
) {
|
|
1041
|
-
for (const [name, rawOverride] of Object.entries(
|
|
1042
|
-
obj.overrides as Record<string, unknown>,
|
|
1043
|
-
)) {
|
|
739
|
+
if (obj.overrides && typeof obj.overrides === "object" && !Array.isArray(obj.overrides)) {
|
|
740
|
+
for (const [name, rawOverride] of Object.entries(obj.overrides as Record<string, unknown>)) {
|
|
1044
741
|
const parsed = parseAgentOverride(rawOverride);
|
|
1045
742
|
if (parsed && name.trim()) overrides[name.trim()] = parsed;
|
|
1046
743
|
}
|
|
@@ -1049,28 +746,18 @@ function parseAgentsConfig(value: unknown): CrewAgentsConfig | undefined {
|
|
|
1049
746
|
disableBuiltins: parseWithSchema(Type.Boolean(), obj.disableBuiltins),
|
|
1050
747
|
overrides: Object.keys(overrides).length > 0 ? overrides : undefined,
|
|
1051
748
|
};
|
|
1052
|
-
return Object.values(agents).some((entry) => entry !== undefined)
|
|
1053
|
-
? agents
|
|
1054
|
-
: undefined;
|
|
749
|
+
return Object.values(agents).some((entry) => entry !== undefined) ? agents : undefined;
|
|
1055
750
|
}
|
|
1056
751
|
|
|
1057
752
|
function parseToolsConfig(value: unknown): CrewToolsConfig | undefined {
|
|
1058
753
|
const obj = asRecord(value);
|
|
1059
754
|
if (!obj) return undefined;
|
|
1060
755
|
const tools: CrewToolsConfig = {
|
|
1061
|
-
enableClaudeStyleAliases: parseWithSchema(
|
|
1062
|
-
Type.Boolean(),
|
|
1063
|
-
obj.enableClaudeStyleAliases,
|
|
1064
|
-
),
|
|
756
|
+
enableClaudeStyleAliases: parseWithSchema(Type.Boolean(), obj.enableClaudeStyleAliases),
|
|
1065
757
|
enableSteer: parseWithSchema(Type.Boolean(), obj.enableSteer),
|
|
1066
|
-
terminateOnForeground: parseWithSchema(
|
|
1067
|
-
Type.Boolean(),
|
|
1068
|
-
obj.terminateOnForeground,
|
|
1069
|
-
),
|
|
758
|
+
terminateOnForeground: parseWithSchema(Type.Boolean(), obj.terminateOnForeground),
|
|
1070
759
|
};
|
|
1071
|
-
return Object.values(tools).some((entry) => entry !== undefined)
|
|
1072
|
-
? tools
|
|
1073
|
-
: undefined;
|
|
760
|
+
return Object.values(tools).some((entry) => entry !== undefined) ? tools : undefined;
|
|
1074
761
|
}
|
|
1075
762
|
|
|
1076
763
|
function parseTelemetryConfig(value: unknown): CrewTelemetryConfig | undefined {
|
|
@@ -1079,125 +766,68 @@ function parseTelemetryConfig(value: unknown): CrewTelemetryConfig | undefined {
|
|
|
1079
766
|
const telemetry: CrewTelemetryConfig = {
|
|
1080
767
|
enabled: parseWithSchema(Type.Boolean(), obj.enabled),
|
|
1081
768
|
};
|
|
1082
|
-
return Object.values(telemetry).some((entry) => entry !== undefined)
|
|
1083
|
-
? telemetry
|
|
1084
|
-
: undefined;
|
|
769
|
+
return Object.values(telemetry).some((entry) => entry !== undefined) ? telemetry : undefined;
|
|
1085
770
|
}
|
|
1086
771
|
|
|
1087
772
|
function parsePolicyConfig(value: unknown): CrewPolicyConfig | undefined {
|
|
1088
773
|
const obj = asRecord(value);
|
|
1089
774
|
if (!obj) return undefined;
|
|
1090
775
|
const policy: CrewPolicyConfig = {
|
|
1091
|
-
requireIntentForDestructiveActions: parseWithSchema(
|
|
1092
|
-
|
|
1093
|
-
obj.requireIntentForDestructiveActions,
|
|
1094
|
-
),
|
|
1095
|
-
disabledCapabilities: parseWithSchema(
|
|
1096
|
-
Type.Array(Type.String()),
|
|
1097
|
-
obj.disabledCapabilities,
|
|
1098
|
-
),
|
|
776
|
+
requireIntentForDestructiveActions: parseWithSchema(Type.Boolean(), obj.requireIntentForDestructiveActions),
|
|
777
|
+
disabledCapabilities: parseWithSchema(Type.Array(Type.String()), obj.disabledCapabilities),
|
|
1099
778
|
};
|
|
1100
|
-
return Object.values(policy).some((entry) => entry !== undefined)
|
|
1101
|
-
? policy
|
|
1102
|
-
: undefined;
|
|
779
|
+
return Object.values(policy).some((entry) => entry !== undefined) ? policy : undefined;
|
|
1103
780
|
}
|
|
1104
781
|
|
|
1105
|
-
function parseNotificationsConfig(
|
|
1106
|
-
value: unknown,
|
|
1107
|
-
): CrewNotificationsConfig | undefined {
|
|
782
|
+
function parseNotificationsConfig(value: unknown): CrewNotificationsConfig | undefined {
|
|
1108
783
|
const obj = asRecord(value);
|
|
1109
784
|
if (!obj) return undefined;
|
|
1110
785
|
const notifications: CrewNotificationsConfig = {
|
|
1111
786
|
enabled: parseWithSchema(Type.Boolean(), obj.enabled),
|
|
1112
787
|
severityFilter: parseWithSchema(
|
|
1113
|
-
Type.Array(
|
|
1114
|
-
Type.Union([
|
|
1115
|
-
Type.Literal("info"),
|
|
1116
|
-
Type.Literal("warning"),
|
|
1117
|
-
Type.Literal("error"),
|
|
1118
|
-
Type.Literal("critical"),
|
|
1119
|
-
]),
|
|
1120
|
-
),
|
|
788
|
+
Type.Array(Type.Union([Type.Literal("info"), Type.Literal("warning"), Type.Literal("error"), Type.Literal("critical")])),
|
|
1121
789
|
obj.severityFilter,
|
|
1122
790
|
),
|
|
1123
|
-
dedupWindowMs: parsePositiveInteger(
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
),
|
|
1127
|
-
batchWindowMs: parseWithSchema(
|
|
1128
|
-
Type.Integer({ minimum: 0, maximum: 60_000 }),
|
|
1129
|
-
obj.batchWindowMs,
|
|
1130
|
-
),
|
|
1131
|
-
quietHours: parseWithSchema(
|
|
1132
|
-
Type.String({ pattern: "^\\d{2}:\\d{2}-\\d{2}:\\d{2}$" }),
|
|
1133
|
-
obj.quietHours,
|
|
1134
|
-
),
|
|
791
|
+
dedupWindowMs: parsePositiveInteger(obj.dedupWindowMs, 24 * 60 * 60 * 1000),
|
|
792
|
+
batchWindowMs: parseWithSchema(Type.Integer({ minimum: 0, maximum: 60_000 }), obj.batchWindowMs),
|
|
793
|
+
quietHours: parseWithSchema(Type.String({ pattern: "^\\d{2}:\\d{2}-\\d{2}:\\d{2}$" }), obj.quietHours),
|
|
1135
794
|
sinkRetentionDays: parsePositiveInteger(obj.sinkRetentionDays, 90),
|
|
1136
795
|
};
|
|
1137
|
-
return Object.values(notifications).some((entry) => entry !== undefined)
|
|
1138
|
-
? notifications
|
|
1139
|
-
: undefined;
|
|
796
|
+
return Object.values(notifications).some((entry) => entry !== undefined) ? notifications : undefined;
|
|
1140
797
|
}
|
|
1141
798
|
|
|
1142
|
-
function parseObservabilityConfig(
|
|
1143
|
-
value: unknown,
|
|
1144
|
-
): CrewObservabilityConfig | undefined {
|
|
799
|
+
function parseObservabilityConfig(value: unknown): CrewObservabilityConfig | undefined {
|
|
1145
800
|
const obj = asRecord(value);
|
|
1146
801
|
if (!obj) return undefined;
|
|
1147
802
|
const observability: CrewObservabilityConfig = {
|
|
1148
803
|
enabled: parseWithSchema(Type.Boolean(), obj.enabled),
|
|
1149
|
-
pollIntervalMs: parseWithSchema(
|
|
1150
|
-
Type.Integer({ minimum: 1000, maximum: 60_000 }),
|
|
1151
|
-
obj.pollIntervalMs,
|
|
1152
|
-
),
|
|
804
|
+
pollIntervalMs: parseWithSchema(Type.Integer({ minimum: 1000, maximum: 60_000 }), obj.pollIntervalMs),
|
|
1153
805
|
metricRetentionDays: parsePositiveInteger(obj.metricRetentionDays, 365),
|
|
1154
806
|
};
|
|
1155
|
-
return Object.values(observability).some((entry) => entry !== undefined)
|
|
1156
|
-
? observability
|
|
1157
|
-
: undefined;
|
|
807
|
+
return Object.values(observability).some((entry) => entry !== undefined) ? observability : undefined;
|
|
1158
808
|
}
|
|
1159
809
|
|
|
1160
|
-
function parseReliabilityConfig(
|
|
1161
|
-
value: unknown,
|
|
1162
|
-
): CrewReliabilityConfig | undefined {
|
|
810
|
+
function parseReliabilityConfig(value: unknown): CrewReliabilityConfig | undefined {
|
|
1163
811
|
const obj = asRecord(value);
|
|
1164
812
|
if (!obj) return undefined;
|
|
1165
813
|
const retryObj = asRecord(obj.retryPolicy);
|
|
1166
814
|
const retryPolicy: CrewRetryPolicyConfig | undefined = retryObj
|
|
1167
815
|
? {
|
|
1168
816
|
maxAttempts: parsePositiveInteger(retryObj.maxAttempts, 10),
|
|
1169
|
-
backoffMs: parseWithSchema(
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
),
|
|
1173
|
-
jitterRatio: parseWithSchema(
|
|
1174
|
-
Type.Number({ minimum: 0, maximum: 1 }),
|
|
1175
|
-
retryObj.jitterRatio,
|
|
1176
|
-
),
|
|
1177
|
-
exponentialFactor: parseWithSchema(
|
|
1178
|
-
Type.Number({ minimum: 1, maximum: 5 }),
|
|
1179
|
-
retryObj.exponentialFactor,
|
|
1180
|
-
),
|
|
817
|
+
backoffMs: parseWithSchema(Type.Integer({ minimum: 100, maximum: 60_000 }), retryObj.backoffMs),
|
|
818
|
+
jitterRatio: parseWithSchema(Type.Number({ minimum: 0, maximum: 1 }), retryObj.jitterRatio),
|
|
819
|
+
exponentialFactor: parseWithSchema(Type.Number({ minimum: 1, maximum: 5 }), retryObj.exponentialFactor),
|
|
1181
820
|
retryableErrors: parseStringList(retryObj.retryableErrors),
|
|
1182
821
|
}
|
|
1183
822
|
: undefined;
|
|
1184
823
|
const reliability: CrewReliabilityConfig = {
|
|
1185
824
|
autoRetry: parseWithSchema(Type.Boolean(), obj.autoRetry),
|
|
1186
|
-
retryPolicy:
|
|
1187
|
-
retryPolicy &&
|
|
1188
|
-
Object.values(retryPolicy).some((entry) => entry !== undefined)
|
|
1189
|
-
? retryPolicy
|
|
1190
|
-
: undefined,
|
|
825
|
+
retryPolicy: retryPolicy && Object.values(retryPolicy).some((entry) => entry !== undefined) ? retryPolicy : undefined,
|
|
1191
826
|
autoRecover: parseWithSchema(Type.Boolean(), obj.autoRecover),
|
|
1192
827
|
deadletterThreshold: parsePositiveInteger(obj.deadletterThreshold),
|
|
1193
|
-
cleanupOrphanedTempDirs: parseWithSchema(
|
|
1194
|
-
Type.Boolean(),
|
|
1195
|
-
obj.cleanupOrphanedTempDirs,
|
|
1196
|
-
),
|
|
828
|
+
cleanupOrphanedTempDirs: parseWithSchema(Type.Boolean(), obj.cleanupOrphanedTempDirs),
|
|
1197
829
|
};
|
|
1198
|
-
return Object.values(reliability).some((entry) => entry !== undefined)
|
|
1199
|
-
? reliability
|
|
1200
|
-
: undefined;
|
|
830
|
+
return Object.values(reliability).some((entry) => entry !== undefined) ? reliability : undefined;
|
|
1201
831
|
}
|
|
1202
832
|
|
|
1203
833
|
function parseOtlpConfig(value: unknown): CrewOtlpConfig | undefined {
|
|
@@ -1236,14 +866,9 @@ function parseOtlpConfig(value: unknown): CrewOtlpConfig | undefined {
|
|
|
1236
866
|
enabled: parseWithSchema(Type.Boolean(), obj.enabled),
|
|
1237
867
|
endpoint: parseWithSchema(Type.String({ minLength: 1 }), obj.endpoint),
|
|
1238
868
|
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
|
1239
|
-
intervalMs: parseWithSchema(
|
|
1240
|
-
Type.Integer({ minimum: 5000 }),
|
|
1241
|
-
obj.intervalMs,
|
|
1242
|
-
),
|
|
869
|
+
intervalMs: parseWithSchema(Type.Integer({ minimum: 5000 }), obj.intervalMs),
|
|
1243
870
|
};
|
|
1244
|
-
return Object.values(otlp).some((entry) => entry !== undefined)
|
|
1245
|
-
? otlp
|
|
1246
|
-
: undefined;
|
|
871
|
+
return Object.values(otlp).some((entry) => entry !== undefined) ? otlp : undefined;
|
|
1247
872
|
}
|
|
1248
873
|
|
|
1249
874
|
export function parseConfig(raw: unknown): PiTeamsConfig {
|
|
@@ -1252,14 +877,8 @@ export function parseConfig(raw: unknown): PiTeamsConfig {
|
|
|
1252
877
|
return {
|
|
1253
878
|
asyncByDefault: parseWithSchema(Type.Boolean(), obj.asyncByDefault),
|
|
1254
879
|
executeWorkers: parseWithSchema(Type.Boolean(), obj.executeWorkers),
|
|
1255
|
-
notifierIntervalMs: parseWithSchema(
|
|
1256
|
-
|
|
1257
|
-
obj.notifierIntervalMs,
|
|
1258
|
-
),
|
|
1259
|
-
requireCleanWorktreeLeader: parseWithSchema(
|
|
1260
|
-
Type.Boolean(),
|
|
1261
|
-
obj.requireCleanWorktreeLeader,
|
|
1262
|
-
),
|
|
880
|
+
notifierIntervalMs: parseWithSchema(Type.Number({ minimum: 1_000 }), obj.notifierIntervalMs),
|
|
881
|
+
requireCleanWorktreeLeader: parseWithSchema(Type.Boolean(), obj.requireCleanWorktreeLeader),
|
|
1263
882
|
autonomous: parseAutonomousConfig(obj.autonomous),
|
|
1264
883
|
limits: parseLimitsConfig(obj.limits),
|
|
1265
884
|
runtime: parseRuntimeConfig(obj.runtime),
|
|
@@ -1279,8 +898,7 @@ export function parseConfig(raw: unknown): PiTeamsConfig {
|
|
|
1279
898
|
}
|
|
1280
899
|
|
|
1281
900
|
export function parseConfigWithWarnings(raw: unknown): ConfigValidationResult {
|
|
1282
|
-
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
1283
|
-
return { config: {}, warnings: [] };
|
|
901
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return { config: {}, warnings: [] };
|
|
1284
902
|
const parsed = parseConfig(raw);
|
|
1285
903
|
const warnings = validateConfigWithWarnings(raw as Record<string, unknown>);
|
|
1286
904
|
return { config: parsed, warnings };
|
|
@@ -1292,8 +910,7 @@ function unsetPath(record: Record<string, unknown>, dottedPath: string): void {
|
|
|
1292
910
|
let target: Record<string, unknown> = record;
|
|
1293
911
|
for (const part of parts.slice(0, -1)) {
|
|
1294
912
|
const current = target[part];
|
|
1295
|
-
if (!current || typeof current !== "object" || Array.isArray(current))
|
|
1296
|
-
return;
|
|
913
|
+
if (!current || typeof current !== "object" || Array.isArray(current)) return;
|
|
1297
914
|
target = current as Record<string, unknown>;
|
|
1298
915
|
}
|
|
1299
916
|
delete target[parts[parts.length - 1]!];
|
|
@@ -1333,17 +950,14 @@ function readOptionalConfig(filePath: string): {
|
|
|
1333
950
|
config: PiTeamsConfig;
|
|
1334
951
|
warnings: string[];
|
|
1335
952
|
} {
|
|
1336
|
-
if (!fs.existsSync(filePath))
|
|
1337
|
-
return { exists: false, config: {}, warnings: [] };
|
|
953
|
+
if (!fs.existsSync(filePath)) return { exists: false, config: {}, warnings: [] };
|
|
1338
954
|
try {
|
|
1339
955
|
const raw = readConfigRecord(filePath);
|
|
1340
956
|
const parsed = parseConfigWithWarnings(raw);
|
|
1341
957
|
return {
|
|
1342
958
|
exists: true,
|
|
1343
959
|
config: parsed.config,
|
|
1344
|
-
warnings: parsed.warnings.map(
|
|
1345
|
-
(warning) => `${filePath}: ${warning}`,
|
|
1346
|
-
),
|
|
960
|
+
warnings: parsed.warnings.map((warning) => `${filePath}: ${warning}`),
|
|
1347
961
|
};
|
|
1348
962
|
} catch (error) {
|
|
1349
963
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1367,12 +981,7 @@ export function loadConfig(cwd?: string): LoadedPiTeamsConfig {
|
|
|
1367
981
|
}
|
|
1368
982
|
const userConfig = readOptionalConfig(filePath);
|
|
1369
983
|
warnings.push(...userConfig.warnings);
|
|
1370
|
-
let config = mergeConfig(
|
|
1371
|
-
legacyConfig.exists && legacyPath !== filePath
|
|
1372
|
-
? legacyConfig.config
|
|
1373
|
-
: {},
|
|
1374
|
-
userConfig.config,
|
|
1375
|
-
);
|
|
984
|
+
let config = mergeConfig(legacyConfig.exists && legacyPath !== filePath ? legacyConfig.config : {}, userConfig.config);
|
|
1376
985
|
if (cwd) {
|
|
1377
986
|
const projectPath = projectConfigPath(cwd);
|
|
1378
987
|
const projectConfig = readOptionalConfig(projectPath);
|
|
@@ -1381,15 +990,8 @@ export function loadConfig(cwd?: string): LoadedPiTeamsConfig {
|
|
|
1381
990
|
// Sensitive fields have already been sanitized by sanitizeProjectConfig.
|
|
1382
991
|
let effectiveConfig = {};
|
|
1383
992
|
if (projectConfig.exists) {
|
|
1384
|
-
const projectSafeConfig = sanitizeProjectConfig(
|
|
1385
|
-
|
|
1386
|
-
config,
|
|
1387
|
-
projectConfig.config,
|
|
1388
|
-
);
|
|
1389
|
-
warnings.push(
|
|
1390
|
-
...projectConfig.warnings,
|
|
1391
|
-
...projectSafeConfig.warnings,
|
|
1392
|
-
);
|
|
993
|
+
const projectSafeConfig = sanitizeProjectConfig(projectPath, config, projectConfig.config);
|
|
994
|
+
warnings.push(...projectConfig.warnings, ...projectSafeConfig.warnings);
|
|
1393
995
|
// merge(base=projectConfig, override=userConfig) → override wins
|
|
1394
996
|
effectiveConfig = mergeConfig(effectiveConfig, projectSafeConfig.config);
|
|
1395
997
|
}
|
|
@@ -1397,7 +999,6 @@ export function loadConfig(cwd?: string): LoadedPiTeamsConfig {
|
|
|
1397
999
|
effectiveConfig = mergeConfig(effectiveConfig, config);
|
|
1398
1000
|
config = effectiveConfig;
|
|
1399
1001
|
|
|
1400
|
-
|
|
1401
1002
|
// `.pi/pi-crew.json` is the project-owned config file.
|
|
1402
1003
|
// Merge project config FIRST (base), then user config on top (override).
|
|
1403
1004
|
// This ensures user preferences always take precedence over project settings.
|
|
@@ -1406,11 +1007,7 @@ export function loadConfig(cwd?: string): LoadedPiTeamsConfig {
|
|
|
1406
1007
|
const piCrewJsonConfig = readOptionalConfig(piCrewJsonPath);
|
|
1407
1008
|
if (piCrewJsonConfig.exists) {
|
|
1408
1009
|
warnings.push(...piCrewJsonConfig.warnings);
|
|
1409
|
-
const projectPart = sanitizeProjectConfig(
|
|
1410
|
-
piCrewJsonPath,
|
|
1411
|
-
config,
|
|
1412
|
-
piCrewJsonConfig.config,
|
|
1413
|
-
);
|
|
1010
|
+
const projectPart = sanitizeProjectConfig(piCrewJsonPath, config, piCrewJsonConfig.config);
|
|
1414
1011
|
warnings.push(...projectPart.warnings);
|
|
1415
1012
|
// base=project config, override=user config → user wins
|
|
1416
1013
|
const mergedProject = mergeConfig(projectPart.config, config);
|
|
@@ -1426,45 +1023,30 @@ export function loadConfig(cwd?: string): LoadedPiTeamsConfig {
|
|
|
1426
1023
|
};
|
|
1427
1024
|
}
|
|
1428
1025
|
|
|
1429
|
-
export function updateConfig(
|
|
1430
|
-
|
|
1431
|
-
options: UpdateConfigOptions = {},
|
|
1432
|
-
): SavedPiTeamsConfig {
|
|
1433
|
-
const filePath =
|
|
1434
|
-
options.scope === "project" && options.cwd
|
|
1435
|
-
? projectConfigPath(options.cwd)
|
|
1436
|
-
: configPath();
|
|
1026
|
+
export function updateConfig(patch: PiTeamsConfig, options: UpdateConfigOptions = {}): SavedPiTeamsConfig {
|
|
1027
|
+
const filePath = options.scope === "project" && options.cwd ? projectConfigPath(options.cwd) : configPath();
|
|
1437
1028
|
const lockPath = filePath + ".lock";
|
|
1438
1029
|
return withFileLockSync(lockPath, () => {
|
|
1439
1030
|
let current: Record<string, unknown>;
|
|
1440
1031
|
try {
|
|
1441
1032
|
current = readConfigRecord(filePath);
|
|
1442
1033
|
} catch (error) {
|
|
1443
|
-
const message =
|
|
1444
|
-
error instanceof Error ? error.message : String(error);
|
|
1034
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1445
1035
|
throw new Error(`Could not update pi-crew config: ${message}`);
|
|
1446
1036
|
}
|
|
1447
1037
|
let merged = mergeConfig(parseConfig(current), patch);
|
|
1448
1038
|
if (options.unsetPaths?.length) {
|
|
1449
|
-
const raw = JSON.parse(JSON.stringify(merged)) as Record<
|
|
1450
|
-
string,
|
|
1451
|
-
unknown
|
|
1452
|
-
>;
|
|
1039
|
+
const raw = JSON.parse(JSON.stringify(merged)) as Record<string, unknown>;
|
|
1453
1040
|
for (const unset of options.unsetPaths) unsetPath(raw, unset);
|
|
1454
1041
|
merged = parseConfig(raw);
|
|
1455
1042
|
}
|
|
1456
1043
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
1457
|
-
atomicWriteFile(
|
|
1458
|
-
filePath,
|
|
1459
|
-
`${JSON.stringify(merged, null, 2)}\n`,
|
|
1460
|
-
);
|
|
1044
|
+
atomicWriteFile(filePath, `${JSON.stringify(merged, null, 2)}\n`);
|
|
1461
1045
|
return { path: filePath, config: merged };
|
|
1462
1046
|
});
|
|
1463
1047
|
}
|
|
1464
1048
|
|
|
1465
|
-
export function updateAutonomousConfig(
|
|
1466
|
-
patch: PiTeamsAutonomousConfig,
|
|
1467
|
-
): SavedPiTeamsConfig {
|
|
1049
|
+
export function updateAutonomousConfig(patch: PiTeamsAutonomousConfig): SavedPiTeamsConfig {
|
|
1468
1050
|
const filePath = configPath();
|
|
1469
1051
|
const lockPath = filePath + ".lock";
|
|
1470
1052
|
return withFileLockSync(lockPath, () => {
|
|
@@ -1472,21 +1054,15 @@ export function updateAutonomousConfig(
|
|
|
1472
1054
|
try {
|
|
1473
1055
|
current = readConfigRecord(filePath);
|
|
1474
1056
|
} catch (error) {
|
|
1475
|
-
const message =
|
|
1476
|
-
error instanceof Error ? error.message : String(error);
|
|
1057
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1477
1058
|
throw new Error(`Could not update pi-crew config: ${message}`);
|
|
1478
1059
|
}
|
|
1479
1060
|
const currentAutonomous =
|
|
1480
|
-
current.autonomous &&
|
|
1481
|
-
typeof current.autonomous === "object" &&
|
|
1482
|
-
!Array.isArray(current.autonomous)
|
|
1061
|
+
current.autonomous && typeof current.autonomous === "object" && !Array.isArray(current.autonomous)
|
|
1483
1062
|
? (current.autonomous as Record<string, unknown>)
|
|
1484
1063
|
: {};
|
|
1485
1064
|
current.autonomous = { ...currentAutonomous, ...patch };
|
|
1486
|
-
atomicWriteFile(
|
|
1487
|
-
filePath,
|
|
1488
|
-
`${JSON.stringify(current, null, 2)}\n`,
|
|
1489
|
-
);
|
|
1065
|
+
atomicWriteFile(filePath, `${JSON.stringify(current, null, 2)}\n`);
|
|
1490
1066
|
return { path: filePath, config: parseConfig(current) };
|
|
1491
1067
|
});
|
|
1492
1068
|
}
|