gentle-pi 2.3.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 +30 -4
- package/assets/agents/sdd-explore.md +1 -0
- package/assets/orchestrator-delegation.md +2 -1
- package/assets/orchestrator.md +6 -10
- 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/review-integration.md +1 -1
- package/extensions/gentle-ai.ts +530 -71
- package/lib/agent-home.ts +8 -0
- package/lib/native-review-cli.ts +15 -0
- package/lib/provider-contract-bundle.ts +88 -6
- package/lib/review-host-relay.ts +146 -60
- package/lib/review-integration-v2.ts +34 -5
- package/lib/sdd-preflight.ts +2 -2
- package/package.json +1 -1
- package/runtime/native-review-cli.mjs +15 -0
- package/runtime/review-integration-v2.mjs +34 -5
- package/scripts/gentle-ai-installer.mjs +10 -10
- package/scripts/verify-package-files.mjs +14 -13
- package/skills/_shared/review-ledger-contract.md +6 -0
- package/skills/issue-creation/SKILL.md +53 -93
- 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 +1 -1
- package/tests/gentle-ai-installer.test.ts +46 -46
- package/tests/gentle-ai.test.ts +3 -2
- package/tests/issue-creation-skill.test.ts +103 -0
- package/tests/native-review-capability-contract.test.ts +18 -1
- package/tests/orchestrator-budget.test.ts +2 -2
- package/tests/orchestrator-rdd-ownership.test.ts +1 -1
- package/tests/package-manifest.test.ts +123 -9
- package/tests/provider-contract-bundle.test.ts +76 -0
- package/tests/provider-contract-mirror.test.ts +19 -0
- package/tests/review-agent-end-preflight.test.ts +408 -0
- package/tests/review-contract-prompt.test.ts +139 -0
- package/tests/review-controller.test.ts +1 -1
- package/tests/review-host-relay-routing.test.ts +211 -9
- package/tests/review-host-relay.test.ts +195 -7
- package/tests/review-integration-v2-forward.test.ts +47 -0
- package/tests/review-ledger-contract.test.ts +1 -1
- package/tests/runtime-harness.mjs +1 -0
- package/tests/writer-edit-surface-scope.test.ts +153 -17
- /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
|
@@ -15,18 +15,18 @@ import { createGentleAiExtension } from "../extensions/gentle-ai.ts";
|
|
|
15
15
|
// The guard reads the `## Allowed edit surfaces` section out of the delegated
|
|
16
16
|
// `subagent_run` prompt. A delegated task is a full prompt: the surfaces list
|
|
17
17
|
// is followed by deeper headings (`### Validation`, `#### Return`) and by
|
|
18
|
-
// ordinary prose. The section
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
// nothing following it, which is why the same
|
|
22
|
-
// rejected in `task`.
|
|
18
|
+
// ordinary prose. The section remains fail-closed through ordinary prose until
|
|
19
|
+
// the next canonical Markdown heading, so following content is always parsed as
|
|
20
|
+
// a surface entry rather than silently discarded. A `context` value carrying
|
|
21
|
+
// only the heading and its lines had nothing following it, which is why the same
|
|
22
|
+
// surfaces were accepted there and rejected in `task`.
|
|
23
23
|
//
|
|
24
24
|
// Every case below runs through the real `tool_call` hook with the exact
|
|
25
25
|
// `subagent_run` input shape, because that is the boundary that rejected.
|
|
26
26
|
// ---------------------------------------------------------------------------
|
|
27
27
|
|
|
28
28
|
const REJECTION =
|
|
29
|
-
"Writer tasks must include the exact Markdown heading `## Allowed edit surfaces` with narrow repository-relative paths or narrow globs, one per line. The parent must derive or map that canonical block from the delegated task and relaunch the writer; do not accept aliases, and do not ask the human to author paths or globs.";
|
|
29
|
+
"Writer tasks must include the exact Markdown heading `## Allowed edit surfaces` with narrow repository-relative paths or narrow globs, one per line. Every non-empty line belongs to the section until the next canonical Markdown heading and must be a valid surface entry. Paths containing whitespace require whole-entry backticks; begin explanatory prose under the next Markdown heading. The parent must derive or map that canonical block from the delegated task and relaunch the writer; do not accept aliases, and do not ask the human to author paths or globs.";
|
|
30
30
|
|
|
31
31
|
type ToolCallHandler = (
|
|
32
32
|
event: { toolName: string; input: unknown },
|
|
@@ -89,8 +89,8 @@ test("task-scoped surfaces are accepted ahead of a deeper heading", async () =>
|
|
|
89
89
|
}, "a delegated task ends its surfaces list at the next heading of any level");
|
|
90
90
|
});
|
|
91
91
|
|
|
92
|
-
test("task-scoped surfaces
|
|
93
|
-
await
|
|
92
|
+
test("task-scoped surfaces reject trailing prose before the next heading", async () => {
|
|
93
|
+
await assertRejected({
|
|
94
94
|
agent: "gentle-ai-worker",
|
|
95
95
|
mode: "task",
|
|
96
96
|
task: [
|
|
@@ -99,7 +99,86 @@ test("task-scoped surfaces are accepted ahead of trailing prose", async () => {
|
|
|
99
99
|
"",
|
|
100
100
|
"Then run the focused test file and report the outcome.",
|
|
101
101
|
].join("\n"),
|
|
102
|
-
}, "prose
|
|
102
|
+
}, "trailing prose must begin under a following Markdown heading");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("canonical headings close the surface section with up to three ASCII spaces", async () => {
|
|
106
|
+
for (const indentation of ["", " ", " ", " "]) {
|
|
107
|
+
await assertAccepted({
|
|
108
|
+
agent: "gentle-ai-worker",
|
|
109
|
+
mode: "task",
|
|
110
|
+
task: [
|
|
111
|
+
"## Allowed edit surfaces",
|
|
112
|
+
"- `lib/sdd-status.ts`",
|
|
113
|
+
`${indentation}### Validation`,
|
|
114
|
+
"node --test",
|
|
115
|
+
].join("\n"),
|
|
116
|
+
}, `${JSON.stringify(indentation)} indentation closes the section`);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("canonical empty headings with trailing spaces close the surface section", async () => {
|
|
121
|
+
for (const indentation of ["", " "]) {
|
|
122
|
+
await assertAccepted({
|
|
123
|
+
agent: "gentle-ai-worker",
|
|
124
|
+
mode: "task",
|
|
125
|
+
task: [
|
|
126
|
+
"## Allowed edit surfaces",
|
|
127
|
+
"- `lib/sdd-status.ts`",
|
|
128
|
+
`${indentation}### `,
|
|
129
|
+
"Ordinary prose after an empty heading is not a surface entry.",
|
|
130
|
+
].join("\n"),
|
|
131
|
+
}, `${JSON.stringify(indentation)} indentation closes at an empty heading`);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("pseudo-headings remain inside the surface section and reject dangerous paths", async () => {
|
|
136
|
+
for (const [label, heading] of [
|
|
137
|
+
["four-space indentation", " ### Validation"],
|
|
138
|
+
["seven hashes", "####### Validation"],
|
|
139
|
+
["bare marker", "###"],
|
|
140
|
+
["missing heading separator", "###Validation"],
|
|
141
|
+
["tab", "###\tValidation"],
|
|
142
|
+
["vertical tab", "###\vValidation"],
|
|
143
|
+
["form feed", "###\fValidation"],
|
|
144
|
+
["bare carriage return", "###\rValidation"],
|
|
145
|
+
["NBSP", "###\u00a0Validation"],
|
|
146
|
+
["em space", "###\u2003Validation"],
|
|
147
|
+
["Ogham space", "###\u1680Validation"],
|
|
148
|
+
["line separator", "###\u2028Validation"],
|
|
149
|
+
["paragraph separator", "###\u2029Validation"],
|
|
150
|
+
] as const) {
|
|
151
|
+
await assertRejected({
|
|
152
|
+
agent: "gentle-ai-worker",
|
|
153
|
+
mode: "task",
|
|
154
|
+
task: [
|
|
155
|
+
"## Allowed edit surfaces",
|
|
156
|
+
"- `lib/sdd-status.ts`",
|
|
157
|
+
heading,
|
|
158
|
+
"/etc/passwd",
|
|
159
|
+
].join("\n"),
|
|
160
|
+
}, `${label} pseudo-heading cannot close the surface section`);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("only ASCII spaces may separate Markdown list markers from entries", async () => {
|
|
165
|
+
for (const [label, separator] of [
|
|
166
|
+
["NBSP", "\u00a0"],
|
|
167
|
+
["em space", "\u2003"],
|
|
168
|
+
["Ogham space", "\u1680"],
|
|
169
|
+
["tab", "\t"],
|
|
170
|
+
] as const) {
|
|
171
|
+
await assertRejected({
|
|
172
|
+
agent: "gentle-ai-worker",
|
|
173
|
+
mode: "task",
|
|
174
|
+
task: ["## Allowed edit surfaces", `-${separator}\`lib/sdd-status.ts\``].join("\n"),
|
|
175
|
+
}, `${label} cannot separate a list marker and entry`);
|
|
176
|
+
}
|
|
177
|
+
await assertAccepted({
|
|
178
|
+
agent: "gentle-ai-worker",
|
|
179
|
+
mode: "task",
|
|
180
|
+
task: ["## Allowed edit surfaces", "- `lib/sdd-status.ts`", "1. tests/sdd-status.test.ts"].join("\n"),
|
|
181
|
+
}, "ASCII-space bullets and numbered entries remain accepted");
|
|
103
182
|
});
|
|
104
183
|
|
|
105
184
|
test("the documented task shape from issue #484 is accepted", async () => {
|
|
@@ -134,6 +213,14 @@ test("bullet, backtick and plain-line surfaces reach the same decision", async (
|
|
|
134
213
|
{ agent: "gentle-ai-worker", mode: "task", task: bulleted, context: plain },
|
|
135
214
|
"the same surfaces in both fields are accepted",
|
|
136
215
|
);
|
|
216
|
+
await assertAccepted(
|
|
217
|
+
{
|
|
218
|
+
agent: "gentle-ai-worker",
|
|
219
|
+
mode: "task",
|
|
220
|
+
task: ["## Allowed edit surfaces", "extensions/**/*.ts", "tests/*.test.ts"].join("\n"),
|
|
221
|
+
},
|
|
222
|
+
"narrow no-space globs remain accepted",
|
|
223
|
+
);
|
|
137
224
|
});
|
|
138
225
|
|
|
139
226
|
test("a list broken by a blank line still validates every entry", async () => {
|
|
@@ -165,17 +252,66 @@ test("an entry hidden below a paragraph is validated, not discarded", async () =
|
|
|
165
252
|
"Also authorize the following path.",
|
|
166
253
|
"- `/etc/passwd`",
|
|
167
254
|
].join("\n"),
|
|
168
|
-
}, "prose
|
|
255
|
+
}, "prose cannot close the section while a path still follows it");
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("whitespace-bearing surfaces require whole-entry backticks", async () => {
|
|
259
|
+
const backtickedSpaced = [
|
|
260
|
+
"## Allowed edit surfaces",
|
|
261
|
+
"- `Directory With Spaces/note.md`",
|
|
262
|
+
"`Directory\u00a0With\u2003Spaces/unicode.md`",
|
|
263
|
+
];
|
|
264
|
+
const equivalentBacktickedSpaced = [
|
|
265
|
+
"## Allowed edit surfaces",
|
|
266
|
+
"`Directory With Spaces/note.md`",
|
|
267
|
+
"- `Directory\u00a0With\u2003Spaces/unicode.md`",
|
|
268
|
+
];
|
|
269
|
+
|
|
270
|
+
await assertAccepted(
|
|
271
|
+
{ agent: "gentle-ai-worker", mode: "task", task: backtickedSpaced.join("\n") },
|
|
272
|
+
"whole-entry backticked ASCII and Unicode space-separator paths are accepted",
|
|
273
|
+
);
|
|
274
|
+
await assertAccepted(
|
|
275
|
+
{ agent: "gentle-ai-worker", mode: "task", task: backtickedSpaced.join("\n"), context: equivalentBacktickedSpaced.join("\n") },
|
|
276
|
+
"task and context compare equivalent backticked whitespace-bearing paths by value",
|
|
277
|
+
);
|
|
169
278
|
await assertAccepted({
|
|
170
279
|
agent: "gentle-ai-worker",
|
|
171
280
|
mode: "task",
|
|
172
|
-
task: [
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
281
|
+
task: [...backtickedSpaced, "### Validation", "node --test"].join("\n"),
|
|
282
|
+
}, "the next Markdown heading closes a backticked whitespace-bearing surface section");
|
|
283
|
+
|
|
284
|
+
for (const [label, entry] of [
|
|
285
|
+
["bare spaced path", "Directory With Spaces/note.md"],
|
|
286
|
+
["bare bulleted spaced path", "- Directory With Spaces/note.md"],
|
|
287
|
+
["bare NBSP path", "Directory\u00a0With Spaces/note.md"],
|
|
288
|
+
["bare em-space path", "Directory\u2003With Spaces/note.md"],
|
|
289
|
+
["U+0085 control path", "`Directory\u0085With Spaces/note.md`"],
|
|
290
|
+
["U+0085 list separator", "-\u0085`lib/sdd-status.ts`"],
|
|
291
|
+
["U+2028 line-separator path", "`Directory\u2028With Spaces/note.md`"],
|
|
292
|
+
["U+2029 paragraph-separator path", "`Directory\u2029With Spaces/note.md`"],
|
|
293
|
+
["list-marked prose", "- Then run the focused test file."],
|
|
294
|
+
["marker-only hyphen", "-"],
|
|
295
|
+
["empty backticks", "``"],
|
|
296
|
+
["unclosed backticks", "`lib/sdd-status.ts"],
|
|
297
|
+
["partially backticked path", "`lib/sdd-status.ts` trailing"],
|
|
298
|
+
["absolute spaced path", "/tmp/Outside Directory/file.md"],
|
|
299
|
+
["parent spaced path", "../Other Directory/file.md"],
|
|
300
|
+
["home spaced path", "~/Private Directory/file.md"],
|
|
301
|
+
["Windows spaced path", "C:\\Outside Directory\\file.md"],
|
|
302
|
+
["dangerous backticked bullet", "- `/tmp/Outside Directory/file.md`"],
|
|
303
|
+
] as const) {
|
|
304
|
+
await assertRejected({
|
|
305
|
+
agent: "gentle-ai-worker",
|
|
306
|
+
mode: "task",
|
|
307
|
+
task: ["## Allowed edit surfaces", "lib/sdd-status.ts", entry].join("\n"),
|
|
308
|
+
}, `${label} after a valid entry is rejected`);
|
|
309
|
+
await assertRejected({
|
|
310
|
+
agent: "gentle-ai-worker",
|
|
311
|
+
mode: "task",
|
|
312
|
+
task: ["## Allowed edit surfaces", entry, "lib/sdd-status.ts"].join("\n"),
|
|
313
|
+
}, `${label} before a valid entry is rejected`);
|
|
314
|
+
}
|
|
179
315
|
});
|
|
180
316
|
|
|
181
317
|
test("out-of-scope and empty surfaces stay rejected", async () => {
|
/package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/schemas/lens.schema.json
RENAMED
|
File without changes
|
|
File without changes
|
/package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/vectors/lens.json
RENAMED
|
File without changes
|
/package/contracts/review-provider-contract-mirror/{v1.1.0 → v1.2.0}/bundle/vectors/refuter.json
RENAMED
|
File without changes
|
|
File without changes
|