gentle-pi 4.0.0 → 4.2.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 +157 -18
- package/assets/agents/gentle-ai-explore.md +11 -4
- package/assets/agents/gentle-ai-verify.md +23 -4
- package/assets/agents/gentle-ai-worker.md +6 -6
- 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 +70 -163
- package/assets/orchestrator-memory.md +11 -4
- package/assets/orchestrator-prompts.md +48 -0
- 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 +29 -25
- 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/bin/engram-guard +9 -0
- package/bin/gentle-shell-install.mjs +302 -0
- package/bin/gentle-shell.mjs +708 -22
- 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/auto-permission-answers.md +25 -0
- package/docs/auto-permission-evidence.md +39 -0
- package/docs/bundled-install.md +399 -0
- package/docs/delegated-verification.md +3 -5
- package/docs/gentle-agents-activity.md +513 -0
- package/docs/gentle-shell-uninstall.md +136 -0
- package/docs/gentle-shell.md +90 -33
- package/docs/install-wizard.md +2040 -0
- package/docs/native-authority-architecture.md +1 -1
- package/docs/permission-modes.md +127 -0
- package/docs/prompt-history.md +1 -1
- package/docs/readme-reference.md +310 -47
- package/docs/render-cache-performance.md +28 -0
- package/docs/session-profile-format.md +39 -0
- package/docs/sound-notifications-proposal.md +191 -0
- package/docs/sound-notifications.md +114 -0
- package/docs/yolo-mode.md +11 -10
- package/extensions/ask-user-choice.ts +4 -0
- package/extensions/ask-user-question.ts +6 -1
- package/extensions/child-context.ts +11 -2
- package/extensions/child-safety.ts +4 -2
- package/extensions/codegraph-tools.ts +18 -5
- package/extensions/gentle-agents.ts +718 -87
- package/extensions/gentle-ai.ts +991 -263
- package/extensions/gentle-herdr-activity.ts +81 -0
- package/extensions/gentle-notifications.ts +104 -0
- package/extensions/gentle-shell.ts +271 -80
- package/extensions/gentle-todo.ts +123 -18
- package/extensions/history/index.ts +11 -6
- package/extensions/pi-pretty.ts +39 -3
- package/extensions/quiet-tools.ts +91 -21
- package/extensions/skill-registry.ts +175 -46
- package/extensions/startup-banner.ts +11 -2
- package/lib/agent-home.ts +10 -1
- package/lib/agent-model-resolution.ts +80 -0
- package/lib/agent-profiles.ts +13 -3
- package/lib/agents-completion-delivery.ts +11 -3
- package/lib/agents-messaging.ts +1 -1
- package/lib/agents-protocol.ts +40 -1
- package/lib/agents-runner.ts +90 -15
- package/lib/auto-permission-answers.ts +133 -0
- package/lib/auto-permission-classifier.ts +459 -0
- package/lib/auto-permission-evidence.ts +88 -0
- package/lib/auto-permission-memory.ts +69 -0
- package/lib/auto-permission-runtime.ts +484 -0
- package/lib/auto-permission-tool-context.ts +61 -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 +83 -10
- package/lib/builtin-codemode-optout.ts +166 -0
- package/lib/card-content-policy.ts +85 -0
- package/lib/child-package-injection.ts +62 -0
- package/lib/codemode-renderer.ts +14 -4
- package/lib/command-palette-catalog.ts +3 -2
- package/lib/context-threshold.ts +26 -0
- package/lib/destructive-command-guard.ts +104 -28
- package/lib/gentle-ai-binary.ts +109 -4
- package/lib/gentle-shell-bundled.ts +535 -0
- package/lib/gentle-shell-launcher.ts +75 -24
- 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/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 +6 -1
- package/lib/nan-provider.ts +33 -11
- package/lib/native-review-cli.ts +194 -73
- 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.ts +1 -1
- 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/permission-mode-policy.ts +59 -0
- package/lib/review-candidate-view-owner.ts +8 -6
- package/lib/review-candidate-view.ts +3 -2
- 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-risk-assessment.ts +77 -51
- package/lib/review-sidebar-state.ts +2 -1
- 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 +22 -7
- package/lib/session-change-capture.ts +5 -4
- package/lib/session-changes.ts +27 -22
- package/lib/session-profile-binding.ts +83 -0
- package/lib/session-profile-freeze.ts +261 -0
- package/lib/session-profile-persistence.ts +187 -0
- package/lib/session-worktree-registry.ts +74 -8
- package/lib/shell-bar.ts +41 -11
- package/lib/shell-changes-view.ts +40 -13
- package/lib/shell-changes.ts +88 -71
- package/lib/shell-prompt.ts +6 -4
- package/lib/shell-sidebar-layout.ts +5 -1
- package/lib/shell-todo.ts +81 -26
- package/lib/shell-usage.ts +4 -3
- package/lib/status-timing-diagnostics.ts +201 -0
- package/lib/telemetry-trigger.ts +3 -1
- package/lib/vim-editor-adapter.ts +27 -5
- package/lib/visual-customization-policy.ts +20 -9
- package/lib/visual-customize-view.ts +171 -20
- package/lib/writer-surfaces.ts +162 -0
- package/lib/yolo-session-policy.ts +198 -121
- package/package.json +5 -5
- package/runtime/child-package-injection.mjs +63 -0
- package/runtime/gentle-ai-binary.mjs +109 -4
- package/runtime/gentle-shell-bundled.mjs +536 -0
- package/runtime/gentle-shell-launcher.mjs +74 -23
- 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 +191 -70
- package/runtime/review-integration-v2.mjs +100 -22
- package/runtime/review-risk-assessment.mjs +77 -51
- 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 +7 -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 +79 -32
- 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/npm-pack-result.mjs +31 -0
- package/scripts/pi-update.mjs +223 -0
- package/scripts/render-cache-performance-worker.mjs +205 -0
- package/scripts/render-cache-performance.mjs +115 -0
- package/scripts/run-test-suite.mjs +22 -8
- package/scripts/test-installer-acceptance.sh +161 -0
- package/scripts/test-packed-runner.mjs +23 -5
- package/scripts/types-baseline.json +2 -92
- package/scripts/verify-package-files.mjs +82 -18
- package/skills/gentle-ai/SKILL.md +12 -11
- package/tests/agent-home.test.ts +56 -3
- package/tests/agent-model-resolution.test.ts +113 -0
- package/tests/agent-profiles.test.ts +12 -0
- package/tests/agent-risk-escalation.test.ts +123 -0
- package/tests/agents-completion-delivery.test.ts +17 -0
- package/tests/agents-protocol.test.ts +57 -0
- package/tests/agents-queries.test.ts +9 -9
- package/tests/agents-runner-process.test.ts +31 -0
- package/tests/agents-runner.test.ts +89 -4
- package/tests/append-system-prompt-route.test.ts +18 -1
- package/tests/auto-mode-human-answers.test.ts +68 -0
- package/tests/auto-mode-invariants.test.ts +276 -0
- package/tests/auto-mode-sdk.test.ts +84 -0
- package/tests/auto-mode.test.ts +49 -0
- package/tests/auto-permission-answers.test.ts +170 -0
- package/tests/auto-permission-classifier.test.ts +634 -0
- package/tests/auto-permission-compound.test.ts +24 -0
- package/tests/auto-permission-evidence.test.ts +88 -0
- package/tests/auto-permission-memory.test.ts +46 -0
- package/tests/auto-permission-public-regressions.test.ts +111 -0
- package/tests/auto-permission-runtime.test.ts +454 -0
- package/tests/autonomous-guard.test.ts +86 -16
- 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 +52 -1
- 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 +34 -2
- package/tests/child-codegraph-bootstrap.test.ts +74 -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 +12 -0
- package/tests/child-web-package.test.ts +224 -0
- package/tests/codegraph-tools.test.ts +35 -0
- package/tests/codemode-rendering.test.ts +107 -2
- package/tests/command-palette.test.ts +5 -5
- 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 -2
- package/tests/devbinary/native-review-parity.devtest.ts +1 -1
- package/tests/devbinary/pi-host-relay.devtest.ts +173 -3
- package/tests/engram-safety.test.ts +316 -0
- package/tests/generic-agent-tools.test.ts +16 -1
- package/tests/gentle-agents.test.ts +1895 -76
- package/tests/gentle-ai-binary-isolation.test.ts +135 -0
- package/tests/gentle-ai-binary.test.ts +19 -2
- 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-installer.test.ts +548 -58
- package/tests/gentle-ai-prompt-model-routing.test.ts +123 -0
- package/tests/gentle-ai-renderer.test.ts +6 -6
- package/tests/gentle-ai.test.ts +999 -31
- package/tests/gentle-herdr-activity.test.ts +100 -0
- package/tests/gentle-notifications.test.ts +427 -0
- package/tests/gentle-shell-bin.test.ts +263 -19
- package/tests/gentle-shell-bundled.test.ts +1036 -0
- package/tests/gentle-shell-launcher.test.ts +251 -25
- 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 +611 -100
- package/tests/gentle-todo.test.ts +317 -14
- package/tests/herdr-activity.test.ts +243 -0
- package/tests/history-agent-home.test.ts +81 -0
- package/tests/history-child-guard.test.ts +70 -0
- package/tests/inprocess-reviewer.test.ts +20 -0
- 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 +2159 -0
- package/tests/installer-server.test.ts +1567 -0
- package/tests/installer-windows-bootstrap.test.ts +2410 -0
- package/tests/jobs-view.test.ts +160 -0
- package/tests/main-channel-upgrade.test.ts +395 -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-package.test.ts +10 -0
- package/tests/nan-provider-runtime.test.ts +156 -0
- package/tests/nan-provider.test.ts +28 -18
- package/tests/native-binary-gate.test.ts +2 -0
- package/tests/native-review-capability-contract.test.ts +7 -1
- package/tests/native-review-cli.test.ts +236 -1
- package/tests/native-review-consent.test.ts +2 -2
- package/tests/native-review-parity-runtime.test.ts +1 -1
- 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 +14 -5
- package/tests/odd-routing-canonical-ratchet.test.ts +53 -12
- package/tests/odd-routing-contract.test.ts +80 -23
- package/tests/odd-runtime-delegation-gate.test.ts +4 -1
- package/tests/orchestrator-budget.test.ts +29 -6
- 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 +6 -5
- 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/package-manifest.test.ts +105 -21
- package/tests/packed-runner-owned-path.test.ts +35 -1
- 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 +4 -12
- package/tests/pi-pretty.test.ts +56 -0
- package/tests/pi-update-launcher.test.ts +87 -0
- package/tests/pi-update.test.ts +186 -0
- package/tests/provider-defect-handoff.test.ts +4 -3
- package/tests/quiet-tool-rendering.test.ts +160 -3
- package/tests/rdd-aware-verification-contract.test.ts +29 -41
- package/tests/rdd-status-line.test.ts +52 -0
- package/tests/render-cache-performance.test.ts +48 -0
- package/tests/review-agent-end-preflight.test.ts +62 -10
- package/tests/review-candidate-view.test.ts +101 -12
- package/tests/review-contract-prompt.test.ts +33 -4
- package/tests/review-controller-native-recovery.test.ts +327 -17
- package/tests/review-controller-native-routing.test.ts +255 -1
- package/tests/review-controller-workspace-root.test.ts +43 -2
- 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 +5 -3
- package/tests/review-host-relay.test.ts +49 -2
- package/tests/review-integration-v2-forward.test.ts +21 -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 +5 -4
- 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-risk-assessment.test.ts +109 -33
- 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-test-fixtures.ts +7 -1
- package/tests/review-transaction.test.ts +18 -1
- package/tests/run-test-suite.test.ts +83 -5
- package/tests/runtime-harness.mjs +42 -15
- package/tests/runtime-metrics-extension.test.ts +1 -1
- package/tests/runtime-metrics-native.test.ts +31 -1
- package/tests/selection-engine.test.ts +65 -5
- 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-profile-binding.test.ts +163 -0
- package/tests/session-profile-freeze.test.ts +394 -0
- package/tests/session-profile-persistence.test.ts +334 -0
- package/tests/session-subject-routing-contract.test.ts +34 -0
- package/tests/session-worktree-registry.test.ts +57 -2
- package/tests/shell-bar.test.ts +40 -0
- package/tests/shell-changes-view.test.ts +85 -34
- package/tests/shell-changes.test.ts +79 -62
- 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 +27 -2
- package/tests/shell-todo.test.ts +132 -11
- package/tests/shell-usage-view.test.ts +1 -1
- package/tests/shell-usage.test.ts +35 -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 +311 -5
- package/tests/startup-banner.test.ts +117 -3
- package/tests/startup-home-repo.test.ts +170 -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 +27 -1
- package/tests/test-strength-contract.test.ts +124 -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.test.ts +47 -21
- package/tests/visual-customization-policy.test.ts +32 -3
- package/tests/visual-customize-view.test.ts +288 -10
- package/tests/writer-edit-surface-scope.test.ts +59 -2
- package/tests/writer-surfaces.test.ts +199 -0
- package/tests/yolo-customize.test.ts +18 -3
- package/tests/yolo-mode-runtime.test.ts +37 -10
- package/tests/yolo-mode.test.ts +38 -5
- package/tests/yolo-session-policy.test.ts +1 -1
- /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
|
@@ -286,14 +286,15 @@ function posixPrivateModeProbe(path: string): boolean {
|
|
|
286
286
|
}
|
|
287
287
|
|
|
288
288
|
function directory(path: string, privateMode = false, platform: NodeJS.Platform = process.platform, classifyParentPrivacy = false): string {
|
|
289
|
-
|
|
289
|
+
// 64-bit dev/ino may collide as Numbers; validate metadata and identity from one exact observation.
|
|
290
|
+
const stat = lstatSync(path, { bigint: true });
|
|
290
291
|
if (!stat.isDirectory() || stat.isSymbolicLink() || !samePath(realpathSync(path), path, platform)) throw new Error("Unsafe candidate owner directory");
|
|
291
292
|
if (privateMode && platform !== "win32") {
|
|
292
293
|
const uid = process.getuid?.();
|
|
293
294
|
if (uid === undefined) throw new Error("Unsafe candidate owner directory");
|
|
294
|
-
if (stat.uid !== uid || (stat.mode &
|
|
295
|
+
if (stat.uid !== BigInt(uid) || (stat.mode & 0o077n) !== 0n) {
|
|
295
296
|
if (classifyParentPrivacy) {
|
|
296
|
-
if (stat.uid === uid && (stat.mode &
|
|
297
|
+
if (stat.uid === BigInt(uid) && (stat.mode & 0o077n) !== 0n && !posixPrivateModeProbe(path)) throw new PosixCandidateOwnerParentChmodIneffectiveError();
|
|
297
298
|
throw new PosixCandidateOwnerParentPrivacyError();
|
|
298
299
|
}
|
|
299
300
|
throw new Error("Unsafe candidate owner directory");
|
|
@@ -329,10 +330,11 @@ export function prepareCandidateOwnerParent(commonDir: string, platform: NodeJS.
|
|
|
329
330
|
}
|
|
330
331
|
|
|
331
332
|
function regular(path: string, privateMode = false, platform: NodeJS.Platform = process.platform): string {
|
|
332
|
-
|
|
333
|
+
// Keep 64-bit identity exact without a second stat lookup racing the privacy checks.
|
|
334
|
+
const stat = lstatSync(path, { bigint: true });
|
|
333
335
|
const uid = process.getuid?.();
|
|
334
|
-
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !==
|
|
335
|
-
(privateMode && platform !== "win32" && (uid === undefined || stat.uid !== uid || (stat.mode &
|
|
336
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1n || !samePath(realpathSync(path), path, platform) || stat.size > 16384n ||
|
|
337
|
+
(privateMode && platform !== "win32" && (uid === undefined || stat.uid !== BigInt(uid) || (stat.mode & 0o777n) !== 0o600n))) throw new Error("Unsafe candidate owner file");
|
|
336
338
|
if (privateMode && platform === "win32") privateWindowsDacl(path, "file", false);
|
|
337
339
|
return `${stat.dev}:${stat.ino}`;
|
|
338
340
|
}
|
|
@@ -1861,7 +1861,7 @@ export function decodeCandidateContextManifest(encoded: string, sha256: string):
|
|
|
1861
1861
|
if (actualSha256 !== sha256) throw new CandidateViewError("candidate context manifest integrity check failed", "candidate-context-manifest-integrity");
|
|
1862
1862
|
const text = bytes.toString("utf8");
|
|
1863
1863
|
if (!Buffer.from(text, "utf8").equals(bytes)) return invalidCandidateContextManifest("candidate context manifest is not valid UTF-8");
|
|
1864
|
-
if (gzipSync(bytes
|
|
1864
|
+
if (gzipSync(bytes).toString("base64url") !== encoded) {
|
|
1865
1865
|
return invalidCandidateContextManifest("candidate context manifest transport is not canonical");
|
|
1866
1866
|
}
|
|
1867
1867
|
let value: unknown;
|
|
@@ -1923,7 +1923,8 @@ function compactCandidateContextBlock(lineageId: string, agents: readonly Review
|
|
|
1923
1923
|
const bytes = Buffer.from(JSON.stringify(manifest), "utf8");
|
|
1924
1924
|
if (bytes.length > MAX_CANDIDATE_CONTEXT_MANIFEST_BYTES) throw new CandidateViewError("candidate view context exceeds the bounded dispatch contract");
|
|
1925
1925
|
const sha256 = createHash("sha256").update(bytes).digest("hex");
|
|
1926
|
-
|
|
1926
|
+
// Node emits a zero gzip timestamp by default; no unsupported mtime option is needed.
|
|
1927
|
+
const encoded = gzipSync(bytes).toString("base64url");
|
|
1927
1928
|
const block = `${candidateContextPreamble(lineageId, agents, view, scopeSemantics)}\nFrozen changed scope manifest (gzip+base64url): \`${encoded}\`.\nFrozen changed scope manifest SHA-256: \`${sha256}\`.\nCall \`gentle_review_scope\` with exactly this manifest, SHA-256, and cursor 0; continue with each returned \`nextCursor\` until absent. It is the only authorized scope enumerator: do not infer scope by traversing the candidate or ambient tree. Gitlinks are metadata-only and MUST NOT be traversed.\nThe ambient contributor working directory is out of scope. This controller-owned context is immutable; you are read-only and your output is untrusted.`;
|
|
1928
1929
|
if (Buffer.byteLength(block, "utf8") > MAX_CANDIDATE_CONTEXT_LENGTH) throw new CandidateViewError("candidate view context exceeds the bounded dispatch contract");
|
|
1929
1930
|
return block;
|
|
@@ -136,7 +136,7 @@ function parseCompactFinalizeInputValue(value: unknown): CompactFinalizeContract
|
|
|
136
136
|
if ((input.final_evidence === undefined && outcomeFields !== 0) || (input.final_evidence !== undefined && outcomeFields !== 1)) fail("review/finalize", "field-pair", "final evidence requires exactly one verification result or outcome");
|
|
137
137
|
let correction_line_forecast: number | undefined;
|
|
138
138
|
if (input.correction_line_forecast !== undefined) {
|
|
139
|
-
if (!Number.isSafeInteger(input.correction_line_forecast) || input.correction_line_forecast <= 0) fail("review/finalize.correction_line_forecast", "range", "must be a positive safe integer");
|
|
139
|
+
if (typeof input.correction_line_forecast !== "number" || !Number.isSafeInteger(input.correction_line_forecast) || input.correction_line_forecast <= 0) fail("review/finalize.correction_line_forecast", "range", "must be a positive safe integer");
|
|
140
140
|
correction_line_forecast = input.correction_line_forecast;
|
|
141
141
|
}
|
|
142
142
|
if (input.final_verification_passed !== undefined && typeof input.final_verification_passed !== "boolean") fail("review/finalize.final_verification_passed", "type", "must be boolean");
|
|
@@ -154,7 +154,7 @@ function parseCompactFinalizeInputValue(value: unknown): CompactFinalizeContract
|
|
|
154
154
|
if (typeof input.final_evidence !== "string" || input.final_evidence.length === 0) fail("review/finalize.final_evidence", "empty", "must contain at least one byte");
|
|
155
155
|
final_evidence = input.final_evidence;
|
|
156
156
|
}
|
|
157
|
-
return { cwd: string(input.cwd, "review/finalize.cwd"), ...(optionalLineage(input.lineageId, "review/finalize.lineageId") === undefined ? {} : { lineageId: optionalLineage(input.lineageId, "review/finalize.lineageId")! }), ...(correction_line_forecast === undefined ? {} : { correction_line_forecast }), ...(input.validation === undefined ? {} : { validation: parseValidation(input.validation, "review/finalize.validation") }), ...(final_evidence === undefined ? {} : { final_evidence }), ...(input.final_verification_passed
|
|
157
|
+
return { cwd: string(input.cwd, "review/finalize.cwd"), ...(optionalLineage(input.lineageId, "review/finalize.lineageId") === undefined ? {} : { lineageId: optionalLineage(input.lineageId, "review/finalize.lineageId")! }), ...(correction_line_forecast === undefined ? {} : { correction_line_forecast }), ...(input.validation === undefined ? {} : { validation: parseValidation(input.validation, "review/finalize.validation") }), ...(final_evidence === undefined ? {} : { final_evidence }), ...(typeof input.final_verification_passed !== "boolean" ? {} : { final_verification_passed: input.final_verification_passed }), ...(final_verification_outcome === undefined ? {} : { final_verification_outcome }), ...(typeof input.reviewer_run_acknowledged !== "boolean" ? {} : { reviewer_run_acknowledged: input.reviewer_run_acknowledged }) };
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
export function parseNativeCompactFinalizeInput(value: unknown): CompactFinalizeContractInput {
|
package/lib/review-host-relay.ts
CHANGED
|
@@ -34,13 +34,14 @@ import { spawn } from "node:child_process";
|
|
|
34
34
|
import { tmpdir } from "node:os";
|
|
35
35
|
import { isAbsolute, join } from "node:path";
|
|
36
36
|
import { completeSimple } from "@earendil-works/pi-ai/compat";
|
|
37
|
-
import { resolveGentleAiBinary } from "./gentle-ai-binary.ts";
|
|
37
|
+
import { gentleAiIsolationEnv, resolveGentleAiBinary } from "./gentle-ai-binary.ts";
|
|
38
38
|
import {
|
|
39
39
|
INPROCESS_REVIEWER_FAILURE,
|
|
40
40
|
runInProcessReviewer,
|
|
41
41
|
type InProcessReviewerFailureCode,
|
|
42
42
|
type InProcessReviewerOutcome,
|
|
43
43
|
type InProcessReviewerRegistry,
|
|
44
|
+
type InProcessReviewerRequest,
|
|
44
45
|
} from "./inprocess-reviewer.ts";
|
|
45
46
|
import { REVIEW_PROVIDER_ROLE_CAPTURE_OPERATION, REVIEW_PROVIDER_ROLE_CAPTURE_OPERATIONS, type ReviewCaptureSubmissionV1, type ReviewCollectInputV3 } from "./review-integration-v2.ts";
|
|
46
47
|
import { GENTLE_PI_REVIEW_RELAY_CONTRACT, GENTLE_PI_REVIEW_RELAY_CONTRACT_ENV } from "./review-relay-contract.ts";
|
|
@@ -386,6 +387,8 @@ export interface ReviewHostRelayRequest {
|
|
|
386
387
|
readonly selection?: string;
|
|
387
388
|
/** The routing entry's thinking label, forwarded verbatim to the completion. */
|
|
388
389
|
readonly thinking?: string;
|
|
390
|
+
/** Effective Pi transport preference, forwarded without forcing a provider default. */
|
|
391
|
+
readonly reviewerTransport?: InProcessReviewerRequest["transport"];
|
|
389
392
|
/**
|
|
390
393
|
* The caller's live pi session id, forwarded into the in-process completion
|
|
391
394
|
* request so an OpenCode-routed reviewer model carries its
|
|
@@ -498,7 +501,8 @@ function collectGentleAiProcess(
|
|
|
498
501
|
const startedAt = Date.now();
|
|
499
502
|
const child = spawn(file, [...arguments_], {
|
|
500
503
|
cwd: options.cwd,
|
|
501
|
-
|
|
504
|
+
// S9: a bundled install keeps Gentle AI's state inside its prefix.
|
|
505
|
+
env: gentleAiIsolationEnv(options.env),
|
|
502
506
|
stdio: ["pipe", "pipe", "pipe"],
|
|
503
507
|
shell: false,
|
|
504
508
|
windowsHide: true,
|
|
@@ -737,6 +741,7 @@ export async function prepareReviewHostRelaySlot(
|
|
|
737
741
|
{
|
|
738
742
|
selection: preparedRequest.selection!,
|
|
739
743
|
...(preparedRequest.thinking === undefined ? {} : { thinking: preparedRequest.thinking }),
|
|
744
|
+
...(preparedRequest.reviewerTransport === undefined ? {} : { transport: preparedRequest.reviewerTransport }),
|
|
740
745
|
...(preparedRequest.reviewerSessionId === undefined ? {} : { sessionId: preparedRequest.reviewerSessionId }),
|
|
741
746
|
prompt: promptBytes,
|
|
742
747
|
timeoutMs: piTimeoutMs,
|
|
@@ -77,8 +77,8 @@ export type ReviewStartState = (typeof REVIEW_START_STATE)[keyof typeof REVIEW_S
|
|
|
77
77
|
const START_ACTIONS = ["created", "resumed", "closed", "blocked-scope-action"] as const;
|
|
78
78
|
const RISK_LEVELS = ["low", "medium", "high"] as const;
|
|
79
79
|
const REVIEW_LENSES = ["review-risk", "review-resilience", "review-readability", "review-reliability"] as const;
|
|
80
|
-
const RISK_REASON_CODES = ["configuration_change", "empty_content", "executable_change", "executable_mode", "hot_path", "large_change", "non_executable_only", "process_boundary", "process_scan_limit", "service_token", "shell_source"] as const;
|
|
81
|
-
const RISK_SIGNALS = ["auth", "update", "security", "payments", "permissions", "shell_process"] as const;
|
|
80
|
+
const RISK_REASON_CODES = ["agent_escalation", "configuration_change", "dangerous_sink", "empty_content", "executable_change", "executable_mode", "hot_path", "large_change", "non_executable_only", "process_boundary", "process_scan_limit", "service_token", "shell_source"] as const;
|
|
81
|
+
const RISK_SIGNALS = ["auth", "update", "security", "payments", "permissions", "shell_process", "dangerous_sink", "agent_escalation"] as const;
|
|
82
82
|
// "collect" and "execute" are the v2 envelope's projection of a live
|
|
83
83
|
// transaction whose next_transition is mandatory: the root action names the
|
|
84
84
|
// transition kind instead of reading as a terminal stop.
|
|
@@ -636,6 +636,23 @@ export interface ReviewEscalationV1 {
|
|
|
636
636
|
refuterOutcomes?: readonly ReviewEscalationRefuterOutcomeV1[];
|
|
637
637
|
}
|
|
638
638
|
|
|
639
|
+
function decodeReviewEscalationV1(value: unknown, label: string): ReviewEscalationV1 {
|
|
640
|
+
const source = exactRecord(value, label, ["cause", "finding_ids"], ["refuter_outcomes"]);
|
|
641
|
+
const escalation: ReviewEscalationV1 = {
|
|
642
|
+
cause: enumeration(source.cause, ESCALATION_CAUSES, `${label}.cause`),
|
|
643
|
+
findingIds: stringArray(source.finding_ids, `${label}.finding_ids`),
|
|
644
|
+
};
|
|
645
|
+
if (Object.hasOwn(source, "refuter_outcomes")) escalation.refuterOutcomes = array(source.refuter_outcomes, `${label}.refuter_outcomes`, (entry, itemLabel) => {
|
|
646
|
+
const row = exactRecord(entry, itemLabel, ["finding_id", "outcome", "proof"]);
|
|
647
|
+
return {
|
|
648
|
+
findingId: nonempty(row.finding_id, `${itemLabel}.finding_id`),
|
|
649
|
+
outcome: enumeration(row.outcome, ESCALATION_REFUTER_OUTCOMES, `${itemLabel}.outcome`),
|
|
650
|
+
proof: nonempty(row.proof, `${itemLabel}.proof`),
|
|
651
|
+
};
|
|
652
|
+
});
|
|
653
|
+
return escalation;
|
|
654
|
+
}
|
|
655
|
+
|
|
639
656
|
export interface ReviewStatusV3 {
|
|
640
657
|
/** Optional v7 diagnostic evidence, never routing or reconstructed authority. */
|
|
641
658
|
escalation?: ReviewEscalationV1;
|
|
@@ -2104,23 +2121,9 @@ export function decodeReviewStatusV3(value: unknown): ReviewStatusV3 {
|
|
|
2104
2121
|
};
|
|
2105
2122
|
}
|
|
2106
2123
|
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
const source = exactRecord(body.escalation, label, ["cause", "finding_ids"], ["refuter_outcomes"]);
|
|
2111
|
-
escalation = {
|
|
2112
|
-
cause: enumeration(source.cause, ESCALATION_CAUSES, `${label}.cause`),
|
|
2113
|
-
findingIds: stringArray(source.finding_ids, `${label}.finding_ids`),
|
|
2114
|
-
};
|
|
2115
|
-
if (Object.hasOwn(source, "refuter_outcomes")) escalation.refuterOutcomes = array(source.refuter_outcomes, `${label}.refuter_outcomes`, (entry, itemLabel) => {
|
|
2116
|
-
const row = exactRecord(entry, itemLabel, ["finding_id", "outcome", "proof"]);
|
|
2117
|
-
return {
|
|
2118
|
-
findingId: nonempty(row.finding_id, `${itemLabel}.finding_id`),
|
|
2119
|
-
outcome: enumeration(row.outcome, ESCALATION_REFUTER_OUTCOMES, `${itemLabel}.outcome`),
|
|
2120
|
-
proof: nonempty(row.proof, `${itemLabel}.proof`),
|
|
2121
|
-
};
|
|
2122
|
-
});
|
|
2123
|
-
}
|
|
2124
|
+
const escalation = Object.hasOwn(body, "escalation")
|
|
2125
|
+
? decodeReviewEscalationV1(body.escalation, "status.escalation")
|
|
2126
|
+
: undefined;
|
|
2124
2127
|
|
|
2125
2128
|
// status/v7 top-level optional digest (gentle-ai v2.6.0): resolves #4066's
|
|
2126
2129
|
// closed loop where `sdd-attempt finish` named a digest status never
|
|
@@ -2583,7 +2586,11 @@ function assertReviewApprovedAcknowledgementExecuteShapeV1(execute: ReviewNextTr
|
|
|
2583
2586
|
if (execute.arguments.length !== REVIEW_APPROVED_ACKNOWLEDGEMENT_ARGUMENTS.length) throw new TypeError(`acknowledgement.execute.arguments must carry exactly ${REVIEW_APPROVED_ACKNOWLEDGEMENT_ARGUMENTS.length} provider-issued arguments`);
|
|
2584
2587
|
const values = execute.arguments.map((argument, index) => { const name = REVIEW_APPROVED_ACKNOWLEDGEMENT_ARGUMENTS[index]!; if (argument.name !== name) throw new TypeError(`acknowledgement.execute.arguments[${index}].name must be ${name}`); const value = nonempty(argument.value, `acknowledgement.execute.arguments[${index}].value`); if (nonempty(argument.token, `acknowledgement.execute.arguments[${index}].token`) !== `--${name}=${value}`) throw new TypeError(`acknowledgement.execute.arguments[${index}].token must exactly match ${name}`); return value; });
|
|
2585
2588
|
if (execute.preconditions.length !== 1 || execute.preconditions[0]?.name !== "state" || execute.preconditions[0]?.value !== "approved") throw new TypeError("acknowledgement.execute.preconditions must be the single approved state precondition");
|
|
2586
|
-
|
|
2589
|
+
const tokens = execute.arguments.map((argument) => argument.token!);
|
|
2590
|
+
// The exact five-argument check above validates both vectors before tuple construction.
|
|
2591
|
+
const tokenTuple: ReviewApprovedAcknowledgementExecuteTokensV1 = [tokens[0]!, tokens[1]!, tokens[2]!, tokens[3]!, tokens[4]!];
|
|
2592
|
+
const valueTuple: readonly [string, string, string, string, string] = [values[0]!, values[1]!, values[2]!, values[3]!, values[4]!];
|
|
2593
|
+
return { tokens: tokenTuple, values: valueTuple, lineageId: lineage(execute.binding.lineageId, "acknowledgement.execute.binding.lineage_id"), targetIdentity: sha256(execute.binding.targetIdentity, "acknowledgement.execute.binding.target_identity"), revision: sha256(execute.binding.revision, "acknowledgement.execute.binding.revision") };
|
|
2587
2594
|
}
|
|
2588
2595
|
|
|
2589
2596
|
/**
|
|
@@ -2682,6 +2689,26 @@ export interface ReviewLastEventReviewerResultV1 {
|
|
|
2682
2689
|
resultHash: string;
|
|
2683
2690
|
}
|
|
2684
2691
|
|
|
2692
|
+
export interface ReviewTargetedValidatorCheckEvidenceV1 {
|
|
2693
|
+
passed: boolean;
|
|
2694
|
+
evidence: readonly string[];
|
|
2695
|
+
regressions?: readonly {
|
|
2696
|
+
id?: string;
|
|
2697
|
+
location: string;
|
|
2698
|
+
claim: string;
|
|
2699
|
+
proofRefs: readonly string[];
|
|
2700
|
+
}[];
|
|
2701
|
+
}
|
|
2702
|
+
|
|
2703
|
+
export interface ReviewTargetedValidatorEvidenceV1 {
|
|
2704
|
+
targetedValidationRequestHash: string;
|
|
2705
|
+
correctionTargetIdentity: string;
|
|
2706
|
+
originalCriteria: ReviewTargetedValidatorCheckEvidenceV1;
|
|
2707
|
+
correctionRegression: ReviewTargetedValidatorCheckEvidenceV1;
|
|
2708
|
+
followUps: readonly { observation: string; proofRefs: readonly string[] }[];
|
|
2709
|
+
raw: Record<string, unknown>;
|
|
2710
|
+
}
|
|
2711
|
+
|
|
2685
2712
|
export interface ReviewLastEventClosureV1 {
|
|
2686
2713
|
schema: typeof REVIEW_LAST_EVENT_CLOSURE_SCHEMA;
|
|
2687
2714
|
operation: ReviewLastEventClosureOperation;
|
|
@@ -2692,6 +2719,8 @@ export interface ReviewLastEventClosureV1 {
|
|
|
2692
2719
|
targetIdentity?: string;
|
|
2693
2720
|
requestHash?: string;
|
|
2694
2721
|
correctionLines?: number;
|
|
2722
|
+
escalation?: ReviewEscalationV1;
|
|
2723
|
+
targetedValidatorEvidence?: ReviewTargetedValidatorEvidenceV1;
|
|
2695
2724
|
advisoryFindings?: ReviewAdvisoryFindingsV1;
|
|
2696
2725
|
reviewerResults?: readonly ReviewLastEventReviewerResultV1[];
|
|
2697
2726
|
statusContinuation?: ReviewStatusContinuationV1;
|
|
@@ -2835,18 +2864,67 @@ function decodeReviewLastEventReviewerResultV1(value: unknown, label: string): R
|
|
|
2835
2864
|
};
|
|
2836
2865
|
}
|
|
2837
2866
|
|
|
2867
|
+
function decodeReviewTargetedValidatorCheckEvidenceV1(value: unknown, label: string): ReviewTargetedValidatorCheckEvidenceV1 {
|
|
2868
|
+
const check = exactRecord(value, label, ["passed", "evidence"], ["regressions"]);
|
|
2869
|
+
const regressions = Object.hasOwn(check, "regressions")
|
|
2870
|
+
? array(check.regressions, `${label}.regressions`, (entry, entryLabel) => {
|
|
2871
|
+
const row = exactRecord(entry, entryLabel, ["location", "claim", "proof_refs"], ["id"]);
|
|
2872
|
+
return {
|
|
2873
|
+
...(Object.hasOwn(row, "id") ? { id: nonempty(row.id, `${entryLabel}.id`) } : {}),
|
|
2874
|
+
location: nonempty(row.location, `${entryLabel}.location`),
|
|
2875
|
+
claim: nonempty(row.claim, `${entryLabel}.claim`),
|
|
2876
|
+
proofRefs: stringArray(row.proof_refs, `${entryLabel}.proof_refs`, { minimum: 1 }),
|
|
2877
|
+
};
|
|
2878
|
+
}) : undefined;
|
|
2879
|
+
return {
|
|
2880
|
+
passed: boolean(check.passed, `${label}.passed`),
|
|
2881
|
+
// Native admission rejects observations containing only Unicode whitespace.
|
|
2882
|
+
evidence: stringArray(check.evidence, `${label}.evidence`, { minimum: 1, pattern: /[^\p{White_Space}]/u }),
|
|
2883
|
+
...(regressions === undefined ? {} : { regressions }),
|
|
2884
|
+
};
|
|
2885
|
+
}
|
|
2886
|
+
|
|
2887
|
+
function decodeReviewTargetedValidatorEvidenceV1(value: unknown, label: string): ReviewTargetedValidatorEvidenceV1 {
|
|
2888
|
+
const body = exactRecord(value, label, ["targeted_validation_request_hash", "correction_target_identity", "original_criteria", "correction_regression", "follow_ups"]);
|
|
2889
|
+
return {
|
|
2890
|
+
targetedValidationRequestHash: sha256(body.targeted_validation_request_hash, `${label}.targeted_validation_request_hash`),
|
|
2891
|
+
correctionTargetIdentity: sha256(body.correction_target_identity, `${label}.correction_target_identity`),
|
|
2892
|
+
originalCriteria: decodeReviewTargetedValidatorCheckEvidenceV1(body.original_criteria, `${label}.original_criteria`),
|
|
2893
|
+
correctionRegression: decodeReviewTargetedValidatorCheckEvidenceV1(body.correction_regression, `${label}.correction_regression`),
|
|
2894
|
+
followUps: array(body.follow_ups, `${label}.follow_ups`, (entry, entryLabel) => {
|
|
2895
|
+
const row = exactRecord(entry, entryLabel, ["observation", "proof_refs"]);
|
|
2896
|
+
return { observation: nonempty(row.observation, `${entryLabel}.observation`), proofRefs: stringArray(row.proof_refs, `${entryLabel}.proof_refs`, { minimum: 1 }) };
|
|
2897
|
+
}),
|
|
2898
|
+
raw: body,
|
|
2899
|
+
};
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2838
2902
|
export function decodeReviewLastEventClosureV1(value: unknown): ReviewLastEventClosureV1 {
|
|
2839
|
-
const body = exactRecord(value, "last_event_closure", ["schema", "operation", "lineage_id", "state", "store_revision"], ["target_identity", "request_hash", "correction_lines", "action", "advisory_findings", "reviewer_results", "status_continuation", "acknowledgement"]);
|
|
2903
|
+
const body = exactRecord(value, "last_event_closure", ["schema", "operation", "lineage_id", "state", "store_revision"], ["target_identity", "request_hash", "correction_lines", "action", "escalation", "targeted_validator_evidence", "advisory_findings", "reviewer_results", "status_continuation", "acknowledgement"]);
|
|
2840
2904
|
if (body.schema !== REVIEW_LAST_EVENT_CLOSURE_SCHEMA) throw new TypeError(`last_event_closure.schema must be ${REVIEW_LAST_EVENT_CLOSURE_SCHEMA}`);
|
|
2841
2905
|
const operation = enumeration(body.operation, Object.values(REVIEW_LAST_EVENT_CLOSURE_OPERATION), "last_event_closure.operation") as ReviewLastEventClosureOperation;
|
|
2842
2906
|
const state = enumeration(body.state, REVIEW_LAST_EVENT_TERMINAL_STATES, "last_event_closure.state") as ReviewLastEventClosureState;
|
|
2907
|
+
if (Object.hasOwn(body, "targeted_validator_evidence") && (operation !== REVIEW_LAST_EVENT_CLOSURE_OPERATION.CAPTURE_VALIDATION || state !== "escalated")) {
|
|
2908
|
+
throw new TypeError("last_event_closure targeted_validator_evidence requires escalated review/capture-validation");
|
|
2909
|
+
}
|
|
2910
|
+
// Diagnostic evidence is optional for older providers and never controls terminal authority.
|
|
2911
|
+
const targetedValidatorEvidence = Object.hasOwn(body, "targeted_validator_evidence")
|
|
2912
|
+
? decodeReviewTargetedValidatorEvidenceV1(body.targeted_validator_evidence, "last_event_closure.targeted_validator_evidence")
|
|
2913
|
+
: undefined;
|
|
2914
|
+
if (Object.hasOwn(body, "escalation") && state !== "escalated") throw new TypeError("last_event_closure escalation requires escalated state");
|
|
2915
|
+
// Older native closures omit this diagnostic; its presence never grants a continuation.
|
|
2916
|
+
const escalation = Object.hasOwn(body, "escalation")
|
|
2917
|
+
? decodeReviewEscalationV1(body.escalation, "last_event_closure.escalation")
|
|
2918
|
+
: undefined;
|
|
2843
2919
|
const shared = {
|
|
2844
2920
|
schema: REVIEW_LAST_EVENT_CLOSURE_SCHEMA,
|
|
2845
2921
|
operation,
|
|
2846
2922
|
lineageId: lineage(body.lineage_id, "last_event_closure.lineage_id"),
|
|
2847
2923
|
state,
|
|
2848
2924
|
storeRevision: sha256(body.store_revision, "last_event_closure.store_revision"),
|
|
2849
|
-
|
|
2925
|
+
...(escalation === undefined ? {} : { escalation }),
|
|
2926
|
+
...(targetedValidatorEvidence === undefined ? {} : { targetedValidatorEvidence }),
|
|
2927
|
+
} as const;
|
|
2850
2928
|
if (operation === REVIEW_LAST_EVENT_CLOSURE_OPERATION.CAPTURE_CORRECTION_PLAN) {
|
|
2851
2929
|
if (body.reviewer_results !== undefined) throw new TypeError("last_event_closure reviewer_results requires approved state");
|
|
2852
2930
|
if (body.action !== undefined || body.advisory_findings !== undefined || body.status_continuation !== undefined) throw new TypeError("last_event_closure correction-plan cannot carry action, advisory_findings, or status_continuation");
|
|
@@ -393,7 +393,7 @@ function deriveReleaseCiStatusForShaV1(options: {
|
|
|
393
393
|
} catch {
|
|
394
394
|
return { proven: false, status: null };
|
|
395
395
|
}
|
|
396
|
-
if (!isRecord(summary) || !Number.isSafeInteger(summary.total_count) || !Number.isSafeInteger(summary.returned) || !Array.isArray(summary.checks) || summary.total_count < 0 || summary.returned < 0 || summary.returned !== summary.checks.length || summary.total_count !== summary.checks.length) {
|
|
396
|
+
if (!isRecord(summary) || typeof summary.total_count !== "number" || typeof summary.returned !== "number" || !Number.isSafeInteger(summary.total_count) || !Number.isSafeInteger(summary.returned) || !Array.isArray(summary.checks) || summary.total_count < 0 || summary.returned < 0 || summary.returned !== summary.checks.length || summary.total_count !== summary.checks.length) {
|
|
397
397
|
return { proven: false, status: null };
|
|
398
398
|
}
|
|
399
399
|
if (summary.total_count > 0) {
|
|
@@ -139,18 +139,23 @@ const DUE_REVIEW_REASONS: readonly ReviewDueReason[] = [REVIEW_DUE_REASON.HIGH_R
|
|
|
139
139
|
|
|
140
140
|
/**
|
|
141
141
|
* Rejects a review_due/review_due_reason/consumed combination the native
|
|
142
|
-
* schema can never produce.
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
142
|
+
* schema can never produce. A consumed candidate must report
|
|
143
|
+
* `already_reviewed`, but that reason can also describe an acknowledged
|
|
144
|
+
* committed predecessor followed by a passive delta (#1954). It does not
|
|
145
|
+
* prove this exact candidate was consumed; preserve native's explicit false.
|
|
146
|
+
* Native v2 requires consumed, so missing evidence with already_reviewed is
|
|
147
|
+
* still rejected. Envelopes without the pair (older binaries) are unchecked.
|
|
146
148
|
*/
|
|
147
149
|
function validateReviewDueConsistency(reviewDue: boolean, reviewDueReason: ReviewDueReason, candidate: ReviewAssessmentCandidate): void {
|
|
148
150
|
if (reviewDue !== DUE_REVIEW_REASONS.includes(reviewDueReason)) {
|
|
149
151
|
throw new TypeError(`review assessment review_due ${reviewDue} contradicts review_due_reason ${reviewDueReason}`);
|
|
150
152
|
}
|
|
151
153
|
const alreadyReviewed = reviewDueReason === REVIEW_DUE_REASON.ALREADY_REVIEWED;
|
|
152
|
-
if (
|
|
153
|
-
throw new TypeError("review assessment
|
|
154
|
+
if (candidate.consumed === true && !alreadyReviewed) {
|
|
155
|
+
throw new TypeError("review assessment candidate.consumed true requires review_due_reason already_reviewed");
|
|
156
|
+
}
|
|
157
|
+
if (alreadyReviewed && candidate.consumed === undefined) {
|
|
158
|
+
throw new TypeError("review assessment review_due_reason already_reviewed requires explicit candidate.consumed evidence");
|
|
154
159
|
}
|
|
155
160
|
}
|
|
156
161
|
|
|
@@ -259,6 +264,53 @@ export const VERIFICATION_TIER = {
|
|
|
259
264
|
} as const;
|
|
260
265
|
export type VerificationTier = (typeof VERIFICATION_TIER)[keyof typeof VERIFICATION_TIER];
|
|
261
266
|
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
// Agent-raised risk (gentle-shell#1494). The agent that made the change may
|
|
269
|
+
// raise its risk to high by citing one item of the high-risk list in the
|
|
270
|
+
// always-on Task Size section; it can never lower a tier. Lowering stays with
|
|
271
|
+
// the deterministic native assessment.
|
|
272
|
+
// ---------------------------------------------------------------------------
|
|
273
|
+
|
|
274
|
+
export const HIGH_RISK_ITEMS = Object.freeze({
|
|
275
|
+
1: "data or irreversible effects",
|
|
276
|
+
2: "security",
|
|
277
|
+
3: "changing or removing contracts others already consume",
|
|
278
|
+
4: "concurrency",
|
|
279
|
+
5: "delivery or environment",
|
|
280
|
+
6: "no test would catch a regression",
|
|
281
|
+
} as const);
|
|
282
|
+
export type HighRiskItem = keyof typeof HIGH_RISK_ITEMS;
|
|
283
|
+
|
|
284
|
+
export interface AgentRiskEscalation {
|
|
285
|
+
readonly item: HighRiskItem;
|
|
286
|
+
readonly reason: string;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export const AGENT_RISK_ESCALATION_REASON_MAX = 500;
|
|
290
|
+
|
|
291
|
+
export function decodeAgentRiskEscalation(value: unknown): AgentRiskEscalation {
|
|
292
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
293
|
+
throw new Error("escalate must be an object with item and reason");
|
|
294
|
+
}
|
|
295
|
+
const record = value as Record<string, unknown>;
|
|
296
|
+
const unexpected = Object.keys(record).find((key) => key !== "item" && key !== "reason");
|
|
297
|
+
if (unexpected !== undefined) throw new Error(`escalate does not accept ${unexpected}`);
|
|
298
|
+
const { item, reason } = record;
|
|
299
|
+
if (typeof item !== "number" || !Number.isInteger(item) || !(item in HIGH_RISK_ITEMS)) {
|
|
300
|
+
throw new Error("escalate item must be an integer from 1 to 6 naming a high-risk item");
|
|
301
|
+
}
|
|
302
|
+
if (typeof reason !== "string" || reason.trim() === "" || reason.length > AGENT_RISK_ESCALATION_REASON_MAX) {
|
|
303
|
+
throw new Error(`escalate reason must be a non-empty string of at most ${AGENT_RISK_ESCALATION_REASON_MAX} characters`);
|
|
304
|
+
}
|
|
305
|
+
return Object.freeze({ item: item as HighRiskItem, reason });
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** Raises passive or medium to high when the agent escalated; never lowers. */
|
|
309
|
+
export function escalatedRisk(risk: VerificationTier, escalation: AgentRiskEscalation | undefined): VerificationTier {
|
|
310
|
+
if (escalation === undefined) return risk;
|
|
311
|
+
return risk === VERIFICATION_TIER.PASSIVE || risk === VERIFICATION_TIER.MEDIUM ? VERIFICATION_TIER.HIGH : risk;
|
|
312
|
+
}
|
|
313
|
+
|
|
262
314
|
export const RDD_LINE = {
|
|
263
315
|
ON: "on",
|
|
264
316
|
OFF: "off",
|
|
@@ -363,59 +415,33 @@ function nonClosedOutcomeClause(outcome: NativeReviewOutcome): string {
|
|
|
363
415
|
}
|
|
364
416
|
|
|
365
417
|
/**
|
|
366
|
-
* Computes who verifies a delegated writer's change
|
|
367
|
-
*
|
|
418
|
+
* Computes who verifies a delegated writer's change. Verification follows the
|
|
419
|
+
* native risk tier whatever the RDD line and native review outcome
|
|
420
|
+
* (verify-always-rdd-high S1, superseding the gentle-pi#662/#668 on-path): the
|
|
421
|
+
* native review is an additional outside view and never replaces or skips the
|
|
422
|
+
* tier's verification. `nativeReviewOutcome` only shapes the reason text.
|
|
368
423
|
*
|
|
369
|
-
* - `
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
* never lowers a tier relative to `off`; `nativeReviewOutcome` is ignored
|
|
380
|
-
* entirely on these lines):
|
|
381
|
-
* - `passive`: structural readback by the parent only; no verifier, no
|
|
382
|
-
* tests.
|
|
383
|
-
* - `medium`: the writer self-verifies; a separate independent verifier
|
|
384
|
-
* runs only when `writerProfile` is `"small"` -- the small-model bias
|
|
385
|
-
* raises the tier by one for verification purposes (medium -> high). An
|
|
386
|
-
* unknown or omitted writer profile resolves to `"small"` before it
|
|
387
|
-
* reaches this function (see `resolveWriterProfile`), so an unrecognized
|
|
388
|
-
* profile never lowers a tier below what a known small model would get.
|
|
389
|
-
* - `high` or `unassessable` (a failed or unrecognized native assessment is
|
|
390
|
-
* always treated as high): the writer self-verifies and an independent
|
|
391
|
-
* verifier always runs.
|
|
424
|
+
* - `passive`: structural readback by the parent only; no verifier, no tests.
|
|
425
|
+
* - `medium`: the writer self-verifies; a separate independent verifier runs
|
|
426
|
+
* only when `writerProfile` is `"small"` -- the small-model bias raises the
|
|
427
|
+
* tier by one for verification purposes (medium -> high). An unknown or
|
|
428
|
+
* omitted writer profile resolves to `"small"` before it reaches this
|
|
429
|
+
* function (see `resolveWriterProfile`), so an unrecognized profile never
|
|
430
|
+
* lowers a tier below what a known small model would get.
|
|
431
|
+
* - `high` or `unassessable` (a failed or unrecognized native assessment is
|
|
432
|
+
* always treated as high): the writer self-verifies and an independent
|
|
433
|
+
* verifier always runs.
|
|
392
434
|
*/
|
|
393
435
|
export function verificationPlan(input: VerificationPlanInput): VerificationPlan {
|
|
394
436
|
const { rddLine, risk, writerProfile } = input;
|
|
395
437
|
|
|
396
438
|
if (rddLine === RDD_LINE.ON) {
|
|
397
439
|
const outcome = input.nativeReviewOutcome ?? NATIVE_REVIEW_OUTCOME.UNKNOWN;
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
writerSelfVerification: false,
|
|
402
|
-
structuralReadbackOnly: true,
|
|
403
|
-
independentVerifier: false,
|
|
404
|
-
reason: "receipt-driven development is on and the native review closed for this candidate; the assessment also reports passive risk: a structural readback by the parent replaces both the writer's self-verification and any separate verifier.",
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
return Object.freeze({
|
|
408
|
-
writerSelfVerification: true,
|
|
409
|
-
structuralReadbackOnly: false,
|
|
410
|
-
independentVerifier: false,
|
|
411
|
-
reason: "receipt-driven development is on and the native review closed for this candidate: the writer's self-verification is the record and the closed native review was the independent check the writer cannot influence, so no separate verifier is required.",
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
// The on branch holds only while the native review reaches a terminal
|
|
415
|
-
// outcome for this candidate. Anything else -- declined, unavailable, or
|
|
416
|
-
// unknown -- falls back to the exact same risk-gated path as off.
|
|
440
|
+
const clause = outcome === NATIVE_REVIEW_OUTCOME.CLOSED
|
|
441
|
+
? "the native review closed for this candidate"
|
|
442
|
+
: nonClosedOutcomeClause(outcome);
|
|
417
443
|
return riskGatedPlan(
|
|
418
|
-
`receipt-driven development is on
|
|
444
|
+
`receipt-driven development is on and ${clause}; the native review is an additional outside view that never replaces verification, so the risk-gated path applies exactly as receipt-driven development off, and`,
|
|
419
445
|
risk,
|
|
420
446
|
writerProfile,
|
|
421
447
|
);
|
|
@@ -2,6 +2,7 @@ import type { ExtensionAPI, ExtensionContext, ToolDefinition } from "@earendil-w
|
|
|
2
2
|
import type { TSchema } from "typebox";
|
|
3
3
|
import { NATIVE_REVIEW_ERROR_CODE, NativeReviewCliError } from "./native-review-cli.ts";
|
|
4
4
|
import { sanitizeTerminalText } from "./terminal-theme.ts";
|
|
5
|
+
import { measureStatusSidebar } from "./status-timing-diagnostics.ts";
|
|
5
6
|
|
|
6
7
|
/** Ephemeral display data only; this event grants no review authority. */
|
|
7
8
|
export const REVIEW_SIDEBAR_EVENT = "gentle-ai:review-sidebar";
|
|
@@ -163,7 +164,7 @@ export function createReviewSidebarPublisher(pi: ExtensionAPI) {
|
|
|
163
164
|
let generation = 0;
|
|
164
165
|
let scope: ReviewScope | undefined;
|
|
165
166
|
const publish = (id: string, snapshot: ReviewSidebarSnapshot) => {
|
|
166
|
-
try { pi.events?.emit(REVIEW_SIDEBAR_EVENT, { sessionId: id, snapshot }); } catch { /* Display only. */ }
|
|
167
|
+
try { measureStatusSidebar(() => pi.events?.emit(REVIEW_SIDEBAR_EVENT, { sessionId: id, snapshot })); } catch { /* Display only. */ }
|
|
167
168
|
};
|
|
168
169
|
return {
|
|
169
170
|
reset(ctx?: ExtensionContext) {
|
|
@@ -994,11 +994,80 @@ function operationForTransition(transition: ReviewTransition): ReviewOperation {
|
|
|
994
994
|
throw new ReviewIntegrityError(`Unsupported reducer transition: ${transition}`);
|
|
995
995
|
}
|
|
996
996
|
|
|
997
|
+
function assertReducerInput(transition: ReviewTransition, input: unknown): asserts input is ReviewReducerInput {
|
|
998
|
+
const fail = (field: string): never => { throw new ReviewIntegrityError(`Graph reducer input ${field} is invalid`); };
|
|
999
|
+
const object = (value: unknown, field: string): Record<string, unknown> => {
|
|
1000
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return fail(field);
|
|
1001
|
+
return value as Record<string, unknown>;
|
|
1002
|
+
};
|
|
1003
|
+
const string = (value: unknown, field: string): void => { if (typeof value !== "string") fail(field); };
|
|
1004
|
+
const boolean = (value: unknown, field: string): void => { if (typeof value !== "boolean") fail(field); };
|
|
1005
|
+
const strings = (value: unknown, field: string): void => { if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) fail(field); };
|
|
1006
|
+
const rows = (value: unknown, field: string): void => {
|
|
1007
|
+
if (!Array.isArray(value)) return fail(field);
|
|
1008
|
+
// The canonical row decoder checks every field, enum and duplicate ID.
|
|
1009
|
+
createFrozenLedger(value);
|
|
1010
|
+
};
|
|
1011
|
+
const results = (value: unknown, field: string): void => {
|
|
1012
|
+
if (!Array.isArray(value)) return fail(field);
|
|
1013
|
+
for (const item of value) {
|
|
1014
|
+
const result = object(item, field);
|
|
1015
|
+
string(result.id, `${field}.id`);
|
|
1016
|
+
if (!Object.values(RESOLUTION_OUTCOME).some((outcome) => outcome === result.outcome)) fail(`${field}.outcome`);
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
const body = object(input, "object");
|
|
1020
|
+
switch (transition) {
|
|
1021
|
+
case REVIEW_TRANSITION.ORDINARY_DISCOVERY: rows(body.rows, "rows"); break;
|
|
1022
|
+
case REVIEW_TRANSITION.JUDGMENT_DAY_DISCOVERY: rows(body.judgeA, "judgeA"); rows(body.judgeB, "judgeB"); break;
|
|
1023
|
+
case REVIEW_TRANSITION.ORDINARY_EVIDENCE:
|
|
1024
|
+
results(body.deterministicResults, "deterministicResults");
|
|
1025
|
+
if (body.refuterResults !== undefined) results(body.refuterResults, "refuterResults");
|
|
1026
|
+
break;
|
|
1027
|
+
case REVIEW_TRANSITION.ORDINARY_FIX:
|
|
1028
|
+
case REVIEW_TRANSITION.JUDGMENT_DAY_FIX:
|
|
1029
|
+
string(body.candidateTree, "candidateTree"); strings(body.fixedIds, "fixedIds"); string(body.fixDiff, "fixDiff");
|
|
1030
|
+
if (body.changedPaths !== undefined) strings(body.changedPaths, "changedPaths");
|
|
1031
|
+
break;
|
|
1032
|
+
case REVIEW_TRANSITION.ORDINARY_NO_FIX: string(body.reason, "reason"); break;
|
|
1033
|
+
case REVIEW_TRANSITION.ORDINARY_FINAL_VERIFICATION:
|
|
1034
|
+
case REVIEW_TRANSITION.JUDGMENT_DAY_FINAL_VERIFICATION:
|
|
1035
|
+
boolean(body.passed, "passed"); if (body.reason !== undefined) string(body.reason, "reason"); break;
|
|
1036
|
+
case REVIEW_TRANSITION.ORDINARY_VALIDATION:
|
|
1037
|
+
case REVIEW_TRANSITION.JUDGMENT_DAY_REJUDGMENT: {
|
|
1038
|
+
const request = object(body.request, "request");
|
|
1039
|
+
strings(request.requested_ids, "request.requested_ids"); rows(request.frozen_rows, "request.frozen_rows"); string(request.frozen_ledger_hash, "request.frozen_ledger_hash");
|
|
1040
|
+
if (transition === REVIEW_TRANSITION.JUDGMENT_DAY_REJUDGMENT) {
|
|
1041
|
+
for (const field of ["fix_diff", "fix_diff_hash", "candidate_tree"]) string(request[field], `request.${field}`);
|
|
1042
|
+
if (typeof request.round !== "number" || !Number.isSafeInteger(request.round) || request.round < 1) fail("request.round");
|
|
1043
|
+
results(body.judgeAResults, "judgeAResults"); results(body.judgeBResults, "judgeBResults");
|
|
1044
|
+
} else {
|
|
1045
|
+
results(body.results, "results");
|
|
1046
|
+
const acceptance = object(request.original_acceptance_tests, "request.original_acceptance_tests");
|
|
1047
|
+
boolean(acceptance.passed, "request.original_acceptance_tests.passed"); string(acceptance.evidence_hash, "request.original_acceptance_tests.evidence_hash");
|
|
1048
|
+
strings(request.original_criterion_regressions, "request.original_criterion_regressions");
|
|
1049
|
+
if (!Array.isArray(request.correction_regressions) || !Array.isArray(request.follow_ups)) return fail("request.validation arrays");
|
|
1050
|
+
for (const value of request.correction_regressions) {
|
|
1051
|
+
const regression = object(value, "request.correction_regressions");
|
|
1052
|
+
string(regression.finding_id, "regression.finding_id"); string(regression.evidence_hash, "regression.evidence_hash"); boolean(regression.passed, "regression.passed");
|
|
1053
|
+
}
|
|
1054
|
+
for (const value of request.follow_ups) {
|
|
1055
|
+
const followUp = object(value, "request.follow_ups");
|
|
1056
|
+
for (const field of ["id", "location", "summary", "evidence_hash"]) string(followUp[field], `follow_up.${field}`);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
break;
|
|
1060
|
+
}
|
|
1061
|
+
default: fail("transition");
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
|
|
997
1065
|
function reduceReviewState(
|
|
998
1066
|
state: ReviewStateV1,
|
|
999
1067
|
transition: ReviewTransition,
|
|
1000
|
-
input:
|
|
1068
|
+
input: unknown,
|
|
1001
1069
|
): ReviewStateV1 {
|
|
1070
|
+
assertReducerInput(transition, input);
|
|
1002
1071
|
switch (transition) {
|
|
1003
1072
|
case REVIEW_TRANSITION.ORDINARY_DISCOVERY:
|
|
1004
1073
|
return recordOrdinaryDiscovery(state, input as OrdinaryDiscoveryInput);
|
|
@@ -1456,8 +1525,8 @@ export class ReviewTransactionStore {
|
|
|
1456
1525
|
const existing = current ? (current.body.lineages as Array<Record<string, unknown>>).find((value) => value.lineage_id === next.lineage_id && value.mode === "graph") : undefined;
|
|
1457
1526
|
if (previous && !existing) throw new ReviewIntegrityError("Graph predecessor is missing");
|
|
1458
1527
|
if (!previous && existing) throw new ReviewIntegrityError("Graph lineage already exists");
|
|
1459
|
-
|
|
1460
|
-
|
|
1528
|
+
if (existing?.head_event_id !== undefined && typeof existing.head_event_id !== "string") throw new ReviewIntegrityError("Graph head is invalid");
|
|
1529
|
+
const predecessor = typeof existing?.head_event_id === "string" ? existing.head_event_id : undefined;
|
|
1461
1530
|
const last = next.request_journal.at(-1);
|
|
1462
1531
|
const descriptor = (() => { try { return graph.readStoreDescriptor(); } catch { return undefined; } })();
|
|
1463
1532
|
const reducerTransition = eventContext?.transition ?? (predecessor === undefined ? "start" : last?.operation ?? "state-update");
|
|
@@ -18,7 +18,7 @@ const tokenFields = ["input", "output", "cacheRead", "cacheWrite", "reasoning",
|
|
|
18
18
|
* Only the fixed package catalog is cached; runtime instructions are not retained.
|
|
19
19
|
*/
|
|
20
20
|
let definitions: Array<{ name: string; fingerprint: string; fingerprintClass?: AgentClass }> | undefined;
|
|
21
|
-
const packagedAgentClassAliases = new Map([["sdd-proposal", "sdd-propose"]]
|
|
21
|
+
const packagedAgentClassAliases = new Map<string, string>([["sdd-proposal", "sdd-propose"]]);
|
|
22
22
|
function fingerprintAgentClassName(name: string): string {
|
|
23
23
|
const compatibilityName = name.startsWith("gentle-ai-") ? name.slice("gentle-ai-".length) : name;
|
|
24
24
|
return packagedAgentClassAliases.get(compatibilityName) ?? compatibilityName;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn, type ChildProcessWithoutNullStreams, type SpawnOptionsWithoutStdio } from "node:child_process";
|
|
2
|
-
import { resolveGentleAiBinary } from "./gentle-ai-binary.ts";
|
|
2
|
+
import { gentleAiIsolationEnv, resolveGentleAiBinary } from "./gentle-ai-binary.ts";
|
|
3
3
|
import { runtimeMetricsEnvAllows } from "./runtime-metrics-policy.ts";
|
|
4
4
|
import { isPublicRuntimeModelId, normalizeRuntimeModel, type RuntimeMetricBucket } from "./runtime-metrics.ts";
|
|
5
5
|
import type { ChildLaunchBucket } from "./runtime-metrics-children.ts";
|
|
@@ -105,7 +105,8 @@ export async function sendNativeRuntimeEvent(rows: readonly RuntimeMetricBucket[
|
|
|
105
105
|
const file = (deps.resolve ?? resolveGentleAiBinary)();
|
|
106
106
|
if (!runtimeMetricsEnvAllows(env)) return "disabled";
|
|
107
107
|
if (deps.signal?.aborted || deps.current?.() === false) return "discarded";
|
|
108
|
-
|
|
108
|
+
// S9: a bundled install keeps Gentle AI's state inside its prefix.
|
|
109
|
+
return await sendProcess(file, cwd, payload, gentleAiIsolationEnv(env), deps.spawn ?? spawn, deps.signal);
|
|
109
110
|
} catch { return "discarded"; }
|
|
110
111
|
finally { busy = false; }
|
|
111
112
|
}
|