gentle-pi 3.7.0 → 4.1.0
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/README.md +155 -10
- package/assets/agents/gentle-ai-explore.md +13 -6
- package/assets/agents/gentle-ai-verify.md +27 -6
- package/assets/agents/gentle-ai-worker.md +13 -15
- package/assets/install-wizard/index.html +47 -0
- package/assets/install-wizard/wizard.css +1279 -0
- package/assets/install-wizard/wizard.js +951 -0
- package/assets/orchestrator-delegation.md +75 -169
- package/assets/orchestrator-memory.md +12 -26
- package/assets/orchestrator-prompts.md +48 -0
- package/assets/orchestrator-skills.md +1 -1
- package/assets/orchestrator-tracking.md +53 -0
- package/assets/orchestrator-verification.md +28 -0
- package/assets/orchestrator-writer.md +38 -0
- package/assets/orchestrator.md +30 -38
- package/assets/sounds/LICENSE.md +52 -0
- package/assets/sounds/attention.wav +0 -0
- package/assets/sounds/error.wav +0 -0
- package/assets/sounds/success.wav +0 -0
- package/assets/support/strict-tdd-verify.md +4 -266
- package/assets/support/strict-tdd.md +8 -360
- package/bin/engram-guard +9 -0
- package/bin/gentle-shell-install.mjs +302 -0
- package/bin/gentle-shell.mjs +956 -50
- package/contracts/review-provider-contract-mirror/provider-contract.lock.json +7 -7
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/manifest.json +2 -2
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/orchestration/pi.md +16 -4
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/generated/provider-capabilities.baseline.json +2 -2
- package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/generated/provider-roles.baseline.json +1 -1
- package/docs/bundled-install.md +397 -0
- package/docs/delegated-verification.md +27 -4
- package/docs/gentle-agents-activity.md +537 -0
- package/docs/gentle-shell-uninstall.md +136 -0
- package/docs/gentle-shell.md +165 -51
- package/docs/install-wizard.md +2040 -0
- package/docs/native-authority-architecture.md +3 -3
- package/docs/permission-modes.md +82 -0
- package/docs/prompt-history.md +280 -0
- package/docs/readme-reference.md +364 -231
- package/docs/render-cache-performance.md +28 -0
- package/docs/sound-notifications-proposal.md +191 -0
- package/docs/sound-notifications.md +114 -0
- package/docs/telemetry.md +1 -1
- package/docs/yolo-mode.md +86 -0
- package/extensions/child-context.ts +35 -0
- package/extensions/child-safety.ts +25 -0
- package/extensions/codegraph-tools.ts +18 -5
- package/extensions/gentle-agents.ts +1093 -326
- package/extensions/gentle-ai.ts +1636 -890
- package/extensions/gentle-herdr-activity.ts +81 -0
- package/extensions/gentle-notifications.ts +104 -0
- package/extensions/gentle-shell.ts +1619 -136
- package/extensions/gentle-stats.ts +101 -0
- package/extensions/gentle-todo.ts +126 -20
- package/extensions/history/atomic-write.ts +38 -0
- package/extensions/history/hide-prompts.ts +183 -0
- package/extensions/history/index.ts +1424 -0
- package/extensions/history/load-shared-history.ts +39 -0
- package/extensions/history/selector-helpers.ts +538 -0
- package/extensions/history/session-scan.ts +233 -0
- package/extensions/history/store.ts +1119 -0
- package/extensions/nan-provider.ts +6 -0
- package/extensions/pi-pretty.ts +39 -3
- package/extensions/quiet-tools.ts +254 -92
- package/extensions/resume-hint.ts +60 -0
- package/extensions/skill-registry.ts +191 -58
- package/extensions/startup-banner.ts +71 -33
- package/lib/agent-assets.ts +604 -0
- package/lib/agent-home.ts +10 -1
- package/lib/agent-model-resolution.ts +80 -0
- package/lib/agent-profile-pin.ts +12 -0
- package/lib/agent-profiles.ts +13 -3
- package/lib/agents-completion-delivery.ts +11 -3
- package/lib/agents-message-delivery.ts +181 -0
- package/lib/agents-messaging.ts +1 -1
- package/lib/agents-protocol.ts +99 -0
- package/lib/agents-runner.ts +180 -107
- package/lib/agents-view.ts +26 -3
- package/lib/agents-widget.ts +16 -9
- package/lib/append-system-prompt.ts +21 -0
- package/lib/auto-permission-classifier.ts +280 -0
- package/lib/auto-permission-runtime.ts +268 -0
- package/lib/background-jobs-tools.ts +202 -0
- package/lib/background-jobs.ts +253 -0
- package/lib/background-monitor.ts +154 -0
- package/lib/bounded-writer-admission.ts +220 -0
- package/lib/builtin-codemode-optout.ts +166 -0
- package/lib/card-content-policy.ts +85 -0
- package/lib/card-style-policy.ts +60 -0
- package/lib/child-context-files.ts +166 -0
- package/lib/child-package-injection.ts +62 -0
- package/lib/codemode-renderer.ts +195 -0
- package/lib/command-palette-catalog.ts +5 -10
- package/lib/command-palette.ts +25 -14
- package/lib/context-threshold.ts +26 -0
- package/lib/destructive-command-guard.ts +144 -0
- package/lib/gentle-ai-binary.ts +109 -4
- package/lib/gentle-ai-elapsed-store.ts +87 -0
- package/lib/gentle-ai-renderer.ts +196 -39
- package/lib/gentle-shell-bundled.ts +517 -0
- package/lib/gentle-shell-launcher.ts +89 -27
- package/lib/gentle-shell-resume-hint.ts +176 -0
- package/lib/gentle-shell-startup-flow.ts +54 -0
- package/lib/gentle-shell-startup-update.ts +92 -0
- package/lib/gentle-shell-uninstall.ts +840 -0
- package/lib/gentle-shell-update-panel.ts +231 -0
- package/lib/herdr-activity.ts +183 -0
- package/lib/history-capture-policy.ts +95 -0
- package/lib/inprocess-reviewer.ts +3 -0
- package/lib/jobs-sidebar-state.ts +20 -0
- package/lib/jobs-view.ts +161 -0
- package/lib/model-price-ratio.ts +83 -0
- package/lib/model-routing-authority.ts +5 -1
- package/lib/nan-provider.ts +249 -0
- package/lib/native-review-cli.ts +242 -180
- package/lib/notification-audio-native.ts +162 -0
- package/lib/notification-audio-windows.ts +399 -0
- package/lib/notification-audio.ts +332 -0
- package/lib/notification-customize.ts +241 -0
- package/lib/notification-events.ts +113 -0
- package/lib/notification-policy.ts +148 -0
- package/lib/notification-pulse-client.ts +367 -0
- package/lib/notification-pulse-protocol.ts +305 -0
- package/lib/notification-pulse-stream.ts +213 -0
- package/lib/notification-pulse-worker.ts +66 -0
- package/lib/notification-scheduler.ts +173 -0
- package/lib/notification-service.ts +202 -0
- package/lib/notification-ui.ts +87 -0
- package/lib/odd-phase-inference.ts +231 -0
- package/lib/odd-phase.ts +141 -0
- package/lib/orchestrator-alias.ts +20 -0
- package/lib/orchestrator-catalog.ts +79 -0
- package/lib/orchestrator-consultation.ts +61 -0
- package/lib/orchestrator-discovery.ts +49 -0
- package/lib/orchestrator-helper-consent.ts +122 -0
- package/lib/orchestrator-helper.ts +167 -0
- package/lib/orchestrator-message-card.ts +105 -0
- package/lib/orchestrator-presence.ts +115 -14
- package/lib/orchestrator-scope.ts +80 -0
- package/lib/orchestrator-state.ts +107 -0
- package/lib/orchestrator-tool-card.ts +280 -0
- package/lib/orchestrator-work-search.ts +218 -0
- package/lib/orchestrator-work.ts +79 -0
- package/lib/overlay-repaint.ts +26 -0
- package/lib/permission-mode-policy.ts +59 -0
- package/lib/pi-tui-keys.ts +53 -0
- package/lib/review-candidate-view-owner.ts +73 -21
- package/lib/review-candidate-view.ts +115 -27
- package/lib/review-compact-contract.ts +2 -2
- package/lib/review-host-relay.ts +7 -2
- package/lib/review-integration-v2.ts +100 -22
- package/lib/review-publication-gate.ts +1 -1
- package/lib/review-reminder-receipt.ts +48 -8
- package/lib/review-risk-assessment.ts +227 -56
- package/lib/review-sidebar-state.ts +224 -0
- package/lib/review-transaction.ts +72 -3
- package/lib/runtime-metrics-children.ts +1 -1
- package/lib/runtime-metrics-native.ts +3 -2
- package/lib/selection-engine.ts +527 -0
- package/lib/session-change-capture.ts +5 -4
- package/lib/session-changes.ts +27 -22
- package/lib/session-messaging-grants.ts +135 -0
- package/lib/session-profile-binding.ts +83 -0
- package/lib/session-profile-freeze.ts +266 -0
- package/lib/session-worktree-registry.ts +87 -9
- package/lib/shell-bar.ts +215 -30
- package/lib/shell-card.ts +287 -18
- package/lib/shell-changes-view.ts +42 -14
- package/lib/shell-changes.ts +86 -71
- package/lib/shell-prompt.ts +102 -6
- package/lib/shell-sidebar-layout.ts +83 -15
- package/lib/shell-sidebar.ts +15 -1
- package/lib/shell-todo.ts +84 -31
- package/lib/shell-usage-view.ts +9 -4
- package/lib/shell-usage.ts +70 -13
- package/lib/stats-collector.ts +381 -0
- package/lib/stats-view.ts +431 -0
- package/lib/status-timing-diagnostics.ts +201 -0
- package/lib/telemetry-trigger.ts +3 -1
- package/lib/theme-customization.ts +52 -0
- package/lib/vim-editor-adapter.ts +383 -0
- package/lib/vim-normal-engine.ts +154 -0
- package/lib/vim-operator-engine.ts +416 -0
- package/lib/vim-policy.ts +49 -0
- package/lib/vim-visual-engine.ts +107 -0
- package/lib/visual-customization-policy.ts +119 -0
- package/lib/visual-customize-view.ts +481 -0
- package/lib/visual-profiles.ts +228 -0
- package/lib/writer-surfaces.ts +162 -0
- package/lib/yolo-session-policy.ts +317 -0
- package/package.json +20 -8
- package/runtime/child-package-injection.mjs +63 -0
- package/runtime/gentle-ai-binary.mjs +109 -4
- package/runtime/gentle-shell-bundled.mjs +518 -0
- package/runtime/gentle-shell-launcher.mjs +87 -25
- package/runtime/gentle-shell-resume-hint.mjs +177 -0
- package/runtime/gentle-shell-startup-flow.mjs +55 -0
- package/runtime/gentle-shell-startup-update.mjs +93 -0
- package/runtime/gentle-shell-uninstall.mjs +841 -0
- package/runtime/gentle-shell-update-panel.mjs +232 -0
- package/runtime/native-review-cli.mjs +239 -177
- package/runtime/review-integration-v2.mjs +100 -22
- package/runtime/review-risk-assessment.mjs +225 -54
- package/runtime/status-timing-diagnostics.mjs +202 -0
- package/runtime/telemetry-trigger.mjs +3 -1
- package/scripts/bootstrap.cmd +255 -0
- package/scripts/bootstrap.sh +168 -0
- package/scripts/build-distribution.mjs +157 -0
- package/scripts/build-installer-bundles.mjs +136 -0
- package/scripts/build-runtime-modules.mjs +8 -0
- package/scripts/bundled-install.mjs +1308 -0
- package/scripts/bundled-wizard.mjs +399 -0
- package/scripts/check-types.mjs +4 -6
- package/scripts/gentle-ai-installer.mjs +83 -35
- package/scripts/install-gentle-ai.mjs +48 -1
- package/scripts/install-tui-mode-setting.mjs +10 -1
- package/scripts/install-wizard-preview.mjs +154 -0
- package/scripts/installer-downloads.mjs +648 -0
- package/scripts/installer-preflight.mjs +387 -0
- package/scripts/installer-probes.mjs +564 -0
- package/scripts/installer-runner.mjs +1102 -0
- package/scripts/installer-server.mjs +881 -0
- package/scripts/installer-windows-artifacts.json +15 -0
- package/scripts/installer-windows.mjs +752 -0
- package/scripts/main-channel.mjs +452 -0
- package/scripts/mirror-odd-routing.mjs +2 -2
- package/scripts/npm-pack-result.mjs +31 -0
- package/scripts/render-cache-performance-worker.mjs +205 -0
- package/scripts/render-cache-performance.mjs +115 -0
- package/scripts/run-test-suite.mjs +90 -0
- package/scripts/test-installer-acceptance.sh +161 -0
- package/scripts/test-packed-runner.mjs +54 -19
- package/scripts/types-baseline.json +2 -92
- package/scripts/verify-package-files.mjs +91 -38
- package/skills/branch-pr/SKILL.md +24 -52
- package/skills/chained-pr/SKILL.md +31 -15
- package/skills/chained-pr/references/chaining-details.md +31 -20
- package/skills/gentle-ai/SKILL.md +16 -21
- package/skills/issue-creation/SKILL.md +8 -2
- package/skills/issue-creation/references/delegated-workflow-actions.md +19 -0
- package/skills/work-unit-commits/SKILL.md +4 -3
- package/tests/agent-home.test.ts +56 -3
- package/tests/agent-model-resolution.test.ts +113 -0
- package/tests/agent-profiles.test.ts +30 -0
- package/tests/agent-risk-escalation.test.ts +123 -0
- package/tests/agents-completion-delivery.test.ts +17 -0
- package/tests/agents-fake-child.ts +2 -2
- package/tests/agents-message-delivery.test.ts +106 -0
- package/tests/agents-protocol.test.ts +97 -0
- package/tests/agents-queries.test.ts +9 -9
- package/tests/agents-runner-process.test.ts +31 -0
- package/tests/agents-runner.test.ts +467 -93
- package/tests/agents-view-thread-identity.test.ts +169 -0
- package/tests/agents-view.test.ts +8 -2
- package/tests/agents-widget.test.ts +154 -15
- package/tests/append-system-prompt-route.test.ts +160 -0
- package/tests/append-system-prompt.test.ts +46 -0
- package/tests/artifact-language.test.ts +19 -213
- package/tests/ask-user-question.test.ts +44 -1
- package/tests/asset-installation-runtime.test.ts +5 -16
- package/tests/auto-mode-invariants.test.ts +258 -0
- package/tests/auto-mode-sdk.test.ts +57 -0
- package/tests/auto-mode.test.ts +49 -0
- package/tests/auto-permission-classifier.test.ts +283 -0
- package/tests/auto-permission-runtime.test.ts +196 -0
- package/tests/autonomous-guard.test.ts +69 -1
- package/tests/background-cache-warming.test.ts +2 -1
- package/tests/background-jobs.test.ts +227 -0
- package/tests/background-monitor.test.ts +195 -0
- package/tests/background-subagents-default-mode.test.ts +28 -5
- package/tests/bounded-stop-contract.test.ts +85 -0
- package/tests/bounded-writer-admission.test.ts +146 -0
- package/tests/branch-pr-skill.test.ts +43 -0
- package/tests/bridge-wake-lifecycle.test.ts +129 -0
- package/tests/bridge-wake-rendering.test.ts +56 -0
- package/tests/build-distribution.test.ts +269 -0
- package/tests/builtin-codemode-optout.test.ts +242 -0
- package/tests/bundled-install.test.ts +1319 -0
- package/tests/bundled-wizard.test.ts +505 -0
- package/tests/card-content-policy.test.ts +68 -0
- package/tests/card-style-policy.test.ts +87 -0
- package/tests/chained-pr-skill.test.ts +124 -0
- package/tests/child-codegraph-bootstrap.test.ts +74 -0
- package/tests/child-context-files.test.ts +255 -0
- package/tests/child-package-entrypoints.test.ts +65 -0
- package/tests/child-package-injection.test.ts +74 -0
- package/tests/child-safety.test.ts +94 -0
- package/tests/child-web-package.test.ts +224 -0
- package/tests/codegraph-tools.test.ts +35 -0
- package/tests/codemode-rendering.test.ts +596 -0
- package/tests/command-palette.test.ts +43 -7
- package/tests/context-threshold.test.ts +31 -0
- package/tests/delegate-for-reason-contract.test.ts +327 -0
- package/tests/delegated-key-learnings-contract.test.ts +3 -78
- package/tests/destructive-command-guard.test.ts +84 -0
- package/tests/devbinary/native-review-parity.devtest.ts +171 -3
- package/tests/devbinary/non-git-subagent-bootstrap.devtest.ts +193 -0
- package/tests/devbinary/pi-host-relay.devtest.ts +173 -3
- package/tests/engram-safety.test.ts +288 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-28T10-00-00-000Z_aaa.jsonl +7 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-29T23-00-00-000Z_bbb.jsonl +3 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-30T08-00-00-000Z_ddd.jsonl +2 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/2026-09-30T09-00-00-000Z_eee.jsonl +3 -0
- package/tests/fixtures/stats/sessions/--work-alpha--/run-1/session.jsonl +2 -0
- package/tests/fixtures/stats/sessions/--work-beta--/2026-09-01T12-00-00-000Z_ccc.jsonl +2 -0
- package/tests/fixtures/stats/user-pi/sessions/--work-alpha--/2026-09-28T10-00-00-000Z_aaa.jsonl +2 -0
- package/tests/fixtures/stats/user-pi/sessions/--work-alpha--/2026-09-29T23-00-00-000Z_bbb.jsonl +4 -0
- package/tests/fixtures/stats/user-pi/sessions/--work-gamma--/2026-09-20T09-00-00-000Z_fff.jsonl +3 -0
- package/tests/generic-agent-tools.test.ts +69 -0
- package/tests/gentle-agents.test.ts +2804 -346
- package/tests/gentle-ai-binary-isolation.test.ts +135 -0
- package/tests/gentle-ai-binary.test.ts +21 -4
- package/tests/gentle-ai-child-guards.test.ts +202 -0
- package/tests/gentle-ai-dev-binary-surfacing.test.ts +125 -15
- package/tests/gentle-ai-elapsed-store.test.ts +68 -0
- package/tests/gentle-ai-installer.test.ts +566 -62
- package/tests/gentle-ai-prompt-model-routing.test.ts +123 -0
- package/tests/gentle-ai-renderer.test.ts +493 -14
- package/tests/gentle-ai.test.ts +1427 -236
- package/tests/gentle-card-text.ts +2 -1
- package/tests/gentle-herdr-activity.test.ts +100 -0
- package/tests/gentle-notifications.test.ts +427 -0
- package/tests/gentle-shell-bin.test.ts +896 -115
- package/tests/gentle-shell-bundled.test.ts +1002 -0
- package/tests/gentle-shell-launcher.test.ts +333 -65
- package/tests/gentle-shell-resume-hint.test.ts +270 -0
- package/tests/gentle-shell-startup-flow.test.ts +75 -0
- package/tests/gentle-shell-startup-update.test.ts +85 -0
- package/tests/gentle-shell-uninstall.test.ts +978 -0
- package/tests/gentle-shell-update-panel.test.ts +158 -0
- package/tests/gentle-shell.test.ts +4216 -119
- package/tests/gentle-stats.test.ts +152 -0
- package/tests/gentle-theme.test.ts +4 -1
- package/tests/gentle-todo.test.ts +333 -10
- package/tests/herdr-activity.test.ts +243 -0
- package/tests/history-agent-home.test.ts +81 -0
- package/tests/history-atomic-write.test.ts +57 -0
- package/tests/history-capture-policy.test.ts +102 -0
- package/tests/history-child-guard.test.ts +70 -0
- package/tests/history-command-registration.test.ts +164 -0
- package/tests/history-dedupe-entries.test.ts +123 -0
- package/tests/history-delete-backfill.test.ts +190 -0
- package/tests/history-delete-confirm.test.ts +460 -0
- package/tests/history-dispatch.test.ts +180 -0
- package/tests/history-drain-hidden.test.ts +110 -0
- package/tests/history-drain-order.test.ts +98 -0
- package/tests/history-expanded-globals.test.ts +62 -0
- package/tests/history-gc.test.ts +832 -0
- package/tests/history-header-layout.test.ts +265 -0
- package/tests/history-hide-prompts.test.ts +275 -0
- package/tests/history-lazy-windowing.test.ts +508 -0
- package/tests/history-legacy-migrate-v2.test.ts +297 -0
- package/tests/history-load-shared-history.test.ts +53 -0
- package/tests/history-max-results-cap.test.ts +76 -0
- package/tests/history-multi-reader.test.ts +203 -0
- package/tests/history-off-path.test.ts +170 -0
- package/tests/history-openflow-integration.test.ts +173 -0
- package/tests/history-overlay-margin.test.ts +326 -0
- package/tests/history-preview-layout.test.ts +93 -0
- package/tests/history-registry.test.ts +143 -0
- package/tests/history-scope-delete.test.ts +411 -0
- package/tests/history-search-caret-keys.test.ts +142 -0
- package/tests/history-seed-bootstrap.test.ts +170 -0
- package/tests/history-seed-regen.test.ts +129 -0
- package/tests/history-selector-windowing.test.ts +94 -0
- package/tests/history-session-scan-directory.test.ts +87 -0
- package/tests/history-session-scan-extract.test.ts +583 -0
- package/tests/history-session-writer.test.ts +351 -0
- package/tests/history-store-paths.test.ts +79 -0
- package/tests/history-tombstone-exact.test.ts +139 -0
- package/tests/history-wheel-mouse.test.ts +242 -0
- package/tests/inprocess-reviewer.test.ts +49 -19
- package/tests/install-gentle-ai-failure.test.ts +69 -0
- package/tests/install-tui-mode-setting.test.ts +15 -0
- package/tests/install-wizard.test.ts +1223 -0
- package/tests/installer-bundles.test.ts +122 -0
- package/tests/installer-posix-bootstrap.test.ts +1201 -0
- package/tests/installer-preflight.test.ts +750 -0
- package/tests/installer-probes.test.ts +1106 -0
- package/tests/installer-runner.test.ts +2158 -0
- package/tests/installer-server.test.ts +1567 -0
- package/tests/installer-windows-bootstrap.test.ts +2410 -0
- package/tests/issue-creation-skill.test.ts +61 -0
- package/tests/jobs-view.test.ts +160 -0
- package/tests/main-channel-upgrade.test.ts +389 -0
- package/tests/main-channel.test.ts +344 -0
- package/tests/maintainer/provider-relay.maintest.ts +13 -12
- package/tests/model-price-ratio.test.ts +116 -0
- package/tests/model-profiles-preservation.test.ts +153 -0
- package/tests/model-routing-authority.test.ts +16 -0
- package/tests/model-routing-package.test.ts +10 -0
- package/tests/nan-provider-runtime.test.ts +156 -0
- package/tests/nan-provider.test.ts +481 -0
- package/tests/native-binary-gate.test.ts +2 -0
- package/tests/native-review-capability-contract.test.ts +13 -1
- package/tests/native-review-cli.test.ts +239 -118
- package/tests/native-review-consent.test.ts +2 -2
- package/tests/native-review-parity-runtime.test.ts +101 -4
- package/tests/native-review-parity.test.ts +12 -48
- package/tests/native-risk-evidence-parity.test.ts +21 -0
- package/tests/no-self-pause-contract.test.ts +87 -0
- package/tests/notification-audio-native.test.ts +281 -0
- package/tests/notification-audio-windows.test.ts +581 -0
- package/tests/notification-audio.test.ts +548 -0
- package/tests/notification-customize.test.ts +655 -0
- package/tests/notification-events.test.ts +115 -0
- package/tests/notification-policy.test.ts +201 -0
- package/tests/notification-pulse-client.test.ts +356 -0
- package/tests/notification-pulse-protocol.test.ts +213 -0
- package/tests/notification-pulse-stream.test.ts +320 -0
- package/tests/notification-scheduler.test.ts +258 -0
- package/tests/notification-ui.test.ts +157 -0
- package/tests/npm-pack-result.test.ts +41 -0
- package/tests/odd-integration.test.ts +76 -0
- package/tests/odd-phase-inference.test.ts +213 -0
- package/tests/odd-phase-loader.test.ts +253 -0
- package/tests/odd-phase.test.ts +307 -0
- package/tests/odd-routing-canonical-ratchet.test.ts +60 -14
- package/tests/odd-routing-contract.test.ts +158 -50
- package/tests/orchestrator-budget.test.ts +43 -45
- package/tests/orchestrator-catalog.test.ts +146 -0
- package/tests/orchestrator-consultation-sdk.test.ts +614 -0
- package/tests/orchestrator-consultation.test.ts +111 -0
- package/tests/orchestrator-discovery.test.ts +241 -0
- package/tests/orchestrator-helper-consent.test.ts +171 -0
- package/tests/orchestrator-helper.test.ts +297 -0
- package/tests/orchestrator-message-card.test.ts +190 -0
- package/tests/orchestrator-presence.test.ts +23 -0
- package/tests/orchestrator-rdd-ownership.test.ts +8 -7
- package/tests/orchestrator-scope.test.ts +107 -0
- package/tests/orchestrator-state.test.ts +114 -0
- package/tests/orchestrator-status-publication-contract.test.ts +34 -0
- package/tests/orchestrator-tool-card.test.ts +398 -0
- package/tests/orchestrator-work-search.test.ts +318 -0
- package/tests/orchestrator-work.test.ts +147 -0
- package/tests/overlay-repaint.test.ts +74 -0
- package/tests/package-manifest.test.ts +344 -123
- package/tests/packed-runner-owned-path.test.ts +80 -0
- package/tests/parallel-reason-contract.test.ts +21 -0
- package/tests/parallel-writer-admission.test.ts +210 -0
- package/tests/permission-mode-policy.test.ts +194 -0
- package/tests/persona-single-channel.test.ts +10 -18
- package/tests/pi-pretty.test.ts +56 -0
- package/tests/provider-defect-handoff.test.ts +7 -14
- package/tests/quiet-bash-runtime.test.ts +76 -0
- package/tests/quiet-tool-rendering.test.ts +569 -187
- package/tests/rdd-aware-verification-contract.test.ts +103 -40
- package/tests/rdd-status-line.test.ts +9 -4
- package/tests/render-cache-performance.test.ts +48 -0
- package/tests/resume-hint-extension.test.ts +122 -0
- package/tests/review-agent-end-preflight.test.ts +237 -21
- package/tests/review-candidate-owner-retry.test.ts +22 -1
- package/tests/review-candidate-view.test.ts +399 -12
- package/tests/review-contract-prompt.test.ts +138 -44
- package/tests/review-controller-lock-status.test.ts +0 -1
- package/tests/review-controller-native-recovery.test.ts +327 -17
- package/tests/review-controller-native-routing.test.ts +866 -6
- package/tests/review-controller-workspace-root.test.ts +206 -6
- package/tests/review-controller.test.ts +2 -2
- package/tests/review-correction-lifecycle.test.ts +2 -1
- package/tests/review-gate.test.ts +7 -4
- package/tests/review-graph-schema.test.ts +2 -2
- package/tests/review-host-relay-routing.test.ts +342 -4
- package/tests/review-host-relay.test.ts +49 -2
- package/tests/review-integration-v2-forward.test.ts +221 -1
- package/tests/review-json-arguments.test.ts +167 -0
- package/tests/review-last-event-closure.test.ts +157 -8
- package/tests/review-ledger-contract.test.ts +14 -37
- package/tests/review-policy-judgment-day.test.ts +1 -1
- package/tests/review-policy-ordinary.test.ts +2 -2
- package/tests/review-relay-transport-agent.test.ts +66 -1
- package/tests/review-reminder-receipt.test.ts +47 -1
- package/tests/review-risk-assessment.test.ts +594 -26
- package/tests/review-session-standing-permission-controller.test.ts +16 -17
- package/tests/review-session-standing-permission-ipc.test.ts +6 -5
- package/tests/review-sidebar-state.test.ts +402 -0
- package/tests/review-test-fixtures.ts +7 -1
- package/tests/review-transaction.test.ts +18 -1
- package/tests/run-test-suite.test.ts +202 -0
- package/tests/runtime-harness.mjs +186 -800
- package/tests/runtime-metrics-children.test.ts +16 -23
- package/tests/runtime-metrics-extension.test.ts +1 -1
- package/tests/runtime-metrics-native.test.ts +31 -1
- package/tests/selection-engine.test.ts +451 -0
- package/tests/session-change-capture.test.ts +10 -10
- package/tests/session-changes-shell.test.ts +242 -48
- package/tests/session-changes.test.ts +17 -24
- package/tests/session-messaging-grants.test.ts +255 -0
- package/tests/session-profile-binding.test.ts +163 -0
- package/tests/session-profile-freeze.test.ts +381 -0
- package/tests/session-subject-routing-contract.test.ts +34 -0
- package/tests/session-worktree-registry.test.ts +134 -2
- package/tests/shell-bar.test.ts +422 -1
- package/tests/shell-card.test.ts +353 -1
- package/tests/shell-changes-view.test.ts +135 -32
- package/tests/shell-changes.test.ts +61 -62
- package/tests/shell-prompt.test.ts +94 -2
- package/tests/shell-render-cache-runtime.test.ts +69 -0
- package/tests/shell-render-cache.test.ts +129 -0
- package/tests/shell-sidebar-layout.test.ts +351 -22
- package/tests/shell-sidebar-scroll-benchmark.test.ts +255 -0
- package/tests/shell-todo.test.ts +207 -3
- package/tests/shell-usage-view.test.ts +28 -1
- package/tests/shell-usage.test.ts +108 -0
- package/tests/short-check-routing-contract.test.ts +49 -0
- package/tests/skill-registry-watcher.test.ts +119 -0
- package/tests/skill-registry.test.ts +357 -2
- package/tests/startup-banner.test.ts +246 -4
- package/tests/startup-home-repo.test.ts +170 -0
- package/tests/stats-collector.test.ts +195 -0
- package/tests/stats-view.test.ts +202 -0
- package/tests/status-timing-diagnostics.test.ts +283 -0
- package/tests/support/orchestrator-modules.ts +19 -0
- package/tests/task-size-routing-contract.test.ts +241 -0
- package/tests/telemetry-trigger.test.ts +107 -20
- package/tests/test-strength-contract.test.ts +124 -0
- package/tests/theme-customization.test.ts +72 -0
- package/tests/verify-agent-contract.test.ts +90 -0
- package/tests/verify-first-launch-contract.test.ts +139 -0
- package/tests/verify-package-files.test.ts +56 -0
- package/tests/vim-editor-adapter-host-resolution.test.ts +37 -0
- package/tests/vim-editor-adapter.test.ts +804 -0
- package/tests/vim-normal-engine.test.ts +101 -0
- package/tests/vim-operator-engine.test.ts +215 -0
- package/tests/vim-policy.test.ts +19 -0
- package/tests/vim-visual-engine.test.ts +52 -0
- package/tests/visual-customization-policy.test.ts +139 -0
- package/tests/visual-customize-view.test.ts +696 -0
- package/tests/visual-profiles.test.ts +87 -0
- package/tests/writer-edit-surface-scope.test.ts +59 -2
- package/tests/writer-surfaces.test.ts +199 -0
- package/tests/yolo-customize.test.ts +271 -0
- package/tests/yolo-mode-runtime.test.ts +188 -0
- package/tests/yolo-mode.test.ts +289 -0
- package/tests/yolo-session-policy.test.ts +59 -0
- package/themes/Gentle.json +2 -1
- package/themes/Gentleman-Cute.json +2 -1
- package/themes/Gentleman-Sexy.json +2 -1
- package/assets/agents/sdd-apply.md +0 -159
- package/assets/agents/sdd-archive.md +0 -228
- package/assets/agents/sdd-design.md +0 -49
- package/assets/agents/sdd-explore.md +0 -48
- package/assets/agents/sdd-init.md +0 -56
- package/assets/agents/sdd-onboard.md +0 -52
- package/assets/agents/sdd-proposal.md +0 -64
- package/assets/agents/sdd-remediate.md +0 -37
- package/assets/agents/sdd-research.md +0 -49
- package/assets/agents/sdd-spec.md +0 -192
- package/assets/agents/sdd-status.md +0 -54
- package/assets/agents/sdd-tasks.md +0 -108
- package/assets/agents/sdd-verify.md +0 -124
- package/assets/chains/sdd-full.chain.md +0 -83
- package/assets/chains/sdd-plan.chain.md +0 -56
- package/assets/chains/sdd-verify.chain.md +0 -43
- package/assets/sdd-orchestrator-workflow.md +0 -319
- package/assets/support/sdd-status-contract.md +0 -77
- package/docs/assets/diagrams/sdd-cycle.svg +0 -14
- package/extensions/sdd-init.ts +0 -816
- package/lib/openspec-deltas.ts +0 -156
- package/lib/sdd-preflight.ts +0 -1066
- package/lib/sdd-research-capabilities.ts +0 -94
- package/lib/sdd-status.ts +0 -26
- package/tests/fixtures/legacy/sdd-research-v2.5.0.md +0 -54
- package/tests/fixtures/native-review-cli/v2.1.3/bind-sdd.json +0 -25
- package/tests/fixtures/v0.10.7/assets/agents/sdd-apply.md +0 -132
- package/tests/openspec-deltas.test.ts +0 -209
- package/tests/sdd-agent-tools.test.ts +0 -156
- package/tests/sdd-archive-replay.test.ts +0 -82
- package/tests/sdd-classical-continuation.test.ts +0 -74
- package/tests/sdd-execution-routing-contract.test.ts +0 -44
- package/tests/sdd-managed-runtime-settlement.test.ts +0 -155
- package/tests/sdd-native-managed-uptake.test.ts +0 -243
- package/tests/sdd-no-attempts-contract.test.ts +0 -15
- package/tests/sdd-odd-integration.test.ts +0 -33
- package/tests/sdd-optional-research.test.ts +0 -124
- package/tests/sdd-planning-routing-contract.test.ts +0 -45
- package/tests/sdd-preflight-rpc-input.test.ts +0 -125
- package/tests/sdd-preflight.test.ts +0 -541
- package/tests/sdd-research-capabilities.test.ts +0 -114
- package/tests/sdd-research-live.test.ts +0 -241
- package/tests/sdd-selection-transport.test.ts +0 -653
- package/tests/sdd-status.test.ts +0 -9
- package/tests/sdd-task-truth.test.ts +0 -43
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/README.md +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/lens.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/refuter.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/schemas/targeted-validator.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/lens.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/refuter.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.2.0 → v1.3.0}/bundle/vectors/targeted-validator.json +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Generated by scripts/build-runtime-modules.mjs. Do not edit.
|
|
2
|
-
import { execFile } from "node:child_process";
|
|
2
|
+
import { execFile, spawn } from "node:child_process";
|
|
3
3
|
import { chmod, mkdtemp, realpath, rm, writeFile } from "node:fs/promises";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { dirname, isAbsolute, join, posix, win32 } from "node:path";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
|
-
import { PackageLocalGentleAiBinaryMissingError, resolveGentleAiBinary } from "./gentle-ai-binary.mjs";
|
|
7
|
+
import { gentleAiIsolationEnv, PackageLocalGentleAiBinaryMissingError, resolveGentleAiBinary } from "./gentle-ai-binary.mjs";
|
|
8
8
|
import { GENTLE_PI_REVIEW_RELAY_CONTRACT, GENTLE_PI_REVIEW_RELAY_CONTRACT_ENV } from "./review-relay-contract.mjs";
|
|
9
9
|
import { decodeReviewAssessmentV1, } from "./review-risk-assessment.mjs";
|
|
10
10
|
import {
|
|
@@ -32,6 +32,8 @@ import {
|
|
|
32
32
|
|
|
33
33
|
} from "./review-integration-v2.mjs";
|
|
34
34
|
|
|
35
|
+
import { observeNativeStatus, measureStatusSync, measureStatusAsync } from "./status-timing-diagnostics.mjs";
|
|
36
|
+
|
|
35
37
|
const execFileAsync = promisify(execFile);
|
|
36
38
|
|
|
37
39
|
// Negotiated review/status responses can carry a complete authority inventory.
|
|
@@ -68,8 +70,6 @@ export const NATIVE_REVIEW_OPERATION = {
|
|
|
68
70
|
CAPTURE_PROVIDER_ROLE: "review/capture-provider-role",
|
|
69
71
|
CAPTURE_UNACHIEVABLE: "review/capture-unachievable",
|
|
70
72
|
ACKNOWLEDGE_APPROVED: "review/acknowledge-approved",
|
|
71
|
-
SDD_STATUS: "sdd-status",
|
|
72
|
-
SDD_CONTINUE: "sdd-continue",
|
|
73
73
|
} ;
|
|
74
74
|
|
|
75
75
|
|
|
@@ -124,11 +124,6 @@ export const NATIVE_REVIEW_ERROR_CODE = {
|
|
|
124
124
|
|
|
125
125
|
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
127
|
export const NATIVE_REVIEW_MODE_OPERATION = {
|
|
133
128
|
STATUS: "status",
|
|
134
129
|
ENABLE: "enable",
|
|
@@ -168,35 +163,6 @@ export const NATIVE_REVIEW_MODE_SCOPE = {
|
|
|
168
163
|
|
|
169
164
|
|
|
170
165
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* The native CLI owns this complete v2 record. The decoder validates the fields
|
|
177
|
-
* Pi relies on and returns the original object without adding, omitting, or
|
|
178
|
-
* reconciling local SDD state.
|
|
179
|
-
*/
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
166
|
|
|
201
167
|
// Read-only risk assessment request (gentle-pi#662). `baseRef` requires
|
|
202
168
|
// explicit `committedOnly` acknowledgement, exactly like Native START's
|
|
@@ -294,6 +260,8 @@ export const NATIVE_REVIEW_RECOVER_DISPOSITION = ["scope_changed", "invalidated"
|
|
|
294
260
|
|
|
295
261
|
|
|
296
262
|
|
|
263
|
+
|
|
264
|
+
|
|
297
265
|
|
|
298
266
|
|
|
299
267
|
|
|
@@ -312,6 +280,13 @@ export const NATIVE_REVIEW_LEGACY_ALIAS_REPAIR = {
|
|
|
312
280
|
} ;
|
|
313
281
|
|
|
314
282
|
|
|
283
|
+
export const NATIVE_REVIEW_ABANDON_REASONS = ["operator_disposition", "retired_schema"] ;
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
export function isNativeReviewAbandonReason(value ) {
|
|
287
|
+
return value === "operator_disposition" || value === "retired_schema";
|
|
288
|
+
}
|
|
289
|
+
|
|
315
290
|
|
|
316
291
|
|
|
317
292
|
|
|
@@ -508,6 +483,24 @@ export const NATIVE_REVIEW_UNACHIEVABLE_LENS_DETAIL_LIMIT = 512;
|
|
|
508
483
|
// gentle-pi#638 fail-open capability gate: `review capture-unachievable` is younger than every released binary pinned in NATIVE_CLI_CONTRACTS, so the verb is gated invocation-adjacent instead of by a capability row. An older binary renders Go's exact `unknown review command "capture-unachievable"` refusal (internal/cli/review_facade.go) on stderr with no stdout, so the invocation rejects before any decode and the captured diagnostics are the only place that text survives. Every other failure -- a typed binding-mismatch refusal, a timeout, a decode failure -- is a real outcome the caller must surface, never a capability signal. Duck-typed on purpose: the classifier must survive a duplicated module instance exactly like the error it inspects.
|
|
509
484
|
const NATIVE_REVIEW_UNKNOWN_UNACHIEVABLE_VERB_REFUSAL = /unknown review command "capture-unachievable"/;
|
|
510
485
|
|
|
486
|
+
// A provider older than START lens selection refuses the STATUS preflight
|
|
487
|
+
// flags with this exact typed preflight cause, before reading the repository.
|
|
488
|
+
const NATIVE_REVIEW_UNKNOWN_LENS_FLAG_CAUSE = /^flag provided but not defined: -lenses(?:-reason)?$/;
|
|
489
|
+
|
|
490
|
+
/** Reports a not-started STATUS refusal caused only by an unknown --lenses flag. */
|
|
491
|
+
export function isNativeReviewLensSelectionUnsupported(error ) {
|
|
492
|
+
if (!(error instanceof NativeReviewIntegrationError)) return false;
|
|
493
|
+
const failure = error.failureEnvelope;
|
|
494
|
+
return failure.code === "invalid_request" && failure.mutationOutcome === "not_started" &&
|
|
495
|
+
typeof failure.cause === "string" && NATIVE_REVIEW_UNKNOWN_LENS_FLAG_CAUSE.test(failure.cause);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function nativeLensSelectionArguments(request ) {
|
|
499
|
+
if (request.lenses === undefined && request.lensesReason === undefined) return [];
|
|
500
|
+
if (request.lenses === undefined || request.lenses.length === 0 || request.lensesReason === undefined) throw new TypeError("Native lens selection requires lenses and lensesReason together");
|
|
501
|
+
return ["--lenses", request.lenses.join(","), "--lenses-reason", request.lensesReason];
|
|
502
|
+
}
|
|
503
|
+
|
|
511
504
|
export function isNativeReviewUnachievableVerbRefused(error ) {
|
|
512
505
|
if (typeof error !== "object" || error === null) return false;
|
|
513
506
|
const stderr = (error ).diagnostics?.stderr;
|
|
@@ -534,6 +527,16 @@ export const NATIVE_UNTRACKED_SCOPE = {
|
|
|
534
527
|
|
|
535
528
|
|
|
536
529
|
|
|
530
|
+
/**
|
|
531
|
+
* The agent's own review lens choice (gentle-ai START --lenses): canonical
|
|
532
|
+
* lens names in 4R order and one reason. Providers that predate it refuse
|
|
533
|
+
* the flag, and START then falls back to the tier default.
|
|
534
|
+
*/
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
|
|
537
540
|
|
|
538
541
|
|
|
539
542
|
|
|
@@ -748,6 +751,21 @@ function nativeUntrackedSelectionArguments(selection )
|
|
|
748
751
|
];
|
|
749
752
|
}
|
|
750
753
|
|
|
754
|
+
// Fail closed on malformed or duplicate provider selectors before invoking STATUS.
|
|
755
|
+
function providerFlagValue(tokens , flag ) {
|
|
756
|
+
let found ;
|
|
757
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
758
|
+
const token = tokens[index] ;
|
|
759
|
+
if (token !== `--${flag}` && !token.startsWith(`--${flag}=`)) continue;
|
|
760
|
+
if (found !== undefined) throw new TypeError(`Native intended-untracked selection repeats --${flag}`);
|
|
761
|
+
const value = token === `--${flag}` ? tokens[index + 1] : token.slice(`--${flag}=`.length);
|
|
762
|
+
if (value === undefined || value === "" || value.startsWith("--")) throw new TypeError(`Native intended-untracked selection has an invalid --${flag} value`);
|
|
763
|
+
found = value;
|
|
764
|
+
if (token === `--${flag}`) index++;
|
|
765
|
+
}
|
|
766
|
+
return found;
|
|
767
|
+
}
|
|
768
|
+
|
|
751
769
|
const NATIVE_RISK_LEVEL = ["low", "medium", "high"] ;
|
|
752
770
|
|
|
753
771
|
// gentle-ai's negotiated `start/v2` envelope is a closed schema
|
|
@@ -774,6 +792,8 @@ const REVIEW_RISK_SUBJECT_BY_CODE = Object.fre
|
|
|
774
792
|
shell_source: "shell scripting",
|
|
775
793
|
process_boundary: "code that starts other processes",
|
|
776
794
|
process_scan_limit: "code that starts other processes",
|
|
795
|
+
dangerous_sink: "a dangerous code pattern",
|
|
796
|
+
agent_escalation: "the agent that made this change flagged it as high risk",
|
|
777
797
|
executable_mode: "an executable permission change",
|
|
778
798
|
executable_change: "an executable change",
|
|
779
799
|
configuration_change: "a configuration change",
|
|
@@ -1049,6 +1069,20 @@ export const NATIVE_CLI_CONTRACTS = Object.freeze({
|
|
|
1049
1069
|
// 547b68e172cc87aa297309d61624e5fc2c24d407a494b53eeb5a2b053904352c
|
|
1050
1070
|
// at contract 1.2.0. No new negotiated capability is asserted.
|
|
1051
1071
|
"3.7.0": Object.freeze({ start: true, finalize: true, validate: true, bindSdd: true, status: true, inventory: true, reclaim: true, recover: true, abandon: true, quarantineLegacy: true, reconcileAuthority: true, repairLegacyAlias: true, mode: true, riskEvidence: false, hint: false, delivery: true }),
|
|
1072
|
+
// v4.0.0 repeats 3.7.0: the published provider-contract tar remains SHA-256
|
|
1073
|
+
// 547b68e172cc87aa297309d61624e5fc2c24d407a494b53eeb5a2b053904352c
|
|
1074
|
+
// at contract 1.2.0, and the published binary still advertises
|
|
1075
|
+
// capabilities/v2.6 under review-integration/v2. The Go module path moved
|
|
1076
|
+
// to /v4 without a review-integration/v2 change. No new negotiated
|
|
1077
|
+
// capability is asserted.
|
|
1078
|
+
"4.0.0": Object.freeze({ start: true, finalize: true, validate: true, bindSdd: true, status: true, inventory: true, reclaim: true, recover: true, abandon: true, quarantineLegacy: true, reconcileAuthority: true, repairLegacyAlias: true, mode: true, riskEvidence: false, hint: false, delivery: true }),
|
|
1079
|
+
// v4.1.1 repeats 4.0.0: the published binary still advertises
|
|
1080
|
+
// capabilities/v2.6 under review-integration/v2. The provider contract moved
|
|
1081
|
+
// from 1.2.0 to 1.3.0 (additive agent lens selection, gentle-ai#5348), with
|
|
1082
|
+
// published tarball SHA-256
|
|
1083
|
+
// b04b6b75d1a446c942a816fbf92932d16f8c3d3001f93159dee2a92bbf2ebe44.
|
|
1084
|
+
// No new negotiated capability identity is asserted.
|
|
1085
|
+
"4.1.1": Object.freeze({ start: true, finalize: true, validate: true, bindSdd: true, status: true, inventory: true, reclaim: true, recover: true, abandon: true, quarantineLegacy: true, reconcileAuthority: true, repairLegacyAlias: true, mode: true, riskEvidence: false, hint: false, delivery: true }),
|
|
1052
1086
|
});
|
|
1053
1087
|
|
|
1054
1088
|
|
|
@@ -1090,21 +1124,98 @@ export class NativeReviewCliError extends Error {
|
|
|
1090
1124
|
// makes. It declares the Pi host relay handshake on each spawn: gentle-ai
|
|
1091
1125
|
// refuses pi admission pre-authority without it (gentle-pi#311 P4), and a
|
|
1092
1126
|
// single injection point keeps the declaration impossible to forget on any
|
|
1093
|
-
// individual operation.
|
|
1127
|
+
// individual operation. In a bundled install it also keeps Gentle AI's state
|
|
1128
|
+
// inside the prefix (gentleAiIsolationEnv, S9).
|
|
1094
1129
|
export function gentleAiProcessEnvironment(base = process.env) {
|
|
1095
|
-
return { ...base, [GENTLE_PI_REVIEW_RELAY_CONTRACT_ENV]: GENTLE_PI_REVIEW_RELAY_CONTRACT };
|
|
1130
|
+
return { ...gentleAiIsolationEnv(base), [GENTLE_PI_REVIEW_RELAY_CONTRACT_ENV]: GENTLE_PI_REVIEW_RELAY_CONTRACT };
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
|
|
1134
|
+
|
|
1135
|
+
|
|
1136
|
+
|
|
1137
|
+
// A bare "taskkill" resolves from the current directory before PATH on
|
|
1138
|
+
// Windows, so a repository shipping its own taskkill.exe would run on any
|
|
1139
|
+
// abort. Always use the System32 binary, from System32 itself.
|
|
1140
|
+
export function windowsTaskkillPath(env = process.env) {
|
|
1141
|
+
const systemRoot = env.SystemRoot || env.windir;
|
|
1142
|
+
return win32.join(systemRoot && win32.isAbsolute(systemRoot) ? systemRoot : "C:\\Windows", "System32", "taskkill.exe");
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
// taskkill walks the tree from a live parent, so it must run before the
|
|
1146
|
+
// parent dies; Node's own abort/timeout kill would orphan the git children.
|
|
1147
|
+
export function createWindowsProcessTreeKiller(env = process.env, spawnProcess = spawn) {
|
|
1148
|
+
const taskkill = windowsTaskkillPath(env);
|
|
1149
|
+
return (pid) => {
|
|
1150
|
+
try {
|
|
1151
|
+
spawnProcess(taskkill, ["/pid", String(pid), "/T", "/F"], { cwd: win32.dirname(taskkill), stdio: "ignore", windowsHide: true }).on("error", () => {});
|
|
1152
|
+
} catch { /* Best effort: the call still settles through its own child. */ }
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
export const windowsProcessTreeKiller = (pid) => createWindowsProcessTreeKiller()(pid);
|
|
1157
|
+
|
|
1158
|
+
const WINDOWS_TREE_KILL_GRACE_MS = 1_000;
|
|
1159
|
+
|
|
1160
|
+
function abortError(signal ) {
|
|
1161
|
+
return Object.assign(new Error("The operation was aborted", { cause: signal.reason }), { name: "AbortError", code: "ABORT_ERR" });
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
function execFileResultFromError(error , timedOut ) {
|
|
1165
|
+
const detail = error ;
|
|
1166
|
+
if (detail.code === "ENOENT" || detail.code === "EACCES" || detail.name === "AbortError") throw error;
|
|
1167
|
+
const outputLimitExceeded = detail.code === "ENOBUFS" || detail.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER";
|
|
1168
|
+
return { stdout: detail.stdout ?? "", stderr: detail.stderr ?? "", exitCode: typeof detail.code === "number" ? detail.code : 1, signal: detail.signal ?? null, timedOut: !outputLimitExceeded && (timedOut || detail.killed === true), outputLimitExceeded };
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
// Windows: abort and timeout kill the whole process tree instead of only
|
|
1172
|
+
// gentle-ai, with the same observable results as the POSIX path.
|
|
1173
|
+
function windowsExecFile(request , killProcessTree ) {
|
|
1174
|
+
const { signal } = request;
|
|
1175
|
+
if (signal?.aborted) return Promise.reject(abortError(signal));
|
|
1176
|
+
return new Promise((resolve, reject) => {
|
|
1177
|
+
let timedOut = false, aborted = false, pipesCut = false;
|
|
1178
|
+
let timer ;
|
|
1179
|
+
let fallback ;
|
|
1180
|
+
const child = execFile(request.file, [...request.arguments], { cwd: request.cwd, encoding: "utf8", shell: false, windowsHide: true, maxBuffer: request.maxBufferBytes, env: gentleAiProcessEnvironment() }, (error, stdout, stderr) => {
|
|
1181
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
1182
|
+
if (fallback !== undefined) clearTimeout(fallback);
|
|
1183
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1184
|
+
if (aborted && signal) { reject(abortError(signal)); return; }
|
|
1185
|
+
if (error === null) { resolve({ stdout, stderr, exitCode: 0, signal: null, timedOut: pipesCut, outputLimitExceeded: false }); return; }
|
|
1186
|
+
try { resolve(execFileResultFromError(Object.assign(error, { stdout, stderr }), timedOut)); } catch (failure) { reject(failure); }
|
|
1187
|
+
});
|
|
1188
|
+
// If taskkill cannot run (AppLocker, a broken PATH), still end gentle-ai
|
|
1189
|
+
// itself so the deadline holds; its children may then outlive it. A
|
|
1190
|
+
// surviving descendant can still hold the inherited stdout/stderr open,
|
|
1191
|
+
// which would keep execFile from calling back, so cut the pipes too.
|
|
1192
|
+
const killTree = () => {
|
|
1193
|
+
if (child.pid === undefined) { child.kill(); return; }
|
|
1194
|
+
killProcessTree(child.pid);
|
|
1195
|
+
fallback = setTimeout(() => {
|
|
1196
|
+
if (child.exitCode === null && child.signalCode === null) child.kill();
|
|
1197
|
+
pipesCut = true;
|
|
1198
|
+
child.stdout?.destroy();
|
|
1199
|
+
child.stderr?.destroy();
|
|
1200
|
+
}, WINDOWS_TREE_KILL_GRACE_MS);
|
|
1201
|
+
};
|
|
1202
|
+
function onAbort() { if (aborted || timedOut) return; aborted = true; killTree(); }
|
|
1203
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1204
|
+
if (request.timeoutMs !== undefined && request.timeoutMs > 0) timer = setTimeout(() => { if (aborted) return; timedOut = true; killTree(); }, request.timeoutMs);
|
|
1205
|
+
});
|
|
1096
1206
|
}
|
|
1097
1207
|
|
|
1098
|
-
export function createNodeExecFileAdapter() {
|
|
1208
|
+
export function createNodeExecFileAdapter(options = {}) {
|
|
1209
|
+
if ((options.platform ?? process.platform) === "win32") {
|
|
1210
|
+
const killProcessTree = options.killProcessTree ?? windowsProcessTreeKiller;
|
|
1211
|
+
return (request) => windowsExecFile(request, killProcessTree);
|
|
1212
|
+
}
|
|
1099
1213
|
return async (request) => {
|
|
1100
1214
|
try {
|
|
1101
1215
|
const output = await execFileAsync(request.file, [...request.arguments], { cwd: request.cwd, encoding: "utf8", shell: false, windowsHide: true, timeout: request.timeoutMs, maxBuffer: request.maxBufferBytes, signal: request.signal, env: gentleAiProcessEnvironment() });
|
|
1102
1216
|
return { stdout: output.stdout, stderr: output.stderr, exitCode: 0, signal: null, timedOut: false, outputLimitExceeded: false };
|
|
1103
1217
|
} catch (error) {
|
|
1104
|
-
|
|
1105
|
-
if (detail.code === "ENOENT" || detail.code === "EACCES" || detail.name === "AbortError") throw error;
|
|
1106
|
-
const outputLimitExceeded = detail.code === "ENOBUFS" || detail.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER";
|
|
1107
|
-
return { stdout: detail.stdout ?? "", stderr: detail.stderr ?? "", exitCode: typeof detail.code === "number" ? detail.code : 1, signal: detail.signal ?? null, timedOut: !outputLimitExceeded && detail.killed === true, outputLimitExceeded };
|
|
1218
|
+
return execFileResultFromError(error, false);
|
|
1108
1219
|
}
|
|
1109
1220
|
};
|
|
1110
1221
|
}
|
|
@@ -1201,8 +1312,10 @@ function nativeProcessDiagnostics(operation , code
|
|
|
1201
1312
|
}
|
|
1202
1313
|
|
|
1203
1314
|
function parseJson(stdout , operation , mutating , diagnostics ) {
|
|
1204
|
-
|
|
1205
|
-
|
|
1315
|
+
return measureStatusSync("decode", () => {
|
|
1316
|
+
if (stdout.length === 0) throw new NativeReviewCliError(NATIVE_REVIEW_ERROR_CODE.EMPTY_OUTPUT, operation, true, mutating, "native command returned empty output", { ...diagnostics, error_code: NATIVE_REVIEW_ERROR_CODE.EMPTY_OUTPUT });
|
|
1317
|
+
try { return object(JSON.parse(stdout)); } catch { throw new NativeReviewCliError(NATIVE_REVIEW_ERROR_CODE.MALFORMED_JSON, operation, true, mutating, "native command returned malformed JSON", { ...diagnostics, error_code: NATIVE_REVIEW_ERROR_CODE.MALFORMED_JSON }); }
|
|
1318
|
+
});
|
|
1206
1319
|
}
|
|
1207
1320
|
function decodeNativeMaintenanceResult(value , expectedOperation ) {
|
|
1208
1321
|
const body = exactObject(value, ["operation", "record"]);
|
|
@@ -1233,7 +1346,7 @@ function assertSupportedNextTransitionOperation(body )
|
|
|
1233
1346
|
}
|
|
1234
1347
|
}
|
|
1235
1348
|
function decode (operation , mutating , callback , diagnostics = nativeProcessDiagnostics(operation, NATIVE_REVIEW_ERROR_CODE.SCHEMA_INCOMPATIBLE)) {
|
|
1236
|
-
try { return callback
|
|
1349
|
+
try { return measureStatusSync("decode", callback); } catch (error) { if (error instanceof NativeReviewCliError) throw error; throw new NativeReviewCliError(NATIVE_REVIEW_ERROR_CODE.SCHEMA_INCOMPATIBLE, operation, true, mutating, "native response is schema incompatible", { ...diagnostics, error_code: NATIVE_REVIEW_ERROR_CODE.SCHEMA_INCOMPATIBLE }); }
|
|
1237
1350
|
}
|
|
1238
1351
|
function decodeReviewStartResponse(value ) {
|
|
1239
1352
|
const body = object(value);
|
|
@@ -1246,50 +1359,6 @@ function decodeReleaseEvidence(value ) {
|
|
|
1246
1359
|
for (const field of ["release_tree", "configuration_hash", "generated_artifact_hash", "provenance_hash", "publication_boundary_hash", "evidence_freshness_hash"]) requiredString(release[field]);
|
|
1247
1360
|
if (release.publication_state !== "sealed" || release.evidence_freshness_state !== "current") throw new Error("invalid release evidence");
|
|
1248
1361
|
}
|
|
1249
|
-
function decodeNonDecidingGateContext(value , expectedGate ) {
|
|
1250
|
-
const context = exactObject(value, ["gate"]);
|
|
1251
|
-
const gate = enumString(context.gate, NATIVE_GATE);
|
|
1252
|
-
if (gate !== expectedGate) throw new Error("native non-deciding gate context does not match the requested gate");
|
|
1253
|
-
return { lineageId: "", storeRevision: "", raw: context };
|
|
1254
|
-
}
|
|
1255
|
-
function decodeGateContext(value ) {
|
|
1256
|
-
const context = exactObject(
|
|
1257
|
-
value,
|
|
1258
|
-
["gate", "lineage_id", "generation", "base_tree", "candidate_tree", "paths_digest", "fix_delta_hash", "policy_hash", "ledger_hash", "evidence_hash", "base_relationship_valid"],
|
|
1259
|
-
["store_revision", "genesis_revision", "chain_identity", "bundle_digest", "external_evidence", "base_advanced_compatible", "release", "pre_pr_boundary", "denial"],
|
|
1260
|
-
);
|
|
1261
|
-
const gate = stringValue(context.gate);
|
|
1262
|
-
if (gate !== "" && !(NATIVE_GATE ).includes(gate)) throw new Error("invalid gate context gate");
|
|
1263
|
-
for (const field of ["lineage_id", "base_tree", "candidate_tree", "paths_digest", "fix_delta_hash", "policy_hash", "ledger_hash", "evidence_hash"]) stringValue(context[field]);
|
|
1264
|
-
for (const field of ["store_revision", "genesis_revision", "chain_identity", "bundle_digest"]) if (context[field] !== undefined) stringValue(context[field]);
|
|
1265
|
-
nonNegativeInteger(context.generation);
|
|
1266
|
-
booleanValue(context.base_relationship_valid);
|
|
1267
|
-
if (context.external_evidence !== undefined) enumString(context.external_evidence, ["invalidating", "escalating"]);
|
|
1268
|
-
let sanitizedContext = context;
|
|
1269
|
-
if (context.denial !== undefined) {
|
|
1270
|
-
const denial = exactObject(context.denial, ["stage", "code"]);
|
|
1271
|
-
const stage = sanitizeNativeDiagnosticText(requiredString(denial.stage), NATIVE_REVIEW_DENIAL_TEXT_LIMIT);
|
|
1272
|
-
const code = sanitizeNativeDiagnosticText(requiredString(denial.code), NATIVE_REVIEW_DENIAL_TEXT_LIMIT);
|
|
1273
|
-
if (!isCanonicalProcessString(stage) || !isCanonicalProcessString(code)) throw new Error("non-canonical denial evidence");
|
|
1274
|
-
sanitizedContext = { ...context, denial: { stage, code } };
|
|
1275
|
-
}
|
|
1276
|
-
if (context.pre_pr_boundary !== undefined) {
|
|
1277
|
-
const boundary = exactObject(context.pre_pr_boundary, ["source", "selector", "commit"], ["remote", "remote_ref", "remote_identity"]);
|
|
1278
|
-
enumString(boundary.source, ["explicit", "publication-default"]); requiredString(boundary.selector); stringValue(boundary.commit);
|
|
1279
|
-
for (const field of ["remote", "remote_ref", "remote_identity"]) if (boundary[field] !== undefined) requiredString(boundary[field]);
|
|
1280
|
-
}
|
|
1281
|
-
if (context.base_advanced_compatible !== undefined) {
|
|
1282
|
-
const proof = exactObject(context.base_advanced_compatible, ["status", "compatible", "old_base_tree", "new_base_tree", "original_patch_identity", "delivered_patch_identity", "delivered_paths_digest", "base_advance_paths_digest", "paths_disjoint", "merged_result_tree", "ci_attestation_artifact_hash", "ci_attestation_issuer", "ci_status"]);
|
|
1283
|
-
for (const field of ["status", "old_base_tree", "new_base_tree", "original_patch_identity", "delivered_patch_identity", "delivered_paths_digest", "base_advance_paths_digest", "merged_result_tree", "ci_attestation_artifact_hash", "ci_attestation_issuer", "ci_status"]) requiredString(proof[field]);
|
|
1284
|
-
booleanValue(proof.compatible); booleanValue(proof.paths_disjoint);
|
|
1285
|
-
}
|
|
1286
|
-
if (context.release !== undefined) decodeReleaseEvidence(context.release);
|
|
1287
|
-
return {
|
|
1288
|
-
lineageId: stringValue(context.lineage_id),
|
|
1289
|
-
storeRevision: context.store_revision === undefined ? "" : stringValue(context.store_revision),
|
|
1290
|
-
raw: sanitizedContext,
|
|
1291
|
-
};
|
|
1292
|
-
}
|
|
1293
1362
|
function decodeNativeReviewRecovery(value ) {
|
|
1294
1363
|
const recovery = exactObject(value, ["predecessor_lineage_id", "predecessor_revision", "disposition", "reason", "actor", "recovered_at"], ["maintainer_authorization"]);
|
|
1295
1364
|
return {
|
|
@@ -1380,7 +1449,7 @@ function decodeNativeReviewStatus(value ) {
|
|
|
1380
1449
|
const complete = booleanValue(body.complete);
|
|
1381
1450
|
const authoritative = booleanValue(body.authoritative);
|
|
1382
1451
|
if (authoritative && !complete) throw new Error("incomplete inventory cannot be authoritative");
|
|
1383
|
-
if (!Array.isArray(body.entries) || !Array.isArray(body.locks)) throw new Error("invalid native status inventory");
|
|
1452
|
+
if (!Array.isArray(body.entries) || !Array.isArray(body.locks) || !Array.isArray(body.diagnostics)) throw new Error("invalid native status inventory");
|
|
1384
1453
|
return {
|
|
1385
1454
|
repository: requiredString(body.repository),
|
|
1386
1455
|
complete,
|
|
@@ -1516,52 +1585,6 @@ function nativeError(code , operation
|
|
|
1516
1585
|
|
|
1517
1586
|
|
|
1518
1587
|
|
|
1519
|
-
const NATIVE_SDD_DEPENDENCIES = ["proposal", "specs", "design", "tasks", "apply", "verify", "archive"] ;
|
|
1520
|
-
const NATIVE_SDD_INSTRUCTION_PHASES = ["apply", "verify", "archive"] ;
|
|
1521
|
-
const NATIVE_SDD_NEXT_RECOMMENDATIONS = ["apply", "verify", "remediate", "archive", "archived", "resolve-blockers", "sdd-new", "select-change", "propose", "spec", "design", "tasks"] ;
|
|
1522
|
-
const NATIVE_SDD_DEPENDENCY_STATES = ["blocked", "ready", "all_done"] ;
|
|
1523
|
-
|
|
1524
|
-
/** Strictly validates the native v2 contract while preserving its whole record. */
|
|
1525
|
-
export function decodeNativeSddStatusV2(value , request ) {
|
|
1526
|
-
const status = object(value);
|
|
1527
|
-
if (status.schemaName !== "gentle-ai.sdd-status" || status.schemaVersion !== 2) throw new Error("wrong native SDD status schema");
|
|
1528
|
-
if ((request.changeName !== undefined && status.changeName !== request.changeName) || (status.changeName !== null && !isCanonicalProcessString(status.changeName))) throw new Error("native SDD status change identity mismatch");
|
|
1529
|
-
const artifactStore = enumString(status.artifactStore, ["openspec", "engram", "hybrid", "none"]);
|
|
1530
|
-
const planningHome = object(status.planningHome);
|
|
1531
|
-
if (planningHome.mode !== "repo-local" || !isCanonicalProcessString(planningHome.path)) throw new Error("invalid native SDD planning home");
|
|
1532
|
-
const expectedOpenSpecHome = join(request.workspaceRoot, "openspec");
|
|
1533
|
-
if (planningHome.path !== expectedOpenSpecHome && !((artifactStore === "engram" || artifactStore === "hybrid") && planningHome.path === "engram:sdd")) throw new Error("native SDD planning home escaped its workspace");
|
|
1534
|
-
if (status.changeRoot !== null && !isCanonicalProcessString(status.changeRoot)) throw new Error("invalid native SDD change root");
|
|
1535
|
-
const actionContext = object(status.actionContext);
|
|
1536
|
-
if (actionContext.mode !== "repo-local" || actionContext.workspaceRoot !== request.workspaceRoot || !isCanonicalProcessString(actionContext.workspaceRoot)) throw new Error("native SDD status workspace root mismatch");
|
|
1537
|
-
const allowedEditRoots = stringArray(actionContext.allowedEditRoots);
|
|
1538
|
-
if (!allowedEditRoots.includes(request.workspaceRoot) || allowedEditRoots.some((root) => !isAbsolute(root) || root !== join(root))) throw new Error("invalid native SDD allowed edit roots");
|
|
1539
|
-
const dependencies = object(status.dependencies);
|
|
1540
|
-
for (const phase of NATIVE_SDD_DEPENDENCIES) {
|
|
1541
|
-
if (enumString(dependencies[phase], NATIVE_SDD_DEPENDENCY_STATES) !== dependencies[phase]) throw new Error("invalid native SDD dependency");
|
|
1542
|
-
}
|
|
1543
|
-
if (Object.keys(dependencies).length !== NATIVE_SDD_DEPENDENCIES.length) throw new Error("native SDD dependencies have an unsupported shape");
|
|
1544
|
-
if (status.instructions !== undefined) throw new Error("native SDD status uses phaseInstructions, not instructions");
|
|
1545
|
-
if (status.phaseInstructions !== undefined) {
|
|
1546
|
-
const instructions = object(status.phaseInstructions);
|
|
1547
|
-
for (const phase of NATIVE_SDD_INSTRUCTION_PHASES) stringArray(instructions[phase]);
|
|
1548
|
-
// Classical SDD no longer emits a remediation phase. Keep the published
|
|
1549
|
-
// producer's optional legacy instructions intact without inventing them
|
|
1550
|
-
// for a newer producer or accepting unknown phase keys.
|
|
1551
|
-
const hasRemediation = Object.hasOwn(instructions, "remediate");
|
|
1552
|
-
if (hasRemediation) stringArray(instructions.remediate);
|
|
1553
|
-
if (Object.keys(instructions).length !== NATIVE_SDD_INSTRUCTION_PHASES.length + Number(hasRemediation)) throw new Error("native SDD instructions have an unsupported shape");
|
|
1554
|
-
if (status.nextRecommended === "remediate" && !hasRemediation) throw new Error("native SDD remediation instructions are missing");
|
|
1555
|
-
}
|
|
1556
|
-
if (status.nextRecommended === "remediate" || status.remediationState !== undefined) {
|
|
1557
|
-
const remediation = object(status.remediationState);
|
|
1558
|
-
if (typeof remediation.required !== "boolean" || typeof remediation.complete !== "boolean" || typeof remediation.failedEvidenceRevision !== "string" || (remediation.failedEvidenceRevision !== "" && !/^sha256:[0-9a-f]{64}$/.test(remediation.failedEvidenceRevision)) || (status.nextRecommended === "remediate" && (!remediation.required || remediation.complete || !remediation.failedEvidenceRevision))) throw new Error("Invalid native remediation state");
|
|
1559
|
-
}
|
|
1560
|
-
stringArray(status.blockedReasons);
|
|
1561
|
-
enumString(status.nextRecommended, NATIVE_SDD_NEXT_RECOMMENDATIONS);
|
|
1562
|
-
return status ;
|
|
1563
|
-
}
|
|
1564
|
-
|
|
1565
1588
|
class NativeReviewPlainCli {
|
|
1566
1589
|
adapter ;
|
|
1567
1590
|
executable ;
|
|
@@ -1598,7 +1621,10 @@ class NativeReviewPlainCli {
|
|
|
1598
1621
|
// membership in the frozen set.
|
|
1599
1622
|
async execute(operation , cwd , arguments_ , mutating , signal , toleratedStderr = []) {
|
|
1600
1623
|
let result ;
|
|
1601
|
-
try {
|
|
1624
|
+
try {
|
|
1625
|
+
const file = measureStatusSync("resolution", () => this.executablePath(operation, mutating));
|
|
1626
|
+
result = await measureStatusAsync("adapter", () => this.adapter({ file, arguments: arguments_, cwd, timeoutMs: mutating ? undefined : this.timeoutMs, maxBufferBytes: this.maxBufferBytes, signal }));
|
|
1627
|
+
}
|
|
1602
1628
|
catch (error) {
|
|
1603
1629
|
if (error instanceof NativeReviewCliError) throw nativeError(error.code, operation, mutating, error.message, undefined, error.launchAttempted);
|
|
1604
1630
|
if (error instanceof Error && error.name === "AbortError") throw nativeError(NATIVE_REVIEW_ERROR_CODE.CANCELLED, operation, mutating, "native process was cancelled");
|
|
@@ -1608,7 +1634,7 @@ class NativeReviewPlainCli {
|
|
|
1608
1634
|
if (result.outputLimitExceeded) throw nativeError(NATIVE_REVIEW_ERROR_CODE.OUTPUT_LIMIT, operation, mutating, "native process output exceeded limit", result, true, undefined, this.maxBufferBytes);
|
|
1609
1635
|
if (result.timedOut) throw nativeError(NATIVE_REVIEW_ERROR_CODE.TIMEOUT, operation, mutating, "native process timed out", result);
|
|
1610
1636
|
if (result.signal) throw nativeError(NATIVE_REVIEW_ERROR_CODE.SIGNAL, operation, mutating, "native process was signalled", result);
|
|
1611
|
-
const maintenancePartialFailure = [NATIVE_REVIEW_OPERATION.ABANDON, NATIVE_REVIEW_OPERATION.QUARANTINE_LEGACY, NATIVE_REVIEW_OPERATION.RECONCILE_AUTHORITY, NATIVE_REVIEW_OPERATION.REPAIR_LEGACY_ALIAS].
|
|
1637
|
+
const maintenancePartialFailure = new Set ([NATIVE_REVIEW_OPERATION.ABANDON, NATIVE_REVIEW_OPERATION.QUARANTINE_LEGACY, NATIVE_REVIEW_OPERATION.RECONCILE_AUTHORITY, NATIVE_REVIEW_OPERATION.REPAIR_LEGACY_ALIAS]).has(operation) && result.exitCode !== 0;
|
|
1612
1638
|
const toleratedNotice = stderrIsTolerated(result.stderr, toleratedStderr);
|
|
1613
1639
|
if (result.exitCode !== 0 && !maintenancePartialFailure) throw nativeError(NATIVE_REVIEW_ERROR_CODE.NON_ZERO, operation, mutating, "native process failed", result);
|
|
1614
1640
|
if (result.stderr.trim().length > 0 && !maintenancePartialFailure && !toleratedNotice) throw nativeError(NATIVE_REVIEW_ERROR_CODE.UNEXPECTED_STDERR, operation, mutating, "native process wrote stderr", result);
|
|
@@ -1616,6 +1642,10 @@ class NativeReviewPlainCli {
|
|
|
1616
1642
|
}
|
|
1617
1643
|
|
|
1618
1644
|
async reviewStatus(request ) {
|
|
1645
|
+
return observeNativeStatus(this.timeoutMs, () => this.observedReviewStatus(request));
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
async observedReviewStatus(request ) {
|
|
1619
1649
|
const { body: result } = await this.execute(NATIVE_REVIEW_OPERATION.STATUS, request.cwd, ["review", "status", "--cwd", request.cwd], false, request.signal);
|
|
1620
1650
|
const status = decode(NATIVE_REVIEW_OPERATION.STATUS, false, () => decodeNativeReviewStatus(result));
|
|
1621
1651
|
if (!await repositoriesMatch(request.cwd, status.repository)) throw nativeError(NATIVE_REVIEW_ERROR_CODE.IDENTITY_MISMATCH, NATIVE_REVIEW_OPERATION.STATUS, false, "native review status repository mismatch");
|
|
@@ -1663,9 +1693,13 @@ class NativeReviewPlainCli {
|
|
|
1663
1693
|
if (request.maintainerAuthorization !== undefined && (request.maintainerAuthorization.length === 0 || /[\u0000-\u0009\u000b-\u001f\u007f]/.test(request.maintainerAuthorization))) {
|
|
1664
1694
|
throw new TypeError("Native RECOVER maintainerAuthorization must be a non-empty LF-only binding");
|
|
1665
1695
|
}
|
|
1696
|
+
if (request.baseRef !== undefined && !isCanonicalProcessString(request.baseRef)) throw new TypeError("Native RECOVER baseRef must be a non-empty, trimmed, NUL-free string");
|
|
1697
|
+
if (request.baseRef !== undefined && request.committedOnly !== true) throw new TypeError("Native RECOVER baseRef requires explicit committedOnly acknowledgement");
|
|
1698
|
+
if (request.baseRef === undefined && request.committedOnly !== undefined) throw new TypeError("Native RECOVER committedOnly requires an explicit baseRef");
|
|
1666
1699
|
if (!(NATIVE_REVIEW_RECOVER_DISPOSITION ).includes(request.disposition)) throw new TypeError("Native RECOVER disposition must be scope_changed, invalidated, or escalated");
|
|
1667
1700
|
const { body } = await this.execute(NATIVE_REVIEW_OPERATION.RECOVER, request.cwd, [
|
|
1668
1701
|
"review", "recover", "--cwd", request.cwd,
|
|
1702
|
+
...(request.baseRef === undefined ? [] : ["--base-ref", request.baseRef, "--committed-only"]),
|
|
1669
1703
|
"--predecessor-lineage", request.predecessorLineage,
|
|
1670
1704
|
"--expected-predecessor-revision", request.expectedPredecessorRevision,
|
|
1671
1705
|
"--successor-lineage", request.successorLineage,
|
|
@@ -1678,6 +1712,7 @@ class NativeReviewPlainCli {
|
|
|
1678
1712
|
}
|
|
1679
1713
|
|
|
1680
1714
|
async abandon(request ) {
|
|
1715
|
+
if (!isNativeReviewAbandonReason(request.reason)) throw new TypeError('Native ABANDON reason must be "operator_disposition" or "retired_schema"');
|
|
1681
1716
|
for (const [name, value] of [["lineage", request.lineage], ["expectedRevision", request.expectedRevision], ["snapshotIdentity", request.snapshotIdentity], ["actor", request.actor], ["reason", request.reason]] ) {
|
|
1682
1717
|
if (!isCanonicalProcessString(value)) throw new TypeError(`Native ABANDON ${name} must be a non-empty, trimmed, NUL-free string`);
|
|
1683
1718
|
}
|
|
@@ -1776,6 +1811,10 @@ export function nativeReviewAbandonAuthorization(request
|
|
|
1776
1811
|
// so the native v2 gate verifies an exact eight-line binding (schema, lineage,
|
|
1777
1812
|
// revision, snapshot_identity, reason, captured_lens_results, findings_present,
|
|
1778
1813
|
// actor) — there is no evidence_records_present line to derive or relay.
|
|
1814
|
+
// capturedLensResults must arrive verbatim from the native authority
|
|
1815
|
+
// inventory projection (issue #1159): the gate recomputes this line from its
|
|
1816
|
+
// own ordered record, whose entries carry the ordinal prefix, so caller- or
|
|
1817
|
+
// facade-authored lens names cannot reproduce it.
|
|
1779
1818
|
return [
|
|
1780
1819
|
"gentle-ai.review-abandon-authorization/v2",
|
|
1781
1820
|
`lineage=${request.lineage}`,
|
|
@@ -1916,10 +1955,11 @@ function splitNativeConsentInvocation(invocation ) {
|
|
|
1916
1955
|
continue;
|
|
1917
1956
|
}
|
|
1918
1957
|
// Provider invocations are not shell commands. Keep every path backslash
|
|
1919
|
-
// verbatim, including quoted UNC and drive-root paths. Outside quotes,
|
|
1920
|
-
// only
|
|
1958
|
+
// verbatim, including quoted UNC and drive-root paths. Outside quotes, a
|
|
1959
|
+
// backslash escapes only whitespace or a quote, so a space-containing token
|
|
1960
|
+
// and a POSIX-quoted apostrophe (`'it'\''s'`) split exactly.
|
|
1921
1961
|
const next = source[index + 1];
|
|
1922
|
-
if (character === "\\" && quote === undefined && next !== undefined && /\s/.test(next)) {
|
|
1962
|
+
if (character === "\\" && quote === undefined && next !== undefined && (/\s/.test(next) || next === "'" || next === '"')) {
|
|
1923
1963
|
escaping = true;
|
|
1924
1964
|
started = true;
|
|
1925
1965
|
continue;
|
|
@@ -2162,7 +2202,7 @@ export class NativeReviewCliV216 {
|
|
|
2162
2202
|
) {
|
|
2163
2203
|
let result ;
|
|
2164
2204
|
try {
|
|
2165
|
-
result = await this.adapter({ file: path, arguments: arguments_, cwd, timeoutMs: mutating ? undefined : this.timeoutMs, maxBufferBytes: this.maxBufferBytes, signal });
|
|
2205
|
+
result = await measureStatusAsync("adapter", () => this.adapter({ file: path, arguments: arguments_, cwd, timeoutMs: mutating ? undefined : this.timeoutMs, maxBufferBytes: this.maxBufferBytes, signal }));
|
|
2166
2206
|
} catch (error) {
|
|
2167
2207
|
if (error instanceof Error && error.name === "AbortError") throw nativeError(NATIVE_REVIEW_ERROR_CODE.CANCELLED, operation, mutating, "native process was cancelled");
|
|
2168
2208
|
throw nativeError(NATIVE_REVIEW_ERROR_CODE.UNAVAILABLE, operation, mutating, "native process could not start");
|
|
@@ -2178,7 +2218,7 @@ export class NativeReviewCliV216 {
|
|
|
2178
2218
|
const body = parseJson(result.stdout, operation, mutating, diagnostics);
|
|
2179
2219
|
if (result.exitCode !== 0) {
|
|
2180
2220
|
try {
|
|
2181
|
-
throw new NativeReviewIntegrationError(decodeReviewFailureV2(body));
|
|
2221
|
+
throw new NativeReviewIntegrationError(measureStatusSync("decode", () => decodeReviewFailureV2(body)));
|
|
2182
2222
|
} catch (error) {
|
|
2183
2223
|
if (error instanceof NativeReviewIntegrationError) throw error;
|
|
2184
2224
|
throw nativeError(NATIVE_REVIEW_ERROR_CODE.NON_ZERO, operation, mutating, "native negotiated operation failed without a valid failure envelope", result);
|
|
@@ -2197,31 +2237,7 @@ export class NativeReviewCliV216 {
|
|
|
2197
2237
|
signal ,
|
|
2198
2238
|
toleratedStderr = [],
|
|
2199
2239
|
) {
|
|
2200
|
-
return this.invoke(operation, cwd, arguments_, mutating, signal, this.executablePath(operation, mutating), toleratedStderr);
|
|
2201
|
-
}
|
|
2202
|
-
|
|
2203
|
-
async sddStatus(request ) {
|
|
2204
|
-
return this.sddProjection(request, false);
|
|
2205
|
-
}
|
|
2206
|
-
|
|
2207
|
-
async sddContinue(request ) {
|
|
2208
|
-
if (!isCanonicalProcessString(request.changeName)) throw new TypeError("Native SDD continuation requires an exact selected change");
|
|
2209
|
-
return this.sddProjection(request, true);
|
|
2210
|
-
}
|
|
2211
|
-
|
|
2212
|
-
async sddProjection(request , mutating ) {
|
|
2213
|
-
if ((request.changeName !== undefined && !isCanonicalProcessString(request.changeName)) || !isCanonicalProcessString(request.workspaceRoot) || !isAbsolute(request.workspaceRoot)) {
|
|
2214
|
-
throw new TypeError("Native SDD status requires a canonical change and absolute workspace root");
|
|
2215
|
-
}
|
|
2216
|
-
const operation = mutating ? NATIVE_REVIEW_OPERATION.SDD_CONTINUE : NATIVE_REVIEW_OPERATION.SDD_STATUS;
|
|
2217
|
-
const execution = await this.negotiated(
|
|
2218
|
-
operation,
|
|
2219
|
-
request.workspaceRoot,
|
|
2220
|
-
[operation, ...(request.changeName === undefined ? [] : [request.changeName]), "--cwd", request.workspaceRoot, "--json", "--instructions"],
|
|
2221
|
-
mutating,
|
|
2222
|
-
request.signal,
|
|
2223
|
-
);
|
|
2224
|
-
return decode(operation, mutating, () => decodeNativeSddStatusV2(execution.body, request));
|
|
2240
|
+
return this.invoke(operation, cwd, arguments_, mutating, signal, measureStatusSync("resolution", () => this.executablePath(operation, mutating)), toleratedStderr);
|
|
2225
2241
|
}
|
|
2226
2242
|
|
|
2227
2243
|
async start(request ) {
|
|
@@ -2234,7 +2250,7 @@ export class NativeReviewCliV216 {
|
|
|
2234
2250
|
// drift; Pi never rebuilds that vector from request fields.
|
|
2235
2251
|
const projection = request.projection ?? "workspace";
|
|
2236
2252
|
const selection = nativeUntrackedSelection(request);
|
|
2237
|
-
const
|
|
2253
|
+
const statusRequest = {
|
|
2238
2254
|
cwd: request.cwd,
|
|
2239
2255
|
projection,
|
|
2240
2256
|
...(request.baseRef === undefined ? {} : { baseRef: request.baseRef, committedOnly: true }),
|
|
@@ -2243,7 +2259,19 @@ export class NativeReviewCliV216 {
|
|
|
2243
2259
|
...(request.intendedUntrackedSelection === undefined ? {} : { intendedUntrackedSelection: request.intendedUntrackedSelection }),
|
|
2244
2260
|
agent: "pi",
|
|
2245
2261
|
...(request.signal === undefined ? {} : { signal: request.signal }),
|
|
2246
|
-
}
|
|
2262
|
+
};
|
|
2263
|
+
const lensSelection = request.lenses === undefined && request.lensesReason === undefined
|
|
2264
|
+
? {}
|
|
2265
|
+
: { lenses: request.lenses, lensesReason: request.lensesReason };
|
|
2266
|
+
let status ;
|
|
2267
|
+
try {
|
|
2268
|
+
status = await this.targetStatus({ ...statusRequest, ...lensSelection });
|
|
2269
|
+
} catch (error) {
|
|
2270
|
+
// Phase A: a provider that predates lens selection still reviews,
|
|
2271
|
+
// with the tier default, instead of refusing the candidate.
|
|
2272
|
+
if (Object.keys(lensSelection).length === 0 || !isNativeReviewLensSelectionUnsupported(error)) throw error;
|
|
2273
|
+
status = await this.targetStatus(statusRequest);
|
|
2274
|
+
}
|
|
2247
2275
|
const transition = status.nextTransition?.kind === "execute" && status.nextTransition.execute?.operation === "review.start"
|
|
2248
2276
|
? status.nextTransition.execute
|
|
2249
2277
|
: undefined;
|
|
@@ -2340,23 +2368,53 @@ export class NativeReviewCliV216 {
|
|
|
2340
2368
|
}
|
|
2341
2369
|
|
|
2342
2370
|
async targetStatus(request ) {
|
|
2371
|
+
return observeNativeStatus(this.timeoutMs, () => this.observedTargetStatus(request));
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
async observedTargetStatus(request ) {
|
|
2343
2375
|
if (request.baseRef !== undefined && !isCanonicalProcessString(request.baseRef)) throw new TypeError("Native STATUS baseRef must be a non-empty, trimmed, NUL-free string");
|
|
2344
2376
|
if (request.baseRef !== undefined && request.committedOnly !== true) throw new TypeError("Native STATUS baseRef requires explicit committedOnly acknowledgement");
|
|
2345
2377
|
if (request.baseRef === undefined && request.committedOnly !== undefined) throw new TypeError("Native STATUS committedOnly requires an explicit baseRef");
|
|
2346
2378
|
const selection = nativeUntrackedSelection(request);
|
|
2347
2379
|
const submitted = request.intendedUntrackedSelection;
|
|
2348
2380
|
if (submitted !== undefined && submitted.argumentTokens.reduce((count, token) => count + token.split("{{value}}").length - 1, 0) !== 1) throw new TypeError("Native intended-untracked selection requires exactly one provider-issued {{value}} token");
|
|
2349
|
-
const
|
|
2381
|
+
const submittedTokens = submitted === undefined ? undefined : submitted.argumentTokens.map((token) => token.replaceAll("{{value}}", submitted.value));
|
|
2382
|
+
// gentle-ai 3.7.1 submission argument_tokens never carry --base-ref /
|
|
2383
|
+
// --committed-only / --lineage, so a forwarded selector must be appended
|
|
2384
|
+
// after them; only bail if the provider already staked out a conflicting
|
|
2385
|
+
// value for the same flag. providerFlagValue also fails closed on a
|
|
2386
|
+
// dangling `--flag` (last token, no value) or an empty `--flag=`.
|
|
2387
|
+
const submittedBaseRefValue = submittedTokens === undefined ? undefined : providerFlagValue(submittedTokens, "base-ref");
|
|
2388
|
+
if (request.baseRef !== undefined && submittedBaseRefValue !== undefined && submittedBaseRefValue !== request.baseRef) throw new TypeError("Native intended-untracked selection already carries a conflicting base-ref");
|
|
2389
|
+
const submittedLineageValue = submittedTokens === undefined ? undefined : providerFlagValue(submittedTokens, "lineage");
|
|
2390
|
+
if (request.lineageId !== undefined && submittedLineageValue !== undefined && submittedLineageValue !== request.lineageId) throw new TypeError("Native intended-untracked selection already carries a conflicting lineage");
|
|
2391
|
+
let submittedHasCommittedOnly = false;
|
|
2392
|
+
for (const token of submittedTokens ?? []) {
|
|
2393
|
+
if (token !== "--committed-only" && !token.startsWith("--committed-only=")) continue;
|
|
2394
|
+
if (token !== "--committed-only" && token !== "--committed-only=true") throw new TypeError("Native intended-untracked selection has an invalid --committed-only value");
|
|
2395
|
+
if (submittedHasCommittedOnly) throw new TypeError("Native intended-untracked selection repeats --committed-only");
|
|
2396
|
+
submittedHasCommittedOnly = true;
|
|
2397
|
+
}
|
|
2398
|
+
// Append only the half of the base-ref/committed-only pair the provider
|
|
2399
|
+
// did not already carry (a present base-ref already equals
|
|
2400
|
+
// request.baseRef; the throw above ruled out the conflicting case).
|
|
2401
|
+
const forwardedBaseRef = request.baseRef === undefined ? [] : [
|
|
2402
|
+
...(submittedBaseRefValue === undefined ? ["--base-ref", request.baseRef] : []),
|
|
2403
|
+
...(submittedHasCommittedOnly ? [] : ["--committed-only"]),
|
|
2404
|
+
];
|
|
2405
|
+
const forwardedLineage = request.lineageId === undefined || submittedLineageValue !== undefined ? [] : ["--lineage", request.lineageId];
|
|
2406
|
+
const statusArguments = submittedTokens === undefined ? [
|
|
2350
2407
|
"review", "status", "--contract", REVIEW_INTEGRATION_CONTRACT, "--cwd", request.cwd,
|
|
2351
2408
|
"--projection", request.projection ?? "workspace",
|
|
2352
2409
|
...nativeUntrackedSelectionArguments(selection),
|
|
2353
2410
|
...(request.baseRef === undefined ? [] : ["--base-ref", request.baseRef, "--committed-only"]),
|
|
2354
2411
|
...(request.lineageId === undefined ? [] : ["--lineage", request.lineageId]),
|
|
2355
2412
|
...(request.agent === undefined ? [] : ["--agent", request.agent]),
|
|
2413
|
+
...nativeLensSelectionArguments(request),
|
|
2356
2414
|
"--next-transition",
|
|
2357
|
-
] : ["review", "status", "--cwd", request.cwd, ...
|
|
2415
|
+
] : ["review", "status", "--cwd", request.cwd, ...submittedTokens, ...forwardedBaseRef, ...forwardedLineage, ...nativeLensSelectionArguments(request)];
|
|
2358
2416
|
const execution = await this.negotiated(NATIVE_REVIEW_OPERATION.STATUS, request.cwd, statusArguments, false, request.signal);
|
|
2359
|
-
assertSupportedNextTransitionOperation(execution.body);
|
|
2417
|
+
measureStatusSync("decode", () => assertSupportedNextTransitionOperation(execution.body));
|
|
2360
2418
|
return decode(NATIVE_REVIEW_OPERATION.STATUS, false, () => decodeReviewStatusV3(execution.body));
|
|
2361
2419
|
}
|
|
2362
2420
|
|
|
@@ -2545,13 +2603,17 @@ export class NativeReviewCliV216 {
|
|
|
2545
2603
|
// mutually exclusive with a path.
|
|
2546
2604
|
|
|
2547
2605
|
async reviewStatus(request ) {
|
|
2606
|
+
return observeNativeStatus(this.timeoutMs, () => this.observedReviewStatus(request));
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2609
|
+
async observedReviewStatus(request ) {
|
|
2548
2610
|
const execution = await this.invoke(
|
|
2549
2611
|
NATIVE_REVIEW_OPERATION.STATUS,
|
|
2550
2612
|
request.cwd,
|
|
2551
2613
|
["review", "status", "--cwd", request.cwd],
|
|
2552
2614
|
false,
|
|
2553
2615
|
request.signal,
|
|
2554
|
-
this.executablePath(NATIVE_REVIEW_OPERATION.STATUS, false),
|
|
2616
|
+
measureStatusSync("resolution", () => this.executablePath(NATIVE_REVIEW_OPERATION.STATUS, false)),
|
|
2555
2617
|
);
|
|
2556
2618
|
const status = decode(NATIVE_REVIEW_OPERATION.STATUS, false, () => decodeNativeReviewStatus(execution.body));
|
|
2557
2619
|
if (!await repositoriesMatch(request.cwd, status.repository)) throw nativeError(NATIVE_REVIEW_ERROR_CODE.IDENTITY_MISMATCH, NATIVE_REVIEW_OPERATION.STATUS, false, "native review status repository mismatch");
|