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.
- package/assets/orchestrator-delegation.md +13 -8
- package/assets/orchestrator.md +2 -2
- package/docs/gentle-shell.md +40 -17
- package/docs/readme-reference.md +41 -7
- package/docs/review-integration.md +25 -11
- package/extensions/gentle-agents.ts +85 -17
- package/extensions/gentle-ai.ts +179 -12
- package/extensions/gentle-shell.ts +408 -38
- package/extensions/gentle-todo.ts +19 -1
- package/lib/agents-view.ts +41 -14
- package/lib/agents-widget.ts +84 -13
- package/lib/command-palette-catalog.ts +1 -0
- package/lib/double-esc-cancel-policy.ts +138 -0
- package/lib/inprocess-reviewer.ts +260 -0
- package/lib/model-routing-authority.ts +1 -1
- package/lib/native-review-cli.ts +23 -0
- package/lib/odd-runtime-delegation-gate.ts +88 -0
- package/lib/review-host-relay.ts +262 -94
- package/lib/review-integration-v2.ts +110 -26
- package/lib/shell-bar.ts +158 -29
- package/lib/shell-card.ts +19 -9
- package/lib/shell-changes-view.ts +43 -5
- package/lib/shell-changes.ts +92 -5
- package/lib/shell-hover.ts +39 -0
- package/lib/shell-prompt.ts +10 -1
- package/lib/shell-sidebar-layout.ts +111 -15
- package/lib/shell-sidebar.ts +16 -0
- package/lib/shell-todo.ts +7 -1
- package/lib/shell-usage-view.ts +98 -10
- package/lib/shell-usage.ts +226 -10
- package/package.json +2 -1
- package/runtime/native-review-cli.mjs +23 -0
- package/runtime/review-integration-v2.mjs +110 -26
- package/scripts/gentle-ai-installer.mjs +10 -10
- package/scripts/maintainer/provider-relay-matrix.mjs +118 -47
- package/scripts/mirror-odd-routing.mjs +242 -0
- package/scripts/verify-package-files.mjs +3 -3
- package/tests/agents-grouping.test.ts +75 -18
- package/tests/agents-view.test.ts +28 -18
- package/tests/agents-widget.test.ts +100 -12
- package/tests/command-palette.test.ts +1 -0
- package/tests/devbinary/pi-host-relay.devtest.ts +176 -138
- package/tests/double-esc-cancel-policy.test.ts +194 -0
- package/tests/gentle-agents.test.ts +528 -5
- package/tests/gentle-ai-binary.test.ts +1 -1
- package/tests/gentle-ai-installer.test.ts +47 -47
- package/tests/gentle-ai.test.ts +69 -5
- package/tests/gentle-shell.test.ts +903 -25
- package/tests/gentle-todo.test.ts +17 -4
- package/tests/inprocess-reviewer.test.ts +368 -0
- package/tests/maintainer/provider-relay.maintest.ts +101 -143
- package/tests/native-review-capability-contract.test.ts +32 -1
- package/tests/odd-routing-canonical-ratchet.test.ts +293 -0
- package/tests/odd-routing-contract.test.ts +57 -0
- package/tests/odd-runtime-delegation-gate.test.ts +212 -0
- package/tests/orchestrator-rdd-ownership.test.ts +3 -3
- package/tests/package-manifest.test.ts +6 -6
- package/tests/review-controller-native-routing.test.ts +60 -1
- package/tests/review-host-relay-routing.test.ts +77 -0
- package/tests/review-host-relay.test.ts +285 -239
- package/tests/review-integration-v2-forward.test.ts +61 -0
- package/tests/review-integration-v2.test.ts +116 -1
- package/tests/review-relay-transport-agent.test.ts +83 -0
- package/tests/runtime-harness.mjs +11 -0
- package/tests/session-changes-shell.test.ts +27 -0
- package/tests/session-worktree-registry.test.ts +41 -0
- package/tests/shell-bar.test.ts +224 -6
- package/tests/shell-card.test.ts +5 -3
- package/tests/shell-changes-view.test.ts +47 -0
- package/tests/shell-changes.test.ts +177 -0
- package/tests/shell-hover.test.ts +19 -0
- package/tests/shell-prompt.test.ts +20 -0
- package/tests/shell-sidebar-fullscreen.test.ts +59 -0
- package/tests/shell-sidebar-layout.test.ts +243 -5
- package/tests/shell-sidebar.test.ts +25 -1
- package/tests/shell-todo.test.ts +36 -0
- package/tests/shell-usage-view.test.ts +123 -3
- package/tests/shell-usage.test.ts +254 -6
- package/lib/opaque-pi-reviewer-adapter.ts +0 -284
- 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
|
-
{
|
|
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.
|