gentle-pi 3.2.0 → 3.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 (80) hide show
  1. package/assets/orchestrator-delegation.md +13 -8
  2. package/assets/orchestrator.md +2 -2
  3. package/docs/gentle-shell.md +40 -17
  4. package/docs/readme-reference.md +41 -7
  5. package/docs/review-integration.md +25 -11
  6. package/extensions/gentle-agents.ts +85 -17
  7. package/extensions/gentle-ai.ts +179 -12
  8. package/extensions/gentle-shell.ts +408 -38
  9. package/extensions/gentle-todo.ts +19 -1
  10. package/lib/agents-view.ts +41 -14
  11. package/lib/agents-widget.ts +84 -13
  12. package/lib/command-palette-catalog.ts +1 -0
  13. package/lib/double-esc-cancel-policy.ts +138 -0
  14. package/lib/inprocess-reviewer.ts +260 -0
  15. package/lib/model-routing-authority.ts +1 -1
  16. package/lib/native-review-cli.ts +23 -0
  17. package/lib/odd-runtime-delegation-gate.ts +88 -0
  18. package/lib/review-host-relay.ts +262 -94
  19. package/lib/review-integration-v2.ts +110 -26
  20. package/lib/shell-bar.ts +158 -29
  21. package/lib/shell-card.ts +19 -9
  22. package/lib/shell-changes-view.ts +43 -5
  23. package/lib/shell-changes.ts +92 -5
  24. package/lib/shell-hover.ts +39 -0
  25. package/lib/shell-prompt.ts +10 -1
  26. package/lib/shell-sidebar-layout.ts +111 -15
  27. package/lib/shell-sidebar.ts +16 -0
  28. package/lib/shell-todo.ts +7 -1
  29. package/lib/shell-usage-view.ts +98 -10
  30. package/lib/shell-usage.ts +226 -10
  31. package/package.json +2 -1
  32. package/runtime/native-review-cli.mjs +23 -0
  33. package/runtime/review-integration-v2.mjs +110 -26
  34. package/scripts/gentle-ai-installer.mjs +10 -10
  35. package/scripts/maintainer/provider-relay-matrix.mjs +118 -47
  36. package/scripts/mirror-odd-routing.mjs +242 -0
  37. package/scripts/verify-package-files.mjs +3 -3
  38. package/tests/agents-grouping.test.ts +75 -18
  39. package/tests/agents-view.test.ts +28 -18
  40. package/tests/agents-widget.test.ts +100 -12
  41. package/tests/command-palette.test.ts +1 -0
  42. package/tests/devbinary/pi-host-relay.devtest.ts +176 -138
  43. package/tests/double-esc-cancel-policy.test.ts +194 -0
  44. package/tests/gentle-agents.test.ts +528 -5
  45. package/tests/gentle-ai-binary.test.ts +1 -1
  46. package/tests/gentle-ai-installer.test.ts +47 -47
  47. package/tests/gentle-ai.test.ts +69 -5
  48. package/tests/gentle-shell.test.ts +903 -25
  49. package/tests/gentle-todo.test.ts +17 -4
  50. package/tests/inprocess-reviewer.test.ts +368 -0
  51. package/tests/maintainer/provider-relay.maintest.ts +101 -143
  52. package/tests/native-review-capability-contract.test.ts +32 -1
  53. package/tests/odd-routing-canonical-ratchet.test.ts +293 -0
  54. package/tests/odd-routing-contract.test.ts +57 -0
  55. package/tests/odd-runtime-delegation-gate.test.ts +212 -0
  56. package/tests/orchestrator-rdd-ownership.test.ts +3 -3
  57. package/tests/package-manifest.test.ts +6 -6
  58. package/tests/review-controller-native-routing.test.ts +60 -1
  59. package/tests/review-host-relay-routing.test.ts +77 -0
  60. package/tests/review-host-relay.test.ts +285 -239
  61. package/tests/review-integration-v2-forward.test.ts +61 -0
  62. package/tests/review-integration-v2.test.ts +116 -1
  63. package/tests/review-relay-transport-agent.test.ts +83 -0
  64. package/tests/runtime-harness.mjs +11 -0
  65. package/tests/session-changes-shell.test.ts +27 -0
  66. package/tests/session-worktree-registry.test.ts +41 -0
  67. package/tests/shell-bar.test.ts +224 -6
  68. package/tests/shell-card.test.ts +5 -3
  69. package/tests/shell-changes-view.test.ts +47 -0
  70. package/tests/shell-changes.test.ts +177 -0
  71. package/tests/shell-hover.test.ts +19 -0
  72. package/tests/shell-prompt.test.ts +20 -0
  73. package/tests/shell-sidebar-fullscreen.test.ts +59 -0
  74. package/tests/shell-sidebar-layout.test.ts +243 -5
  75. package/tests/shell-sidebar.test.ts +25 -1
  76. package/tests/shell-todo.test.ts +36 -0
  77. package/tests/shell-usage-view.test.ts +123 -3
  78. package/tests/shell-usage.test.ts +254 -6
  79. package/lib/opaque-pi-reviewer-adapter.ts +0 -284
  80. package/tests/opaque-pi-reviewer-adapter.test.ts +0 -266
