@mgiles/perk 2.2.0 → 3.0.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 +1 -1
- package/extension/adapters/planAdapterPlannotator.ts +132 -91
- package/extension/adapters/planAdapterTombell.ts +5 -3
- package/extension/doors/address.ts +235 -37
- package/extension/doors/annotationPush.ts +924 -0
- package/extension/doors/auditWaveTools.ts +352 -0
- package/extension/doors/ciExecutor.ts +220 -55
- package/extension/doors/draftReviewWaveTools.ts +384 -0
- package/extension/doors/harvestWaveTools.ts +279 -0
- package/extension/doors/land.ts +1 -0
- package/extension/doors/learn.ts +238 -24
- package/extension/doors/learnFactory.ts +33 -7
- package/extension/doors/lifecycleGates.ts +1 -1
- package/extension/doors/objectiveReviewBrowser.ts +514 -0
- package/extension/doors/objectiveStack.ts +1143 -0
- package/extension/doors/planReviewBrowser.ts +453 -0
- package/extension/doors/plannotatorHandoff.ts +120 -49
- package/extension/doors/prReview.ts +203 -22
- package/extension/doors/prReviewBrowser.ts +31 -23
- package/extension/doors/prReviewDynamic.ts +256 -0
- package/extension/doors/prReviewTerminal.ts +6 -11
- package/extension/doors/reviewWaveTools.ts +374 -0
- package/extension/doors/selfcheck.ts +1 -1
- package/extension/doors/submit.ts +86 -10
- package/extension/factories/gistAuthor.ts +4 -4
- package/extension/factories/gistDraft.ts +1 -1
- package/extension/factories/gistSave.ts +2 -2
- package/extension/factories/objective.ts +7 -7
- package/extension/factories/objectiveAuthor.ts +5 -4
- package/extension/factories/objectiveDraft.ts +61 -6
- package/extension/factories/objectivePlan.ts +163 -8
- package/extension/factories/objectiveSave.ts +16 -1
- package/extension/factories/planMode.ts +5 -3
- package/extension/factories/planReview.ts +131 -36
- package/extension/factories/planTitle.ts +27 -9
- package/extension/hunkFeedback/inbox.ts +389 -0
- package/extension/hunkFeedback/perkFeedback.ts +364 -0
- package/extension/hunkFeedback/receiver.ts +296 -0
- package/extension/hunkFeedback/store.ts +533 -0
- package/extension/index.ts +116 -44
- package/extension/substrate/cache.ts +75 -22
- package/extension/substrate/coldDoor.ts +2 -3
- package/extension/substrate/config.ts +42 -14
- package/extension/substrate/git.ts +10 -0
- package/extension/substrate/providers.ts +4 -33
- package/extension/substrate/resources.ts +11 -0
- package/extension/substrate/result.ts +13 -5
- package/extension/substrate/sessionData.ts +3 -3
- package/extension/substrate/sessionPointers.ts +3 -3
- package/extension/substrate/structuredOutput.ts +78 -19
- package/extension/substrate/terminalLaunch.ts +1 -1
- package/extension/substrate/toolGating.ts +206 -26
- package/extension/substrate/workflowState.ts +6 -0
- package/extension/surfaces/footerProvider.ts +6 -6
- package/extension/surfaces/surfaces.ts +98 -282
- package/extension/vendor/btw/btw.ts +57 -12
- package/extension/waves/adversarialReviewWave.ts +164 -0
- package/extension/waves/auditWave.ts +312 -0
- package/extension/waves/draftReviewWave.ts +189 -0
- package/extension/waves/harvestWave.ts +399 -0
- package/extension/waves/learnWave.ts +155 -0
- package/extension/waves/memoryAdapter.ts +139 -0
- package/extension/waves/objectiveExplorerWave.ts +120 -0
- package/extension/waves/prReviewDynamicWave.ts +696 -0
- package/extension/waves/prReviewWave.ts +274 -0
- package/extension/waves/reportWave.ts +749 -0
- package/extension/waves/reviewClassifierWave.ts +109 -0
- package/extension/waves/rpcAdapter.ts +264 -0
- package/extension/worker/readOnlySession.ts +3 -3
- package/extension/worker/worker.ts +71 -93
- package/extension/workerMain.ts +4 -6
- package/package.json +20 -5
- package/prompts/README.md +19 -0
- package/prompts/_fixtures/live.yaml +71 -72
- package/prompts/common/resume-advisory.md +1 -0
- package/prompts/contexts/adapters/plannotator-gist.md +13 -0
- package/prompts/contexts/adapters/plannotator-objective.md +1 -2
- package/prompts/contexts/adapters/plannotator-plan.md +1 -2
- package/prompts/contexts/gist-authoring.md +6 -20
- package/prompts/contexts/objective-authoring.md +6 -18
- package/prompts/stages/address/action.md +4 -4
- package/prompts/stages/address/preview.md +3 -3
- package/prompts/stages/audit.md +18 -0
- package/prompts/stages/conflict-resolution.md +1 -1
- package/prompts/stages/gist-author/seed.md +4 -4
- package/prompts/stages/gist-save.md +1 -1
- package/prompts/stages/implement.md +3 -1
- package/prompts/stages/learn-code.md +1 -1
- package/prompts/stages/learn-docs.md +2 -2
- package/prompts/stages/learn-harvest.md +15 -0
- package/prompts/stages/learn-orchestrate.md +7 -5
- package/prompts/stages/objective-author/adopt.md +5 -3
- package/prompts/stages/objective-author/file.md +5 -4
- package/prompts/stages/objective-author/seed.md +5 -4
- package/prompts/stages/objective-land.md +7 -0
- package/prompts/stages/objective-plan/guidance.md +2 -2
- package/prompts/stages/objective-plan/seed.md +5 -1
- package/prompts/stages/objective-recover.md +8 -0
- package/prompts/stages/objective-replan.md +4 -2
- package/prompts/stages/objective-review-browser.md +9 -0
- package/prompts/stages/objective-sync.md +6 -0
- package/prompts/stages/plan-from/adopt.md +2 -2
- package/prompts/stages/plan-from/file.md +2 -2
- package/prompts/stages/plan-review-browser.md +9 -0
- package/prompts/stages/pr-review-browser/active.md +6 -6
- package/prompts/stages/pr-review-browser/foreign.md +6 -6
- package/prompts/stages/pr-review-dynamic.md +7 -0
- package/prompts/stages/pr-review-terminal/active.md +7 -7
- package/prompts/stages/pr-review-terminal/foreign.md +7 -7
- package/prompts/stages/pr-review.md +7 -6
- package/prompts/stages/replan.md +3 -3
- package/shared/README.md +2 -2
- package/shared/bindings.yaml +15 -0
- package/shared/contracts-history.md +11 -0
- package/shared/contracts.md +3713 -227
- package/shared/providers.yaml +27 -60
- package/shared/registry.yaml +32 -8
- package/shared/schemas/outputs/objective-doctor.schema.json +449 -0
- package/shared/schemas/outputs/objective-stack-land.schema.json +732 -0
- package/shared/schemas/outputs/objective-stack-recover.schema.json +397 -0
- package/shared/schemas/outputs/objective-stack-status.schema.json +577 -0
- package/shared/schemas/outputs/objective-stack-sync.schema.json +184 -0
- package/shared/schemas/outputs/plan-save.schema.json +12 -0
- package/shared/schemas/outputs/pr-submit.schema.json +177 -1
- package/extension/adapters/todoAdapterJuicesharp.ts +0 -102
- package/extension/checkpoints/checkpoints.ts +0 -550
- package/extension/checkpoints/planSteps.ts +0 -108
- package/extension/doors/askUser.ts +0 -198
- package/prompts/contexts/adapters/juicesharp-todo.md +0 -7
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Twin of perk/substrate/providers.py: both planes parse the SAME bundled file (no codegen). This is the
|
|
4
4
|
// THIRD parsed cross-plane contract (after registry.yaml and bindings.yaml). It is the SUPPORTED
|
|
5
|
-
// SET — the catalog of plan/
|
|
5
|
+
// SET — the catalog of plan/footer/web providers perk knows how to wire —
|
|
6
6
|
// distinct from the per-repo SELECTION (the flat `[providers]` table in .perk/config.toml).
|
|
7
7
|
//
|
|
8
8
|
// The Python CLI is the authoritative validator (perk/substrate/providers.py); this side does a thin
|
|
9
9
|
// structural parse only — no deep content validation here. `resolveProviders` is CONSUMED by
|
|
10
|
-
// `planMode` (plan-seam registration-time vacating when a foreign plan provider is selected)
|
|
11
|
-
// todo-seam runtime deferral is likewise live.
|
|
10
|
+
// `planMode` (plan-seam registration-time vacating when a foreign plan provider is selected).
|
|
12
11
|
|
|
13
12
|
import { readFileSync } from "node:fs";
|
|
14
13
|
import { join } from "node:path";
|
|
@@ -24,12 +23,10 @@ export interface Provider {
|
|
|
24
23
|
packageFilter?: Record<string, unknown>;
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
export const PROVIDER_SEAMS = ["plan", "
|
|
26
|
+
export const PROVIDER_SEAMS = ["plan", "footer", "web"] as const;
|
|
28
27
|
|
|
29
28
|
/** The bundled reference provider ids (the behavior-preserving no-config defaults per seam). */
|
|
30
29
|
export const PERK_PLAN_PROVIDER_ID = "perk-plan";
|
|
31
|
-
export const PERK_CHECKPOINTS_PROVIDER_ID = "perk-checkpoints";
|
|
32
|
-
export const PERK_ASK_USER_PROVIDER_ID = "perk-ask-user";
|
|
33
30
|
export const PERK_FOOTER_PROVIDER_ID = "perk-footer";
|
|
34
31
|
|
|
35
32
|
/** The foreign `@tombell/pi-plan` plan-provider id. */
|
|
@@ -38,12 +35,6 @@ export const TOMBELL_PLAN_PROVIDER_ID = "tombell-plan";
|
|
|
38
35
|
/** The foreign `@plannotator/pi-extension` plan-provider id (augment-posture adapter selection check). */
|
|
39
36
|
export const PLANNOTATOR_PLAN_PROVIDER_ID = "plannotator-plan";
|
|
40
37
|
|
|
41
|
-
/** The foreign `@juicesharp/rpiv-todo` todo-provider id. */
|
|
42
|
-
export const JUICESHARP_TODO_PROVIDER_ID = "juicesharp-todo";
|
|
43
|
-
|
|
44
|
-
/** The foreign `@juicesharp/rpiv-ask-user-question` askuser-provider id (vacate-only interface seam). */
|
|
45
|
-
export const JUICESHARP_ASK_USER_PROVIDER_ID = "juicesharp-ask-user";
|
|
46
|
-
|
|
47
38
|
/** The foreign `pi-powerline-footer` footer-provider id (vacate-only interface seam). */
|
|
48
39
|
export const POWERLINE_FOOTER_PROVIDER_ID = "powerline-footer";
|
|
49
40
|
|
|
@@ -106,8 +97,6 @@ export function loadProviders(): Provider[] {
|
|
|
106
97
|
*/
|
|
107
98
|
export interface ResolvedProviders {
|
|
108
99
|
plan: Provider;
|
|
109
|
-
todo: Provider;
|
|
110
|
-
askuser: Provider;
|
|
111
100
|
footer: Provider;
|
|
112
101
|
web: Provider;
|
|
113
102
|
issues: string[];
|
|
@@ -124,20 +113,6 @@ export interface ResolvedProviders {
|
|
|
124
113
|
*/
|
|
125
114
|
const REFERENCE_FALLBACKS: Record<(typeof PROVIDER_SEAMS)[number], Provider> = {
|
|
126
115
|
plan: { id: PERK_PLAN_PROVIDER_ID, seam: "plan", package: null, adapter: null, default: true },
|
|
127
|
-
todo: {
|
|
128
|
-
id: PERK_CHECKPOINTS_PROVIDER_ID,
|
|
129
|
-
seam: "todo",
|
|
130
|
-
package: null,
|
|
131
|
-
adapter: null,
|
|
132
|
-
default: true,
|
|
133
|
-
},
|
|
134
|
-
askuser: {
|
|
135
|
-
id: PERK_ASK_USER_PROVIDER_ID,
|
|
136
|
-
seam: "askuser",
|
|
137
|
-
package: null,
|
|
138
|
-
adapter: null,
|
|
139
|
-
default: true,
|
|
140
|
-
},
|
|
141
116
|
footer: {
|
|
142
117
|
id: PERK_FOOTER_PROVIDER_ID,
|
|
143
118
|
seam: "footer",
|
|
@@ -183,8 +158,6 @@ function byId(set: Provider[]): Map<string, Provider> {
|
|
|
183
158
|
export function resolveProviders(
|
|
184
159
|
selection: {
|
|
185
160
|
plan?: string;
|
|
186
|
-
todo?: string;
|
|
187
|
-
askuser?: string;
|
|
188
161
|
footer?: string;
|
|
189
162
|
web?: string;
|
|
190
163
|
},
|
|
@@ -205,7 +178,7 @@ export function resolveProviders(
|
|
|
205
178
|
return def;
|
|
206
179
|
};
|
|
207
180
|
|
|
208
|
-
const resolveSeam = (seam: "plan" | "
|
|
181
|
+
const resolveSeam = (seam: "plan" | "footer" | "web"): Provider => {
|
|
209
182
|
const selected = selection[seam];
|
|
210
183
|
if (selected == null) return requireDefault(seam);
|
|
211
184
|
const provider = ids.get(selected);
|
|
@@ -222,8 +195,6 @@ export function resolveProviders(
|
|
|
222
195
|
|
|
223
196
|
return {
|
|
224
197
|
plan: resolveSeam("plan"),
|
|
225
|
-
todo: resolveSeam("todo"),
|
|
226
|
-
askuser: resolveSeam("askuser"),
|
|
227
198
|
footer: resolveSeam("footer"),
|
|
228
199
|
web: resolveSeam("web"),
|
|
229
200
|
issues,
|
|
@@ -39,3 +39,14 @@ export function perkVersion(): string {
|
|
|
39
39
|
return "0.0.0";
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The vintage-stamp filter over a perkVersion() value: the version, or undefined for the
|
|
45
|
+
* "0.0.0" failure sentinel. A `perk_version` stamp claims an EXACT session vintage
|
|
46
|
+
* (contracts §8.3 — the session-audit read side treats it as the exact basis), so the
|
|
47
|
+
* self-confessed-unknown sentinel must not masquerade as one; omitting the stamp leaves the
|
|
48
|
+
* session on the honest timestamp-estimate arm.
|
|
49
|
+
*/
|
|
50
|
+
export function versionStamp(version: string): string | undefined {
|
|
51
|
+
return version === "0.0.0" ? undefined : version;
|
|
52
|
+
}
|
|
@@ -55,18 +55,26 @@ export function ok<D extends object>(
|
|
|
55
55
|
* `failFor(ctx, scope, label)` when the content label differs from the report scope). Each call
|
|
56
56
|
* reports loudly (`report(ctx, scope, "error", message, { alsoLog: true })`) and returns the
|
|
57
57
|
* canonical soft failure: content `"<label> failed: <message>"`, details
|
|
58
|
-
* `{ ok: false, error: message, error_type: errorType }`, no `terminate`.
|
|
58
|
+
* `{ ok: false, error: message, error_type: errorType }`, no `terminate`. `X` is the
|
|
59
|
+
* `FailDetails` extras hook: `failFor<X>(…)` lets a call attach module-specific fail details
|
|
60
|
+
* (spread AFTER `error`/`error_type`) — e.g. the learn wave's attempt receipts. Extras stay
|
|
61
|
+
* optional, so existing extras-free call sites are unchanged.
|
|
59
62
|
*/
|
|
60
|
-
export function failFor(
|
|
63
|
+
export function failFor<X extends object = Record<never, never>>(
|
|
61
64
|
target: ReportTarget,
|
|
62
65
|
scope: string,
|
|
63
66
|
label: string = scope,
|
|
64
|
-
): (message: string, errorType: string) => FailResult {
|
|
65
|
-
return (message, errorType) => {
|
|
67
|
+
): (message: string, errorType: string, extras?: X) => FailResult<X> {
|
|
68
|
+
return (message, errorType, extras) => {
|
|
66
69
|
report(target, scope, "error", message, { alsoLog: true });
|
|
67
70
|
return {
|
|
68
71
|
content: [{ type: "text", text: `${label} failed: ${message}` }],
|
|
69
|
-
details: {
|
|
72
|
+
details: {
|
|
73
|
+
ok: false,
|
|
74
|
+
error: message,
|
|
75
|
+
error_type: errorType,
|
|
76
|
+
...(extras ?? {}),
|
|
77
|
+
} as FailDetails<X>,
|
|
70
78
|
};
|
|
71
79
|
};
|
|
72
80
|
}
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
// loadable under `node --test`; accepts a minimal structural ctx (`BranchSource & { cwd }`).
|
|
34
34
|
|
|
35
35
|
import { createHash } from "node:crypto";
|
|
36
|
-
import { existsSync, mkdirSync, readFileSync
|
|
36
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
37
37
|
import { join, relative } from "node:path";
|
|
38
38
|
import type { ReportTarget } from "../surfaces/report.ts";
|
|
39
|
-
import { sessionDataDir } from "./cache.ts";
|
|
39
|
+
import { atomicWriteFileSync, sessionDataDir } from "./cache.ts";
|
|
40
40
|
import {
|
|
41
41
|
appendWorkflowState,
|
|
42
42
|
type BranchSource,
|
|
@@ -115,7 +115,7 @@ export function writeSessionData(
|
|
|
115
115
|
if (dir === null) return null;
|
|
116
116
|
const path = join(dir, name);
|
|
117
117
|
try {
|
|
118
|
-
|
|
118
|
+
atomicWriteFileSync(path, content);
|
|
119
119
|
} catch (error) {
|
|
120
120
|
console.error(`perk: warning: could not write session data ${path}: ${error}`);
|
|
121
121
|
return null;
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
// wedge the save/launch/drive it rides on. Node builtins + cache.ts + git.ts only (loads under
|
|
14
14
|
// `node --test`).
|
|
15
15
|
|
|
16
|
-
import { existsSync, mkdirSync, readFileSync
|
|
16
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
17
17
|
import { basename, join } from "node:path";
|
|
18
|
-
import { runScratchDir } from "./cache.ts";
|
|
18
|
+
import { atomicWriteFileSync, runScratchDir } from "./cache.ts";
|
|
19
19
|
import { mainCheckoutRoot } from "./git.ts";
|
|
20
20
|
|
|
21
21
|
export const SESSION_POINTERS_FILE = "session-pointers.json";
|
|
@@ -121,7 +121,7 @@ export function recordSessionPointer(
|
|
|
121
121
|
record[klass][site] = pointer;
|
|
122
122
|
const dir = runScratchDir(root, runId);
|
|
123
123
|
mkdirSync(dir, { recursive: true });
|
|
124
|
-
|
|
124
|
+
atomicWriteFileSync(sessionPointersPath(root, runId), serialize(record));
|
|
125
125
|
return true;
|
|
126
126
|
} catch (error) {
|
|
127
127
|
console.error(`perk: warning: could not record session pointer (${klass}.${site}): ${error}`);
|
|
@@ -2,16 +2,19 @@
|
|
|
2
2
|
//
|
|
3
3
|
// pi-ai has no dedicated JSON-mode; structured output is done via tool calling. This module wraps
|
|
4
4
|
// that idiom into two pure, dependency-light, NEVER-throwing helpers:
|
|
5
|
-
// - `resolveModelAuth(ctx)` reuses the session's configured + authenticated model (the
|
|
6
|
-
// `ModelRegistry.getApiKeyAndHeaders` path
|
|
7
|
-
//
|
|
8
|
-
//
|
|
5
|
+
// - `resolveModelAuth(ctx)` reuses the session's configured + authenticated model (the compat
|
|
6
|
+
// `ModelRegistry.getApiKeyAndHeaders` path — the fallback for hosts without registry
|
|
7
|
+
// dispatch), and
|
|
8
|
+
// - `completeStructured(opts)` builds a single-tool `Context`, completes it (via the injected
|
|
9
|
+
// registry `dispatch` when the host provides one, else the compat `complete`), and validates
|
|
10
|
+
// the returned tool-call arguments against a TypeBox schema.
|
|
9
11
|
// Both report failure via a soft `{ ok:false, error }` outcome — no throws ever reach the caller, so
|
|
10
12
|
// every consumer can stay fail-safe with a deterministic fallback. The first consumer is
|
|
11
13
|
// `extension/factories/planTitle.ts` (LLM-generated plan-issue titles).
|
|
12
14
|
|
|
13
15
|
import {
|
|
14
16
|
type Api,
|
|
17
|
+
type AssistantMessage,
|
|
15
18
|
type Context,
|
|
16
19
|
type Model,
|
|
17
20
|
type Static,
|
|
@@ -24,21 +27,54 @@ import {
|
|
|
24
27
|
// keeps the types. Pi's extension loader aliases both the root and /compat to the compat entry.
|
|
25
28
|
import { complete } from "@earendil-works/pi-ai/compat";
|
|
26
29
|
|
|
27
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Registry dispatch (mirrors pi ≥ 0.84's `ModelRegistry.complete`): pi owns final request
|
|
32
|
+
* assembly — resolved auth, nullable headers, credential-resolved `baseUrl`, provider `env` —
|
|
33
|
+
* end to end. Feature-detect it (`typeof … === "function"`); absent on older hosts.
|
|
34
|
+
*/
|
|
35
|
+
export type ModelDispatch = (
|
|
36
|
+
model: Model<Api>,
|
|
37
|
+
context: Context,
|
|
38
|
+
options: { signal?: AbortSignal; timeoutMs?: number },
|
|
39
|
+
) => Promise<AssistantMessage>;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Structurally-minimal slice of `ExtensionContext` needed to reuse the session's model + auth.
|
|
43
|
+
* Header values mirror pi-ai's `ProviderHeaders` (`string | null` — null is a header-deletion
|
|
44
|
+
* marker); `baseUrl`/`env` mirror pi 0.84's `ResolvedRequestAuth`.
|
|
45
|
+
*/
|
|
28
46
|
export interface ModelAuthContext {
|
|
29
47
|
model: Model<Api> | undefined;
|
|
30
48
|
modelRegistry: {
|
|
31
|
-
getApiKeyAndHeaders(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
49
|
+
getApiKeyAndHeaders(model: Model<Api>): Promise<
|
|
50
|
+
| {
|
|
51
|
+
ok: true;
|
|
52
|
+
apiKey?: string;
|
|
53
|
+
headers?: Record<string, string | null>;
|
|
54
|
+
baseUrl?: string;
|
|
55
|
+
env?: Record<string, string>;
|
|
56
|
+
}
|
|
57
|
+
| { ok: false; error: string }
|
|
35
58
|
>;
|
|
59
|
+
/** pi ≥ 0.84 registry dispatch; absent on older hosts (feature-detected, never assumed). */
|
|
60
|
+
complete?(
|
|
61
|
+
model: Model<Api>,
|
|
62
|
+
context: Context,
|
|
63
|
+
options?: { signal?: AbortSignal; timeoutMs?: number },
|
|
64
|
+
): Promise<AssistantMessage>;
|
|
36
65
|
};
|
|
37
66
|
}
|
|
38
67
|
|
|
39
68
|
/** Resolved model + auth, or a soft failure (no model / unresolved auth). */
|
|
40
69
|
export type ResolvedModelAuth =
|
|
41
|
-
| {
|
|
70
|
+
| {
|
|
71
|
+
ok: true;
|
|
72
|
+
model: Model<Api>;
|
|
73
|
+
apiKey?: string;
|
|
74
|
+
headers?: Record<string, string | null>;
|
|
75
|
+
baseUrl?: string;
|
|
76
|
+
env?: Record<string, string>;
|
|
77
|
+
}
|
|
42
78
|
| { ok: false; error: string };
|
|
43
79
|
|
|
44
80
|
/** The generic structured-output outcome — soft success/failure, never a throw. */
|
|
@@ -60,7 +96,14 @@ export async function resolveModelAuth(ctx: ModelAuthContext): Promise<ResolvedM
|
|
|
60
96
|
try {
|
|
61
97
|
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
|
|
62
98
|
if (!auth.ok) return { ok: false, error: auth.error };
|
|
63
|
-
return {
|
|
99
|
+
return {
|
|
100
|
+
ok: true,
|
|
101
|
+
model,
|
|
102
|
+
apiKey: auth.apiKey,
|
|
103
|
+
headers: auth.headers,
|
|
104
|
+
baseUrl: auth.baseUrl,
|
|
105
|
+
env: auth.env,
|
|
106
|
+
};
|
|
64
107
|
} catch (err) {
|
|
65
108
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
66
109
|
}
|
|
@@ -79,8 +122,17 @@ export interface CompleteStructuredOptions<S extends TSchema> {
|
|
|
79
122
|
instruction: string;
|
|
80
123
|
/** The payload (e.g. the document to summarize/classify). */
|
|
81
124
|
input: string;
|
|
125
|
+
/**
|
|
126
|
+
* Registry dispatch (pi ≥ 0.84). When present it REPLACES the compat `complete` +
|
|
127
|
+
* apiKey/headers assembly below — pi owns auth end to end, including the credential-resolved
|
|
128
|
+
* `baseUrl` the fallback path cannot carry.
|
|
129
|
+
*/
|
|
130
|
+
dispatch?: ModelDispatch;
|
|
82
131
|
apiKey?: string;
|
|
83
|
-
|
|
132
|
+
/** `string | null` mirrors pi-ai's `ProviderHeaders` — a null value deletes a default header. */
|
|
133
|
+
headers?: Record<string, string | null>;
|
|
134
|
+
/** Provider-scoped environment values (pi 0.84 `ResolvedRequestAuth.env`), fallback path only. */
|
|
135
|
+
env?: Record<string, string>;
|
|
84
136
|
signal?: AbortSignal;
|
|
85
137
|
timeoutMs?: number;
|
|
86
138
|
}
|
|
@@ -114,14 +166,21 @@ export async function completeStructured<S extends TSchema>(
|
|
|
114
166
|
tools: [tool],
|
|
115
167
|
};
|
|
116
168
|
|
|
117
|
-
|
|
169
|
+
// Primary: registry dispatch (pi ≥ 0.84 owns auth/headers/baseUrl/env). Fallback: the compat
|
|
170
|
+
// `complete` with caller-resolved auth — it forwards apiKey/headers/env but has NO `baseUrl`
|
|
171
|
+
// option, so a credential-resolved endpoint is silently dropped on old hosts (the fallback's
|
|
172
|
+
// named, pre-existing limitation; the dispatch path is the fix).
|
|
173
|
+
let msg: AssistantMessage;
|
|
118
174
|
try {
|
|
119
|
-
msg =
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
175
|
+
msg = opts.dispatch
|
|
176
|
+
? await opts.dispatch(opts.model, context, { signal: opts.signal, timeoutMs: opts.timeoutMs })
|
|
177
|
+
: await complete(opts.model, context, {
|
|
178
|
+
apiKey: opts.apiKey,
|
|
179
|
+
headers: opts.headers,
|
|
180
|
+
env: opts.env,
|
|
181
|
+
signal: opts.signal,
|
|
182
|
+
timeoutMs: opts.timeoutMs,
|
|
183
|
+
});
|
|
125
184
|
} catch (err) {
|
|
126
185
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
127
186
|
}
|