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
@@ -11,6 +11,8 @@ import {
11
11
  CONTEXT_BACKSTOP_TOKENS_ENV,
12
12
  contextBackstopReached,
13
13
  contextBackstopThreshold,
14
+ createThrottledContextMeasure,
15
+ type MeasuredContext,
14
16
  } from "../lib/context-backstop.ts";
15
17
 
16
18
  // Harness roadmap R1: the parent-context backstop was a fixed "~150k tokens" in prompt text, so the model had to
@@ -63,7 +65,7 @@ test("unknown tokens never count as reaching the backstop", () => {
63
65
  type Handler = (event: unknown, ctx: ExtensionContext) => unknown;
64
66
  type Patch = { content?: Array<{ type: string; text?: string }>; structuredContent?: unknown; isError?: boolean } | undefined;
65
67
 
66
- function session(t: TestContext, child: boolean) {
68
+ function session(t: TestContext, child: boolean, options: { now?: () => number; minIntervalMs?: number } = {}) {
67
69
  const cwd = realpathSync(mkdtempSync(join(tmpdir(), "context-backstop-")));
68
70
  const configHome = mkdtempSync(join(tmpdir(), "context-backstop-config-"));
69
71
  const previous = process.env.GENTLE_PI_CONFIG_HOME;
@@ -76,10 +78,19 @@ function session(t: TestContext, child: boolean) {
76
78
  });
77
79
  const handlers = new Map<string, Handler>();
78
80
  const pi = { on: (name: string, handler: Handler) => { handlers.set(name, handler); }, events: { on() {}, emit() {} }, registerCommand() {}, registerTool() {} } as unknown as ExtensionAPI;
79
- createGentleAiExtension({ nativeReviewCli: null, processEnv: { GENTLE_PI_AGENTS_CHILD: child ? "1" : "0", GENTLE_AI_TELEMETRY: "0" } })(pi);
81
+ // Semantic tests without options keep per-call measuring (minIntervalMs 0); callers that pass
82
+ // options (a fake clock) keep the production window resolution.
83
+ const measureOptions = Object.keys(options).length === 0 ? { minIntervalMs: 0 } : options;
84
+ createGentleAiExtension({
85
+ nativeReviewCli: null,
86
+ processEnv: { GENTLE_PI_AGENTS_CHILD: child ? "1" : "0", GENTLE_AI_TELEMETRY: "0" },
87
+ contextBackstopMeasure: measureOptions,
88
+ })(pi);
80
89
  let usage: ContextUsage | undefined = { tokens: 10_000, contextWindow: 1_000_000, percent: 1 };
90
+ let measures = 0;
81
91
  const sessionManager = { getSessionId: () => "session-backstop", getCwd: () => cwd, getEntries: () => [], getBranch: () => [] };
82
- const ctx = { cwd, hasUI: false, ui: { notify() {} }, sessionManager, getContextUsage: () => usage } as unknown as ExtensionContext;
92
+ const ctx = { cwd, hasUI: false, ui: { notify() {} }, sessionManager, getContextUsage: () => { measures++; return usage; } } as unknown as ExtensionContext;
93
+ const measureCount = () => measures;
83
94
  const setTokens = (tokens: number | null, contextWindow = 1_000_000) => {
84
95
  usage = { tokens, contextWindow, percent: tokens === null ? null : (tokens / contextWindow) * 100 };
85
96
  };
@@ -87,7 +98,7 @@ function session(t: TestContext, child: boolean) {
87
98
  toolName: "read", toolCallId: "call-read", isError: false, input: { path: "a.ts" },
88
99
  content: [{ type: "text", text: "file body" }], structuredContent: { lines: 1 }, ...overrides,
89
100
  }, ctx) as Promise<Patch>;
90
- return { cwd, ctx, handlers, setTokens, read };
101
+ return { cwd, ctx, handlers, setTokens, read, measureCount, options };
91
102
  }
92
103
 
93
104
  test("the primary session gets one notice when its measured context first reaches the backstop", async (t) => {
@@ -157,3 +168,58 @@ test("a parent write result carries the notice after its own bookkeeping", async
157
168
  assert.equal(result?.content?.[0]?.text, "Wrote src/a.ts");
158
169
  assert.match(result?.content?.[1]?.text ?? "", /Context backstop/);
159
170
  });
171
+
172
+ // The measurement behind the backstop (ctx.getContextUsage()) rebuilds the whole session
173
+ // projection, so it is sampled at most once per window per session instead of once per
174
+ // tool result. The window never decides the notice: it only decides when the context is
175
+ // looked at again.
176
+ test("the throttled measure samples once per window and the first sample of a session is immediate", () => {
177
+ let clock = 1_000;
178
+ const measure = createThrottledContextMeasure({ now: () => clock });
179
+ let calls = 0;
180
+ const sample = (value: MeasuredContext | undefined) => measure("session-a", () => { calls++; return value; });
181
+ assert.deepEqual(sample({ tokens: 10_000 }), { tokens: 10_000 }, "the first sample of a session measures immediately");
182
+ assert.equal(calls, 1);
183
+ clock = 2_000;
184
+ sample({ tokens: 10_000 });
185
+ assert.equal(calls, 1, "a sample inside the window skips the measurement");
186
+ clock = 6_001;
187
+ sample({ tokens: 10_000 });
188
+ assert.equal(calls, 2, "the window expired: measure again");
189
+ clock = 6_002;
190
+ sample({ tokens: 10_000 });
191
+ assert.equal(calls, 2, "the window restarts at every measurement");
192
+ });
193
+
194
+ test("the throttled measure is per session and a throwing measure never poisons the window", () => {
195
+ let clock = 0;
196
+ const measure = createThrottledContextMeasure({ now: () => clock });
197
+ assert.ok(measure("session-a", () => ({ tokens: 1 })), "session a measures");
198
+ assert.ok(measure("session-b", () => ({ tokens: 2 })), "session b inside the same window still measures");
199
+ assert.equal(measure("session-a", () => ({ tokens: 3 })), undefined, "session a inside its own window skips");
200
+ clock = 5_001;
201
+ let boom = false;
202
+ assert.equal(measure("session-a", () => { boom = true; throw new Error("boom"); }), undefined, "a throwing measure returns undefined");
203
+ assert.equal(boom, true, "the measure itself actually ran");
204
+ assert.equal(measure("session-a", () => ({ tokens: 4 }))?.tokens, undefined, "the failed measurement still consumed its window");
205
+ clock = 10_002;
206
+ assert.equal(measure("session-a", () => ({ tokens: 4 }))?.tokens, 4, "the next window measures again");
207
+ });
208
+
209
+ test("the extension samples the backstop context at most once per window per session", async (t) => {
210
+ let clock = 0;
211
+ const s = session(t, false, { now: () => clock });
212
+ assert.equal(s.measureCount(), 0);
213
+ await s.read();
214
+ assert.equal(s.measureCount(), 1, "the first tool result measures immediately");
215
+ await s.read();
216
+ await s.read();
217
+ assert.equal(s.measureCount(), 1, "tool results inside the window skip the measurement");
218
+ clock = 5_001;
219
+ s.setTokens(160_000);
220
+ const crossed = await s.read();
221
+ assert.equal(s.measureCount(), 2, "the window expired: measure again");
222
+ assert.match(crossed?.content?.[1]?.text ?? "", /Context backstop/, "a crossing is announced on the next sampled result");
223
+ await s.read();
224
+ assert.equal(s.measureCount(), 2, "and the window restarts after that sample");
225
+ });
@@ -244,9 +244,10 @@ test("T11/S4: the parent hands verify the whole spec, authorizes its probes and
244
244
  // names every line that admits it. The pin still guards against accidental drift.
245
245
  test("AC4/S4: the normative verification rule text is unchanged by the review protocol", () => {
246
246
  const normative = sectionFrom(verification, "## Verification rule (normative)");
247
+ // Re-pinned on purpose: the runtime admission now also accepts a `Risk: item N` sentence (verifier-admission.ts).
247
248
  assert.equal(
248
249
  createHash("sha256").update(normative).digest("hex"),
249
- "b159829cad24a31e23d6309b3bfe0e4cc71eec2316775ba07314fecd1fb6a63f",
250
+ "f052518b2905e4560d629397876057079aa71b45352272646261a611fb4a1e17",
250
251
  "the normative Verification rule section changed",
251
252
  );
252
253
  assert.ok(verification.includes("or a delegated writer returns"), "the module must still load when a delegated writer returns");