gentle-pi 2.1.2 → 2.3.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.
Files changed (228) hide show
  1. package/README.md +108 -79
  2. package/assets/agents/gentle-ai-worker.md +7 -3
  3. package/assets/agents/jd-fix-agent.md +1 -1
  4. package/assets/agents/jd-judge-a.md +3 -1
  5. package/assets/agents/jd-judge-b.md +3 -1
  6. package/assets/agents/review-readability.md +4 -3
  7. package/assets/agents/review-reliability.md +4 -3
  8. package/assets/agents/review-resilience.md +4 -3
  9. package/assets/agents/review-risk.md +4 -3
  10. package/assets/agents/sdd-apply.md +11 -4
  11. package/assets/agents/sdd-archive.md +6 -1
  12. package/assets/agents/sdd-design.md +6 -1
  13. package/assets/agents/sdd-explore.md +6 -2
  14. package/assets/agents/sdd-init.md +10 -2
  15. package/assets/agents/sdd-onboard.md +6 -1
  16. package/assets/agents/sdd-proposal.md +8 -1
  17. package/assets/agents/sdd-research.md +54 -0
  18. package/assets/agents/sdd-spec.md +6 -1
  19. package/assets/agents/sdd-status.md +10 -5
  20. package/assets/agents/sdd-sync.md +6 -1
  21. package/assets/agents/sdd-tasks.md +8 -4
  22. package/assets/agents/sdd-verify.md +27 -2
  23. package/assets/chains/4r-review.chain.md +2 -0
  24. package/assets/chains/sdd-full.chain.md +2 -2
  25. package/assets/chains/sdd-plan.chain.md +1 -1
  26. package/assets/chains/sdd-verify.chain.md +2 -2
  27. package/assets/orchestrator-delegation.md +145 -160
  28. package/assets/orchestrator-memory.md +2 -0
  29. package/assets/orchestrator.md +25 -48
  30. package/assets/sdd-orchestrator-workflow.md +163 -25
  31. package/assets/support/sdd-status-contract.md +24 -6
  32. package/contracts/review-integration/v1/fixtures/consent.fixture.json +3 -3
  33. package/contracts/review-integration/v1/fixtures/start-v2.fixture.json +19 -28
  34. package/contracts/review-integration/v1/fixtures/start.fixture.json +1 -10
  35. package/contracts/review-integration/v1/fixtures/status-v2.fixture.json +12 -21
  36. package/contracts/review-integration/v1/schemas/correction-plan-request.schema.json +49 -0
  37. package/contracts/review-integration/v1/schemas/operation.schema.json +76 -0
  38. package/contracts/review-integration/v1/schemas/repair.schema.json +39 -0
  39. package/contracts/review-integration/v1/schemas/status-v2.schema.json +4 -2
  40. package/contracts/review-integration/v1/schemas/status.schema.json +4 -2
  41. package/contracts/review-integration/v1/schemas/transition-execution.schema.json +42 -0
  42. package/contracts/review-integration/v2/fixtures/consent.fixture.json +1 -1
  43. package/contracts/review-integration/v2/fixtures/start.fixture.json +1 -10
  44. package/contracts/review-integration/v2/fixtures/status.fixture.json +1 -10
  45. package/contracts/review-integration/v2/schemas/failure.schema.json +5 -1
  46. package/contracts/review-integration/v2/schemas/last-event-closure.schema.json +66 -0
  47. package/contracts/review-integration/v2/schemas/opencode-provider-role.schema.json +14 -0
  48. package/contracts/review-integration/v2/schemas/operation.schema.json +6 -1
  49. package/contracts/review-integration/v2/schemas/repair.schema.json +4 -2
  50. package/contracts/review-integration/v2/schemas/start.schema.json +5 -2
  51. package/contracts/review-integration/v2/schemas/status.schema.json +4 -2
  52. package/contracts/review-provider-contract-mirror/provider-contract.lock.json +30 -0
  53. package/contracts/review-provider-contract-mirror/v1.1.0/bundle/README.md +12 -0
  54. package/contracts/review-provider-contract-mirror/v1.1.0/bundle/manifest.json +65 -0
  55. package/contracts/review-provider-contract-mirror/v1.1.0/bundle/schemas/lens.schema.json +16 -0
  56. package/contracts/review-provider-contract-mirror/v1.1.0/bundle/schemas/refuter.schema.json +1 -0
  57. package/contracts/review-provider-contract-mirror/v1.1.0/bundle/schemas/targeted-validator.schema.json +1 -0
  58. package/contracts/review-provider-contract-mirror/v1.1.0/bundle/vectors/lens.json +1 -0
  59. package/contracts/review-provider-contract-mirror/v1.1.0/bundle/vectors/refuter.json +1 -0
  60. package/contracts/review-provider-contract-mirror/v1.1.0/bundle/vectors/targeted-validator.json +1 -0
  61. package/contracts/review-provider-contract-mirror/v1.1.0/generated/provider-capabilities.baseline.json +15 -0
  62. package/contracts/review-provider-contract-mirror/v1.1.0/generated/provider-roles.baseline.json +42 -0
  63. package/docs/native-authority-architecture.md +14 -16
  64. package/docs/review-integration.md +27 -353
  65. package/extensions/ask-user-choice.ts +151 -0
  66. package/extensions/gentle-ai.ts +2710 -3211
  67. package/extensions/quiet-tools.ts +515 -32
  68. package/extensions/sdd-init.ts +21 -12
  69. package/extensions/skill-registry.ts +10 -2
  70. package/extensions/startup-banner.ts +10 -4
  71. package/lib/gentle-ai-binary.ts +173 -2
  72. package/lib/gentle-ai-renderer.ts +70 -0
  73. package/lib/model-routing-authority.ts +133 -0
  74. package/lib/native-review-cli.ts +676 -781
  75. package/lib/opaque-pi-reviewer-adapter.ts +284 -0
  76. package/lib/provider-contract-bundle.ts +704 -0
  77. package/lib/review-candidate-view.ts +836 -118
  78. package/lib/review-compact-contract.ts +59 -248
  79. package/lib/review-host-relay.ts +578 -0
  80. package/lib/review-integration-v2.ts +1254 -221
  81. package/lib/review-last-event-controller.ts +35 -0
  82. package/lib/review-relay-contract.ts +16 -0
  83. package/lib/sdd-preflight.ts +230 -67
  84. package/lib/sdd-status.ts +66 -111
  85. package/lib/terminal-theme.ts +1 -1
  86. package/package.json +83 -79
  87. package/runtime/gentle-ai-binary.mjs +174 -3
  88. package/runtime/native-review-cli.mjs +610 -715
  89. package/runtime/review-integration-v2.mjs +1223 -190
  90. package/runtime/review-relay-contract.mjs +17 -0
  91. package/scripts/{build-git-commit-transaction-runner.mjs → build-runtime-modules.mjs} +5 -5
  92. package/scripts/check-provider-contract.mjs +138 -0
  93. package/scripts/gentle-ai-installer.mjs +85 -22
  94. package/scripts/maintainer/provider-relay-matrix.mjs +403 -0
  95. package/scripts/mirror-provider-contract.mjs +143 -0
  96. package/scripts/test-packed-runner.mjs +20 -9
  97. package/scripts/verify-package-files.mjs +119 -43
  98. package/skills/_shared/review-ledger-contract.md +11 -19
  99. package/skills/chained-pr/SKILL.md +3 -0
  100. package/skills/cognitive-doc-design/SKILL.md +1 -1
  101. package/skills/comment-writer/SKILL.md +1 -1
  102. package/skills/gentle-ai/SKILL.md +7 -74
  103. package/skills/issue-creation/SKILL.md +94 -168
  104. package/skills/judgment-day/SKILL.md +9 -5
  105. package/skills/judgment-day/references/prompts-and-formats.md +2 -0
  106. package/skills/rdd-defect-workflow/SKILL.md +54 -0
  107. package/skills/release/SKILL.md +3 -3
  108. package/skills/skill-registry/SKILL.md +1 -1
  109. package/skills/work-unit-commits/SKILL.md +3 -1
  110. package/tests/artifact-language.test.ts +24 -11
  111. package/tests/ask-user-choice.test.ts +264 -0
  112. package/tests/background-subagents.test.ts +771 -0
  113. package/tests/codegraph-tools.test.ts +3 -3
  114. package/tests/crosslane/cross-lane.mjs +16 -0
  115. package/tests/delegated-key-learnings-contract.test.ts +240 -0
  116. package/tests/devbinary/native-review-parity.devtest.ts +167 -250
  117. package/tests/devbinary/pi-host-relay.devtest.ts +867 -0
  118. package/tests/fixtures/devbinary/capabilities-v2.1.derived.json +331 -0
  119. package/tests/fixtures/devbinary/capabilities-v2.2.captured.json +340 -0
  120. package/tests/fixtures/devbinary/consent-v3.captured.json +37 -0
  121. package/tests/fixtures/devbinary/failure-v2-capture-evidence.captured.json +16 -0
  122. package/tests/fixtures/devbinary/last-event-capture-correction-plan.captured.json +10 -0
  123. package/tests/fixtures/devbinary/last-event-capture-refuter-approved.captured.json +20 -0
  124. package/tests/fixtures/devbinary/last-event-capture-refuter-correction-required.captured.json +26 -0
  125. package/tests/fixtures/devbinary/last-event-capture-result-approved.captured.json +8 -0
  126. package/tests/fixtures/devbinary/last-event-capture-result-correction-required.captured.json +26 -0
  127. package/tests/fixtures/devbinary/last-event-capture-validation-approved.captured.json +8 -0
  128. package/tests/fixtures/devbinary/last-event-closure.provenance.md +13 -0
  129. package/tests/fixtures/devbinary/result-artifact-v2-path.captured.json +12 -0
  130. package/tests/fixtures/devbinary/result-artifact-v2.captured.json +12 -0
  131. package/tests/fixtures/devbinary/review-acknowledged-v1.captured.json +9 -0
  132. package/tests/fixtures/devbinary/review-acknowledged.provenance.md +31 -0
  133. package/tests/fixtures/devbinary/start-v3-consent-declined.captured.json +19 -0
  134. package/tests/fixtures/devbinary/start-v3-consent-granted.captured.json +109 -0
  135. package/tests/fixtures/devbinary/start-v3-zero-lens-closed.captured.json +21 -0
  136. package/tests/fixtures/devbinary/status-v5-capture-result-submission.captured.json +184 -0
  137. package/tests/fixtures/devbinary/status-v5-repository-context.captured.json +138 -0
  138. package/tests/fixtures/devbinary/status-v5.captured.json +88 -0
  139. package/tests/fixtures/native-review-cli/v2.5.0-rc.1/PROVENANCE.txt +15 -0
  140. package/tests/fixtures/provider-contract-bundle/v1.1.0/README.md +12 -0
  141. package/tests/fixtures/provider-contract-bundle/v1.1.0/manifest.json +65 -0
  142. package/tests/fixtures/provider-contract-bundle/v1.1.0/schemas/lens.schema.json +16 -0
  143. package/tests/fixtures/provider-contract-bundle/v1.1.0/schemas/refuter.schema.json +1 -0
  144. package/tests/fixtures/provider-contract-bundle/v1.1.0/schemas/targeted-validator.schema.json +1 -0
  145. package/tests/fixtures/provider-contract-bundle/v1.1.0/vectors/lens.json +1 -0
  146. package/tests/fixtures/provider-contract-bundle/v1.1.0/vectors/refuter.json +1 -0
  147. package/tests/fixtures/provider-contract-bundle/v1.1.0/vectors/targeted-validator.json +1 -0
  148. package/tests/gentle-ai-binary.test.ts +83 -13
  149. package/tests/gentle-ai-dev-binary-surfacing.test.ts +195 -0
  150. package/tests/gentle-ai-dev-binary.test.ts +159 -0
  151. package/tests/gentle-ai-installer.test.ts +138 -48
  152. package/tests/gentle-ai.test.ts +820 -131
  153. package/tests/gentle-theme.test.ts +133 -0
  154. package/tests/maintainer/provider-relay.maintest.ts +601 -0
  155. package/tests/model-routing-authority.test.ts +257 -0
  156. package/tests/native-review-capability-contract.test.ts +75 -2
  157. package/tests/native-review-cli.test.ts +588 -908
  158. package/tests/native-review-consent.test.ts +283 -38
  159. package/tests/native-review-parity-runtime.test.ts +108 -350
  160. package/tests/native-review-parity.test.ts +684 -732
  161. package/tests/native-sdd-attempt-authority.test.ts +235 -0
  162. package/tests/opaque-pi-reviewer-adapter.test.ts +266 -0
  163. package/tests/orchestrator-budget.test.ts +158 -89
  164. package/tests/orchestrator-rdd-ownership.test.ts +103 -0
  165. package/tests/package-manifest.test.ts +128 -100
  166. package/tests/provider-contract-bundle.test.ts +385 -0
  167. package/tests/provider-contract-mirror.test.ts +206 -0
  168. package/tests/provider-defect-handoff.test.ts +252 -0
  169. package/tests/quiet-tool-rendering.test.ts +1055 -28
  170. package/tests/review-actor-tool-deny.test.ts +12 -13
  171. package/tests/review-authority-recovery-docs.test.ts +1 -2
  172. package/tests/review-candidate-view.test.ts +898 -12
  173. package/tests/review-compact-contract.test.ts +29 -122
  174. package/tests/review-controller-lock-status.test.ts +2 -2
  175. package/tests/review-controller-native-recovery.test.ts +366 -857
  176. package/tests/review-controller-native-routing.test.ts +1253 -4199
  177. package/tests/review-controller-retired-ops.test.ts +1 -1
  178. package/tests/review-controller-workspace-root.test.ts +236 -70
  179. package/tests/review-controller.test.ts +26 -816
  180. package/tests/review-corrected-finalize-binding.test.ts +134 -0
  181. package/tests/review-dispatch-hydration-gap.test.ts +145 -0
  182. package/tests/review-gate.test.ts +0 -45
  183. package/tests/review-host-relay-restart-parity.test.ts +360 -0
  184. package/tests/review-host-relay-routing.test.ts +352 -0
  185. package/tests/review-host-relay.test.ts +754 -0
  186. package/tests/review-integration-v2-forward.test.ts +654 -0
  187. package/tests/review-integration-v2.test.ts +317 -129
  188. package/tests/review-last-event-closure.test.ts +408 -0
  189. package/tests/review-ledger-contract.test.ts +106 -60
  190. package/tests/review-recovered-lineage-routing.test.ts +199 -0
  191. package/tests/review-relay-transport-agent.test.ts +322 -0
  192. package/tests/review-snapshot.test.ts +3 -2
  193. package/tests/runtime-harness.mjs +573 -180
  194. package/tests/sdd-agent-tools.test.ts +53 -32
  195. package/tests/sdd-preflight.test.ts +81 -15
  196. package/tests/sdd-status.test.ts +109 -110
  197. package/tests/skill-collision-prefixes.test.ts +6 -8
  198. package/tests/skill-registry.test.ts +50 -1
  199. package/tests/verify-package-files.test.ts +62 -0
  200. package/tests/writer-edit-surface-scope.test.ts +230 -0
  201. package/themes/Gentleman-Cute.json +94 -0
  202. package/themes/Gentleman-Sexy.json +92 -0
  203. package/assets/agents/review-refuter.md +0 -40
  204. package/assets/agents/review-validator.md +0 -23
  205. package/lib/git-commit-transaction.ts +0 -801
  206. package/lib/native-review-remediation.ts +0 -49
  207. package/lib/review-compact.ts +0 -947
  208. package/lib/review-refuter-adapter.ts +0 -129
  209. package/lib/review-runtime-contract.ts +0 -68
  210. package/prompts/gcl.md +0 -54
  211. package/prompts/gis.md +0 -25
  212. package/prompts/gpr.md +0 -41
  213. package/prompts/gwr.md +0 -31
  214. package/runtime/git-commit-transaction.mjs +0 -802
  215. package/scripts/run-git-commit-transaction.mjs +0 -35
  216. package/tests/fixtures/native-review-cli/v2.1.2/bind-sdd.json +0 -25
  217. package/tests/fixtures/native-review-cli/v2.1.2/finalize.json +0 -8
  218. package/tests/fixtures/native-review-cli/v2.1.2/sdd-status-engram.json +0 -139
  219. package/tests/fixtures/native-review-cli/v2.1.2/sdd-status.json +0 -200
  220. package/tests/fixtures/native-review-cli/v2.1.2/start.json +0 -12
  221. package/tests/fixtures/native-review-cli/v2.1.2/validate-allow.json +0 -24
  222. package/tests/fixtures/native-review-cli/v2.1.2/validate-deny-empty-context.json +0 -20
  223. package/tests/fixtures/native-review-cli/v2.1.2/validate-deny.json +0 -28
  224. package/tests/fixtures/native-review-cli/v2.1.3/sdd-status-engram.json +0 -139
  225. package/tests/fixtures/native-review-cli/v2.1.3/sdd-status.json +0 -200
  226. package/tests/git-commit-transaction.test.ts +0 -302
  227. package/tests/review-compact.test.ts +0 -243
  228. package/tests/review-refuter-adapter.test.ts +0 -89