@@ -1171,6 +1171,7 @@ for (const statusSchema of [
1171
1171
 
1172
1172
  test("inspect with untrackedScope exclude resolves the intended-untracked stop in one round trip", async (t) => {
1173
1173
  const { cwd, initial, target, selection } = untrackedStopFixture(t);
1174
+ const baseRef = execFileSync("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf8" }).trim();
1174
1175
  const requests: Array<Record<string, unknown>> = [],
1175
1176
  retained = new Map();
1176
1177
  const native = {
@@ -1180,7 +1181,11 @@ test("inspect with untrackedScope exclude resolves the intended-untracked stop i
1180
1181
  },
1181
1182
  } as unknown as NativeReviewCli;
1182
1183
  const result = await __testing.executeReviewControllerOperation(
1183
- { operation: "inspect", untrackedScope: "exclude" },
1184
+ {
1185
+ operation: "inspect",
1186
+ input: JSON.stringify({ baseRef: "HEAD", committedOnly: true }),
1187
+ untrackedScope: "exclude",
1188
+ },
1184
1189
  cwd,
1185
1190
  native,
1186
1191
  undefined,
@@ -1191,6 +1196,10 @@ test("inspect with untrackedScope exclude resolves the intended-untracked stop i
1191
1196
  assert.equal(result.status, "ready");
1192
1197
  assert.equal("selectionBinding" in result, false);
1193
1198
  assert.equal(requests.length, 2);
1199
+ for (const request of requests) {
1200
+ assert.equal(request.baseRef, baseRef);
1201
+ assert.equal(request.committedOnly, true);
1202
+ }
1194
1203
  assert.equal(requests[1]!.untrackedScope, "exclude");
1195
1204
  assert.equal(requests[1]!.expectedUntrackedInventory, SHA);
1196
1205
  assert.deepEqual(requests[1]!.intendedUntracked, []);
@@ -1960,6 +1969,56 @@ test("ordinary START transports native focus and safe policy inputs without rebu
1960
1969
  assert.equal(statusCalls, 0);
1961
1970
  });
1962
1971
 
1972
+ test("INSPECT forwards an explicit committed-only base selector to negotiated STATUS", async (t) => {
1973
+ const cwd = repository(t);
1974
+ const baseRef = execFileSync("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf8" }).trim();
1975
+ const requests: Array<Record<string, unknown>> = [];
1976
+ const native = {
1977
+ targetStatus: async (request: Record<string, unknown>) => {
1978
+ requests.push(request);
1979
+ return startStatus(cwd, baseRef);
1980
+ },
1981
+ } as unknown as NativeReviewCli;
1982
+
1983
+ const result = await __testing.executeReviewControllerOperation(
1984
+ { operation: "inspect", input: JSON.stringify({ baseRef, committedOnly: true }) },
1985
+ cwd,
1986
+ native,
1987
+ );
1988
+
1989
+ assert.equal(result.status, "ready");
1990
+ assert.deepEqual(
1991
+ requests.map(({ baseRef: selectedBase, committedOnly }) => ({ baseRef: selectedBase, committedOnly })),
1992
+ [{ baseRef, committedOnly: true }],
1993
+ );
1994
+ });
1995
+
1996
+ test("INSPECT rejects malformed committed-range selectors before negotiated STATUS", async (t) => {
1997
+ const cwd = repository(t);
1998
+ let targetCalls = 0;
1999
+ const native = {
2000
+ targetStatus: async () => {
2001
+ targetCalls += 1;
2002
+ return startStatus(cwd);
2003
+ },
2004
+ } as unknown as NativeReviewCli;
2005
+
2006
+ for (const input of [
2007
+ { baseRef: "HEAD", committedOnly: false },
2008
+ { committedOnly: true },
2009
+ { baseRef: "HEAD", committedOnly: true, mode: "ordinary" },
2010
+ ]) {
2011
+ const rejected = await __testing.executeReviewControllerOperation(
2012
+ { operation: "inspect", input: JSON.stringify(input) },
2013
+ cwd,
2014
+ native,
2015
+ );
2016
+ assert.equal(rejected.outcome, "native-inspect-input-invalid");
2017
+ assert.equal(rejected.mutation_outcome, "none");
2018
+ }
2019
+ assert.equal(targetCalls, 0);
2020
+ });
2021
+
1963
2022
  test("ordinary START keeps default and explicit base selection fail-closed before native mutation", async (t) => {
1964
2023
  const cwd = repository(t);
1965
2024
  let targetCalls = 0;
@@ -155,6 +155,38 @@ function providerRefuterRequiredStatus(lineageId: string): ReviewStatusV3 {
155
155
  };
156
156
  }
157
157
 
158
+ // gentle-pi#311 P3: gentle-ai's v9 contract renders the refuter and
159
+ // targeted-validator role captures host-mediated, exactly like a lens
160
+ // materialize slot, instead of the self-contained --execute=true vector
161
+ // `providerRefuterRequiredStatus` above models.
162
+ function roleBindingArguments(lineageId: string, revision = SHA): ReviewCollectInputV3["arguments"] {
163
+ return [
164
+ { name: "lineage", value: lineageId, token: `--lineage=${lineageId}` },
165
+ { name: "expected-revision", value: revision, token: `--expected-revision=${revision}` },
166
+ { name: "target", value: SHA, token: `--target=${SHA}` },
167
+ { name: "repository-context", value: `rctx1_${"e".repeat(64)}`, token: `--repository-context=rctx1_${"e".repeat(64)}` },
168
+ ];
169
+ }
170
+
171
+ function hostMediatedRefuterCollectInput(lineageId: string, revision = SHA): ReviewCollectInputV3 {
172
+ const bindingTokens = roleBindingArguments(lineageId, revision).map((argument) => argument.token!);
173
+ return {
174
+ name: "provider_refuter",
175
+ schema: "https://gentle-ai.dev/schema/review/refuter/v1",
176
+ captureOperation: "review.capture-refuter",
177
+ arguments: [
178
+ ...roleBindingArguments(lineageId, revision),
179
+ { name: "agent", value: "pi", token: "--agent=pi" },
180
+ { name: "materialize", value: "true", token: "--materialize=true" },
181
+ ],
182
+ submission: {
183
+ operationToken: "capture-refuter",
184
+ argumentTokens: [...bindingTokens, "--agent=pi", "--input={{value}}"],
185
+ values: [{ slot: "provider_refuter", domain: "artifact_path_or_stdin", substitutionLocation: bindingTokens.length + 1 }],
186
+ },
187
+ };
188
+ }
189
+
158
190
  interface RoutingHarness {
159
191
  statusQueue: ReviewStatusV3[];
160
192
  statusCalls: Array<{ cwd: string; lineageId?: string; agent?: "pi" }>;
@@ -216,6 +248,51 @@ test("one materialize binding routes exactly one provider slot through the host
216
248
  assert.equal((result.host_relay as { transport: string }).transport, "pi_host_relay");
217
249
  });
218
250
 
251
+ // gentle-pi#311 P3: a v9 host-mediated refuter/targeted-validator slot reuses
252
+ // this exact relay machinery — the only role-specific part reachable from
253
+ // here is the fixed routing key it resolves through, since it carries no
254
+ // per-slot lens identity.
255
+ test("a v9 host-mediated refuter slot routes through the host relay with its fixed routing key", async (t) => {
256
+ t.after(() => __testing.setReviewHostRelayRunnerForTesting());
257
+ const cwd = repository(t);
258
+ const lineageId = "relay-role-lineage";
259
+ const input = hostMediatedRefuterCollectInput(lineageId);
260
+ const harness = nativeHarness([finalizeStatus(lineageId, [input])]);
261
+ const relayed: ReviewHostRelayRequest[] = [];
262
+ __testing.setReviewHostRelayRunnerForTesting(async (request: ReviewHostRelayRequest) => {
263
+ relayed.push(request);
264
+ return { promptByteLength: 64, resultByteLength: 32, submission: '{"admission_decision":"completed"}' };
265
+ });
266
+
267
+ const result = await runCapture(cwd, harness, lineageId);
268
+
269
+ assert.equal(relayed.length, 1);
270
+ assert.equal(relayed[0]!.routingKey, "review-refuter");
271
+ assert.deepEqual(relayed[0]!.captureArgumentTokens, input.arguments.map((argument) => argument.token));
272
+ assert.deepEqual(relayed[0]!.submission, input.submission);
273
+ assert.equal(harness.statusCalls.length, 1, "a nonterminal capture does not auto-follow STATUS");
274
+ assert.equal(result.status, "captured");
275
+ assert.equal((result.host_relay as { transport: string }).transport, "pi_host_relay");
276
+ });
277
+
278
+ // The reviewer selection is validated before any process launches (the same
279
+ // discipline a lens slot already has): a routing config with no
280
+ // review-refuter entry is refused typed, naming that exact key, never a
281
+ // mid-relay transport failure.
282
+ test("a missing review-refuter routing model is refused typed, naming the key, before any relay launch", async (t) => {
283
+ const cwd = repository(t);
284
+ const lineageId = "relay-role-missing-model";
285
+ const input = hostMediatedRefuterCollectInput(lineageId);
286
+ const harness = nativeHarness([finalizeStatus(lineageId, [input])]);
287
+
288
+ const result = await runCapture(cwd, harness, lineageId);
289
+
290
+ assert.equal(result.status, "blocked");
291
+ const failure = result.failure as { kind?: string } | undefined;
292
+ assert.equal(failure?.kind, "reviewer-config-invalid");
293
+ assert.match(String(result.reason), /review-refuter/);
294
+ });
295
+
219
296
  test("Pi-authored review documents are rejected at the capture input boundary", async (t) => {
220
297
  // The narrow capture schema rejects a caller-authored reviewer document
221
298
  // before any relay launch or native call.