@mgiles/perk 2.3.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 +24 -6
- 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 +26 -16
- package/extension/doors/prReviewBrowser.ts +31 -23
- package/extension/doors/prReviewDynamic.ts +25 -18
- 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 +82 -7
- 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 -9
- 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 +109 -44
- package/extension/substrate/cache.ts +75 -22
- package/extension/substrate/coldDoor.ts +2 -3
- package/extension/substrate/config.ts +36 -12
- 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/toolGating.ts +170 -23
- 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/memoryAdapter.ts +14 -1
- package/extension/waves/objectiveExplorerWave.ts +120 -0
- package/extension/waves/prReviewDynamicWave.ts +291 -61
- package/extension/waves/prReviewWave.ts +58 -13
- package/extension/waves/reportWave.ts +406 -106
- package/extension/waves/reviewClassifierWave.ts +109 -0
- package/extension/waves/rpcAdapter.ts +65 -2
- package/extension/worker/readOnlySession.ts +3 -3
- package/extension/worker/worker.ts +71 -93
- package/extension/workerMain.ts +4 -6
- package/package.json +15 -5
- package/prompts/README.md +19 -0
- package/prompts/_fixtures/live.yaml +66 -65
- 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 +3 -14
- package/prompts/stages/address/preview.md +2 -13
- package/prompts/stages/audit.md +18 -0
- 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 +1 -1
- 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 -13
- package/prompts/stages/objective-plan/seed.md +5 -12
- 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 -14
- package/prompts/stages/pr-review-browser/foreign.md +6 -14
- package/prompts/stages/pr-review-dynamic.md +3 -3
- package/prompts/stages/pr-review-terminal/active.md +7 -15
- package/prompts/stages/pr-review-terminal/foreign.md +7 -15
- package/prompts/stages/pr-review.md +2 -2
- package/prompts/stages/replan.md +3 -3
- package/shared/README.md +2 -2
- package/shared/bindings.yaml +12 -0
- package/shared/contracts-history.md +11 -0
- package/shared/contracts.md +3652 -236
- 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/common/output-schemas/objective-explorer.md +0 -36
- package/prompts/common/output-schemas/review-classifier.md +0 -47
- 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
|
}
|
|
@@ -89,8 +89,9 @@ export const LINEAR_MUTATING_TOOLS: readonly string[] = [
|
|
|
89
89
|
* (≈830 schema chars — accepted, documented, test-pinned). A later `session_tree` re-apply
|
|
90
90
|
* filters over the original snapshot (which lacks the late names), so a tree navigation drops
|
|
91
91
|
* them — the pre-existing snapshot behavior, unchanged. Child-side tools (`contact_supervisor`,
|
|
92
|
-
* `structured_output`) are out of scope
|
|
93
|
-
* adopt-never-impersonates)
|
|
92
|
+
* `structured_output`) are out of scope for the STAGE census — spawned children stay
|
|
93
|
+
* stage-unscoped by design (§8.40 adopt-never-impersonates) — but they DO ride READ_ONLY_TOOLS,
|
|
94
|
+
* because the read-only gate IS inherited by adopted children (see SUBAGENT_CHILD_TOOLS).
|
|
94
95
|
*/
|
|
95
96
|
export const SUBAGENT_TOOLS: readonly string[] = [
|
|
96
97
|
"subagent",
|
|
@@ -99,6 +100,31 @@ export const SUBAGENT_TOOLS: readonly string[] = [
|
|
|
99
100
|
"intercom",
|
|
100
101
|
];
|
|
101
102
|
|
|
103
|
+
/**
|
|
104
|
+
* pi-subagents' CHILD-side engine tools. `structured_output` and `contact_supervisor` register
|
|
105
|
+
* only inside spawned child sessions (the env-keyed prompt-runtime registration — never in
|
|
106
|
+
* parents, so those names are inert in every gated parent session; `setActiveTools` ignores
|
|
107
|
+
* unknown names). `subagent_wait` is ALSO registered by the top-level parent extension
|
|
108
|
+
* (pi-subagents 0.45.1's `registerWaitTool`) — the accepted widening: a wait-only,
|
|
109
|
+
* non-repo-mutating tool active in gated parents. A gated ADOPTED child (mode inherited via the
|
|
110
|
+
* `adopt` arm, contracts.md §8.3) must keep them active:
|
|
111
|
+
* - `structured_output` is the engine-REQUIRED completion call when the launch carries an
|
|
112
|
+
* `outputSchema` — stripping it makes the child physically unable to finish and fails the
|
|
113
|
+
* run with `structuredOutputFailed`;
|
|
114
|
+
* - `contact_supervisor` is the child→parent intercom door;
|
|
115
|
+
* - `subagent_wait` is the fanout-child wait door.
|
|
116
|
+
* None mutates the repo (`structured_output` writes only the engine's capture file under
|
|
117
|
+
* `.pi-subagents/` scratch). Census decision, recorded: these names deliberately join NEITHER
|
|
118
|
+
* PERK_TOOLS nor BORROWED_TOOLS — the stage-filter universe never sees them because children
|
|
119
|
+
* are stage-unscoped by design (adopt never impersonates a stage), so gate membership is their
|
|
120
|
+
* only governance surface.
|
|
121
|
+
*/
|
|
122
|
+
export const SUBAGENT_CHILD_TOOLS: readonly string[] = [
|
|
123
|
+
"structured_output",
|
|
124
|
+
"contact_supervisor",
|
|
125
|
+
"subagent_wait",
|
|
126
|
+
];
|
|
127
|
+
|
|
102
128
|
/**
|
|
103
129
|
* @ff-labs/pi-fff's search tools. BOTH mode name-sets are enumerated (static names, inert
|
|
104
130
|
* when absent — the code_search version-tolerance precedent): warm sessions run pi-fff's
|
|
@@ -131,9 +157,12 @@ export const FFF_SEARCH_TOOLS: readonly string[] = [
|
|
|
131
157
|
* stage set over a foreign restriction. Pre-existing interplay, recorded, not re-engineered.
|
|
132
158
|
* - Zero-tool packages: @tombell/pi-diff (commands only), the footer providers, and the hunk
|
|
133
159
|
* review CLI (not a Pi package) register nothing — nothing to enumerate.
|
|
134
|
-
* - Single-governance rule:
|
|
135
|
-
*
|
|
136
|
-
* name
|
|
160
|
+
* - Single-governance rule: a name is governed ONCE — it lives in exactly one census. perk
|
|
161
|
+
* registers no same-named `ask_user_question` anymore (the first-party tool is deleted), so
|
|
162
|
+
* the name lives HERE, in the borrowed census, not in PERK_TOOLS (hygiene-tested).
|
|
163
|
+
* Registration timing nuance: @juicesharp/rpiv-ask-user-question registers the tool at load
|
|
164
|
+
* time, then a `hasUI`-keyed reconcile strips/restores it — headless sessions carry no
|
|
165
|
+
* `ask_user_question` schema at all.
|
|
137
166
|
* - @ff-labs/pi-fff (FFF_SEARCH_TOOLS): registration timing load-time (both modes); no
|
|
138
167
|
* `setFooter` (only a keyed optional-chained `setStatus`); zero bundled skills.
|
|
139
168
|
*/
|
|
@@ -143,7 +172,12 @@ export const BORROWED_TOOLS: readonly string[] = [
|
|
|
143
172
|
...LINEAR_MUTATING_TOOLS,
|
|
144
173
|
...SUBAGENT_TOOLS,
|
|
145
174
|
...FFF_SEARCH_TOOLS,
|
|
146
|
-
|
|
175
|
+
// @juicesharp/rpiv-todo (required borrow) — registers at load; its checklist overlay is
|
|
176
|
+
// `hasUI`-gated (headless-safe).
|
|
177
|
+
"todo",
|
|
178
|
+
// @juicesharp/rpiv-ask-user-question (required borrow) — registers at load; strips itself
|
|
179
|
+
// headlessly (!hasUI reconcile).
|
|
180
|
+
"ask_user_question",
|
|
147
181
|
// @plannotator/pi-extension: perk never drives its plan phases (the adapter bridges
|
|
148
182
|
// `plan_review` to its event API), so the submit tool is dead weight in stage sessions.
|
|
149
183
|
"plannotator_submit_plan",
|
|
@@ -189,23 +223,52 @@ export const READ_ONLY_TOOLS = [
|
|
|
189
223
|
// FFF local search belongs in read-only exploration (the override names find/grep are
|
|
190
224
|
// already present above; these are the additive tools-and-ui names + multi_grep).
|
|
191
225
|
...FFF_SEARCH_TOOLS,
|
|
192
|
-
// The delegation carve-in: the
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
// children are unscoped by design (§8.40
|
|
198
|
-
//
|
|
199
|
-
// agents/objective-explorer.md), but the `subagent` tool itself can spawn ad-hoc read-write
|
|
226
|
+
// The delegation carve-in: `subagent`/`wait` (+ the parent supervisor pair, which already
|
|
227
|
+
// leaks active into cold-door gated sessions via late registration — keeping warm-entered
|
|
228
|
+
// gates consistent, and letting the parent answer child `contact_supervisor` asks) stay
|
|
229
|
+
// reachable while gated for the other delegation flows (the gated objective-plan guidance now
|
|
230
|
+
// names the `explore_objective_node` tool below, not a direct spawn). ACCEPTED LENIENCY,
|
|
231
|
+
// deliberately documented: spawned children are unscoped by design (§8.40
|
|
232
|
+
// adopt-never-impersonates), and the `subagent` tool itself can spawn ad-hoc read-write
|
|
200
233
|
// children — a posture choice with NO agent-allowlist backstop, consistent with the arg-blind
|
|
201
234
|
// `curl`/`agent-browser` precedents (contracts.md §8.3).
|
|
202
235
|
...SUBAGENT_TOOLS,
|
|
236
|
+
// The explorer-wave carve-in: the gated objective-plan session's OPTIONAL explore step is the
|
|
237
|
+
// `explore_objective_node` tool — it spawns the read-only `perk.objective-explorer` child over
|
|
238
|
+
// the already-carved-in SUBAGENT tools (the draft-review-wave precedent) and writes nothing to
|
|
239
|
+
// the worktree.
|
|
240
|
+
"explore_objective_node",
|
|
241
|
+
// The child-side carve-in: gated adopt-children must keep the engine's injected tools — see
|
|
242
|
+
// SUBAGENT_CHILD_TOOLS.
|
|
243
|
+
...SUBAGENT_CHILD_TOOLS,
|
|
244
|
+
// The draft-review-door carve-in: plan-authoring sessions run GATED, so the
|
|
245
|
+
// /plan-review-browser companions must be reachable while read-only. `push_annotations` only
|
|
246
|
+
// POSTs findings to the door-primed local plannotator server (no worktree writes — the
|
|
247
|
+
// fetch_content cache-write precedent class); the wave pair spawns the read-only
|
|
248
|
+
// `perk.draft-reviewer` over the already-carved-in SUBAGENT_TOOLS/SUBAGENT_CHILD_TOOLS.
|
|
249
|
+
"push_annotations",
|
|
250
|
+
"start_draft_review_wave",
|
|
251
|
+
"collect_draft_review_wave",
|
|
252
|
+
// The audit-wave carve-in: the seeded `perk-dev audit judge` session runs GATED (the `audit`
|
|
253
|
+
// stage is read-only), so `run_audit_wave` must be reachable while read-only. Its one write
|
|
254
|
+
// (`<bundle>/verdicts.json`) is structurally bound to the cold door's workflow-state
|
|
255
|
+
// `audit_bundle_dir` — the tool takes NO parameters, so no caller-supplied path exists and a
|
|
256
|
+
// gated session cannot aim the writer anywhere (contracts.md §8.50).
|
|
257
|
+
"run_audit_wave",
|
|
258
|
+
// The harvest-wave carve-in: the seeded learn-harvest session runs GATED (the read-only
|
|
259
|
+
// objective-author borrow), so `run_harvest_wave` must be reachable while read-only. Its
|
|
260
|
+
// manifest read is structurally bound to the session's claimed run-scoped scratch path (the
|
|
261
|
+
// `manifest_path` param is verified against it and any other path refused — the
|
|
262
|
+
// `run_audit_wave` no-aimable-writer posture, read-side), it spawns the read-only
|
|
263
|
+
// `perk.harvest-analyst` over the already-carved-in SUBAGENT_TOOLS/SUBAGENT_CHILD_TOOLS, and
|
|
264
|
+
// it writes nothing to the worktree (contracts.md §8.48).
|
|
265
|
+
"run_harvest_wave",
|
|
203
266
|
];
|
|
204
267
|
|
|
205
268
|
/**
|
|
206
269
|
* Every tool perk itself registers (contracts.md §8.40). Name-keyed: `setActiveTools` ignores
|
|
207
|
-
* unknown names, so
|
|
208
|
-
*
|
|
270
|
+
* unknown names, so an absent tool is inert (e.g. a borrowed census name whose package stripped
|
|
271
|
+
* or never registered it — `ask_user_question` in a headless session — has nothing to enable).
|
|
209
272
|
* Stage scoping filters the scoped universe `PERK_TOOLS ∪ BORROWED_TOOLS` — builtins and
|
|
210
273
|
* un-enumerated foreign names pass through untouched (fail-open).
|
|
211
274
|
*/
|
|
@@ -222,16 +285,33 @@ export const PERK_TOOLS: readonly string[] = [
|
|
|
222
285
|
"gist_save",
|
|
223
286
|
"learn",
|
|
224
287
|
"run_learn_wave",
|
|
225
|
-
"
|
|
288
|
+
"run_audit_wave",
|
|
289
|
+
"run_harvest_wave",
|
|
226
290
|
"land",
|
|
227
291
|
"post_pr_review",
|
|
228
292
|
"ready",
|
|
229
|
-
"
|
|
293
|
+
"classify_review_feedback",
|
|
294
|
+
"finalize_address",
|
|
295
|
+
"explore_objective_node",
|
|
230
296
|
"run_pr_review_wave",
|
|
231
297
|
"run_pr_review_dynamic_wave",
|
|
232
298
|
"submit_pr_review",
|
|
299
|
+
"start_review_wave",
|
|
300
|
+
"collect_review_wave",
|
|
301
|
+
"push_annotations",
|
|
302
|
+
"start_draft_review_wave",
|
|
303
|
+
"collect_draft_review_wave",
|
|
233
304
|
"run_ci",
|
|
234
305
|
"submit",
|
|
306
|
+
// The stacked-delivery warm surface (contracts.md §8.51/§8.56): read + control tools over
|
|
307
|
+
// the cold `objective stack` workers. Never in READ_ONLY_TOOLS — sync/adopt/recover/land
|
|
308
|
+
// mutate published branches and PRs; the gated posture is the driving commands' soft
|
|
309
|
+
// refusal.
|
|
310
|
+
"objective_stack_status",
|
|
311
|
+
"objective_stack_sync",
|
|
312
|
+
"objective_stack_adopt",
|
|
313
|
+
"objective_stack_recover",
|
|
314
|
+
"objective_stack_land",
|
|
235
315
|
];
|
|
236
316
|
|
|
237
317
|
/**
|
|
@@ -253,7 +333,7 @@ const RESEARCH_TOOLS: readonly string[] = [
|
|
|
253
333
|
* the post-land reconcile drive — `/land` auto-drives `/objective-reconcile` in-session, whose
|
|
254
334
|
* guidance names the reconcile trio, so the trio must be active in every worktree stage). The
|
|
255
335
|
* headless worker also REQUIRES the model-invoked `submit` (implement) /
|
|
256
|
-
* `
|
|
336
|
+
* `finalize_address` (address) to reach its completion bar. Borrowed additions: delegation
|
|
257
337
|
* (SUBAGENT_TOOLS — the `/pr-review`/`/address`/`/submit`-conflict/`/learn` orchestration flows)
|
|
258
338
|
* and `todo` (the foreign checklist overlay the implement-progress discipline rides) are
|
|
259
339
|
* worktree-family only.
|
|
@@ -266,17 +346,34 @@ const WORKTREE_STAGE_TOOLS: readonly string[] = [
|
|
|
266
346
|
"land",
|
|
267
347
|
"learn",
|
|
268
348
|
"run_learn_wave",
|
|
269
|
-
"
|
|
349
|
+
"classify_review_feedback",
|
|
350
|
+
"finalize_address",
|
|
270
351
|
"post_pr_review",
|
|
271
352
|
"run_pr_review_wave",
|
|
272
353
|
"run_pr_review_dynamic_wave",
|
|
273
354
|
"submit_pr_review",
|
|
355
|
+
// The human review doors' companion tools (/pr-review-terminal, /pr-review-browser): the
|
|
356
|
+
// review-wave pair + the door-primed annotation push. The plan-stage widening landed via the
|
|
357
|
+
// draft-review door (/plan-review-browser): the plan-family stage lists carry the draft-wave
|
|
358
|
+
// pair + push_annotations.
|
|
359
|
+
"start_review_wave",
|
|
360
|
+
"collect_review_wave",
|
|
361
|
+
"push_annotations",
|
|
274
362
|
// The reconcile trio: `/land` auto-drives the objective-reconcile pass inside the CURRENT
|
|
275
363
|
// worktree session (driveReconcileAfterLand), and the manual `/objective-reconcile` gesture is
|
|
276
364
|
// registered globally — both inject guidance naming these three tools.
|
|
277
365
|
"reconcile_objective",
|
|
278
366
|
"add_objective_node",
|
|
279
367
|
"objective_node",
|
|
368
|
+
// The stacked-delivery quintet: `/objective-sync`/`/objective-recover`/`/objective-land`
|
|
369
|
+
// drive worktree sessions (post-amend sync from implement/address; recovery and the atomic
|
|
370
|
+
// landing from anywhere in the PR loop), so their guidance-named tools must be active
|
|
371
|
+
// across the whole family.
|
|
372
|
+
"objective_stack_status",
|
|
373
|
+
"objective_stack_sync",
|
|
374
|
+
"objective_stack_adopt",
|
|
375
|
+
"objective_stack_recover",
|
|
376
|
+
"objective_stack_land",
|
|
280
377
|
...RESEARCH_TOOLS,
|
|
281
378
|
...SUBAGENT_TOOLS,
|
|
282
379
|
"todo",
|
|
@@ -286,7 +383,8 @@ const WORKTREE_STAGE_TOOLS: readonly string[] = [
|
|
|
286
383
|
* Per-stage active perk tools for gate-OFF sessions (contracts.md §8.40). Keys = the registry
|
|
287
384
|
* stage ids; an unknown/absent stage id is fail-open (no filtering — version-skew safety).
|
|
288
385
|
* Rationale pins:
|
|
289
|
-
* - `ask_user_question` is universal (every stage list carries it)
|
|
386
|
+
* - `ask_user_question` is universal (every stage list carries it); the name is BORROWED now
|
|
387
|
+
* (the @juicesharp questionnaire, via BORROWED_TOOLS — headless sessions carry no schema).
|
|
290
388
|
* - `plan`/`save` cover the plan-family stage borrowers (`plan from`/`plan replan`/
|
|
291
389
|
* `learn docs`/`learn code` borrow `plan`; `skills create/refine` borrow `save`).
|
|
292
390
|
* - `objective-author`/`objective-save` cover `objective replan` + `objective author --from`.
|
|
@@ -296,6 +394,12 @@ const WORKTREE_STAGE_TOOLS: readonly string[] = [
|
|
|
296
394
|
* three objective stages (the post-save `/objective-reconcile` gesture) AND the worktree
|
|
297
395
|
* family (the post-land `driveReconcileAfterLand` drive + the manual `/objective-reconcile`
|
|
298
396
|
* gesture — its guidance names all three).
|
|
397
|
+
* - the draft-review companions (`start_draft_review_wave`/`collect_draft_review_wave`/
|
|
398
|
+
* `push_annotations`) also ride the two objective stages (§8.23's
|
|
399
|
+
* `/objective-review-browser` — gate-OFF coverage: after `objectiveApprovalSave` exits the
|
|
400
|
+
* gate mid-flow, late collects/pushes must not dead-end), and `plan_review` rides them
|
|
401
|
+
* because the door guidance names it (in both objective stages it routes to the objective
|
|
402
|
+
* review arm; the drive-coverage guard forces both the moment the guidance names them).
|
|
299
403
|
*/
|
|
300
404
|
export const STAGE_TOOLS: Readonly<Record<string, readonly string[]>> = {
|
|
301
405
|
"gist-author": ["ask_user_question", "gist_draft", "gist_save", ...RESEARCH_TOOLS],
|
|
@@ -307,6 +411,13 @@ export const STAGE_TOOLS: Readonly<Record<string, readonly string[]>> = {
|
|
|
307
411
|
"reconcile_objective",
|
|
308
412
|
"add_objective_node",
|
|
309
413
|
"objective_node",
|
|
414
|
+
// The /objective-review-browser companions (gate-OFF coverage: after objectiveApprovalSave
|
|
415
|
+
// exits the gate mid-flow, late collects/pushes must not dead-end) + plan_review (the door
|
|
416
|
+
// guidance names it; it routes to the objective review arm here).
|
|
417
|
+
"start_draft_review_wave",
|
|
418
|
+
"collect_draft_review_wave",
|
|
419
|
+
"push_annotations",
|
|
420
|
+
"plan_review",
|
|
310
421
|
...RESEARCH_TOOLS,
|
|
311
422
|
],
|
|
312
423
|
"objective-save": [
|
|
@@ -316,6 +427,11 @@ export const STAGE_TOOLS: Readonly<Record<string, readonly string[]>> = {
|
|
|
316
427
|
"reconcile_objective",
|
|
317
428
|
"add_objective_node",
|
|
318
429
|
"objective_node",
|
|
430
|
+
// The /objective-review-browser companions + plan_review (see the objective-author note).
|
|
431
|
+
"start_draft_review_wave",
|
|
432
|
+
"collect_draft_review_wave",
|
|
433
|
+
"push_annotations",
|
|
434
|
+
"plan_review",
|
|
319
435
|
...RESEARCH_TOOLS,
|
|
320
436
|
],
|
|
321
437
|
"objective-plan": [
|
|
@@ -324,17 +440,48 @@ export const STAGE_TOOLS: Readonly<Record<string, readonly string[]>> = {
|
|
|
324
440
|
"plan_review",
|
|
325
441
|
"plan_save",
|
|
326
442
|
"objective_node",
|
|
443
|
+
"explore_objective_node",
|
|
327
444
|
"reconcile_objective",
|
|
328
445
|
"add_objective_node",
|
|
446
|
+
// The /plan-review-browser companions (gate-OFF coverage: after approvalSave exits the gate
|
|
447
|
+
// mid-flow, late collects/pushes must not dead-end — the drive-coverage guard forces this
|
|
448
|
+
// the moment the guidance names them).
|
|
449
|
+
"start_draft_review_wave",
|
|
450
|
+
"collect_draft_review_wave",
|
|
451
|
+
"push_annotations",
|
|
452
|
+
...RESEARCH_TOOLS,
|
|
453
|
+
],
|
|
454
|
+
plan: [
|
|
455
|
+
"ask_user_question",
|
|
456
|
+
"plan_draft",
|
|
457
|
+
"plan_review",
|
|
458
|
+
"plan_save",
|
|
459
|
+
// The /plan-review-browser companions (see the objective-plan note).
|
|
460
|
+
"start_draft_review_wave",
|
|
461
|
+
"collect_draft_review_wave",
|
|
462
|
+
"push_annotations",
|
|
463
|
+
...RESEARCH_TOOLS,
|
|
464
|
+
],
|
|
465
|
+
save: [
|
|
466
|
+
"ask_user_question",
|
|
467
|
+
"plan_draft",
|
|
468
|
+
"plan_review",
|
|
469
|
+
"plan_save",
|
|
470
|
+
// The /plan-review-browser companions (see the objective-plan note).
|
|
471
|
+
"start_draft_review_wave",
|
|
472
|
+
"collect_draft_review_wave",
|
|
473
|
+
"push_annotations",
|
|
329
474
|
...RESEARCH_TOOLS,
|
|
330
475
|
],
|
|
331
|
-
plan: ["ask_user_question", "plan_draft", "plan_review", "plan_save", ...RESEARCH_TOOLS],
|
|
332
|
-
save: ["ask_user_question", "plan_draft", "plan_review", "plan_save", ...RESEARCH_TOOLS],
|
|
333
476
|
implement: WORKTREE_STAGE_TOOLS,
|
|
334
477
|
submit: WORKTREE_STAGE_TOOLS,
|
|
335
478
|
address: WORKTREE_STAGE_TOOLS,
|
|
336
479
|
land: WORKTREE_STAGE_TOOLS,
|
|
337
480
|
learn: WORKTREE_STAGE_TOOLS,
|
|
481
|
+
// The dev-only session-audit orchestrator (`perk-dev audit judge`). The session runs GATED
|
|
482
|
+
// (read-only mode), where this list is inert; it exists for the keys≡registry pin and the
|
|
483
|
+
// defensive gate-off arm.
|
|
484
|
+
audit: ["ask_user_question", "run_audit_wave", ...RESEARCH_TOOLS],
|
|
338
485
|
};
|
|
339
486
|
|
|
340
487
|
/** The read-only marker / custom-message type injected into context while active. */
|