@@ -1,6 +1,7 @@
1
- // Generated by scripts/build-git-commit-transaction-runner.mjs. Do not edit.
1
+ // Generated by scripts/build-runtime-modules.mjs. Do not edit.
2
2
  import { createHash } from "node:crypto";
3
- import { existsSync, lstatSync, readFileSync } from "node:fs";
3
+ import { existsSync, lstatSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
4
+ import { homedir } from "node:os";
4
5
  import { dirname, isAbsolute, join, relative, resolve } from "node:path";
5
6
  import {
6
7
  GENTLE_AI_INSTALL_METHOD,
@@ -8,6 +9,7 @@ import {
8
9
  GENTLE_AI_WINDOWS_SOURCE_MODULE_CHECKSUM,
9
10
  GENTLE_AI_WINDOWS_SOURCE_PACKAGE_PATH,
10
11
  GENTLE_AI_WINDOWS_SOURCE_TAG,
12
+ INSTALLER_VERSION,
11
13
  isGentleAiWindowsGoVersionSupported,
12
14
  isWindowsGoSumdbSourceTarget,
13
15
  resolveGentleAiReleaseAsset,
@@ -15,7 +17,10 @@ import {
15
17
  import { fileURLToPath } from "node:url";
16
18
 
17
19
  export const GENTLE_AI_BINARY_MISSING_CODE = "package-local-binary-missing";
18
- export const GENTLE_AI_VERSION = "2.2.2";
20
+ // Derived from the one authoritative pinned version in
21
+ // scripts/gentle-ai-installer.mjs rather than repeating the literal here, so
22
+ // the two can never independently drift apart the way they once did.
23
+ export const GENTLE_AI_VERSION = INSTALLER_VERSION;
19
24
 
20
25
  export class PackageLocalGentleAiBinaryMissingError extends Error {
21
26
  code = GENTLE_AI_BINARY_MISSING_CODE;
@@ -43,6 +48,165 @@ function sha256(value ) {
43
48
  return createHash("sha256").update(value).digest("hex");
44
49
  }
45
50
 
51
+ // ---------------------------------------------------------------------------
52
+ // Dev-binary override — the maintainer field-test lane.
53
+ //
54
+ // Two explicit activation paths, in precedence order:
55
+ // 1. GENTLE_PI_GENTLE_AI_DEV_BINARY (session override, absolute path), then
56
+ // 2. the persistent registration file at
57
+ // <GENTLE_PI_CONFIG_HOME|~/.pi/gentle-ai>/dev-binary.json with the strict
58
+ // shape {"schema":"gentle-pi.dev-binary/v1","path":"<absolute path>"}.
59
+ //
60
+ // The registration deliberately pins no digest: it is the unpinned field-test
61
+ // mode, and the binary at that path changes on every rebuild. Every resolution
62
+ // re-validates the file and recomputes the sha256, so a rebuilt binary is
63
+ // followed automatically with a fresh digest and no re-registration.
64
+ //
65
+ // Guardrails per resolution: absolute path, regular non-symlink file, POSIX
66
+ // executable. Any failure — including a malformed registration document or a
67
+ // registered-but-missing binary — is a typed error naming its origin, never a
68
+ // silent fallback to the pinned binary: silently running the pin while the
69
+ // maintainer believes he is field-testing main is the worst possible outcome.
70
+ // With neither activation path present, the pinned supply-chain resolution
71
+ // below stays byte-identical.
72
+ // ---------------------------------------------------------------------------
73
+
74
+ export const GENTLE_AI_DEV_BINARY_ENV = "GENTLE_PI_GENTLE_AI_DEV_BINARY";
75
+ export const GENTLE_AI_DEV_BINARY_REGISTRATION_SCHEMA = "gentle-pi.dev-binary/v1";
76
+ export const GENTLE_AI_DEV_BINARY_OVERRIDE_INVALID_CODE = "dev-binary-override-invalid";
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+
87
+
88
+
89
+
90
+
91
+ export class GentleAiDevBinaryOverrideError extends Error {
92
+ code = GENTLE_AI_DEV_BINARY_OVERRIDE_INVALID_CODE;
93
+ source ;
94
+ origin ;
95
+ constructor(source , origin , reason ) {
96
+ super(`${GENTLE_AI_DEV_BINARY_OVERRIDE_INVALID_CODE}: ${origin} ${reason}. Fix or remove the override; the pinned binary is never used silently while an override is declared.`);
97
+ this.name = "GentleAiDevBinaryOverrideError";
98
+ this.source = source;
99
+ this.origin = origin;
100
+ }
101
+ }
102
+
103
+ let devBinaryEnvironmentTestingOverlay ;
104
+
105
+ /** Testing-only environment overlay; production code never calls this. */
106
+ export function setGentleAiDevBinaryEnvironmentForTesting(environment ) {
107
+ devBinaryEnvironmentTestingOverlay = environment;
108
+ }
109
+
110
+ function ambientDevBinaryEnvironment() {
111
+ return devBinaryEnvironmentTestingOverlay ?? { env: process.env, home: homedir() };
112
+ }
113
+
114
+ export function gentleAiDevBinaryRegistrationPath(environment = ambientDevBinaryEnvironment()) {
115
+ const configHome = environment.env.GENTLE_PI_CONFIG_HOME ?? join(environment.home, ".pi", "gentle-ai");
116
+ return join(configHome, "dev-binary.json");
117
+ }
118
+
119
+ function validateDevBinary(source , origin , path , platform ) {
120
+ if (typeof path !== "string" || path.length === 0) throw new GentleAiDevBinaryOverrideError(source, origin, "declares an empty dev binary path");
121
+ if (!isAbsolute(path)) throw new GentleAiDevBinaryOverrideError(source, origin, `must name an absolute path, received "${path}"`);
122
+ let details ;
123
+ try {
124
+ details = lstatSync(path);
125
+ } catch {
126
+ throw new GentleAiDevBinaryOverrideError(source, origin, `names "${path}", which does not exist`);
127
+ }
128
+ if (!details.isFile() || details.isSymbolicLink()) throw new GentleAiDevBinaryOverrideError(source, origin, `names "${path}", which is not a regular non-symlink file`);
129
+ if (platform !== "win32" && (details.mode & 0o111) === 0) throw new GentleAiDevBinaryOverrideError(source, origin, `names "${path}", which is not a POSIX executable`);
130
+ let digest ;
131
+ try {
132
+ digest = sha256(readFileSync(path));
133
+ } catch {
134
+ throw new GentleAiDevBinaryOverrideError(source, origin, `names "${path}", which could not be read`);
135
+ }
136
+ return { source, origin, path, sha256: digest };
137
+ }
138
+
139
+ function readDevBinaryRegistration(registrationPath ) {
140
+ let contents ;
141
+ try {
142
+ contents = readFileSync(registrationPath, "utf8");
143
+ } catch {
144
+ throw new GentleAiDevBinaryOverrideError("registration", registrationPath, "could not be read");
145
+ }
146
+ let parsed ;
147
+ try {
148
+ parsed = JSON.parse(contents);
149
+ } catch {
150
+ throw new GentleAiDevBinaryOverrideError("registration", registrationPath, "is not valid JSON");
151
+ }
152
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new GentleAiDevBinaryOverrideError("registration", registrationPath, "must be a JSON object");
153
+ const record = parsed ;
154
+ const keys = Object.keys(record).sort();
155
+ if (keys.length !== 2 || keys[0] !== "path" || keys[1] !== "schema") throw new GentleAiDevBinaryOverrideError("registration", registrationPath, `must carry exactly the keys "schema" and "path"`);
156
+ if (record.schema !== GENTLE_AI_DEV_BINARY_REGISTRATION_SCHEMA) throw new GentleAiDevBinaryOverrideError("registration", registrationPath, `must declare schema ${GENTLE_AI_DEV_BINARY_REGISTRATION_SCHEMA}`);
157
+ if (typeof record.path !== "string" || record.path.length === 0) throw new GentleAiDevBinaryOverrideError("registration", registrationPath, "must declare a non-empty string path");
158
+ return record.path;
159
+ }
160
+
161
+ /**
162
+ * Resolves the active dev-binary override, if any. Returns undefined only when
163
+ * neither activation path is present; a present-but-invalid override always
164
+ * throws a typed GentleAiDevBinaryOverrideError naming its origin.
165
+ */
166
+ export function resolveGentleAiDevBinaryOverride(
167
+ environment = ambientDevBinaryEnvironment(),
168
+ platform = process.platform,
169
+ ) {
170
+ const envValue = environment.env[GENTLE_AI_DEV_BINARY_ENV];
171
+ if (envValue !== undefined && envValue.length > 0) return validateDevBinary("env", GENTLE_AI_DEV_BINARY_ENV, envValue, platform);
172
+ const registrationPath = gentleAiDevBinaryRegistrationPath(environment);
173
+ if (!existsSync(registrationPath)) return undefined;
174
+ return validateDevBinary("registration", registrationPath, readDevBinaryRegistration(registrationPath), platform);
175
+ }
176
+
177
+ /**
178
+ * Cheap presence probe: is a dev-binary override declared at all? Used by the
179
+ * native CLI to select the unpinned version gate without hashing the binary.
180
+ * Declared-but-invalid still counts as configured — the resolution path will
181
+ * fail loudly with the typed error instead of quietly using the pin.
182
+ */
183
+ export function gentleAiDevBinaryOverrideConfigured(environment = ambientDevBinaryEnvironment()) {
184
+ const envValue = environment.env[GENTLE_AI_DEV_BINARY_ENV];
185
+ if (envValue !== undefined && envValue.length > 0) return true;
186
+ return existsSync(gentleAiDevBinaryRegistrationPath(environment));
187
+ }
188
+
189
+ /** Validates and persistently registers a dev binary; returns the fresh override. */
190
+ export function registerGentleAiDevBinary(
191
+ path ,
192
+ environment = ambientDevBinaryEnvironment(),
193
+ platform = process.platform,
194
+ ) {
195
+ const registrationPath = gentleAiDevBinaryRegistrationPath(environment);
196
+ const validated = validateDevBinary("registration", registrationPath, path, platform);
197
+ mkdirSync(dirname(registrationPath), { recursive: true });
198
+ writeFileSync(registrationPath, `${JSON.stringify({ schema: GENTLE_AI_DEV_BINARY_REGISTRATION_SCHEMA, path })}\n`);
199
+ return { registrationPath, override: validated };
200
+ }
201
+
202
+ /** Deletes the persistent registration; returns whether one existed. */
203
+ export function unregisterGentleAiDevBinary(environment = ambientDevBinaryEnvironment()) {
204
+ const registrationPath = gentleAiDevBinaryRegistrationPath(environment);
205
+ if (!existsSync(registrationPath)) return false;
206
+ rmSync(registrationPath);
207
+ return true;
208
+ }
209
+
46
210
  function isConfined(path , directory ) {
47
211
  const relativePath = relative(directory, path);
48
212
  return relativePath !== "" && !relativePath.startsWith("..") && !isAbsolute(relativePath);
@@ -107,7 +271,14 @@ export function resolveGentleAiBinary(
107
271
  packageRoot = dirname(dirname(fileURLToPath(import.meta.url))),
108
272
  platform = process.platform,
109
273
  readBinary = readFileSync,
274
+ environment = ambientDevBinaryEnvironment(),
110
275
  ) {
276
+ // The explicit dev-binary override wins over the pinned supply-chain path.
277
+ // Its typed errors propagate: a declared override never falls back to the
278
+ // pin. Without a declared override this call returns undefined and the
279
+ // pinned resolution below is byte-identical to the pre-override behavior.
280
+ const override = resolveGentleAiDevBinaryOverride(environment, platform);
281
+ if (override !== undefined) return override.path;
111
282
  const binaryPath = gentleAiBinaryPath(packageRoot, platform);
112
283
  const versionDirectory = dirname(binaryPath);
113
284
  const manifestPath = join(versionDirectory, "integrity.json");