gentle-pi 2.2.0 → 2.4.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/README.md +76 -79
- package/assets/agents/review-readability.md +0 -2
- package/assets/agents/review-reliability.md +0 -2
- package/assets/agents/review-resilience.md +0 -2
- package/assets/agents/review-risk.md +0 -2
- package/assets/agents/sdd-apply.md +5 -3
- package/assets/agents/sdd-explore.md +1 -0
- package/assets/agents/sdd-proposal.md +2 -0
- package/assets/agents/sdd-research.md +54 -0
- package/assets/agents/sdd-status.md +4 -4
- package/assets/agents/sdd-tasks.md +2 -3
- package/assets/agents/sdd-verify.md +21 -1
- package/assets/chains/sdd-full.chain.md +1 -1
- package/assets/chains/sdd-verify.chain.md +1 -1
- package/assets/orchestrator-delegation.md +56 -249
- package/assets/orchestrator-memory.md +2 -0
- package/assets/orchestrator.md +20 -40
- package/assets/sdd-orchestrator-workflow.md +30 -37
- package/assets/support/sdd-status-contract.md +7 -7
- package/contracts/review-integration/v1/schemas/transition-execution.schema.json +42 -0
- package/contracts/review-integration/v2/schemas/last-event-closure.schema.json +66 -0
- package/contracts/review-integration/v2/schemas/opencode-provider-role.schema.json +14 -0
- package/contracts/review-provider-contract-mirror/provider-contract.lock.json +8 -7
- package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/README.md +10 -0
- package/contracts/review-provider-contract-mirror/v1.2.0/bundle/manifest.json +74 -0
- package/contracts/review-provider-contract-mirror/v1.2.0/bundle/orchestration/pi.md +53 -0
- package/contracts/review-provider-contract-mirror/v1.2.0/bundle/schemas/targeted-validator.schema.json +1 -0
- package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/generated/provider-capabilities.baseline.json +9 -2
- package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/generated/provider-roles.baseline.json +2 -2
- package/docs/native-authority-architecture.md +9 -11
- package/docs/review-integration.md +27 -373
- package/extensions/ask-user-choice.ts +151 -0
- package/extensions/gentle-ai.ts +2349 -3785
- package/extensions/quiet-tools.ts +515 -32
- package/extensions/sdd-init.ts +4 -8
- package/lib/agent-home.ts +8 -0
- package/lib/gentle-ai-renderer.ts +70 -0
- package/lib/model-routing-authority.ts +133 -0
- package/lib/native-review-cli.ts +387 -940
- package/lib/opaque-pi-reviewer-adapter.ts +284 -0
- package/lib/provider-contract-bundle.ts +88 -6
- package/lib/review-candidate-view.ts +341 -132
- package/lib/review-host-relay.ts +324 -96
- package/lib/review-integration-v2.ts +868 -307
- package/lib/review-last-event-controller.ts +35 -0
- package/lib/sdd-preflight.ts +179 -68
- package/lib/sdd-status.ts +66 -111
- package/lib/terminal-theme.ts +1 -1
- package/package.json +83 -82
- package/runtime/gentle-ai-binary.mjs +1 -1
- package/runtime/native-review-cli.mjs +337 -890
- package/runtime/review-integration-v2.mjs +816 -255
- package/runtime/review-relay-contract.mjs +1 -1
- package/scripts/{build-git-commit-transaction-runner.mjs → build-runtime-modules.mjs} +4 -5
- package/scripts/gentle-ai-installer.mjs +75 -22
- package/scripts/maintainer/provider-relay-matrix.mjs +195 -11
- package/scripts/test-packed-runner.mjs +4 -7
- package/scripts/verify-package-files.mjs +23 -23
- package/skills/_shared/review-ledger-contract.md +14 -14
- package/skills/chained-pr/SKILL.md +3 -0
- package/skills/cognitive-doc-design/SKILL.md +1 -1
- package/skills/comment-writer/SKILL.md +1 -1
- package/skills/gentle-ai/SKILL.md +7 -74
- package/skills/issue-creation/SKILL.md +53 -93
- package/skills/judgment-day/SKILL.md +5 -7
- package/skills/rdd-defect-workflow/SKILL.md +3 -3
- package/skills/release/SKILL.md +3 -3
- package/skills/skill-registry/SKILL.md +1 -1
- package/skills/work-unit-commits/SKILL.md +3 -1
- package/tests/artifact-language.test.ts +24 -11
- package/tests/ask-user-choice.test.ts +264 -0
- package/tests/codegraph-tools.test.ts +3 -3
- package/tests/crosslane/cross-lane.mjs +15 -1168
- package/tests/delegated-key-learnings-contract.test.ts +8 -6
- package/tests/devbinary/native-review-parity.devtest.ts +167 -250
- package/tests/devbinary/pi-host-relay.devtest.ts +867 -0
- package/tests/fixtures/devbinary/last-event-capture-correction-plan.captured.json +10 -0
- package/tests/fixtures/devbinary/last-event-capture-refuter-approved.captured.json +20 -0
- package/tests/fixtures/devbinary/last-event-capture-refuter-correction-required.captured.json +26 -0
- package/tests/fixtures/devbinary/last-event-capture-result-approved.captured.json +8 -0
- package/tests/fixtures/devbinary/last-event-capture-result-correction-required.captured.json +26 -0
- package/tests/fixtures/devbinary/last-event-capture-validation-approved.captured.json +8 -0
- package/tests/fixtures/devbinary/last-event-closure.provenance.md +13 -0
- package/tests/fixtures/devbinary/review-acknowledged-v1.captured.json +9 -0
- package/tests/fixtures/devbinary/review-acknowledged.provenance.md +31 -0
- package/tests/fixtures/devbinary/start-v3-zero-lens-closed.captured.json +21 -0
- package/tests/fixtures/native-review-cli/v2.5.0-rc.1/PROVENANCE.txt +15 -0
- package/tests/fixtures/provider-contract-bundle/v1.2.0/README.md +22 -0
- package/{contracts/review-provider-contract-mirror/v1.1.0/bundle → tests/fixtures/provider-contract-bundle/v1.2.0}/manifest.json +11 -2
- package/tests/fixtures/provider-contract-bundle/v1.2.0/orchestration/pi.md +97 -0
- package/tests/fixtures/provider-contract-bundle/v1.2.0/schemas/lens.schema.json +16 -0
- package/tests/fixtures/provider-contract-bundle/v1.2.0/schemas/refuter.schema.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.2.0/vectors/lens.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.2.0/vectors/refuter.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.2.0/vectors/targeted-validator.json +1 -0
- package/tests/gentle-ai-binary.test.ts +83 -13
- package/tests/gentle-ai-dev-binary.test.ts +51 -228
- package/tests/gentle-ai-installer.test.ts +137 -47
- package/tests/gentle-ai.test.ts +821 -131
- package/tests/gentle-theme.test.ts +133 -0
- package/tests/issue-creation-skill.test.ts +103 -0
- package/tests/maintainer/provider-relay.maintest.ts +340 -4
- package/tests/model-routing-authority.test.ts +257 -0
- package/tests/native-review-capability-contract.test.ts +45 -1
- package/tests/native-review-cli.test.ts +586 -962
- package/tests/native-review-consent.test.ts +150 -66
- package/tests/native-review-parity-runtime.test.ts +80 -359
- package/tests/native-review-parity.test.ts +674 -925
- package/tests/opaque-pi-reviewer-adapter.test.ts +266 -0
- package/tests/orchestrator-budget.test.ts +132 -88
- package/tests/orchestrator-rdd-ownership.test.ts +103 -0
- package/tests/package-manifest.test.ts +152 -36
- package/tests/provider-contract-bundle.test.ts +76 -0
- package/tests/provider-contract-mirror.test.ts +19 -0
- package/tests/provider-defect-handoff.test.ts +15 -118
- package/tests/quiet-tool-rendering.test.ts +1055 -28
- package/tests/review-agent-end-preflight.test.ts +408 -0
- package/tests/review-authority-recovery-docs.test.ts +1 -2
- package/tests/review-candidate-view.test.ts +409 -3
- package/tests/review-compact-contract.test.ts +4 -30
- package/tests/review-contract-prompt.test.ts +139 -0
- package/tests/review-controller-lock-status.test.ts +2 -2
- package/tests/review-controller-native-recovery.test.ts +363 -1450
- package/tests/review-controller-native-routing.test.ts +1251 -5642
- package/tests/review-controller-retired-ops.test.ts +1 -1
- package/tests/review-controller-workspace-root.test.ts +222 -71
- package/tests/review-controller.test.ts +26 -816
- package/tests/review-corrected-finalize-binding.test.ts +112 -153
- package/tests/review-dispatch-hydration-gap.test.ts +1 -53
- package/tests/review-gate.test.ts +0 -45
- package/tests/review-host-relay-restart-parity.test.ts +360 -0
- package/tests/review-host-relay-routing.test.ts +328 -91
- package/tests/review-host-relay.test.ts +436 -14
- package/tests/review-integration-v2-forward.test.ts +623 -553
- package/tests/review-integration-v2.test.ts +239 -165
- package/tests/review-last-event-closure.test.ts +408 -0
- package/tests/review-ledger-contract.test.ts +97 -35
- package/tests/review-recovered-lineage-routing.test.ts +0 -47
- package/tests/review-relay-transport-agent.test.ts +119 -46
- package/tests/review-snapshot.test.ts +3 -2
- package/tests/runtime-harness.mjs +362 -196
- package/tests/sdd-agent-tools.test.ts +36 -0
- package/tests/sdd-preflight.test.ts +81 -15
- package/tests/sdd-status.test.ts +109 -110
- package/tests/skill-collision-prefixes.test.ts +5 -8
- package/tests/writer-edit-surface-scope.test.ts +366 -0
- package/themes/Gentleman-Cute.json +94 -0
- package/themes/Gentleman-Sexy.json +92 -0
- package/lib/git-commit-transaction.ts +0 -861
- package/runtime/git-commit-transaction.mjs +0 -862
- package/scripts/run-git-commit-transaction.mjs +0 -35
- package/tests/fixtures/native-review-cli/v2.1.3/sdd-status-engram.json +0 -139
- package/tests/fixtures/native-review-cli/v2.1.3/sdd-status.json +0 -200
- package/tests/git-commit-transaction.test.ts +0 -530
- /package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/schemas/lens.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/schemas/refuter.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/vectors/lens.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/vectors/refuter.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/vectors/targeted-validator.json +0 -0
- /package/{contracts/review-provider-contract-mirror/v1.1.0/bundle → tests/fixtures/provider-contract-bundle/v1.2.0}/schemas/targeted-validator.schema.json +0 -0
|
@@ -7,13 +7,9 @@ import {
|
|
|
7
7
|
NativeReviewCliV216,
|
|
8
8
|
NativeReviewConsentBindingError,
|
|
9
9
|
NativeReviewConsentRequiredError,
|
|
10
|
-
clearNativeReviewCapabilitiesCacheForTesting,
|
|
11
10
|
type ExecFileAdapter,
|
|
12
11
|
} from "../lib/native-review-cli.ts";
|
|
13
|
-
import {
|
|
14
|
-
NativeReviewCliV216 as RuntimeNativeReviewCliV216,
|
|
15
|
-
clearNativeReviewCapabilitiesCacheForTesting as clearRuntimeNativeReviewCapabilitiesCacheForTesting,
|
|
16
|
-
} from "../runtime/native-review-cli.mjs";
|
|
12
|
+
import { NativeReviewCliV216 as RuntimeNativeReviewCliV216 } from "../runtime/native-review-cli.mjs";
|
|
17
13
|
import type { ReviewConsentV2 } from "../lib/review-integration-v2.ts";
|
|
18
14
|
|
|
19
15
|
const fixtureRoot = join(process.cwd(), "contracts", "review-integration", "v2", "fixtures");
|
|
@@ -29,7 +25,7 @@ function capabilities(): Record<string, unknown> {
|
|
|
29
25
|
function unrelatedStatus(targetIdentity: string): Record<string, unknown> {
|
|
30
26
|
const status = fixture<Record<string, unknown>>("status.fixture.json");
|
|
31
27
|
status.applicability = "unrelated";
|
|
32
|
-
status.receipt
|
|
28
|
+
delete status.receipt;
|
|
33
29
|
status.action = "start";
|
|
34
30
|
status.replayability = "not_replayable";
|
|
35
31
|
status.target_identity = targetIdentity;
|
|
@@ -43,8 +39,41 @@ function unrelatedStatus(targetIdentity: string): Record<string, unknown> {
|
|
|
43
39
|
return status;
|
|
44
40
|
}
|
|
45
41
|
|
|
42
|
+
function startTransitionTokens(targetIdentity: string): readonly string[] {
|
|
43
|
+
return [
|
|
44
|
+
"--contract=gentle-ai.review-integration/v2",
|
|
45
|
+
"--cwd=/repo",
|
|
46
|
+
`--target=${targetIdentity}`,
|
|
47
|
+
"--projection=workspace",
|
|
48
|
+
"--agent=pi",
|
|
49
|
+
"--consent=relay",
|
|
50
|
+
] as const;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function executableStartStatus(targetIdentity: string): Record<string, unknown> {
|
|
54
|
+
const status = unrelatedStatus(targetIdentity);
|
|
55
|
+
const tokens = startTransitionTokens(targetIdentity);
|
|
56
|
+
status.next_transition = {
|
|
57
|
+
kind: "execute",
|
|
58
|
+
reason_code: "review_start_required",
|
|
59
|
+
execute: {
|
|
60
|
+
operation: "review.start",
|
|
61
|
+
arguments: tokens.map((token) => {
|
|
62
|
+
const separator = token.indexOf("=");
|
|
63
|
+
return { name: token.slice(2, separator), value: token.slice(separator + 1), token };
|
|
64
|
+
}),
|
|
65
|
+
preconditions: [],
|
|
66
|
+
binding: { target_identity: targetIdentity },
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
return status;
|
|
70
|
+
}
|
|
71
|
+
|
|
46
72
|
function queuedAdapter(outputs: readonly Record<string, unknown>[]): { adapter: ExecFileAdapter; calls: Array<readonly string[]> } {
|
|
47
|
-
|
|
73
|
+
// V216 no longer probes capabilities before negotiated STATUS. Keep the
|
|
74
|
+
// historical queue construction below, but discard only its obsolete probe
|
|
75
|
+
// response so every remaining entry binds the current invocation.
|
|
76
|
+
const queue = outputs.filter((body) => body.schema !== "gentle-ai.review-integration.capabilities/v2");
|
|
48
77
|
const calls: Array<readonly string[]> = [];
|
|
49
78
|
return {
|
|
50
79
|
calls,
|
|
@@ -58,52 +87,49 @@ function queuedAdapter(outputs: readonly Record<string, unknown>[]): { adapter:
|
|
|
58
87
|
}
|
|
59
88
|
|
|
60
89
|
function client(adapter: ExecFileAdapter): NativeReviewCliV216 {
|
|
61
|
-
clearNativeReviewCapabilitiesCacheForTesting();
|
|
62
90
|
return new NativeReviewCliV216(adapter, "/package/.gentle-ai/gentle-ai", 30_000, 1024 * 1024, async () => undefined, () => executableDigest);
|
|
63
91
|
}
|
|
64
92
|
|
|
65
93
|
function runtimeClient(adapter: ExecFileAdapter): RuntimeNativeReviewCliV216 {
|
|
66
|
-
clearRuntimeNativeReviewCapabilitiesCacheForTesting();
|
|
67
94
|
return new RuntimeNativeReviewCliV216(adapter, "/package/.gentle-ai/gentle-ai", 30_000, 1024 * 1024, async () => undefined, () => executableDigest);
|
|
68
95
|
}
|
|
69
96
|
|
|
70
|
-
test("negotiated ordinary START
|
|
97
|
+
test("negotiated ordinary START executes the complete STATUS-rendered relay vector and preserves the consent envelope", async () => {
|
|
71
98
|
const consent = fixture<Record<string, unknown>>("consent.fixture.json");
|
|
72
99
|
const target = String(consent.target_identity);
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
assert.equal(queue.calls.some((arguments_) => arguments_[1] === "status"), true);
|
|
100
|
+
const tokens = startTransitionTokens(target);
|
|
101
|
+
for (const createClient of [client, runtimeClient]) {
|
|
102
|
+
const queue = queuedAdapter([capabilities(), executableStartStatus(target), structuredClone(consent)]);
|
|
103
|
+
await assert.rejects(
|
|
104
|
+
() => createClient(queue.adapter).start({ cwd: "/repo" }),
|
|
105
|
+
(error: unknown) => {
|
|
106
|
+
assert.equal((error as { name?: string }).name, "NativeReviewConsentRequiredError");
|
|
107
|
+
const required = error as { consent: { raw: Record<string, unknown>; targetIdentity: string } };
|
|
108
|
+
assert.deepEqual(required.consent.raw, consent);
|
|
109
|
+
assert.equal(required.consent.targetIdentity, target);
|
|
110
|
+
return true;
|
|
111
|
+
},
|
|
112
|
+
);
|
|
113
|
+
assert.deepEqual(queue.calls, [
|
|
114
|
+
["review", "status", "--contract", "gentle-ai.review-integration/v2", "--cwd", "/repo", "--projection", "workspace", "--agent", "pi", "--next-transition"],
|
|
115
|
+
["review", "start", ...tokens],
|
|
116
|
+
]);
|
|
117
|
+
}
|
|
92
118
|
});
|
|
93
119
|
|
|
94
|
-
test("controller-prebound START target
|
|
120
|
+
test("controller-prebound START target checks STATUS once and executes its exact matching transition", async () => {
|
|
95
121
|
const consent = fixture<Record<string, unknown>>("consent.fixture.json");
|
|
96
122
|
const target = String(consent.target_identity);
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
"
|
|
105
|
-
|
|
106
|
-
|
|
123
|
+
const tokens = startTransitionTokens(target);
|
|
124
|
+
for (const createClient of [client, runtimeClient]) {
|
|
125
|
+
const queue = queuedAdapter([capabilities(), executableStartStatus(target), structuredClone(consent)]);
|
|
126
|
+
await assert.rejects(
|
|
127
|
+
() => createClient(queue.adapter).start({ cwd: "/repo", targetIdentity: target, projection: "workspace" }),
|
|
128
|
+
(error: unknown) => (error as { name?: string }).name === "NativeReviewConsentRequiredError",
|
|
129
|
+
);
|
|
130
|
+
assert.equal(queue.calls.filter((arguments_) => arguments_[1] === "status").length, 1, "a prebound target is a drift check, never a STATUS bypass");
|
|
131
|
+
assert.deepEqual(queue.calls.at(-1), ["review", "start", ...tokens]);
|
|
132
|
+
}
|
|
107
133
|
});
|
|
108
134
|
|
|
109
135
|
test("consent follow-up executes the provider-named invocation exactly once and refuses changed lineage or target bindings", async () => {
|
|
@@ -308,34 +334,89 @@ test("declined consent decodes the provider's explicit empty authority fields wi
|
|
|
308
334
|
const devbinaryFixtureRoot = join(process.cwd(), "tests", "fixtures", "devbinary");
|
|
309
335
|
const devbinaryFixture = <T = Record<string, unknown>>(name: string): T => JSON.parse(readFileSync(join(devbinaryFixtureRoot, name), "utf8")) as T;
|
|
310
336
|
|
|
311
|
-
|
|
337
|
+
function piConsent(rewriteInvocation: (invocation: string) => string = (invocation) => invocation): Record<string, unknown> {
|
|
312
338
|
const consent = devbinaryFixture<Record<string, unknown>>("consent-v3.captured.json");
|
|
313
|
-
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
assert.deepEqual(error.consent.raw, consent);
|
|
320
|
-
assert.equal(error.consent.schema, "gentle-ai.review-integration.consent/v3");
|
|
321
|
-
assert.equal(error.consent.targetIdentity, target);
|
|
322
|
-
return true;
|
|
323
|
-
},
|
|
324
|
-
);
|
|
325
|
-
assert.deepEqual(queue.calls.at(-1), [
|
|
326
|
-
"review", "start", "--contract", "gentle-ai.review-integration/v2", "--cwd", "/repo",
|
|
327
|
-
"--target", target, "--projection", "workspace", "--consent", "relay",
|
|
328
|
-
]);
|
|
339
|
+
consent.agent = "pi";
|
|
340
|
+
for (const choice of consent.choices as Array<Record<string, unknown>>) {
|
|
341
|
+
choice.invocation = rewriteInvocation(String(choice.invocation));
|
|
342
|
+
}
|
|
343
|
+
return consent;
|
|
344
|
+
}
|
|
329
345
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
346
|
+
function piBoundConsent(): Record<string, unknown> {
|
|
347
|
+
return piConsent((invocation) => invocation.replace(" --consent ", " --agent pi --consent "));
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
test("Pi START rejects a Pi-bound consent/v3 envelope when any provider choice omits or misbinds the Pi agent", async () => {
|
|
351
|
+
const target = String(devbinaryFixture<Record<string, unknown>>("consent-v3.captured.json").target_identity);
|
|
352
|
+
const tokens = startTransitionTokens(target);
|
|
353
|
+
const invalidUnselectedChoice = (replace: (invocation: string) => string): Record<string, unknown> => {
|
|
354
|
+
const consent = piBoundConsent();
|
|
355
|
+
const choice = (consent.choices as Array<Record<string, unknown>>).find((candidate) => candidate.answer === "declined")!;
|
|
356
|
+
choice.invocation = replace(String(choice.invocation));
|
|
357
|
+
return consent;
|
|
358
|
+
};
|
|
359
|
+
const cases = [
|
|
360
|
+
["missing", piConsent()],
|
|
361
|
+
["embedded", piConsent((invocation) => invocation.replace(" --consent ", " --note=--agent\\ pi --consent "))],
|
|
362
|
+
["duplicate", invalidUnselectedChoice((invocation) => invocation.replace("--agent pi", "--agent pi --agent pi"))],
|
|
363
|
+
["conflicting", invalidUnselectedChoice((invocation) => invocation.replace("--agent pi", "--agent pi --agent claude-code"))],
|
|
364
|
+
] as const;
|
|
365
|
+
for (const [label, consent] of cases) {
|
|
366
|
+
for (const createClient of [client, runtimeClient]) {
|
|
367
|
+
const queue = queuedAdapter([capabilities(), executableStartStatus(target), structuredClone(consent)]);
|
|
368
|
+
await assert.rejects(
|
|
369
|
+
() => createClient(queue.adapter).start({ cwd: "/repo" }),
|
|
370
|
+
(error: unknown) => {
|
|
371
|
+
assert.equal((error as { name?: string }).name, "NativeReviewCliError", `${label} binding must fail closed before consent presentation`);
|
|
372
|
+
assert.equal((error as { code?: string }).code, "schema-incompatible");
|
|
373
|
+
return true;
|
|
374
|
+
},
|
|
375
|
+
);
|
|
376
|
+
assert.deepEqual(queue.calls.at(-1), ["review", "start", ...tokens]);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
test("Pi START rejects a foreign consent/v3 envelope and only relays exact Pi-bound choice invocations", async () => {
|
|
382
|
+
const consent = devbinaryFixture<Record<string, unknown>>("consent-v3.captured.json");
|
|
383
|
+
const target = String(consent.target_identity);
|
|
384
|
+
const tokens = startTransitionTokens(target);
|
|
385
|
+
for (const createClient of [client, runtimeClient]) {
|
|
386
|
+
const foreignQueue = queuedAdapter([capabilities(), executableStartStatus(target), structuredClone(consent)]);
|
|
387
|
+
await assert.rejects(
|
|
388
|
+
() => createClient(foreignQueue.adapter).start({ cwd: "/repo" }),
|
|
389
|
+
(error: unknown) => {
|
|
390
|
+
assert.equal((error as { name?: string }).name, "NativeReviewCliError");
|
|
391
|
+
assert.equal((error as { code?: string }).code, "schema-incompatible");
|
|
392
|
+
return true;
|
|
393
|
+
},
|
|
394
|
+
);
|
|
395
|
+
assert.deepEqual(foreignQueue.calls.at(-1), ["review", "start", ...tokens]);
|
|
396
|
+
for (const piBound of [
|
|
397
|
+
piBoundConsent(),
|
|
398
|
+
piConsent((invocation) => invocation.replace(" --consent ", " --agent=pi --consent ")),
|
|
399
|
+
]) {
|
|
400
|
+
const piQueue = queuedAdapter([capabilities(), executableStartStatus(target), piBound]);
|
|
401
|
+
await assert.rejects(
|
|
402
|
+
() => createClient(piQueue.adapter).start({ cwd: "/repo" }),
|
|
403
|
+
(error: unknown) => {
|
|
404
|
+
assert.equal((error as { name?: string }).name, "NativeReviewConsentRequiredError");
|
|
405
|
+
const required = error as { consent: { raw: Record<string, unknown>; schema: string; targetIdentity: string; agent?: string } };
|
|
406
|
+
assert.deepEqual(required.consent.raw, piBound);
|
|
407
|
+
assert.equal(required.consent.schema, "gentle-ai.review-integration.consent/v3");
|
|
408
|
+
assert.equal(required.consent.targetIdentity, target);
|
|
409
|
+
assert.equal(required.consent.agent, "pi");
|
|
410
|
+
return true;
|
|
411
|
+
},
|
|
412
|
+
);
|
|
413
|
+
assert.deepEqual(piQueue.calls.at(-1), ["review", "start", ...tokens]);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
335
416
|
});
|
|
336
417
|
|
|
337
|
-
test("a granted consent/v3 answer
|
|
338
|
-
const decoded = (await import("../lib/review-integration-v2.ts")).decodeReviewConsentV3(
|
|
418
|
+
test("a granted Pi consent/v3 answer replays the captured exact agent-bound invocation", async () => {
|
|
419
|
+
const decoded = (await import("../lib/review-integration-v2.ts")).decodeReviewConsentV3(piBoundConsent(), "pi");
|
|
339
420
|
const cwd = decoded.choices[0].invocation.split(" --cwd ")[1]!.split(" ")[0]!;
|
|
340
421
|
const started = devbinaryFixture<Record<string, unknown>>("start-v3-consent-granted.captured.json");
|
|
341
422
|
const queue = queuedAdapter([capabilities(), started]);
|
|
@@ -346,6 +427,7 @@ test("a granted consent/v3 answer decodes the captured start/v3 result with its
|
|
|
346
427
|
assert.equal(result.start.selectedLenses.length, 4);
|
|
347
428
|
// The follow-up runs the provider-owned invocation verbatim, exactly once.
|
|
348
429
|
assert.deepEqual(queue.calls.at(-1), decoded.choices[0].invocation.split(" ").slice(1));
|
|
430
|
+
assert.equal(queue.calls.filter((arguments_) => arguments_.includes("--agent") && arguments_.includes("pi")).length, 1);
|
|
349
431
|
assert.equal(queue.calls.filter((arguments_) => arguments_.includes("granted")).length, 1);
|
|
350
432
|
|
|
351
433
|
const runtimeQueue = queuedAdapter([capabilities(), structuredClone(started)]);
|
|
@@ -353,8 +435,8 @@ test("a granted consent/v3 answer decodes the captured start/v3 result with its
|
|
|
353
435
|
assert.equal(runtimeResult.kind, "started");
|
|
354
436
|
});
|
|
355
437
|
|
|
356
|
-
test("a declined consent/v3 answer
|
|
357
|
-
const decoded = (await import("../lib/review-integration-v2.ts")).decodeReviewConsentV3(
|
|
438
|
+
test("a declined Pi consent/v3 answer replays the captured exact agent-bound invocation and creates no authority", async () => {
|
|
439
|
+
const decoded = (await import("../lib/review-integration-v2.ts")).decodeReviewConsentV3(piBoundConsent(), "pi");
|
|
358
440
|
const cwd = decoded.choices[1].invocation.split(" --cwd ")[1]!.split(" ")[0]!;
|
|
359
441
|
const declined = devbinaryFixture<Record<string, unknown>>("start-v3-consent-declined.captured.json");
|
|
360
442
|
const queue = queuedAdapter([capabilities(), declined]);
|
|
@@ -363,4 +445,6 @@ test("a declined consent/v3 answer accepts the captured declined result and crea
|
|
|
363
445
|
assert.ok(result.kind === "declined");
|
|
364
446
|
assert.equal(result.consent, "declined_this_candidate");
|
|
365
447
|
assert.equal(result.targetIdentity, decoded.targetIdentity);
|
|
448
|
+
assert.deepEqual(queue.calls.at(-1), decoded.choices[1].invocation.split(" ").slice(1));
|
|
449
|
+
assert.equal(queue.calls.filter((arguments_) => arguments_.includes("--agent") && arguments_.includes("pi")).length, 1);
|
|
366
450
|
});
|