gentle-pi 3.5.0 → 3.6.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 +50 -41
- package/assets/orchestrator-delegation.md +2 -0
- package/bin/gentle-shell.mjs +1068 -16
- package/docs/gentle-shell.md +1 -1
- package/docs/readme-reference.md +108 -19
- package/extensions/gentle-ai.ts +27 -48
- package/lib/gentle-shell-launcher.ts +734 -37
- package/lib/inprocess-reviewer.ts +54 -6
- package/lib/native-review-cli.ts +12 -0
- package/package.json +1 -1
- package/runtime/gentle-shell-launcher.mjs +732 -35
- package/runtime/native-review-cli.mjs +12 -0
- package/scripts/gentle-ai-installer.mjs +10 -10
- package/scripts/install-tui-mode-setting.mjs +21 -2
- package/scripts/verify-package-files.mjs +2 -2
- package/tests/agents-rpc-publisher.test.ts +66 -0
- package/tests/gentle-agents.test.ts +46 -0
- package/tests/gentle-ai-binary.test.ts +1 -1
- package/tests/gentle-ai-installer.test.ts +54 -49
- package/tests/gentle-ai.test.ts +147 -2
- package/tests/gentle-shell-bin.test.ts +2389 -6
- package/tests/gentle-shell-launcher.test.ts +1053 -10
- package/tests/inprocess-reviewer.test.ts +179 -0
- package/tests/install-tui-mode-setting.test.ts +22 -4
- package/tests/native-review-capability-contract.test.ts +28 -1
- package/tests/odd-runtime-delegation-gate.test.ts +18 -197
- package/tests/package-manifest.test.ts +6 -6
- package/tests/runtime-harness.mjs +1 -2
- package/lib/odd-runtime-delegation-gate.ts +0 -88
|
@@ -9,7 +9,11 @@
|
|
|
9
9
|
// the caller's "provider/id" selection through pi's live model registry,
|
|
10
10
|
// authenticates through the registry's own resolver, and completes exactly
|
|
11
11
|
// one frozen prompt as a single user message — no systemPrompt, no tools, no
|
|
12
|
-
// session,
|
|
12
|
+
// session, and none of pi's tool/skill/prompt extension hooks. Extension
|
|
13
|
+
// *providers* are the opposite: they are explicitly in scope. Reading the
|
|
14
|
+
// earlier "no extension hooks" wording as "no extension providers" is what
|
|
15
|
+
// produced gentle-shell#1304, where every extension-registered model was
|
|
16
|
+
// unreachable, so the distinction is stated here rather than implied.
|
|
13
17
|
//
|
|
14
18
|
// Every I/O seam is injected (`registry`, `complete`, `now`), so this module
|
|
15
19
|
// runs under tests with no network and no pi process. gentle-pi#311 P2 wires
|
|
@@ -22,18 +26,32 @@ import { SAFE_MODEL_ID_PATTERN } from "./model-routing-authority.ts";
|
|
|
22
26
|
|
|
23
27
|
// ---------------------------------------------------------------------------
|
|
24
28
|
// Registry seam — a structural subset of pi's live ModelRegistry
|
|
25
|
-
// (@earendil-works/pi-coding-agent core/model-registry.ts).
|
|
26
|
-
// `getApiKeyAndHeaders` are
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
+
// (@earendil-works/pi-coding-agent core/model-registry.ts). `find` and
|
|
30
|
+
// `getApiKeyAndHeaders` are required. `getProvider` is optional and selects
|
|
31
|
+
// the dispatch path: when present, the completion goes through the composed
|
|
32
|
+
// provider it returns (which is what reaches extension-registered providers);
|
|
33
|
+
// when absent, it falls back to `deps.complete`, so a test double with no
|
|
34
|
+
// composition layer still satisfies the seam. The real registry's resolved
|
|
35
|
+
// auth carries extra optional fields (`baseUrl`, `env`) that this narrower
|
|
36
|
+
// shape simply ignores.
|
|
29
37
|
// ---------------------------------------------------------------------------
|
|
30
38
|
|
|
39
|
+
/**
|
|
40
|
+
* The single method this module needs from a resolved provider. pi's
|
|
41
|
+
* `Provider.streamSimple` returns an `AssistantMessageEventStream`; only its
|
|
42
|
+
* `result()` is consumed here, so the seam asks for nothing more.
|
|
43
|
+
*/
|
|
44
|
+
export interface InProcessReviewerProvider {
|
|
45
|
+
streamSimple(model: Model<Api>, context: Context, options?: SimpleStreamOptions): { result(): Promise<AssistantMessage> };
|
|
46
|
+
}
|
|
47
|
+
|
|
31
48
|
export interface InProcessReviewerRegistry {
|
|
32
49
|
find(provider: string, modelId: string): Model<Api> | undefined;
|
|
33
50
|
getApiKeyAndHeaders(model: Model<Api>): Promise<
|
|
34
51
|
| { readonly ok: true; readonly apiKey?: string; readonly headers?: ProviderHeaders }
|
|
35
52
|
| { readonly ok: false; readonly error: string }
|
|
36
53
|
>;
|
|
54
|
+
getProvider?(provider: string): InProcessReviewerProvider | undefined;
|
|
37
55
|
}
|
|
38
56
|
|
|
39
57
|
export const INPROCESS_REVIEWER_FAILURE = {
|
|
@@ -180,6 +198,33 @@ export async function runInProcessReviewer(request: InProcessReviewerRequest, de
|
|
|
180
198
|
);
|
|
181
199
|
}
|
|
182
200
|
|
|
201
|
+
// pi's composed provider is the only layer that honors extension-registered
|
|
202
|
+
// providers (core/provider-composer.ts: `extension.streamSimple` when
|
|
203
|
+
// `model.api === extension.api`). pi-ai's `completeSimple` resolves against
|
|
204
|
+
// its own builtin-only registry and throws for any extension api, so a seam
|
|
205
|
+
// that carries `getProvider` is always preferred; a seam without it at all
|
|
206
|
+
// (a test double with no composition layer) still uses `deps.complete`.
|
|
207
|
+
// A registry that has `getProvider` yet owns no provider for a model its
|
|
208
|
+
// own `find` just resolved is incoherent: refuse, never route past it.
|
|
209
|
+
//
|
|
210
|
+
// The lookup deliberately uses `parsed.provider` — the key `find` was
|
|
211
|
+
// called with — and not `model.provider`, which is a field of the object
|
|
212
|
+
// `find` returned. In pi's registry both reads hit one provider map
|
|
213
|
+
// (core/model-runtime.ts `getProvider`/`getModel` -> pi-ai models.ts, where
|
|
214
|
+
// `getModels(provider)` returns [] for an unknown id), so a resolved model
|
|
215
|
+
// always has a provider under its own key and this branch is unreachable by
|
|
216
|
+
// construction. Keying on the returned field instead would make that
|
|
217
|
+
// guarantee depend on every provider's `getModels()` echoing its own id,
|
|
218
|
+
// which a native or OAuth-modified extension provider is free not to do.
|
|
219
|
+
const provider = deps.registry.getProvider?.(parsed.provider);
|
|
220
|
+
if (deps.registry.getProvider !== undefined && provider === undefined) {
|
|
221
|
+
return refuse(
|
|
222
|
+
INPROCESS_REVIEWER_FAILURE.MODEL_NOT_FOUND,
|
|
223
|
+
`The model registry resolved ${JSON.stringify(request.selection)} for ${request.routingKey} but owns no provider for ${JSON.stringify(parsed.provider)}; reassign ${request.routingKey} to a model whose provider the interactive pi can actually dispatch.`,
|
|
224
|
+
{ provider: parsed.provider, api: model.api },
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
183
228
|
const auth = await deps.registry.getApiKeyAndHeaders(model);
|
|
184
229
|
// Negation narrowing (`!auth.ok`) does not eliminate the `ok: true` arm of
|
|
185
230
|
// this discriminated union under this project's `strict: false` tsconfig;
|
|
@@ -245,9 +290,12 @@ export async function runInProcessReviewer(request: InProcessReviewerRequest, de
|
|
|
245
290
|
return undefined;
|
|
246
291
|
};
|
|
247
292
|
|
|
293
|
+
// `SimpleStreamOptions` is identical on both paths; only the return shape
|
|
294
|
+
// differs (an event stream versus a promise), hence `.result()` — which is
|
|
295
|
+
// exactly what pi-ai's own compat layer does with the same stream.
|
|
248
296
|
let assistant: AssistantMessage;
|
|
249
297
|
try {
|
|
250
|
-
assistant = await deps.complete(model, context, options);
|
|
298
|
+
assistant = provider === undefined ? await deps.complete(model, context, options) : await provider.streamSimple(model, context, options).result();
|
|
251
299
|
} catch (error) {
|
|
252
300
|
return abortRefusal() ?? refuse(INPROCESS_REVIEWER_FAILURE.PROVIDER_FAILED, `Reviewer completion failed for ${request.routingKey}: ${sanitizeErrorExcerpt(error)}`);
|
|
253
301
|
}
|
package/lib/native-review-cli.ts
CHANGED
|
@@ -1032,6 +1032,18 @@ export const NATIVE_CLI_CONTRACTS = Object.freeze({
|
|
|
1032
1032
|
// closed START/STATUS fields this row negotiates. riskEvidence and hint
|
|
1033
1033
|
// remain dark; neither is proven to reach Pi's negotiated START path.
|
|
1034
1034
|
"3.5.0": Object.freeze({ start: true, finalize: true, validate: true, bindSdd: true, status: true, inventory: true, reclaim: true, recover: true, abandon: true, quarantineLegacy: true, reconcileAuthority: true, repairLegacyAlias: true, mode: true, riskEvidence: false, hint: false, delivery: true }),
|
|
1035
|
+
// v3.6.0 repeats 3.5.0: the published provider contract bundle is
|
|
1036
|
+
// byte-identical at 1.2.0, both binaries advertise capabilities/v2.6
|
|
1037
|
+
// with only build-identity differences, and no review-integration schema
|
|
1038
|
+
// changed between the v3.5.0 and v3.6.0 tags. riskEvidence and hint
|
|
1039
|
+
// remain dark; neither is proven to reach Pi's negotiated START path.
|
|
1040
|
+
"3.6.0": Object.freeze({ start: true, finalize: true, validate: true, bindSdd: true, status: true, inventory: true, reclaim: true, recover: true, abandon: true, quarantineLegacy: true, reconcileAuthority: true, repairLegacyAlias: true, mode: true, riskEvidence: false, hint: false, delivery: true }),
|
|
1041
|
+
// v3.6.1 repeats 3.6.0: published provider-contract archives are byte-identical
|
|
1042
|
+
// (SHA-256 547b68e172cc87aa297309d61624e5fc2c24d407a494b53eeb5a2b053904352c).
|
|
1043
|
+
// The published v3.6.1 binary advertises capabilities/v2.6, and the tag diff
|
|
1044
|
+
// changes no review-integration schema or capability source. riskEvidence and
|
|
1045
|
+
// hint remain dark because neither is proven in Pi's negotiated START path.
|
|
1046
|
+
"3.6.1": Object.freeze({ start: true, finalize: true, validate: true, bindSdd: true, status: true, inventory: true, reclaim: true, recover: true, abandon: true, quarantineLegacy: true, reconcileAuthority: true, repairLegacyAlias: true, mode: true, riskEvidence: false, hint: false, delivery: true }),
|
|
1035
1047
|
});
|
|
1036
1048
|
|
|
1037
1049
|
export interface NativeReviewProcessDiagnostics {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gentle-pi",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.0",
|
|
4
4
|
"description": "Turn Pi into el Gentleman: a senior-architect development harness with SDD/OpenSpec, subagents, strict TDD evidence, review guardrails, and skill discovery.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|