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
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
|
|
7
|
+
import { applyModelConfig, applyModelConfigAsync, applySavedModelConfig } from "../extensions/gentle-ai.ts";
|
|
8
|
+
|
|
9
|
+
// gentle-ai#4946: the activation sweep re-applies saved routing without user
|
|
10
|
+
// consent. Persisted clear entries (an "inherit" saved from /gentle:models is
|
|
11
|
+
// `{}`) must not delete `model_profiles` entries the user authored in the
|
|
12
|
+
// agent-home `subagents.json`. Clearing stays owned by the consented flows
|
|
13
|
+
// that issue it: the panel save and a confirmed profile apply.
|
|
14
|
+
function preservedProfilesFixture(t: test.TestContext) {
|
|
15
|
+
const root = mkdtempSync(join(tmpdir(), "gentle-pi-model-profiles-"));
|
|
16
|
+
const configHome = join(root, "global");
|
|
17
|
+
const agentHome = join(root, "agent-home");
|
|
18
|
+
for (const dir of [configHome, join(agentHome, "agents")]) {
|
|
19
|
+
mkdirSync(dir, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
const previousConfigHome = process.env.GENTLE_PI_CONFIG_HOME;
|
|
22
|
+
const previousAgentHome = process.env.GENTLE_PI_AGENT_HOME;
|
|
23
|
+
const previousHome = process.env.HOME;
|
|
24
|
+
const previousUserProfile = process.env.USERPROFILE;
|
|
25
|
+
const isolatedHome = join(root, "home");
|
|
26
|
+
mkdirSync(isolatedHome, { recursive: true });
|
|
27
|
+
process.env.HOME = isolatedHome;
|
|
28
|
+
process.env.USERPROFILE = isolatedHome;
|
|
29
|
+
process.env.GENTLE_PI_CONFIG_HOME = configHome;
|
|
30
|
+
process.env.GENTLE_PI_AGENT_HOME = agentHome;
|
|
31
|
+
t.after(() => {
|
|
32
|
+
if (previousConfigHome === undefined) delete process.env.GENTLE_PI_CONFIG_HOME;
|
|
33
|
+
else process.env.GENTLE_PI_CONFIG_HOME = previousConfigHome;
|
|
34
|
+
if (previousAgentHome === undefined) delete process.env.GENTLE_PI_AGENT_HOME;
|
|
35
|
+
else process.env.GENTLE_PI_AGENT_HOME = previousAgentHome;
|
|
36
|
+
if (previousHome === undefined) delete process.env.HOME;
|
|
37
|
+
else process.env.HOME = previousHome;
|
|
38
|
+
if (previousUserProfile === undefined) delete process.env.USERPROFILE;
|
|
39
|
+
else process.env.USERPROFILE = previousUserProfile;
|
|
40
|
+
rmSync(root, { recursive: true, force: true });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// A discoverable user-source agent definition, as gentle-pi installs them.
|
|
44
|
+
writeFileSync(
|
|
45
|
+
join(agentHome, "agents", "worker.md"),
|
|
46
|
+
"---\nname: worker\ndescription: Worker\n---\nbody\n",
|
|
47
|
+
);
|
|
48
|
+
const subagentsPath = join(agentHome, "subagents.json");
|
|
49
|
+
const writeUserRouting = () => {
|
|
50
|
+
writeFileSync(
|
|
51
|
+
subagentsPath,
|
|
52
|
+
JSON.stringify({
|
|
53
|
+
max_concurrency: 3,
|
|
54
|
+
model_profiles: {
|
|
55
|
+
worker: { model: "openai/gpt-4o", effort: "high" },
|
|
56
|
+
"my-custom-agent": { model: "anthropic/opus" },
|
|
57
|
+
},
|
|
58
|
+
}),
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
writeUserRouting();
|
|
62
|
+
const globalModelsPath = join(configHome, "models.json");
|
|
63
|
+
const context = { cwd: root } as Parameters<typeof applySavedModelConfig>[0];
|
|
64
|
+
return { root, configHome, agentHome, subagentsPath, globalModelsPath, context, writeUserRouting };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
test("startup sweep keeps user model_profiles entries for persisted clears", async (t) => {
|
|
68
|
+
const fixture = preservedProfilesFixture(t);
|
|
69
|
+
// Saved routing as /gentle:models leaves it after pressing "inherit" on
|
|
70
|
+
// worker: a persisted clear entry. The sweep must not delete the entries.
|
|
71
|
+
writeFileSync(fixture.globalModelsPath, JSON.stringify({ worker: {} }));
|
|
72
|
+
const result = await applySavedModelConfig(fixture.context);
|
|
73
|
+
assert.equal(result.invalidPath, undefined);
|
|
74
|
+
assert.ok(existsSync(fixture.subagentsPath));
|
|
75
|
+
const stored = JSON.parse(readFileSync(fixture.subagentsPath, "utf8"));
|
|
76
|
+
assert.equal(stored.max_concurrency, 3, "unrelated user keys survive");
|
|
77
|
+
assert.equal(
|
|
78
|
+
stored.model_profiles?.worker?.model,
|
|
79
|
+
"openai/gpt-4o",
|
|
80
|
+
"user-authored worker profile must survive a persisted clear entry",
|
|
81
|
+
);
|
|
82
|
+
assert.equal(
|
|
83
|
+
stored.model_profiles?.["my-custom-agent"]?.model,
|
|
84
|
+
"anthropic/opus",
|
|
85
|
+
"user-authored profile for an agent absent from the store must survive",
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("activation never deletes empty saved routes, including previously materialized and missing agents", async (t) => {
|
|
90
|
+
for (const scenario of ["user-authored", "previously-materialized", "missing-definition"]) {
|
|
91
|
+
await t.test(scenario, async (t) => {
|
|
92
|
+
const fixture = preservedProfilesFixture(t);
|
|
93
|
+
if (scenario === "previously-materialized") await applyModelConfigAsync(fixture.root, { worker: { model: "openai/alpha", thinking: "high" } });
|
|
94
|
+
const target = scenario === "missing-definition" ? "my-custom-agent" : "worker";
|
|
95
|
+
writeFileSync(fixture.globalModelsPath, `${JSON.stringify({ [target]: {} })}\n`);
|
|
96
|
+
const files = [fixture.subagentsPath, join(fixture.agentHome, "agents", "worker.md"), fixture.globalModelsPath];
|
|
97
|
+
const before = files.map((path) => readFileSync(path, "utf8"));
|
|
98
|
+
for (const phase of ["startup", "reload"]) {
|
|
99
|
+
const result = await applySavedModelConfig(fixture.context);
|
|
100
|
+
assert.equal(result.invalidPath, undefined);
|
|
101
|
+
assert.equal(result.updated, 0, `${phase} must not delete or rewrite routing`);
|
|
102
|
+
assert.deepEqual(files.map((path) => readFileSync(path, "utf8")), before, `${phase} preserves routing, frontmatter, instructions and unrelated keys byte-identically`);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("startup sweep still materializes saved non-clear routing", async (t) => {
|
|
109
|
+
const fixture = preservedProfilesFixture(t);
|
|
110
|
+
writeFileSync(
|
|
111
|
+
fixture.globalModelsPath,
|
|
112
|
+
JSON.stringify({ worker: { model: "openai/alpha", thinking: "high" } }),
|
|
113
|
+
);
|
|
114
|
+
const result = await applySavedModelConfig(fixture.context);
|
|
115
|
+
assert.equal(result.invalidPath, undefined);
|
|
116
|
+
const stored = JSON.parse(readFileSync(fixture.subagentsPath, "utf8"));
|
|
117
|
+
assert.deepEqual(
|
|
118
|
+
stored.model_profiles?.worker,
|
|
119
|
+
{ model: "openai/alpha", effort: "high" },
|
|
120
|
+
"saved routing still materializes",
|
|
121
|
+
);
|
|
122
|
+
assert.equal(
|
|
123
|
+
stored.model_profiles?.["my-custom-agent"]?.model,
|
|
124
|
+
"anthropic/opus",
|
|
125
|
+
"materializing one agent leaves other user entries alone",
|
|
126
|
+
);
|
|
127
|
+
assert.equal(stored.max_concurrency, 3);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("explicit clears and equal-value writes retain sync and async behavior", async (t) => {
|
|
131
|
+
for (const [name, apply] of [["sync", applyModelConfig], ["async", applyModelConfigAsync]] as const) {
|
|
132
|
+
await t.test(name, async (t) => {
|
|
133
|
+
const fixture = preservedProfilesFixture(t);
|
|
134
|
+
const agentPath = join(fixture.agentHome, "agents", "worker.md");
|
|
135
|
+
writeFileSync(agentPath, "---\nname: worker\ndescription: Worker\nmodel: openai/gpt-4o\nthinking: high\n---\nUser instructions.\n");
|
|
136
|
+
const original = readFileSync(fixture.subagentsPath, "utf8");
|
|
137
|
+
const markdown = readFileSync(agentPath, "utf8");
|
|
138
|
+
const unchanged = await apply(fixture.root, { worker: { model: "openai/gpt-4o", thinking: "high" } });
|
|
139
|
+
assert.equal(unchanged.updated, 0);
|
|
140
|
+
assert.equal(readFileSync(fixture.subagentsPath, "utf8"), original);
|
|
141
|
+
assert.equal(readFileSync(agentPath, "utf8"), markdown);
|
|
142
|
+
const cleared = await apply(fixture.root, { worker: {} });
|
|
143
|
+
assert.equal(cleared.updated, 2, "explicit clear updates both routing surfaces");
|
|
144
|
+
assert.deepEqual(JSON.parse(readFileSync(fixture.subagentsPath, "utf8")), {
|
|
145
|
+
max_concurrency: 3, model_profiles: { "my-custom-agent": { model: "anthropic/opus" } },
|
|
146
|
+
});
|
|
147
|
+
const stored = readFileSync(agentPath, "utf8");
|
|
148
|
+
assert.doesNotMatch(stored, /^model:/m);
|
|
149
|
+
assert.doesNotMatch(stored, /^thinking:/m);
|
|
150
|
+
assert.ok(stored.endsWith("User instructions.\n"));
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import test from "node:test";
|
|
4
|
+
|
|
5
|
+
// This manifest is the public dependency contract consumed by package managers.
|
|
6
|
+
test("routing activation requires no native ownership dependency", () => {
|
|
7
|
+
const manifest = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
8
|
+
assert.equal(manifest.dependencies["fs-native-extensions"], undefined);
|
|
9
|
+
assert.equal(manifest.dependencies["@heyhuynhgiabuu/pi-pretty"], "0.6.27", "unrelated dependency remains pinned");
|
|
10
|
+
});
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import test from "node:test";
|
|
8
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
9
|
+
import { promisify } from "node:util";
|
|
10
|
+
|
|
11
|
+
const dependencyEntry = import.meta.resolve("@earendil-works/pi-coding-agent");
|
|
12
|
+
const bundledEntry = new URL("./bundle/index.js", dependencyEntry);
|
|
13
|
+
// An explicit override must load successfully; never skip a missing/broken SDK.
|
|
14
|
+
const override = process.env.NAN_RUNTIME_SDK_ENTRY;
|
|
15
|
+
const sdkEntry = override ? pathToFileURL(override).href
|
|
16
|
+
: existsSync(bundledEntry) ? bundledEntry.href : dependencyEntry;
|
|
17
|
+
const sdkLayout = sdkEntry.includes("/bundle/") ? "bundled" : "unbundled";
|
|
18
|
+
const extensionPath = fileURLToPath(new URL("../extensions/nan-provider.ts", import.meta.url));
|
|
19
|
+
const run = promisify(execFile);
|
|
20
|
+
|
|
21
|
+
// Fresh process per case: the real loader owns jiti and its pi-ai module mapping.
|
|
22
|
+
// No direct extension import, agent session, credential store, or provider network.
|
|
23
|
+
const probe = String.raw`
|
|
24
|
+
import assert from "node:assert/strict";
|
|
25
|
+
import { join } from "node:path";
|
|
26
|
+
const [sdkEntry, extensionPath, workspace, scenario] = process.argv.slice(1);
|
|
27
|
+
const originalFetch = globalThis.fetch;
|
|
28
|
+
let requests = 0;
|
|
29
|
+
let unexpectedRequests = 0;
|
|
30
|
+
globalThis.fetch = async () => {
|
|
31
|
+
unexpectedRequests++;
|
|
32
|
+
throw new Error("Automatic/global network is forbidden in this test");
|
|
33
|
+
};
|
|
34
|
+
try {
|
|
35
|
+
const { DefaultResourceLoader, SettingsManager, VERSION } = await import(sdkEntry);
|
|
36
|
+
const loader = new DefaultResourceLoader({
|
|
37
|
+
cwd: workspace, agentDir: join(workspace, "agent"),
|
|
38
|
+
settingsManager: SettingsManager.inMemory({ packages: [], retry: { enabled: false } }),
|
|
39
|
+
noExtensions: true, additionalExtensionPaths: [extensionPath],
|
|
40
|
+
noSkills: true, noPromptTemplates: true, noThemes: true, noContextFiles: true,
|
|
41
|
+
});
|
|
42
|
+
await loader.reload();
|
|
43
|
+
const loaded = loader.getExtensions();
|
|
44
|
+
assert.deepEqual(loaded.errors, []);
|
|
45
|
+
assert.equal(loaded.extensions.length, 1);
|
|
46
|
+
assert.deepEqual(loader.getAgentsFiles().agentsFiles, []);
|
|
47
|
+
const registrations = loaded.runtime.pendingNativeProviderRegistrations;
|
|
48
|
+
assert.equal(registrations.length, 1);
|
|
49
|
+
const { provider } = registrations[0];
|
|
50
|
+
assert.equal(provider.id, "nan");
|
|
51
|
+
const model = provider.getModels().find(m => m.id === "deepseek-v4-flash");
|
|
52
|
+
assert.ok(model);
|
|
53
|
+
const fakeFetch = async (input, init) => {
|
|
54
|
+
requests++;
|
|
55
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
56
|
+
assert.equal(url, "https://api.nan.builders/v1/chat/completions");
|
|
57
|
+
assert.equal(init.method, "POST");
|
|
58
|
+
const body = JSON.parse(init.body);
|
|
59
|
+
assert.equal(body.model, "deepseek-v4-flash");
|
|
60
|
+
assert.equal(body.stream, true);
|
|
61
|
+
if (scenario === "http-error") {
|
|
62
|
+
return new Response(JSON.stringify({ error: { message: "synthetic unavailable" } }), {
|
|
63
|
+
status: 503, headers: { "content-type": "application/json" },
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const delta = scenario === "tool" ? {
|
|
67
|
+
role: "assistant", tool_calls: [{ index: 0, id: "call_synthetic", type: "function",
|
|
68
|
+
function: { name: "lookup", arguments: '{"city":' } }],
|
|
69
|
+
} : { role: "assistant", content: "NAN_" };
|
|
70
|
+
if (scenario === "tool") {
|
|
71
|
+
assert.equal(body.tools[0].function.name, "lookup");
|
|
72
|
+
assert.equal(body.tools[0].function.parameters.properties.city.type, "string");
|
|
73
|
+
}
|
|
74
|
+
const nextDelta = scenario === "tool"
|
|
75
|
+
? { tool_calls: [{ index: 0, function: { arguments: '"Paris"}' } }] }
|
|
76
|
+
: { content: "OK" };
|
|
77
|
+
const chunk = (delta, finish_reason = null, usage) => ({
|
|
78
|
+
id: "synthetic", object: "chat.completion.chunk", created: 1, model: body.model,
|
|
79
|
+
choices: [{ index: 0, delta, finish_reason }], ...(usage ? { usage } : {}),
|
|
80
|
+
});
|
|
81
|
+
const chunks = [chunk(delta), chunk(nextDelta), chunk({}, scenario === "tool" ? "tool_calls" : "stop",
|
|
82
|
+
{ prompt_tokens: 3, completion_tokens: 2, total_tokens: 5 })];
|
|
83
|
+
return new Response(chunks.map(c => "data: " + JSON.stringify(c) + "\n\n").join("")
|
|
84
|
+
+ "data: [DONE]\n\n", { headers: { "content-type": "text/event-stream" } });
|
|
85
|
+
};
|
|
86
|
+
const messages = [{ role: "user", content: [{ type: "text", text: "Synthetic request" }], timestamp: 1 }];
|
|
87
|
+
if (scenario === "tool") messages.unshift({ role: "system", content: "Use lookup", timestamp: 1,
|
|
88
|
+
toolsAdded: [{ name: "lookup", description: "Synthetic lookup", parameters: {
|
|
89
|
+
type: "object", properties: { city: { type: "string" } }, required: ["city"],
|
|
90
|
+
} }] });
|
|
91
|
+
const stream = provider.streamSimple(model, { messages }, {
|
|
92
|
+
apiKey: "synthetic-key", fetch: fakeFetch, maxRetries: 0, maxTokens: 16, timeoutMs: 5000,
|
|
93
|
+
});
|
|
94
|
+
const events = [];
|
|
95
|
+
for await (const event of stream) events.push(event);
|
|
96
|
+
const result = await stream.result();
|
|
97
|
+
const types = events.map(event => event.type);
|
|
98
|
+
assert.equal(requests, 1, "including HTTP failures: retries must remain disabled");
|
|
99
|
+
assert.equal(unexpectedRequests, 0);
|
|
100
|
+
assert.equal(types.filter(type => type === "done" || type === "error").length, 1);
|
|
101
|
+
if (scenario === "http-error") {
|
|
102
|
+
assert.equal(types.at(-1), "error");
|
|
103
|
+
assert.equal(result.stopReason, "error");
|
|
104
|
+
assert.match(result.errorMessage, /synthetic unavailable/);
|
|
105
|
+
assert.equal(events.at(-1).error, result);
|
|
106
|
+
} else {
|
|
107
|
+
assert.equal(types[0], "start", result.errorMessage);
|
|
108
|
+
assert.equal(types.at(-1), "done");
|
|
109
|
+
assert.equal(events.at(-1).message, result);
|
|
110
|
+
assert.equal(result.usage.input, 3);
|
|
111
|
+
assert.equal(result.usage.output, 2);
|
|
112
|
+
assert.equal(result.usage.totalTokens, 5);
|
|
113
|
+
if (scenario === "tool") {
|
|
114
|
+
assert.equal(result.stopReason, "toolUse");
|
|
115
|
+
for (const type of ["toolcall_start", "toolcall_delta", "toolcall_end"]) assert.ok(types.includes(type));
|
|
116
|
+
const toolCall = result.content.find(block => block.type === "toolCall");
|
|
117
|
+
assert.equal(toolCall.name, "lookup");
|
|
118
|
+
assert.deepEqual(toolCall.arguments, { city: "Paris" });
|
|
119
|
+
assert.deepEqual(events.find(event => event.type === "toolcall_end").toolCall.arguments, { city: "Paris" });
|
|
120
|
+
} else {
|
|
121
|
+
assert.equal(result.stopReason, "stop");
|
|
122
|
+
assert.equal(result.content.filter(block => block.type === "text").map(block => block.text).join(""), "NAN_OK");
|
|
123
|
+
assert.equal(events.filter(event => event.type === "text_delta").map(event => event.delta).join(""), "NAN_OK");
|
|
124
|
+
assert.equal(types.filter(type => type === "text_start").length, 1);
|
|
125
|
+
assert.equal(types.filter(type => type === "text_end").length, 1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
console.log(JSON.stringify({ sdkVersion: VERSION, requests, types, stopReason: result.stopReason }));
|
|
129
|
+
} finally {
|
|
130
|
+
globalThis.fetch = originalFetch;
|
|
131
|
+
}
|
|
132
|
+
`;
|
|
133
|
+
|
|
134
|
+
for (const cache of ["default filesystem-cache env", "JITI_FS_CACHE=false"] as const) {
|
|
135
|
+
for (const scenario of ["text", "http-error", "tool"] as const) {
|
|
136
|
+
test(`NaN real loader → ${scenario} stream (${sdkLayout} SDK, cache ${cache})`, { timeout: 30_000 }, async (t) => {
|
|
137
|
+
const workspace = await mkdtemp(join(tmpdir(), "nan-runtime-test-"));
|
|
138
|
+
try {
|
|
139
|
+
const { stdout } = await run(process.execPath, ["--input-type=module", "--eval", probe,
|
|
140
|
+
sdkEntry, extensionPath, workspace, scenario], {
|
|
141
|
+
timeout: 25_000,
|
|
142
|
+
// Do not inherit provider keys or the user's settings/resource locations.
|
|
143
|
+
env: { HOME: workspace, PI_CODING_AGENT_DIR: join(workspace, "agent"), PI_OFFLINE: "1",
|
|
144
|
+
// Confine os.tmpdir fallback only; SDK-local default cache may still be used.
|
|
145
|
+
TMPDIR: workspace, TMP: workspace, TEMP: workspace,
|
|
146
|
+
...(cache === "JITI_FS_CACHE=false" ? { JITI_FS_CACHE: "false" } : {}) },
|
|
147
|
+
});
|
|
148
|
+
const evidence = JSON.parse(stdout.trim());
|
|
149
|
+
assert.equal(evidence.requests, 1);
|
|
150
|
+
t.diagnostic(`SDK ${evidence.sdkVersion} ${sdkLayout}: ${sdkEntry}`);
|
|
151
|
+
} finally {
|
|
152
|
+
await rm(workspace, { recursive: true, force: true });
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -4,7 +4,7 @@ import type { RefreshModelsContext } from "@earendil-works/pi-ai";
|
|
|
4
4
|
import { clampThinkingLevel, createModels, getSupportedThinkingLevels, InMemoryCredentialStore } from "@earendil-works/pi-ai";
|
|
5
5
|
import type { Provider } from "@earendil-works/pi-ai";
|
|
6
6
|
import nanProviderExtension from "../extensions/nan-provider.ts";
|
|
7
|
-
import { createNanProviderConfig as createNativeProvider, NAN_PROVIDER_BASE_URL, NAN_PROVIDER_ID } from "../lib/nan-provider.ts";
|
|
7
|
+
import { createNanProviderConfig as createNativeProvider, isNanAdaptiveReasoningModel, NAN_PROVIDER_BASE_URL, NAN_PROVIDER_ID } from "../lib/nan-provider.ts";
|
|
8
8
|
|
|
9
9
|
// Keep catalog assertions independent of the native refresh's void return contract.
|
|
10
10
|
function createNanProviderConfig(options: Parameters<typeof createNativeProvider>[0] = {}) {
|
|
@@ -195,37 +195,47 @@ test("initial catalog contains all seven documented chat models with configured
|
|
|
195
195
|
assert.deepEqual(model?.cost, { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 });
|
|
196
196
|
});
|
|
197
197
|
|
|
198
|
-
test("
|
|
198
|
+
test("GLM models expose exactly NaN's four efforts and no off", () => {
|
|
199
199
|
const models = createNativeProvider().getModels();
|
|
200
200
|
for (const id of ["glm5.3", "glm5.3-flash"]) {
|
|
201
201
|
const model = models.find((model) => model.id === id);
|
|
202
202
|
assert.ok(model);
|
|
203
|
-
assert.deepEqual(model.thinkingLevelMap, { off: null, minimal:
|
|
204
|
-
assert.deepEqual(getSupportedThinkingLevels(model), ["
|
|
205
|
-
|
|
203
|
+
assert.deepEqual(model.thinkingLevelMap, { off: null, minimal: null, xhigh: null, max: "max" });
|
|
204
|
+
assert.deepEqual(getSupportedThinkingLevels(model), ["low", "medium", "high", "max"]);
|
|
205
|
+
// Pi levels NaN does not declare clamp onto the nearest declared effort.
|
|
206
|
+
assert.equal(clampThinkingLevel(model, "off"), "low");
|
|
207
|
+
assert.equal(clampThinkingLevel(model, "minimal"), "low");
|
|
208
|
+
assert.equal(clampThinkingLevel(model, "xhigh"), "max");
|
|
206
209
|
}
|
|
207
|
-
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test("models with none and minimal expose exactly NaN's six efforts", () => {
|
|
213
|
+
const models = createNativeProvider().getModels();
|
|
214
|
+
// Qwen 3.6 and Gemma 4 honor these values as depth controls; DeepSeek V4 Flash,
|
|
215
|
+
// Qwen 3.8 Flash, and MiMo accept them while NaN manages depth itself.
|
|
216
|
+
for (const id of ["qwen3.6", "gemma4", "deepseek-v4-flash", "qwen3.8-flash", "mimo-v2.6-flash"]) {
|
|
208
217
|
const model = models.find((model) => model.id === id);
|
|
209
218
|
assert.ok(model);
|
|
210
|
-
assert.deepEqual(model.thinkingLevelMap, { off: "none", xhigh:
|
|
211
|
-
assert.deepEqual(getSupportedThinkingLevels(model), ["off", "minimal", "low", "medium", "high", "
|
|
212
|
-
assert.equal(clampThinkingLevel(model, "
|
|
219
|
+
assert.deepEqual(model.thinkingLevelMap, { off: "none", xhigh: null, max: "max" });
|
|
220
|
+
assert.deepEqual(getSupportedThinkingLevels(model), ["off", "minimal", "low", "medium", "high", "max"]);
|
|
221
|
+
assert.equal(clampThinkingLevel(model, "xhigh"), "max");
|
|
213
222
|
}
|
|
214
223
|
});
|
|
215
224
|
|
|
216
|
-
test("
|
|
225
|
+
test("only the self-managing models carry the adaptive reasoning marker", () => {
|
|
217
226
|
const models = createNativeProvider().getModels();
|
|
218
|
-
|
|
227
|
+
assert.deepEqual(
|
|
228
|
+
models.filter((model) => isNanAdaptiveReasoningModel(model)).map((model) => model.id),
|
|
229
|
+
["deepseek-v4-flash", "qwen3.8-flash", "mimo-v2.6-flash"],
|
|
230
|
+
);
|
|
231
|
+
// Qwen 3.6 and Gemma 4 share the same map but honor the level as a depth budget.
|
|
232
|
+
for (const id of ["qwen3.6", "gemma4", "glm5.3", "glm5.3-flash"]) {
|
|
219
233
|
const model = models.find((model) => model.id === id);
|
|
220
234
|
assert.ok(model);
|
|
221
|
-
assert.
|
|
222
|
-
off: null, minimal: null, low: null, high: null, xhigh: null, max: null,
|
|
223
|
-
});
|
|
224
|
-
assert.deepEqual(getSupportedThinkingLevels(model), ["medium"]);
|
|
225
|
-
for (const level of ["off", "minimal", "low", "medium", "high", "xhigh", "max"] as const) {
|
|
226
|
-
assert.equal(clampThinkingLevel(model, level), "medium");
|
|
227
|
-
}
|
|
235
|
+
assert.equal(isNanAdaptiveReasoningModel(model), false);
|
|
228
236
|
}
|
|
237
|
+
assert.equal(isNanAdaptiveReasoningModel(undefined), false);
|
|
238
|
+
assert.equal(isNanAdaptiveReasoningModel({ provider: "openai", id: "deepseek-v4-flash" }), false);
|
|
229
239
|
});
|
|
230
240
|
|
|
231
241
|
test("thinking maps are isolated across snapshots, providers, and live catalog refreshes", async () => {
|
|
@@ -53,6 +53,7 @@ test("without the env var unpinned digests yield a skip with a distinct reason",
|
|
|
53
53
|
const unpinned = requireNativeBinary({ ...PINNED, digestsPinned: false, env: {} });
|
|
54
54
|
|
|
55
55
|
assert.equal(unpinned.run, false);
|
|
56
|
+
assert.ok("reason" in unresolved);
|
|
56
57
|
assert.match(unpinned.reason as string, /digest/i);
|
|
57
58
|
assert.notEqual(unpinned.reason, unresolved.reason, "the two causes must be distinguishable in output");
|
|
58
59
|
});
|
|
@@ -110,6 +111,7 @@ test("without the env var a set but non-existent dev binary path yields a distin
|
|
|
110
111
|
const missingVar = requireDevBinary({ devBinaryPath: undefined, exists: false, env: {} });
|
|
111
112
|
const badPath = requireDevBinary({ devBinaryPath: "/no/such/gentle-ai", exists: false, env: {} });
|
|
112
113
|
assert.equal(badPath.run, false);
|
|
114
|
+
assert.ok("reason" in missingVar);
|
|
113
115
|
assert.match(badPath.reason as string, /\/no\/such\/gentle-ai/);
|
|
114
116
|
assert.notEqual(badPath.reason, missingVar.reason, "the two causes must be distinguishable in output");
|
|
115
117
|
});
|
|
@@ -373,11 +373,17 @@ test("4.0.0 explicitly repeats 3.7.0 because provider contract 1.2.0 and capabil
|
|
|
373
373
|
assert.deepEqual(contract, NATIVE_CLI_CONTRACTS["3.7.0"] as Record<string, boolean>);
|
|
374
374
|
});
|
|
375
375
|
|
|
376
|
+
test("4.1.1 explicitly repeats 4.0.0 because capabilities/v2.6 is unchanged despite provider contract 1.3.0", () => {
|
|
377
|
+
const contract = NATIVE_CLI_CONTRACTS["4.1.1"] as Record<string, boolean>;
|
|
378
|
+
assert.ok(contract);
|
|
379
|
+
assert.deepEqual(contract, NATIVE_CLI_CONTRACTS["4.0.0"] as Record<string, boolean>);
|
|
380
|
+
});
|
|
381
|
+
|
|
376
382
|
test("no shipped version key was added beyond the pin bump", () => {
|
|
377
383
|
// Rows are promises to consumers, so a new key only ever appears in a
|
|
378
384
|
// dedicated commit alongside a pin bump, never as a side effect. v2.2.4 and
|
|
379
385
|
// v2.3.0 shipped upstream while Pi stayed on 2.2.3 and were never pinned,
|
|
380
386
|
// so they get no row: a row asserts ground truth measured against a binary
|
|
381
387
|
// Pi actually ran, and the table only has to be ascending, not gapless.
|
|
382
|
-
assert.deepEqual(Object.keys(NATIVE_CLI_CONTRACTS), [...DARK_VERSIONS, "2.2.0", "2.2.1", "2.2.2", "2.2.3", "2.4.0", "2.5.0-rc.3", "2.5.0", "2.6.0", "2.7.0", "2.8.0", "2.8.1", "2.8.2", "2.9.0", "2.9.1", "3.0.0", "3.0.1", "3.1.0", "3.2.1", "3.4.0", "3.5.0", "3.6.0", "3.6.1", "3.7.0", "4.0.0"]);
|
|
388
|
+
assert.deepEqual(Object.keys(NATIVE_CLI_CONTRACTS), [...DARK_VERSIONS, "2.2.0", "2.2.1", "2.2.2", "2.2.3", "2.4.0", "2.5.0-rc.3", "2.5.0", "2.6.0", "2.7.0", "2.8.0", "2.8.1", "2.8.2", "2.9.0", "2.9.1", "3.0.0", "3.0.1", "3.1.0", "3.2.1", "3.4.0", "3.5.0", "3.6.0", "3.6.1", "3.7.0", "4.0.0", "4.1.1"]);
|
|
383
389
|
});
|