gentle-pi 4.3.0 → 4.5.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 (218) hide show
  1. package/README.md +10 -7
  2. package/assets/agents/gentle-ai-worker.md +1 -1
  3. package/assets/design-canvas/canvas.css +648 -0
  4. package/assets/design-canvas/canvas.js +1497 -0
  5. package/assets/design-canvas/index.html +88 -0
  6. package/assets/design-canvas/inspector.js +290 -0
  7. package/assets/orchestrator-delegation-reference.md +2 -2
  8. package/assets/orchestrator-verification.md +1 -1
  9. package/assets/orchestrator.md +2 -2
  10. package/bin/engram-guard +1 -1
  11. package/bin/gentle-shell.mjs +138 -155
  12. package/docs/bundled-install.md +47 -42
  13. package/docs/delegated-verification.md +6 -0
  14. package/docs/gentle-agents-activity.md +5 -1
  15. package/docs/gentle-music.md +82 -0
  16. package/docs/gentle-shell.md +80 -10
  17. package/docs/install-wizard.md +30 -6
  18. package/docs/permission-modes.md +7 -2
  19. package/docs/readme-reference.md +72 -55
  20. package/docs/session-profile-format.md +26 -0
  21. package/docs/voice.md +259 -0
  22. package/docs/yolo-mode.md +17 -2
  23. package/extensions/ask-user-choice.ts +4 -1
  24. package/extensions/gentle-agents.ts +187 -40
  25. package/extensions/gentle-ai.ts +1765 -1185
  26. package/extensions/gentle-design.ts +158 -0
  27. package/extensions/gentle-music.ts +354 -0
  28. package/extensions/gentle-shell.ts +418 -79
  29. package/extensions/gentle-voice.ts +11 -0
  30. package/extensions/session-scratch.ts +41 -22
  31. package/extensions/skill-registry.ts +63 -8
  32. package/lib/agent-profiles.ts +26 -0
  33. package/lib/agents-config.ts +35 -0
  34. package/lib/agents-protocol.ts +57 -1
  35. package/lib/agents-runner.ts +52 -3
  36. package/lib/agents-session-transport.ts +101 -6
  37. package/lib/agents-widget.ts +14 -1
  38. package/lib/auto-permission-memory.ts +48 -18
  39. package/lib/auto-permission-runtime.ts +21 -6
  40. package/lib/clipboard-image.ts +147 -0
  41. package/lib/command-palette-catalog.ts +3 -2
  42. package/lib/commit-slice-signal.ts +1 -1
  43. package/lib/context-backstop.ts +36 -0
  44. package/lib/design-canvas-server.ts +947 -0
  45. package/lib/design-comments.ts +437 -0
  46. package/lib/design-handoffs.ts +313 -0
  47. package/lib/design-open-browser.ts +58 -0
  48. package/lib/design-sources.ts +120 -0
  49. package/lib/external-editor.ts +31 -0
  50. package/lib/gentle-design.ts +513 -0
  51. package/lib/gentle-shell-bundled.ts +141 -57
  52. package/lib/gentle-shell-launcher.ts +11 -4
  53. package/lib/gentle-shell-startup-update.ts +61 -9
  54. package/lib/gentle-shell-uninstall.ts +33 -1
  55. package/lib/image-attachments.ts +150 -0
  56. package/lib/inprocess-reviewer.ts +33 -2
  57. package/lib/jobs-sidebar-state.ts +22 -3
  58. package/lib/jobs-widget.ts +112 -0
  59. package/lib/nan-provider.ts +3 -0
  60. package/lib/native-review-cli.ts +11 -4
  61. package/lib/notification-audio-native.ts +149 -13
  62. package/lib/notification-audio.ts +4 -0
  63. package/lib/notification-pulse-worker.ts +82 -18
  64. package/lib/notification-service.ts +5 -1
  65. package/lib/orchestrator-presence.ts +78 -8
  66. package/lib/orchestrator-scope.ts +10 -0
  67. package/lib/orchestrator-state.ts +11 -3
  68. package/lib/permission-mode-policy.ts +2 -2
  69. package/lib/review-host-relay.ts +11 -2
  70. package/lib/review-reminder-receipt.ts +24 -1
  71. package/lib/session-profile-append-controller.ts +713 -0
  72. package/lib/session-profile-disk-reader.ts +182 -0
  73. package/lib/session-profile-freeze.ts +2 -2
  74. package/lib/session-profile-persistence.ts +15 -7
  75. package/lib/session-scratch.ts +52 -3
  76. package/lib/session-worktree-registry.ts +7 -0
  77. package/lib/shell-bar.ts +6 -7
  78. package/lib/shell-changes.ts +81 -5
  79. package/lib/shell-prompt.ts +4 -4
  80. package/lib/shell-sidebar-layout.ts +27 -7
  81. package/lib/shell-todo.ts +1 -1
  82. package/lib/verifier-admission.ts +5 -4
  83. package/lib/vim-editor-adapter.ts +14 -10
  84. package/lib/voice-artifacts.ts +188 -0
  85. package/lib/voice-command.ts +576 -0
  86. package/lib/voice-install.ts +455 -0
  87. package/lib/voice-platform.ts +99 -0
  88. package/lib/voice-push-to-talk.ts +262 -0
  89. package/lib/voice-session.ts +460 -0
  90. package/lib/voice-settings.ts +50 -0
  91. package/lib/voice-ui.ts +158 -0
  92. package/lib/yolo-session-policy.ts +58 -22
  93. package/package.json +1 -1
  94. package/runtime/gentle-shell-bundled.mjs +140 -56
  95. package/runtime/gentle-shell-launcher.mjs +11 -4
  96. package/runtime/gentle-shell-startup-update.mjs +61 -9
  97. package/runtime/gentle-shell-uninstall.mjs +33 -1
  98. package/runtime/native-review-cli.mjs +11 -4
  99. package/scripts/bundled-engram-migration.mjs +0 -6
  100. package/scripts/bundled-install.mjs +209 -13
  101. package/scripts/gentle-ai-installer.mjs +10 -10
  102. package/scripts/installer-downloads.mjs +31 -7
  103. package/scripts/installer-preflight.mjs +6 -4
  104. package/scripts/installer-windows.mjs +37 -7
  105. package/scripts/main-channel.mjs +91 -17
  106. package/scripts/pi-update.mjs +37 -7
  107. package/scripts/verify-package-files.mjs +26 -2
  108. package/scripts/voice-worker.cjs +476 -0
  109. package/skills/design/SKILL.md +110 -0
  110. package/tests/agent-profiles.test.ts +24 -0
  111. package/tests/agents-config.test.ts +61 -0
  112. package/tests/agents-fake-child.ts +8 -1
  113. package/tests/agents-protocol.test.ts +32 -0
  114. package/tests/agents-runner.test.ts +226 -5
  115. package/tests/agents-session-transport.test.ts +53 -3
  116. package/tests/ask-user-choice.test.ts +30 -0
  117. package/tests/auto-mode.test.ts +17 -0
  118. package/tests/auto-permission-memory-diagnostics.test.ts +56 -0
  119. package/tests/auto-permission-runtime.test.ts +85 -1
  120. package/tests/autonomous-guard.test.ts +20 -0
  121. package/tests/bundled-install.test.ts +276 -5
  122. package/tests/clipboard-image.test.ts +114 -0
  123. package/tests/command-palette.test.ts +4 -4
  124. package/tests/context-backstop.test.ts +70 -4
  125. package/tests/delegate-for-reason-contract.test.ts +2 -1
  126. package/tests/design-canvas-assets.test.ts +710 -0
  127. package/tests/design-canvas-server.test.ts +1096 -0
  128. package/tests/design-comments.test.ts +201 -0
  129. package/tests/design-handoffs.test.ts +282 -0
  130. package/tests/design-open-browser.test.ts +70 -0
  131. package/tests/design-sources.test.ts +132 -0
  132. package/tests/engram-safety.test.ts +132 -57
  133. package/tests/external-editor.test.ts +84 -0
  134. package/tests/fixtures/primary-session-prompt/gentleman.golden.md +3 -3
  135. package/tests/gentle-agents.test.ts +369 -24
  136. package/tests/gentle-ai-binary.test.ts +1 -1
  137. package/tests/gentle-ai-installer.test.ts +51 -51
  138. package/tests/gentle-ai.test.ts +928 -448
  139. package/tests/gentle-design.test.ts +1313 -0
  140. package/tests/gentle-music.test.ts +452 -0
  141. package/tests/gentle-shell-bundled.test.ts +321 -248
  142. package/tests/gentle-shell-launcher.test.ts +29 -2
  143. package/tests/gentle-shell-startup-update.test.ts +77 -1
  144. package/tests/gentle-shell-uninstall.test.ts +59 -0
  145. package/tests/gentle-shell.test.ts +227 -27
  146. package/tests/gentle-todo.test.ts +11 -0
  147. package/tests/image-attachments.test.ts +119 -0
  148. package/tests/image-paste-chips.test.ts +236 -0
  149. package/tests/inprocess-reviewer.test.ts +55 -2
  150. package/tests/installer-bundles.test.ts +12 -6
  151. package/tests/installer-downloads.test.ts +32 -0
  152. package/tests/installer-posix-bootstrap.test.ts +8 -8
  153. package/tests/installer-preflight.test.ts +16 -0
  154. package/tests/installer-server.test.ts +2 -2
  155. package/tests/installer-windows-bootstrap.test.ts +61 -3
  156. package/tests/installer-windows-diagnostics.test.ts +111 -0
  157. package/tests/jobs-widget.test.ts +138 -0
  158. package/tests/main-channel-upgrade.test.ts +232 -2
  159. package/tests/main-channel.test.ts +140 -21
  160. package/tests/model-profiles-preservation.test.ts +2 -2
  161. package/tests/nan-provider.test.ts +36 -0
  162. package/tests/native-review-capability-contract.test.ts +11 -4
  163. package/tests/native-review-parity.test.ts +1 -1
  164. package/tests/notification-audio-native.test.ts +128 -9
  165. package/tests/notification-customize.test.ts +17 -5
  166. package/tests/odd-routing-canonical-ratchet.test.ts +2 -1
  167. package/tests/orchestrator-budget.test.ts +5 -2
  168. package/tests/orchestrator-presence.test.ts +100 -2
  169. package/tests/orchestrator-rdd-ownership.test.ts +2 -2
  170. package/tests/orchestrator-scope.test.ts +14 -0
  171. package/tests/orchestrator-state.test.ts +29 -0
  172. package/tests/package-files.test.ts +34 -0
  173. package/tests/package-manifest.test.ts +6 -6
  174. package/tests/permission-mode-policy.test.ts +10 -1
  175. package/tests/pi-update.test.ts +104 -4
  176. package/tests/rdd-aware-verification-contract.test.ts +1 -1
  177. package/tests/review-host-relay-routing.test.ts +88 -1
  178. package/tests/review-host-relay.test.ts +9 -2
  179. package/tests/review-json-arguments.test.ts +20 -3
  180. package/tests/review-loadout-rdd-off-sdk.test.ts +23 -2
  181. package/tests/review-relay-transport-agent.test.ts +26 -0
  182. package/tests/review-reminder-receipt.test.ts +31 -1
  183. package/tests/runtime-harness.mjs +188 -211
  184. package/tests/session-changes.test.ts +2 -1
  185. package/tests/session-profile-append-controller-fork.test.ts +458 -0
  186. package/tests/session-profile-append-controller-pi.test.ts +290 -0
  187. package/tests/session-profile-append-controller.test.ts +731 -0
  188. package/tests/session-profile-disk-reader-pi.test.ts +183 -0
  189. package/tests/session-profile-disk-reader.test.ts +311 -0
  190. package/tests/session-profile-freeze.test.ts +6 -4
  191. package/tests/session-scratch.test.ts +68 -2
  192. package/tests/session-worktree-registry.test.ts +13 -1
  193. package/tests/shell-bar.test.ts +18 -6
  194. package/tests/shell-changes.test.ts +148 -1
  195. package/tests/shell-prompt.test.ts +3 -3
  196. package/tests/shell-render-cache.test.ts +134 -1
  197. package/tests/shell-sidebar-layout.test.ts +67 -0
  198. package/tests/short-check-routing-contract.test.ts +1 -1
  199. package/tests/skill-collision-prefixes.test.ts +3 -1
  200. package/tests/skill-registry-watcher.test.ts +17 -5
  201. package/tests/skill-registry.test.ts +47 -3
  202. package/tests/task-size-routing-contract.test.ts +1 -1
  203. package/tests/test-strength-contract.test.ts +1 -1
  204. package/tests/verifier-admission.test.ts +18 -1
  205. package/tests/vim-editor-adapter.test.ts +2 -2
  206. package/tests/voice-command.test.ts +773 -0
  207. package/tests/voice-install.test.ts +323 -0
  208. package/tests/voice-platform.test.ts +176 -0
  209. package/tests/voice-push-to-talk.test.ts +444 -0
  210. package/tests/voice-session.test.ts +434 -0
  211. package/tests/voice-settings.test.ts +49 -0
  212. package/tests/voice-ui.test.ts +97 -0
  213. package/tests/voice-worker.test.ts +445 -0
  214. package/tests/weak-model-economy-prompt.test.ts +32 -0
  215. package/tests/windows-hidden-processes.test.ts +25 -2
  216. package/tests/yolo-customize.test.ts +5 -1
  217. package/tests/yolo-mode.test.ts +1 -1
  218. package/tests/yolo-session-policy-capture.test.ts +104 -0
@@ -120,28 +120,61 @@ test("uniqueExistingDirs normalizes duplicates and ignores missing roots", async
120
120
  );
121
121
  });
122
122
 
123
- test("findSkillFiles scans one skill directory level only", async () => {
123
+ test("findSkillFiles scans flat skills and category-nested skills without descending further", async () => {
124
124
  const root = join(tmpdir(), `gentle-pi-shallow-${Date.now()}`);
125
125
  const skillPath = join(root, "docs", "SKILL.md");
126
126
  const nestedSkillPath = join(root, "fixtures", "nested", "SKILL.md");
127
+ const deeperSkillPath = join(root, "fixtures", "nested", "deeper", "SKILL.md");
127
128
  mkdirSync(dirname(skillPath), { recursive: true });
128
129
  mkdirSync(dirname(nestedSkillPath), { recursive: true });
130
+ mkdirSync(dirname(deeperSkillPath), { recursive: true });
129
131
  writeFileSync(skillPath, "---\nname: docs\ndescription: Docs.\n---\n");
130
132
  writeFileSync(nestedSkillPath, "---\nname: nested\ndescription: Nested fixture.\n---\n");
133
+ writeFileSync(deeperSkillPath, "---\nname: deeper\ndescription: Deeper fixture.\n---\n");
131
134
 
132
- assert.deepEqual(await __testing.findSkillFiles(root), [skillPath]);
135
+ assert.deepEqual(await __testing.findSkillFiles(root), [skillPath, nestedSkillPath]);
133
136
  });
134
137
 
135
- test("findSkillFiles follows symlinked skill directories", async (t) => {
138
+ test("findSkillFiles keeps direct skills ahead of alphabetically earlier nested collisions", async () => {
139
+ const cwd = join(tmpdir(), `gentle-pi-precedence-${Date.now()}`);
140
+ const directSkillPath = join(cwd, "skills", "zeta", "SKILL.md");
141
+ const nestedSkillPath = join(cwd, "skills", "alpha", "duplicate", "SKILL.md");
142
+ mkdirSync(dirname(directSkillPath), { recursive: true });
143
+ mkdirSync(dirname(nestedSkillPath), { recursive: true });
144
+ writeFileSync(directSkillPath, "---\nname: duplicate\ndescription: Direct skill.\n---\n");
145
+ writeFileSync(nestedSkillPath, "---\nname: duplicate\ndescription: Nested skill.\n---\n");
146
+
147
+ await __testing.regenerateRegistry(cwd, true);
148
+ const registry = readFileSync(join(cwd, ".atl", "skill-registry.md"), "utf8");
149
+ assert.match(registry, new RegExp(directSkillPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")));
150
+ assert.doesNotMatch(registry, new RegExp(nestedSkillPath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")));
151
+ });
152
+
153
+ test("findSkillFiles does not descend into excluded categories", async () => {
154
+ const root = join(tmpdir(), `gentle-pi-excluded-category-${Date.now()}`);
155
+ for (const category of ["_shared", "skill-registry", "sdd-fixtures"]) {
156
+ const nestedSkillPath = join(root, category, "hidden", "SKILL.md");
157
+ mkdirSync(dirname(nestedSkillPath), { recursive: true });
158
+ writeFileSync(nestedSkillPath, "---\nname: hidden\ndescription: Hidden skill.\n---\n");
159
+ }
160
+
161
+ assert.deepEqual(await __testing.findSkillFiles(root), []);
162
+ });
163
+
164
+ test("findSkillFiles follows direct symlinked skills but not symlinked categories", async (t) => {
136
165
  const root = join(tmpdir(), `gentle-pi-symlink-root-${Date.now()}`);
137
166
  const realSkillDir = join(tmpdir(), `gentle-pi-symlink-target-${Date.now()}`);
167
+ const categoryTarget = join(tmpdir(), `gentle-pi-symlink-category-${Date.now()}`);
138
168
  const linkedSkillDir = join(root, "linked");
139
169
  const skillPath = join(linkedSkillDir, "SKILL.md");
140
170
  mkdirSync(root, { recursive: true });
141
171
  mkdirSync(realSkillDir, { recursive: true });
172
+ mkdirSync(join(categoryTarget, "nested"), { recursive: true });
142
173
  writeFileSync(join(realSkillDir, "SKILL.md"), "---\nname: linked\ndescription: Linked skill.\n---\n");
174
+ writeFileSync(join(categoryTarget, "nested", "SKILL.md"), "---\nname: nested\ndescription: Nested skill.\n---\n");
143
175
  try {
144
176
  symlinkSync(realSkillDir, linkedSkillDir, "dir");
177
+ symlinkSync(categoryTarget, join(root, "category"), "dir");
145
178
  } catch (error) {
146
179
  t.skip(`symlink creation unavailable: ${error instanceof Error ? error.message : String(error)}`);
147
180
  return;
@@ -150,6 +183,12 @@ test("findSkillFiles follows symlinked skill directories", async (t) => {
150
183
  assert.deepEqual(await __testing.findSkillFiles(root), [skillPath]);
151
184
  });
152
185
 
186
+ test("only missing skill-file errors allow category scanning", () => {
187
+ assert.equal(__testing.isMissingSkillFileError({ code: "ENOENT" }), true);
188
+ assert.equal(__testing.isMissingSkillFileError({ code: "ENOTDIR" }), true);
189
+ assert.equal(__testing.isMissingSkillFileError({ code: "EACCES" }), false);
190
+ });
191
+
153
192
  test("skill registry watchers close on shutdown", async () => {
154
193
  const root = join(tmpdir(), `gentle-pi-watchers-${Date.now()}`);
155
194
  const skillPath = join(root, "skills", "docs", "SKILL.md");
@@ -453,6 +492,11 @@ test("registered watcher protects registry tracked after a cache hit", async (t)
453
492
  while (runtime.notices.length === 0 && Date.now() < deadline) await new Promise((resolve) => setTimeout(resolve, 50));
454
493
  assert.equal(readFileSync(fixture.registry, "utf8"), before);
455
494
  assert.ok(runtime.notices.some(({ message, level }) => level === "warning" && message.includes("/skill-registry:refresh")));
495
+ // The poll re-asks the same tracked-target state every interval: the
496
+ // warning stays a one-shot per unchanged state instead of repeating.
497
+ runtime.notices.length = 0;
498
+ await new Promise((resolve) => setTimeout(resolve, 3000));
499
+ assert.deepEqual(runtime.notices, []);
456
500
  assert.ok(!runtime.notices.some(({ message }) => message.includes("refreshed")));
457
501
  });
458
502
 
@@ -61,7 +61,7 @@ test("AC2: counts never classify, and the step-count classifier is gone", () =>
61
61
  test("AC3: small work runs its checks inline and keeps test-first", () => {
62
62
  const size = sectionOf(core, "## Task Size");
63
63
  for (const clause of [
64
- "run the focused test and the suite inline, once each",
64
+ "run the focused test and the suite inline in one command",
65
65
  "observe RED inline before the fix",
66
66
  "No explore, worker, or verifier",
67
67
  "no feature document, mirror, or commits unless the user asks",
@@ -128,7 +128,7 @@ test("T33: the Close step checks shared code before Risk: none", () => {
128
128
  // command that shares the changed code, old code against new, caught it in
129
129
  // 2 of 2. Inline work and the worker carry the same check.
130
130
  // S15: the differential check runs in scratch and is never committed as a test (blind round 4 found tests pinning a git commit).
131
- const DIFFERENTIAL = "Before finishing, run a differential check: for each existing command that shares the code you changed, run it with the new or changed input (for example a new option passed to a sibling subcommand) against the previous code (a copy of the base commit in a `mktemp -d` scratch directory, never committed as a test; committed tests never read git history or a fixed commit) and against your change, and compare stdout, stderr, and exit code; any difference nobody asked for is a regression to fix and pin with a test.";
131
+ const DIFFERENTIAL = "Only when your diff changes shared code (options, parsers, helpers) that an existing command the request did not name also uses, run one differential check before finishing, as a single script: for each such command, run it with the new or changed input (for example a new option passed to a sibling subcommand) against the previous code (a copy of the base commit in a `mktemp -d` scratch directory, never committed as a test; committed tests never read git history or a fixed commit) and against your change, and compare stdout, stderr, and exit code; any difference nobody asked for is a regression to fix and pin with a test.";
132
132
  test("C8: inline test-first and the worker PRESERVE step run the differential check", () => {
133
133
  for (const persona of ["gentleman", "neutral"] as const) {
134
134
  const principle = __testing.buildGentlePrompt(persona).split("\n").find((line) => line.includes("use test-first by default"));
@@ -47,6 +47,23 @@ test("S3: a Risk line naming a Task Size high-risk item admits the verifier", (t
47
47
  assert.equal(rejectUnriskedVerifierLaunch(viaContext, { root, contextUsage: quiet }), undefined, "the context carries the line");
48
48
  });
49
49
 
50
+ // gentle-shell-bench model-economy L9: a DeepSeek verifier launch was refused twice because its valid declaration
51
+ // opened a sentence mid-paragraph ("... on top of commit ebf3f35). Risk: item 1 (...)"), not a line.
52
+ test("S3: a Risk declaration that opens a sentence admits the verifier", (t) => {
53
+ const root = workspace(false);
54
+ t.after(() => rmSync(root, { recursive: true, force: true }));
55
+ for (const task of [
56
+ "Independently verify an ODD change on top of commit ebf3f35. Risk: item 1 (schema migration of stored data).",
57
+ "Verify the parser change! Risk: item 3 (shared parser)",
58
+ "Is the guard still sound? Risk: item 2",
59
+ ]) {
60
+ assert.equal(rejectUnriskedVerifierLaunch({ agent: "gentle-ai-verify", task }, { root, contextUsage: quiet }), undefined, task);
61
+ }
62
+ for (const task of ["Verify it. Risk: none (local rename)", "Verify it. Do not write Risk: item 3 here.", "Verify it.Risk: item 3"]) {
63
+ assert.ok(rejectUnriskedVerifierLaunch({ agent: "gentle-ai-verify", task }, { root, contextUsage: quiet })?.block, task);
64
+ }
65
+ });
66
+
50
67
  test("S3: an item outside 1-6, a Risk mention inside prose, or none first is rejected", (t) => {
51
68
  const root = workspace(false);
52
69
  t.after(() => rmSync(root, { recursive: true, force: true }));
@@ -128,7 +145,7 @@ test("S3/S5: the orchestrator tool_call hook gates only primary verifier launche
128
145
  test("S6: the Verification rule and the reference docs point at the runtime admission", () => {
129
146
  const read = (path: string) => readFileSync(join(import.meta.dirname, "..", path), "utf8");
130
147
  assert.ok(read("assets/orchestrator.md").includes("independent `gentle-ai-verify`; else inline; runtime admission, gates/overengineering"), "the core rule names the runtime check");
131
- assert.ok(read("assets/orchestrator-verification.md").includes("(the runtime admits its launch on a `Risk: item N` line, a feature-document task line, or past the context backstop)"), "the normative rule names the admitted line");
148
+ assert.ok(read("assets/orchestrator-verification.md").includes("(runtime admits its launch on a `Risk: item N` line or sentence, a feature-document task line, or past the context backstop)"), "the normative rule names the admitted line");
132
149
  const docs = read("docs/readme-reference.md");
133
150
  const writer = docs.indexOf("A second admission check stops small-task delegation");
134
151
  const verifier = docs.indexOf("A third admission check keeps the independent verifier for high-risk changes");
@@ -277,12 +277,12 @@ test("resolveVimRuntime resolves local bundle cli entrypoint when provided", ()
277
277
  });
278
278
 
279
279
  test("audited Pi editor releases are one frozen exact allowlist that includes the installed TUI", () => {
280
- assert.deepEqual([...AUDITED_PI_EDITOR_VERSIONS], ["0.99.1", "0.99.2", "1.0.0", "1.1.0", "1.2.0"]);
280
+ assert.deepEqual([...AUDITED_PI_EDITOR_VERSIONS], ["0.99.1", "0.99.2", "1.0.0", "1.1.0", "1.2.0", "1.3.0", "1.4.0"]);
281
281
  assert.equal(bundledAgent.VERSION, INSTALLED_PI, "bundled and local metadata must describe the same actual installed release");
282
282
  assert.ok(Object.isFrozen(AUDITED_PI_EDITOR_VERSIONS));
283
283
  assert.throws(() => (AUDITED_PI_EDITOR_VERSIONS as unknown as string[]).push("0.99.3"), TypeError);
284
284
  assert.ok(isAuditedPiEditorVersion(INSTALLED_PI), `installed pi-tui ${INSTALLED_PI} must be audited`);
285
- for (const unknown of ["0.99.3", "0.99.10", "0.99", "v0.99.2", " 0.99.2", "0.99.2-rc.1", ">=0.99.2", "1.0.1", ">=1.0.0", "v1.0.0", " 1.0.0", "1.0.0-rc.1", "1.2.1", "1.2.0-rc.1", "v1.2.0", ">=1.2.0", undefined, 99.2]) {
285
+ for (const unknown of ["0.99.3", "0.99.10", "0.99", "v0.99.2", " 0.99.2", "0.99.2-rc.1", ">=0.99.2", "1.0.1", ">=1.0.0", "v1.0.0", " 1.0.0", "1.0.0-rc.1", "1.2.1", "1.2.0-rc.1", "v1.2.0", ">=1.2.0", "1.4.1", "1.5.0", "1.4.0-rc.1", undefined, 99.2]) {
286
286
  assert.equal(isAuditedPiEditorVersion(unknown), false, String(unknown));
287
287
  }
288
288
  });