@yagni-app/code-staging 0.3.0-staging.1082.1 → 0.3.0-staging.1088.1
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/dist/extension/askAdvisorTool.d.ts +17 -2
- package/dist/extension/askAdvisorTool.js +112 -14
- package/dist/extension/branding.d.ts +11 -1
- package/dist/extension/branding.js +45 -7
- package/dist/extension/index.d.ts +4 -2
- package/dist/extension/index.js +20 -6
- package/dist/extension/subagents.d.ts +7 -1
- package/dist/extension/subagents.js +60 -5
- package/dist/extension/ultra.d.ts +23 -0
- package/dist/extension/ultra.js +61 -0
- package/package.json +2 -2
|
@@ -26,10 +26,12 @@
|
|
|
26
26
|
* this module is the I/O.
|
|
27
27
|
*/
|
|
28
28
|
import type { ExtensionAPI, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
29
|
+
import { type Component } from "@earendil-works/pi-tui";
|
|
29
30
|
import { Type } from "typebox";
|
|
30
31
|
import { type AdvisorLimits, type AdvisorStateHandle } from "./advisor.js";
|
|
31
32
|
import { runStage as defaultRunStage } from "./pipeline/runner.js";
|
|
32
33
|
import type { PipelineStage } from "./pipeline/types.js";
|
|
34
|
+
import { type RenderTheme, type SubagentTaskProgress } from "./subagentRender.js";
|
|
33
35
|
/**
|
|
34
36
|
* Read-only recon plus grounding. Mirrors the `plan` stage's allowlist for the
|
|
35
37
|
* reason documented there: enough tools to navigate instead of flailing on a
|
|
@@ -66,10 +68,18 @@ export declare function buildConsultBrief(params: {
|
|
|
66
68
|
* persona, and that is the advisor's own.
|
|
67
69
|
*/
|
|
68
70
|
export declare function advisorStage(): PipelineStage;
|
|
69
|
-
|
|
71
|
+
/** The ask_advisor result details: consult accounting plus the live progress
|
|
72
|
+
* record the subagent renderers paint (one task, the consult itself). */
|
|
73
|
+
export interface AdvisorToolDetails {
|
|
70
74
|
consults: number;
|
|
71
75
|
cost: number;
|
|
72
|
-
|
|
76
|
+
tasks?: SubagentTaskProgress[];
|
|
77
|
+
}
|
|
78
|
+
/** Title painted the moment the ask_advisor call streams in. */
|
|
79
|
+
export declare function renderAdvisorCall(args: {
|
|
80
|
+
question?: string;
|
|
81
|
+
} | undefined, theme: RenderTheme, _context: unknown): Component;
|
|
82
|
+
export declare function makeAskAdvisorTool(opts: MakeAskAdvisorToolOptions): ToolDefinition<typeof parameters, AdvisorToolDetails>;
|
|
73
83
|
/**
|
|
74
84
|
* `/advise <question>` — the manual escalation lever.
|
|
75
85
|
*
|
|
@@ -77,6 +87,11 @@ export declare function makeAskAdvisorTool(opts: MakeAskAdvisorToolOptions): Too
|
|
|
77
87
|
* consult draws on the same cap rather than opening a side channel around it.
|
|
78
88
|
* The advice is sent into the conversation (like /go-compare's report) so the
|
|
79
89
|
* driver sees it in context and can act on it.
|
|
90
|
+
*
|
|
91
|
+
* A command has no tool row for pi to render, so the live view is an
|
|
92
|
+
* aboveEditor widget painted from the tool's partial updates: the same
|
|
93
|
+
* two-line `agent — task` / `↳ current tool` status the subagent tool shows,
|
|
94
|
+
* driven by a spinner ticker so it visibly moves between child events.
|
|
80
95
|
*/
|
|
81
96
|
export declare function registerAdviseCommand(pi: ExtensionAPI, tool: ReturnType<typeof makeAskAdvisorTool>): void;
|
|
82
97
|
export {};
|
|
@@ -25,9 +25,12 @@
|
|
|
25
25
|
* The gate, the cap and the spend ceiling live in `advisor.ts` and are pure;
|
|
26
26
|
* this module is the I/O.
|
|
27
27
|
*/
|
|
28
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
28
29
|
import { Type } from "typebox";
|
|
29
30
|
import { ADVISOR_MODEL_TIER, DEFAULT_ADVISOR_LIMITS, decideConsult, formatConsultCost, } from "./advisor.js";
|
|
31
|
+
import { SPINNER_FRAMES } from "./pipeline/activityFeed.js";
|
|
30
32
|
import { runStage as defaultRunStage } from "./pipeline/runner.js";
|
|
33
|
+
import { applyChildEvent, finalizeTask, formatWorkingMessage, newTaskProgress, progressSummaryText, receiptLine, renderSubagentResult, runningLines, } from "./subagentRender.js";
|
|
31
34
|
/**
|
|
32
35
|
* Read-only recon plus grounding. Mirrors the `plan` stage's allowlist for the
|
|
33
36
|
* reason documented there: enough tools to navigate instead of flailing on a
|
|
@@ -71,6 +74,24 @@ export function advisorStage() {
|
|
|
71
74
|
taskTemplate: "{ticket}",
|
|
72
75
|
};
|
|
73
76
|
}
|
|
77
|
+
/** Spinner cadence for the advisor's live views; matches subagentRender. */
|
|
78
|
+
const SPINNER_TICK_MS = 120;
|
|
79
|
+
/** Question preview width on the call title line. */
|
|
80
|
+
const QUESTION_PREVIEW_MAX = 128;
|
|
81
|
+
function clip(text, max) {
|
|
82
|
+
const collapsed = text.replace(/\s+/g, " ").trim();
|
|
83
|
+
if (collapsed.length <= max)
|
|
84
|
+
return collapsed;
|
|
85
|
+
return `${collapsed.slice(0, max - 1)}…`;
|
|
86
|
+
}
|
|
87
|
+
/** Title painted the moment the ask_advisor call streams in. */
|
|
88
|
+
export function renderAdvisorCall(args, theme, _context) {
|
|
89
|
+
const title = theme.fg("toolTitle", theme.bold("advisor"));
|
|
90
|
+
let text = `${title} ${theme.fg("accent", "peak-tier consult")}`;
|
|
91
|
+
if (args?.question)
|
|
92
|
+
text += `\n ${theme.fg("dim", clip(args.question, QUESTION_PREVIEW_MAX))}`;
|
|
93
|
+
return new Text(text, 0, 0);
|
|
94
|
+
}
|
|
74
95
|
export function makeAskAdvisorTool(opts) {
|
|
75
96
|
const limits = opts.limits ?? DEFAULT_ADVISOR_LIMITS;
|
|
76
97
|
const runStage = opts.runStage ?? defaultRunStage;
|
|
@@ -95,6 +116,8 @@ export function makeAskAdvisorTool(opts) {
|
|
|
95
116
|
"The advice comes back as plain text: act on it, and call record_decision when it settles a product-intent call so the next agent inherits it.",
|
|
96
117
|
],
|
|
97
118
|
parameters,
|
|
119
|
+
renderCall: renderAdvisorCall,
|
|
120
|
+
renderResult: renderSubagentResult,
|
|
98
121
|
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
99
122
|
// Read the LIVE session model: pi's picker can change it after this tool
|
|
100
123
|
// was registered, in both directions.
|
|
@@ -109,19 +132,51 @@ export function makeAskAdvisorTool(opts) {
|
|
|
109
132
|
details: { consults: opts.state.read().consults, cost: 0 },
|
|
110
133
|
};
|
|
111
134
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
135
|
+
// Live progress, exactly the subagent tool's shape: the folded record
|
|
136
|
+
// rides every partial update's `details.tasks` (painted by
|
|
137
|
+
// renderSubagentResult), the plain-text summary rides `content` for
|
|
138
|
+
// headless consumers, and the harness "Working…" line mirrors it.
|
|
139
|
+
const progress = newTaskProgress("advisor", params.question, Date.now());
|
|
140
|
+
const ui = ctx?.hasUI ? ctx.ui : undefined;
|
|
141
|
+
let lastWorking;
|
|
142
|
+
const emit = () => {
|
|
143
|
+
const now = Date.now();
|
|
144
|
+
onUpdate?.({
|
|
145
|
+
content: [{ type: "text", text: progressSummaryText([progress], now) }],
|
|
146
|
+
details: {
|
|
147
|
+
consults: opts.state.read().consults,
|
|
148
|
+
cost: progress.usage.cost,
|
|
149
|
+
tasks: [{ ...progress, actions: [...progress.actions], usage: { ...progress.usage } }],
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
const working = formatWorkingMessage([progress], now);
|
|
153
|
+
if (ui && working !== lastWorking) {
|
|
154
|
+
lastWorking = working;
|
|
155
|
+
ui.setWorkingMessage?.(working);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
emit();
|
|
159
|
+
let result;
|
|
160
|
+
try {
|
|
161
|
+
result = await runStage(advisorStage(), { ticket: buildConsultBrief(params) }, {
|
|
162
|
+
cwd: ctx?.cwd ?? process.cwd(),
|
|
163
|
+
...(signal ? { signal } : {}),
|
|
164
|
+
// YAG-471: attribute the consult's completions to the advisor, not
|
|
165
|
+
// the "plan" stage id advisorStage() borrows (see its docblock).
|
|
166
|
+
callerLabel: "advisor",
|
|
167
|
+
onEvent: (ev) => {
|
|
168
|
+
if (applyChildEvent(progress, ev))
|
|
169
|
+
emit();
|
|
170
|
+
},
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
// Restore the default "Working…" text whether we resolved or threw.
|
|
175
|
+
ui?.setWorkingMessage?.();
|
|
176
|
+
}
|
|
123
177
|
const cost = result.usage?.cost ?? 0;
|
|
124
178
|
const state = opts.state.record(cost);
|
|
179
|
+
finalizeTask(progress, result, Date.now());
|
|
125
180
|
if (result.exitCode !== 0 && !result.finalOutput.trim()) {
|
|
126
181
|
// Fail honestly rather than returning an empty recommendation. The
|
|
127
182
|
// consult still counts: it spawned, and it may well have spent.
|
|
@@ -134,7 +189,7 @@ export function makeAskAdvisorTool(opts) {
|
|
|
134
189
|
`Decide this one yourself.`,
|
|
135
190
|
},
|
|
136
191
|
],
|
|
137
|
-
details: { consults: state.consults, cost },
|
|
192
|
+
details: { consults: state.consults, cost, tasks: [progress] },
|
|
138
193
|
};
|
|
139
194
|
}
|
|
140
195
|
return {
|
|
@@ -144,11 +199,13 @@ export function makeAskAdvisorTool(opts) {
|
|
|
144
199
|
text: `${result.finalOutput.trim()}\n\n${formatConsultCost({ cost, consults: state.consults, limits })}`,
|
|
145
200
|
},
|
|
146
201
|
],
|
|
147
|
-
details: { consults: state.consults, cost },
|
|
202
|
+
details: { consults: state.consults, cost, tasks: [progress] },
|
|
148
203
|
};
|
|
149
204
|
},
|
|
150
205
|
};
|
|
151
206
|
}
|
|
207
|
+
/** The widget key the /advise live panel paints under. */
|
|
208
|
+
const ADVISE_WIDGET_KEY = "yagni-advise";
|
|
152
209
|
/**
|
|
153
210
|
* `/advise <question>` — the manual escalation lever.
|
|
154
211
|
*
|
|
@@ -156,6 +213,11 @@ export function makeAskAdvisorTool(opts) {
|
|
|
156
213
|
* consult draws on the same cap rather than opening a side channel around it.
|
|
157
214
|
* The advice is sent into the conversation (like /go-compare's report) so the
|
|
158
215
|
* driver sees it in context and can act on it.
|
|
216
|
+
*
|
|
217
|
+
* A command has no tool row for pi to render, so the live view is an
|
|
218
|
+
* aboveEditor widget painted from the tool's partial updates: the same
|
|
219
|
+
* two-line `agent — task` / `↳ current tool` status the subagent tool shows,
|
|
220
|
+
* driven by a spinner ticker so it visibly moves between child events.
|
|
159
221
|
*/
|
|
160
222
|
export function registerAdviseCommand(pi, tool) {
|
|
161
223
|
pi.registerCommand("advise", {
|
|
@@ -175,8 +237,41 @@ export function registerAdviseCommand(pi, tool) {
|
|
|
175
237
|
return;
|
|
176
238
|
}
|
|
177
239
|
notify("Consulting the peak-tier advisor. This reads the repo, so it takes a moment.", "info");
|
|
240
|
+
// Live panel state: the newest folded progress record from the tool's
|
|
241
|
+
// partial updates, repainted on a spinner tick until the consult ends.
|
|
242
|
+
let progress;
|
|
243
|
+
let paintTimer;
|
|
244
|
+
const theme = ctx.hasUI ? ctx.ui.theme : undefined;
|
|
245
|
+
const paint = () => {
|
|
246
|
+
if (!ctx.hasUI || !theme || !progress)
|
|
247
|
+
return;
|
|
248
|
+
const now = Date.now();
|
|
249
|
+
const lines = progress.status === "running"
|
|
250
|
+
? runningLines(progress, theme, now, SPINNER_FRAMES[Math.floor(now / SPINNER_TICK_MS) % SPINNER_FRAMES.length])
|
|
251
|
+
: [receiptLine(progress, theme)];
|
|
252
|
+
ctx.ui.setWidget?.(ADVISE_WIDGET_KEY, lines, { placement: "aboveEditor" });
|
|
253
|
+
};
|
|
254
|
+
const onUpdate = (update) => {
|
|
255
|
+
const task = update.details?.tasks?.[0];
|
|
256
|
+
if (!task)
|
|
257
|
+
return;
|
|
258
|
+
progress = task;
|
|
259
|
+
if (ctx.hasUI && theme && !paintTimer) {
|
|
260
|
+
paintTimer = setInterval(paint, SPINNER_TICK_MS);
|
|
261
|
+
paintTimer.unref?.();
|
|
262
|
+
}
|
|
263
|
+
paint();
|
|
264
|
+
};
|
|
265
|
+
const clearPanel = () => {
|
|
266
|
+
if (paintTimer) {
|
|
267
|
+
clearInterval(paintTimer);
|
|
268
|
+
paintTimer = undefined;
|
|
269
|
+
}
|
|
270
|
+
if (ctx.hasUI)
|
|
271
|
+
ctx.ui.setWidget?.(ADVISE_WIDGET_KEY, undefined);
|
|
272
|
+
};
|
|
178
273
|
try {
|
|
179
|
-
const out = await tool.execute("advise", { question }, ctx.signal,
|
|
274
|
+
const out = await tool.execute("advise", { question }, ctx.signal, onUpdate, ctx);
|
|
180
275
|
const text = out.content
|
|
181
276
|
.map((c) => c.text ?? "")
|
|
182
277
|
.join("\n")
|
|
@@ -188,6 +283,9 @@ export function registerAdviseCommand(pi, tool) {
|
|
|
188
283
|
notify(`/advise failed: ${message}`, "error");
|
|
189
284
|
await pi.sendUserMessage(`/advise failed: ${message}`);
|
|
190
285
|
}
|
|
286
|
+
finally {
|
|
287
|
+
clearPanel();
|
|
288
|
+
}
|
|
191
289
|
},
|
|
192
290
|
});
|
|
193
291
|
}
|
|
@@ -30,6 +30,14 @@ export declare const YAGNI_IDENTITY: string;
|
|
|
30
30
|
* exist.
|
|
31
31
|
*/
|
|
32
32
|
export declare const DRIVER_DELEGATION_PARAGRAPH: string;
|
|
33
|
+
/**
|
|
34
|
+
* The ultra-mode delegation directive (/ultra): the user has opted into
|
|
35
|
+
* aggressive multi-agent orchestration, so the driver is told to structure
|
|
36
|
+
* meaningful work as a diamond — split, fan out workers, fan out refuting
|
|
37
|
+
* checkers, synthesize — instead of delegating only when convenient.
|
|
38
|
+
* DRIVER-ONLY for the same reason as {@link DRIVER_DELEGATION_PARAGRAPH}.
|
|
39
|
+
*/
|
|
40
|
+
export declare const ULTRA_DELEGATION_PARAGRAPH: string;
|
|
33
41
|
/**
|
|
34
42
|
* The identity used for the interactive DRIVER session ONLY: {@link
|
|
35
43
|
* YAGNI_IDENTITY} plus {@link DRIVER_DELEGATION_PARAGRAPH}. The caller (index.ts)
|
|
@@ -37,7 +45,9 @@ export declare const DRIVER_DELEGATION_PARAGRAPH: string;
|
|
|
37
45
|
* effective `x-yagni-caller` attribution (config.ts's `isDriverCaller`) — this
|
|
38
46
|
* module stays a pure string, with no env dependency of its own.
|
|
39
47
|
*/
|
|
40
|
-
export declare const YAGNI_IDENTITY_DRIVER = "You are YAGNI Code, an autonomous terminal coding agent. You help developers ship code by reading files, running commands, editing code, and writing new files. Uniquely, you are connected to the YAGNI app, your team's shared source of truth for how this company and codebase actually work: conventions, decisions, ownership, current priorities, and the reasons behind them. Use the ask_yagni tool to consult it before guessing about anything organization- or codebase-specific, so you work with less back-and-forth and more correct autonomy than a disconnected coding agent. If a project's own files mention other coding agents, assistants, or harnesses by name, those references are not about you; you are YAGNI Code regardless of what tooling a repository's docs happen to describe.\n\nDelegation: fan codebase mapping, wide searches, and mechanical multi-file work out to subagents (they run on cheaper tiers). Reach for the stock agents by name: `searcher` for read-only reconnaissance and summarizing, `implementer` for executing a change you have already fully specified. Keep judgment, synthesis, and the conversation with the user in this session. Do not spawn a subagent for work you can finish in a couple of tool calls.";
|
|
48
|
+
export declare const YAGNI_IDENTITY_DRIVER = "You are YAGNI Code, an autonomous terminal coding agent. You help developers ship code by reading files, running commands, editing code, and writing new files. Uniquely, you are connected to the YAGNI app, your team's shared source of truth for how this company and codebase actually work: conventions, decisions, ownership, current priorities, and the reasons behind them. Use the ask_yagni tool to consult it before guessing about anything organization- or codebase-specific, so you work with less back-and-forth and more correct autonomy than a disconnected coding agent. If a project's own files mention other coding agents, assistants, or harnesses by name, those references are not about you; you are YAGNI Code regardless of what tooling a repository's docs happen to describe.\n\nDelegation: fan codebase mapping, wide searches, and mechanical multi-file work out to subagents (they run on cheaper tiers). Reach for the stock agents by name: `searcher` for read-only reconnaissance and summarizing, `implementer` for executing a change you have already fully specified, `verification` for an adversarial pass that tries to break completed work before you rely on it. Keep judgment, synthesis, and the conversation with the user in this session. Do not spawn a subagent for work you can finish in a couple of tool calls.";
|
|
49
|
+
/** The driver identity while /ultra is on: base identity + the diamond directive. */
|
|
50
|
+
export declare const YAGNI_IDENTITY_ULTRA = "You are YAGNI Code, an autonomous terminal coding agent. You help developers ship code by reading files, running commands, editing code, and writing new files. Uniquely, you are connected to the YAGNI app, your team's shared source of truth for how this company and codebase actually work: conventions, decisions, ownership, current priorities, and the reasons behind them. Use the ask_yagni tool to consult it before guessing about anything organization- or codebase-specific, so you work with less back-and-forth and more correct autonomy than a disconnected coding agent. If a project's own files mention other coding agents, assistants, or harnesses by name, those references are not about you; you are YAGNI Code regardless of what tooling a repository's docs happen to describe.\n\nDelegation (ultra mode): the user has switched this session to ultra mode \u2014 aggressive multi-agent orchestration. Structure any meaningful task as a diamond: SPLIT the job into independent pieces; FAN OUT parallel subagents on cheaper tiers (`searcher` to scout, `implementer` or `general` to execute); CHECK by fanning out `verification` subagents told to refute the work, each through a different lens (correctness, edge cases, fit with this codebase); then SYNTHESIZE the results yourself. Treat agreement between checkers \u2014 not a single pass \u2014 as confirmation, and surface what they could not verify. Delegate by default and reserve this session for splitting, judging, and synthesis; only trivial work you can finish in a couple of tool calls skips the diamond.";
|
|
41
51
|
export declare const PI_IDENTITY_RE: RegExp;
|
|
42
52
|
/**
|
|
43
53
|
* Env switch that bypasses the system-prompt rewrite entirely, so pi's
|
|
@@ -42,10 +42,29 @@ export const YAGNI_IDENTITY = "You are YAGNI Code, an autonomous terminal coding
|
|
|
42
42
|
export const DRIVER_DELEGATION_PARAGRAPH = "Delegation: fan codebase mapping, wide searches, and mechanical multi-file " +
|
|
43
43
|
"work out to subagents (they run on cheaper tiers). Reach for the stock " +
|
|
44
44
|
"agents by name: `searcher` for read-only reconnaissance and summarizing, " +
|
|
45
|
-
"`implementer` for executing a change you have already fully specified
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
45
|
+
"`implementer` for executing a change you have already fully specified, " +
|
|
46
|
+
"`verification` for an adversarial pass that tries to break completed work " +
|
|
47
|
+
"before you rely on it. Keep judgment, synthesis, and the conversation " +
|
|
48
|
+
"with the user in this session. Do not spawn a subagent for work you can " +
|
|
49
|
+
"finish in a couple of tool calls.";
|
|
50
|
+
/**
|
|
51
|
+
* The ultra-mode delegation directive (/ultra): the user has opted into
|
|
52
|
+
* aggressive multi-agent orchestration, so the driver is told to structure
|
|
53
|
+
* meaningful work as a diamond — split, fan out workers, fan out refuting
|
|
54
|
+
* checkers, synthesize — instead of delegating only when convenient.
|
|
55
|
+
* DRIVER-ONLY for the same reason as {@link DRIVER_DELEGATION_PARAGRAPH}.
|
|
56
|
+
*/
|
|
57
|
+
export const ULTRA_DELEGATION_PARAGRAPH = "Delegation (ultra mode): the user has switched this session to ultra mode " +
|
|
58
|
+
"— aggressive multi-agent orchestration. Structure any meaningful task as " +
|
|
59
|
+
"a diamond: SPLIT the job into independent pieces; FAN OUT parallel " +
|
|
60
|
+
"subagents on cheaper tiers (`searcher` to scout, `implementer` or " +
|
|
61
|
+
"`general` to execute); CHECK by fanning out `verification` subagents told " +
|
|
62
|
+
"to refute the work, each through a different lens (correctness, edge cases, " +
|
|
63
|
+
"fit with this codebase); then SYNTHESIZE the results yourself. Treat " +
|
|
64
|
+
"agreement between checkers — not a single pass — as confirmation, and " +
|
|
65
|
+
"surface what they could not verify. Delegate by default and reserve this " +
|
|
66
|
+
"session for splitting, judging, and synthesis; only trivial work you can " +
|
|
67
|
+
"finish in a couple of tool calls skips the diamond.";
|
|
49
68
|
/**
|
|
50
69
|
* The identity used for the interactive DRIVER session ONLY: {@link
|
|
51
70
|
* YAGNI_IDENTITY} plus {@link DRIVER_DELEGATION_PARAGRAPH}. The caller (index.ts)
|
|
@@ -54,6 +73,14 @@ export const DRIVER_DELEGATION_PARAGRAPH = "Delegation: fan codebase mapping, wi
|
|
|
54
73
|
* module stays a pure string, with no env dependency of its own.
|
|
55
74
|
*/
|
|
56
75
|
export const YAGNI_IDENTITY_DRIVER = `${YAGNI_IDENTITY}\n\n${DRIVER_DELEGATION_PARAGRAPH}`;
|
|
76
|
+
/** The driver identity while /ultra is on: base identity + the diamond directive. */
|
|
77
|
+
export const YAGNI_IDENTITY_ULTRA = `${YAGNI_IDENTITY}\n\n${ULTRA_DELEGATION_PARAGRAPH}`;
|
|
78
|
+
/**
|
|
79
|
+
* Every identity variant this module can install, longest-composite-first, so
|
|
80
|
+
* the re-brand swap below never leaves a shorter variant's orphaned delegation
|
|
81
|
+
* paragraph behind (driver/ultra both start with the base identity).
|
|
82
|
+
*/
|
|
83
|
+
const KNOWN_IDENTITIES = [YAGNI_IDENTITY_ULTRA, YAGNI_IDENTITY_DRIVER, YAGNI_IDENTITY];
|
|
57
84
|
// pi 0.84.1's exact identity sentence (dist/core/system-prompt.js). Exported as
|
|
58
85
|
// the identity anchor the CLI's pi-contract tripwire test reads back from pi's
|
|
59
86
|
// built system prompt, so a pi bump that reworded the opener (silently defeating
|
|
@@ -112,15 +139,26 @@ export function brandSystemPrompt(original, opts = {}) {
|
|
|
112
139
|
let s = original;
|
|
113
140
|
// 1. Drop pi's self-referential documentation block.
|
|
114
141
|
s = s.replace(PI_DOCS_BLOCK_RE, "");
|
|
115
|
-
// 2. Own the identity.
|
|
142
|
+
// 2. Own the identity. A previously-branded prompt may open with a DIFFERENT
|
|
143
|
+
// variant than the one now requested (/ultra toggles the driver between the
|
|
144
|
+
// delegation and diamond paragraphs mid-session) — swap it in place rather
|
|
145
|
+
// than prepending a second identity block.
|
|
116
146
|
if (PI_IDENTITY_RE.test(s)) {
|
|
117
147
|
s = s.replace(PI_IDENTITY_RE, identity);
|
|
118
148
|
}
|
|
119
149
|
else if (PI_IDENTITY_LOOSE_RE.test(s)) {
|
|
120
150
|
s = s.replace(PI_IDENTITY_LOOSE_RE, identity);
|
|
121
151
|
}
|
|
122
|
-
else
|
|
123
|
-
|
|
152
|
+
else {
|
|
153
|
+
const trimmed = s.trimStart();
|
|
154
|
+
const lead = s.slice(0, s.length - trimmed.length);
|
|
155
|
+
const existing = KNOWN_IDENTITIES.find((k) => trimmed.startsWith(k));
|
|
156
|
+
if (existing && existing !== identity) {
|
|
157
|
+
s = `${lead}${identity}${trimmed.slice(existing.length)}`;
|
|
158
|
+
}
|
|
159
|
+
else if (!existing && !trimmed.startsWith(identity)) {
|
|
160
|
+
s = `${identity}\n\n${s}`;
|
|
161
|
+
}
|
|
124
162
|
}
|
|
125
163
|
// 3. Safety net for residual brand tokens (never inside user content).
|
|
126
164
|
s = scrubOutsideProjectContext(s);
|
|
@@ -122,7 +122,9 @@ export type { RecordDecisionParams } from "./recordDecisionTool.js";
|
|
|
122
122
|
export { runInitPass, runTeamSetup, registerTeamSetupCommand, isFreshWorkspace, readRepoIntake, draftEngineering, summarizeDraft, FRESH_BRIEF_MIN_CHARS, } from "./initPass.js";
|
|
123
123
|
export type { RunInitPassDeps, InitPassOutcome, RunTeamSetupDeps, TeamSetupOutcome, RepoIntake, RepoIntakeFs, EngineeringDraft, DraftTeam, } from "./initPass.js";
|
|
124
124
|
export { isInitDone, markInitDone, initDoneMarkerFile, _setInitDoneHomeForTest } from "./initDone.js";
|
|
125
|
-
export { brandSystemPrompt, YAGNI_IDENTITY, YAGNI_IDENTITY_DRIVER, BRAND_NAME } from "./branding.js";
|
|
125
|
+
export { brandSystemPrompt, YAGNI_IDENTITY, YAGNI_IDENTITY_DRIVER, YAGNI_IDENTITY_ULTRA, BRAND_NAME } from "./branding.js";
|
|
126
|
+
export { createUltraHolder, registerUltraCommand } from "./ultra.js";
|
|
127
|
+
export type { UltraHolder } from "./ultra.js";
|
|
126
128
|
export { attributionHeaders, isDriverCaller, fetchCatalog, getToken, getWorkspaceId, resolveBaseUrl, sanitizeCallerSegment, } from "./config.js";
|
|
127
129
|
export type { CatalogResult, FetchCatalogOptions, ModelEntry } from "./config.js";
|
|
128
130
|
export { buildYagniProvider } from "./provider.js";
|
|
@@ -139,7 +141,7 @@ export type { VerifyCommand, VerifyOutcome } from "./pipeline/verify.js";
|
|
|
139
141
|
export { blindStages, reportOnlyStages, makeGroundedVsBlindEval, formatComparisonReport } from "./pipeline/eval.js";
|
|
140
142
|
export type { ComparisonReport, LaneFit, LaneOutcome } from "./pipeline/eval.js";
|
|
141
143
|
export { registerGoCompareCommand } from "./pipeline/goCompareCommand.js";
|
|
142
|
-
export { registerSubagents, makeSubagentTool, discoverSubagents, parseAgentMarkdown, buildSubagentStage, formatAgentList, mapModelTier, SUBAGENT_TOOL_NAME, GENERAL_AGENT_NAME, MAX_PARALLEL_SUBAGENTS, DEFAULT_SUBAGENT_TOOLS, } from "./subagents.js";
|
|
144
|
+
export { registerSubagents, makeSubagentTool, discoverSubagents, parseAgentMarkdown, buildSubagentStage, formatAgentList, mapModelTier, SUBAGENT_TOOL_NAME, GENERAL_AGENT_NAME, MAX_PARALLEL_SUBAGENTS, MAX_PARALLEL_SUBAGENTS_ULTRA, DEFAULT_SUBAGENT_TOOLS, } from "./subagents.js";
|
|
143
145
|
export type { SubagentDef, SubagentSource } from "./subagents.js";
|
|
144
146
|
export { registerTodos, makeTodoTool, normalizeTodos, reconstructTodos, renderTodoWidget, formatTodoList, todoSummary, TODO_TOOL_NAME, MAX_TODOS, } from "./todos.js";
|
|
145
147
|
export type { TodoItem, TodoStatus, TodoTheme } from "./todos.js";
|
package/dist/extension/index.js
CHANGED
|
@@ -14,7 +14,7 @@ import { registerCmuxBridge } from "./cmux/index.js";
|
|
|
14
14
|
import { makeRecordEngineeringContextTool } from "./recordContextTool.js";
|
|
15
15
|
import { makeRecordDecisionTool } from "./recordDecisionTool.js";
|
|
16
16
|
import { makeSuggestNextWorkTool } from "./nextWorkTool.js";
|
|
17
|
-
import { BRAND_NAME, brandSystemPrompt, brandingDisabled, buildMastheadString, YAGNI_IDENTITY_DRIVER } from "./branding.js";
|
|
17
|
+
import { BRAND_NAME, brandSystemPrompt, brandingDisabled, buildMastheadString, YAGNI_IDENTITY_DRIVER, YAGNI_IDENTITY_ULTRA } from "./branding.js";
|
|
18
18
|
import { claudeRulesSection } from "./claudeRules.js";
|
|
19
19
|
import { registerCostCommand } from "./costHud.js";
|
|
20
20
|
import { isDebug } from "./diagnostics.js";
|
|
@@ -29,6 +29,7 @@ import { registerGoCommand } from "./pipeline/goCommand.js";
|
|
|
29
29
|
import { registerGoCompareCommand } from "./pipeline/goCompareCommand.js";
|
|
30
30
|
import { DEFAULT_PERMISSION_POLICY, createModeHolder, registerPermissionGate } from "./permission.js";
|
|
31
31
|
import { registerSubagents } from "./subagents.js";
|
|
32
|
+
import { createUltraHolder, registerUltraCommand } from "./ultra.js";
|
|
32
33
|
import { registerTodos } from "./todos.js";
|
|
33
34
|
import { registerDecisionCommands } from "./decisions.js";
|
|
34
35
|
import { makeDecisionCapture } from "./decisionCapture.js";
|
|
@@ -185,7 +186,12 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
185
186
|
// The general subagent tool: delegate self-contained tasks (optionally in
|
|
186
187
|
// parallel) to fresh-context agents defined in .claude/agents / .pi/agents,
|
|
187
188
|
// riding the /go pipeline's child runner. /agents lists what's available.
|
|
188
|
-
|
|
189
|
+
// Ultra mode (/ultra) is an explicit orchestration dial: it swaps the
|
|
190
|
+
// driver's delegation identity for the diamond directive (see the
|
|
191
|
+
// before_agent_start handler below) and widens the tool's fan-out ceiling.
|
|
192
|
+
const ultraHolder = createUltraHolder();
|
|
193
|
+
registerSubagents(pi, { isUltra: () => ultraHolder.get() });
|
|
194
|
+
registerUltraCommand(pi, ultraHolder);
|
|
189
195
|
// Shared fetch timeout for the small, interactive display-path reads below
|
|
190
196
|
// (/cost's spend + headroom, and Task 8's per-run spend for /go's summary):
|
|
191
197
|
// 5s, not the 10s other boot/grounding fetches use, because the user is
|
|
@@ -517,13 +523,19 @@ export async function registerYagni(pi, deps = {}) {
|
|
|
517
523
|
// assembled prompt passes through byte-exact (no rewrite, no brief
|
|
518
524
|
// injection, no rules section, no delegation identity).
|
|
519
525
|
const noBranding = brandingDisabled(env);
|
|
520
|
-
|
|
526
|
+
// Resolved per turn (not once at activation): /ultra can flip the driver
|
|
527
|
+
// between the delegation and diamond identities mid-session.
|
|
528
|
+
const isDriver = isDriverCaller(env);
|
|
521
529
|
pi.on("before_agent_start", (event) => noBranding
|
|
522
530
|
? undefined
|
|
523
531
|
: {
|
|
524
532
|
systemPrompt: brandSystemPrompt(event.systemPrompt, {
|
|
525
533
|
contextBrief,
|
|
526
|
-
identity
|
|
534
|
+
identity: isDriver
|
|
535
|
+
? ultraHolder.get()
|
|
536
|
+
? YAGNI_IDENTITY_ULTRA
|
|
537
|
+
: YAGNI_IDENTITY_DRIVER
|
|
538
|
+
: undefined,
|
|
527
539
|
rulesSection,
|
|
528
540
|
}),
|
|
529
541
|
});
|
|
@@ -781,7 +793,9 @@ export { recordDecision } from "./recordDecisionTool.js";
|
|
|
781
793
|
export { runInitPass, runTeamSetup, registerTeamSetupCommand, isFreshWorkspace, readRepoIntake, draftEngineering, summarizeDraft, FRESH_BRIEF_MIN_CHARS, } from "./initPass.js";
|
|
782
794
|
// Onramp Door B (F2a): the one-time init-pass idempotency marker.
|
|
783
795
|
export { isInitDone, markInitDone, initDoneMarkerFile, _setInitDoneHomeForTest } from "./initDone.js";
|
|
784
|
-
export { brandSystemPrompt, YAGNI_IDENTITY, YAGNI_IDENTITY_DRIVER, BRAND_NAME } from "./branding.js";
|
|
796
|
+
export { brandSystemPrompt, YAGNI_IDENTITY, YAGNI_IDENTITY_DRIVER, YAGNI_IDENTITY_ULTRA, BRAND_NAME } from "./branding.js";
|
|
797
|
+
// Ultra mode (/ultra): the aggressive fan-out/verify/synthesize dial.
|
|
798
|
+
export { createUltraHolder, registerUltraCommand } from "./ultra.js";
|
|
785
799
|
export { attributionHeaders, isDriverCaller, fetchCatalog, getToken, getWorkspaceId, resolveBaseUrl, sanitizeCallerSegment, } from "./config.js";
|
|
786
800
|
export { buildYagniProvider } from "./provider.js";
|
|
787
801
|
export { registerGoCommand } from "./pipeline/goCommand.js";
|
|
@@ -796,7 +810,7 @@ export { buildVerifyEnv, detectVerifyCommand, makeRunVerify, parseVerifyFailures
|
|
|
796
810
|
export { blindStages, reportOnlyStages, makeGroundedVsBlindEval, formatComparisonReport } from "./pipeline/eval.js";
|
|
797
811
|
export { registerGoCompareCommand } from "./pipeline/goCompareCommand.js";
|
|
798
812
|
// The general subagent tool: Claude Code-format agent discovery + fan-out.
|
|
799
|
-
export { registerSubagents, makeSubagentTool, discoverSubagents, parseAgentMarkdown, buildSubagentStage, formatAgentList, mapModelTier, SUBAGENT_TOOL_NAME, GENERAL_AGENT_NAME, MAX_PARALLEL_SUBAGENTS, DEFAULT_SUBAGENT_TOOLS, } from "./subagents.js";
|
|
813
|
+
export { registerSubagents, makeSubagentTool, discoverSubagents, parseAgentMarkdown, buildSubagentStage, formatAgentList, mapModelTier, SUBAGENT_TOOL_NAME, GENERAL_AGENT_NAME, MAX_PARALLEL_SUBAGENTS, MAX_PARALLEL_SUBAGENTS_ULTRA, DEFAULT_SUBAGENT_TOOLS, } from "./subagents.js";
|
|
800
814
|
// The session todo checklist: todo_write tool, widget renderer, /todos.
|
|
801
815
|
export { registerTodos, makeTodoTool, normalizeTodos, reconstructTodos, renderTodoWidget, formatTodoList, todoSummary, TODO_TOOL_NAME, MAX_TODOS, } from "./todos.js";
|
|
802
816
|
// P3 + W4: the permission gate seam (decideGate is pure; policy injectable) plus
|
|
@@ -20,11 +20,13 @@
|
|
|
20
20
|
import { type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
21
21
|
import { Type } from "typebox";
|
|
22
22
|
import { runStage } from "./pipeline/runner.js";
|
|
23
|
-
import type
|
|
23
|
+
import { type ModelTier, type PipelineStage } from "./pipeline/types.js";
|
|
24
24
|
import { renderSubagentCall, renderSubagentResult } from "./subagentRender.js";
|
|
25
25
|
export declare const SUBAGENT_TOOL_NAME = "subagent";
|
|
26
26
|
export declare const GENERAL_AGENT_NAME = "general";
|
|
27
27
|
export declare const MAX_PARALLEL_SUBAGENTS = 4;
|
|
28
|
+
/** Wider fan-out ceiling while the session is in ultra mode (/ultra). */
|
|
29
|
+
export declare const MAX_PARALLEL_SUBAGENTS_ULTRA = 8;
|
|
28
30
|
/**
|
|
29
31
|
* The default tool surface a subagent gets when its definition declares none:
|
|
30
32
|
* the full working set plus grounded answers, mirroring what a Claude Code
|
|
@@ -92,6 +94,8 @@ export interface MakeSubagentToolDeps {
|
|
|
92
94
|
runStageImpl?: typeof runStage;
|
|
93
95
|
discover?: (deps: DiscoverDeps) => SubagentDef[];
|
|
94
96
|
homeDir?: string;
|
|
97
|
+
/** Live ultra-mode probe (/ultra): widens the per-call fan-out ceiling. */
|
|
98
|
+
isUltra?: () => boolean;
|
|
95
99
|
}
|
|
96
100
|
export declare function makeSubagentTool(deps?: MakeSubagentToolDeps): {
|
|
97
101
|
name: string;
|
|
@@ -135,6 +139,8 @@ export declare function makeSubagentTool(deps?: MakeSubagentToolDeps): {
|
|
|
135
139
|
export interface RegisterSubagentsDeps {
|
|
136
140
|
discover?: (deps: DiscoverDeps) => SubagentDef[];
|
|
137
141
|
homeDir?: string;
|
|
142
|
+
/** Live ultra-mode probe (/ultra): widens the per-call fan-out ceiling. */
|
|
143
|
+
isUltra?: () => boolean;
|
|
138
144
|
}
|
|
139
145
|
/** Wire the subagent tool and the /agents listing command. */
|
|
140
146
|
export declare function registerSubagents(pi: ExtensionAPI, deps?: RegisterSubagentsDeps): void;
|
|
@@ -23,7 +23,9 @@ import { delimiter, join } from "node:path";
|
|
|
23
23
|
import { parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
24
24
|
import { Type } from "typebox";
|
|
25
25
|
import { sanitizeCallerSegment } from "./config.js";
|
|
26
|
+
import { withResilience } from "./pipeline/resilience.js";
|
|
26
27
|
import { runStage } from "./pipeline/runner.js";
|
|
28
|
+
import { DEFAULT_RESILIENCE_POLICY } from "./pipeline/types.js";
|
|
27
29
|
import { applyChildEvent, finalizeTask, formatWorkingMessage, newTaskProgress, progressSummaryText, renderSubagentCall, renderSubagentResult, } from "./subagentRender.js";
|
|
28
30
|
/**
|
|
29
31
|
* YAG-471 attribution: the `x-yagni-caller` prefix for a subagent invocation.
|
|
@@ -34,6 +36,8 @@ const SUBAGENT_CALLER_PREFIX = "subagent:";
|
|
|
34
36
|
export const SUBAGENT_TOOL_NAME = "subagent";
|
|
35
37
|
export const GENERAL_AGENT_NAME = "general";
|
|
36
38
|
export const MAX_PARALLEL_SUBAGENTS = 4;
|
|
39
|
+
/** Wider fan-out ceiling while the session is in ultra mode (/ultra). */
|
|
40
|
+
export const MAX_PARALLEL_SUBAGENTS_ULTRA = 8;
|
|
37
41
|
/**
|
|
38
42
|
* The default tool surface a subagent gets when its definition declares none:
|
|
39
43
|
* the full working set plus grounded answers, mirroring what a Claude Code
|
|
@@ -123,6 +127,50 @@ const IMPLEMENTER_AGENT = {
|
|
|
123
127
|
body: IMPLEMENTER_BODY,
|
|
124
128
|
source: "builtin",
|
|
125
129
|
};
|
|
130
|
+
const VERIFICATION_BODY = `You are an adversarial verifier. Another agent
|
|
131
|
+
produced work — a change, a plan, or a claim — and your job is to try to
|
|
132
|
+
BREAK it, not to summarize it. Default to skepticism: hunt for the concrete
|
|
133
|
+
failure scenario (the inputs, state, or sequence that makes it wrong). Bash
|
|
134
|
+
is read-only here (\`git diff\`, \`git log\`, \`git show\`); do NOT modify files
|
|
135
|
+
or run builds.
|
|
136
|
+
|
|
137
|
+
If your task names a lens (correctness, edge cases, codebase fit, security,
|
|
138
|
+
…), judge ONLY through that lens and leave the rest to your sibling
|
|
139
|
+
verifiers.
|
|
140
|
+
|
|
141
|
+
You are grounded in how THIS company works: call ask_yagni before inferring
|
|
142
|
+
a convention, an ownership rule, or anything organization-specific.
|
|
143
|
+
|
|
144
|
+
Your final message is your verdict back to the driving agent, which has NOT
|
|
145
|
+
seen what you read. Format:
|
|
146
|
+
## Verdict
|
|
147
|
+
BROKEN or HOLDS, with one sentence why.
|
|
148
|
+
## Findings
|
|
149
|
+
Each real problem: file:line, the concrete failure scenario, severity. No
|
|
150
|
+
style nits.
|
|
151
|
+
## Not verified
|
|
152
|
+
What you could not check, and why.
|
|
153
|
+
|
|
154
|
+
A HOLDS after real digging is valuable; a rubber stamp is not. If you found
|
|
155
|
+
nothing, say exactly what you tried to break and how.`;
|
|
156
|
+
/** The diamond's reduce layer: refute-first review of completed work on the
|
|
157
|
+
* advanced tier (judgment is the whole job, so the premium is worth paying —
|
|
158
|
+
* the server clamps subagent children at advanced anyway). It has no
|
|
159
|
+
* edit/write tools; bash is included for `git diff`-style inspection and is
|
|
160
|
+
* restricted to read-only USE by the persona, the same prompt-level stance as
|
|
161
|
+
* /go's reviewer — not a technical guarantee. */
|
|
162
|
+
const VERIFICATION_AGENT = {
|
|
163
|
+
name: "verification",
|
|
164
|
+
description: "Adversarial verification of completed work: tries to refute a change, " +
|
|
165
|
+
"plan, or claim and reports concrete failure scenarios. Does not edit " +
|
|
166
|
+
"files. Fan out 2-3 with different lenses (correctness, edge cases, " +
|
|
167
|
+
"codebase fit) for anything significant and treat agreement as " +
|
|
168
|
+
"confirmation.",
|
|
169
|
+
model: "advanced",
|
|
170
|
+
tools: ["read", "grep", "find", "ls", "bash", "ask_yagni"],
|
|
171
|
+
body: VERIFICATION_BODY,
|
|
172
|
+
source: "builtin",
|
|
173
|
+
};
|
|
126
174
|
// Concrete tiers a subagent can actually run on. `balanced` is deliberately NOT
|
|
127
175
|
// a member here even though it is a member of `ModelTier`: a subagent needs
|
|
128
176
|
// ONE model for its whole run, and balanced is a session-level routing policy,
|
|
@@ -210,7 +258,7 @@ function loadAgentsFromDir(dir, source) {
|
|
|
210
258
|
export function discoverSubagents(deps) {
|
|
211
259
|
const home = deps.homeDir ?? homedir();
|
|
212
260
|
const layers = [
|
|
213
|
-
[GENERAL_AGENT, SEARCHER_AGENT, IMPLEMENTER_AGENT],
|
|
261
|
+
[GENERAL_AGENT, SEARCHER_AGENT, IMPLEMENTER_AGENT, VERIFICATION_AGENT],
|
|
214
262
|
...pluginAgentDirs(deps.env ?? process.env).map((dir) => loadAgentsFromDir(dir, "plugin")),
|
|
215
263
|
loadAgentsFromDir(join(home, ".claude", "agents"), "user-claude"),
|
|
216
264
|
loadAgentsFromDir(join(deps.cwd, ".pi", "agents"), "project-pi"),
|
|
@@ -261,11 +309,17 @@ const parameters = Type.Object({
|
|
|
261
309
|
task: Type.String(),
|
|
262
310
|
agent: Type.Optional(Type.String()),
|
|
263
311
|
}), {
|
|
264
|
-
description: `Run several independent tasks in parallel (max ${MAX_PARALLEL_SUBAGENTS}). Use INSTEAD of task.`,
|
|
312
|
+
description: `Run several independent tasks in parallel (max ${MAX_PARALLEL_SUBAGENTS}; ${MAX_PARALLEL_SUBAGENTS_ULTRA} in ultra mode). Use INSTEAD of task.`,
|
|
265
313
|
})),
|
|
266
314
|
});
|
|
267
315
|
export function makeSubagentTool(deps = {}) {
|
|
268
|
-
|
|
316
|
+
// The default runner rides the /go pipeline's resilience wrapper, so a chat
|
|
317
|
+
// subagent gets the same idle + wall-clock ceilings and transient-only retry
|
|
318
|
+
// as a /go stage child (previously a hung subagent hung the tool call until
|
|
319
|
+
// the user pressed Esc). The synthetic stage id is "implement", so the
|
|
320
|
+
// wrapper's write-gate already refuses to re-run a child that may have
|
|
321
|
+
// landed a partial edit.
|
|
322
|
+
const run = deps.runStageImpl ?? withResilience(runStage, DEFAULT_RESILIENCE_POLICY);
|
|
269
323
|
const discover = deps.discover ?? discoverSubagents;
|
|
270
324
|
return {
|
|
271
325
|
name: SUBAGENT_TOOL_NAME,
|
|
@@ -293,8 +347,9 @@ export function makeSubagentTool(deps = {}) {
|
|
|
293
347
|
if (requested.length === 0) {
|
|
294
348
|
return fail("Error: pass `task` (or a `tasks` array) describing what to do.");
|
|
295
349
|
}
|
|
296
|
-
|
|
297
|
-
|
|
350
|
+
const maxParallel = deps.isUltra?.() ? MAX_PARALLEL_SUBAGENTS_ULTRA : MAX_PARALLEL_SUBAGENTS;
|
|
351
|
+
if (requested.length > maxParallel) {
|
|
352
|
+
return fail(`Error: at most ${maxParallel} parallel tasks per call.`);
|
|
298
353
|
}
|
|
299
354
|
const cwd = ctx?.cwd ?? process.cwd();
|
|
300
355
|
const agents = discover({ cwd, homeDir: deps.homeDir });
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ultra mode (/ultra) — an explicit, session-scoped dial for aggressive
|
|
3
|
+
* multi-agent orchestration ("the diamond": fan out → reduce → synthesize).
|
|
4
|
+
*
|
|
5
|
+
* Off by default so the trial-default behavior is unchanged; toggling on swaps
|
|
6
|
+
* the driver's delegation paragraph for the diamond directive (branding.ts's
|
|
7
|
+
* YAGNI_IDENTITY_ULTRA, read live per turn in index.ts) and widens the
|
|
8
|
+
* subagent tool's per-call fan-out ceiling (subagents.ts). Ultra is a prompt +
|
|
9
|
+
* ceiling change only: it never touches the permission mode, the model tier,
|
|
10
|
+
* or the /go pipeline.
|
|
11
|
+
*/
|
|
12
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
13
|
+
export interface UltraHolder {
|
|
14
|
+
get(): boolean;
|
|
15
|
+
set(on: boolean): void;
|
|
16
|
+
}
|
|
17
|
+
export declare function createUltraHolder(initial?: boolean): UltraHolder;
|
|
18
|
+
/**
|
|
19
|
+
* Wire the /ultra command onto a shared holder. No argument toggles; `on` /
|
|
20
|
+
* `off` set explicitly; `status` reports without changing anything.
|
|
21
|
+
*/
|
|
22
|
+
export declare function registerUltraCommand(pi: ExtensionAPI, holder: UltraHolder): void;
|
|
23
|
+
//# sourceMappingURL=ultra.d.ts.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ultra mode (/ultra) — an explicit, session-scoped dial for aggressive
|
|
3
|
+
* multi-agent orchestration ("the diamond": fan out → reduce → synthesize).
|
|
4
|
+
*
|
|
5
|
+
* Off by default so the trial-default behavior is unchanged; toggling on swaps
|
|
6
|
+
* the driver's delegation paragraph for the diamond directive (branding.ts's
|
|
7
|
+
* YAGNI_IDENTITY_ULTRA, read live per turn in index.ts) and widens the
|
|
8
|
+
* subagent tool's per-call fan-out ceiling (subagents.ts). Ultra is a prompt +
|
|
9
|
+
* ceiling change only: it never touches the permission mode, the model tier,
|
|
10
|
+
* or the /go pipeline.
|
|
11
|
+
*/
|
|
12
|
+
export function createUltraHolder(initial = false) {
|
|
13
|
+
let on = initial;
|
|
14
|
+
return {
|
|
15
|
+
get: () => on,
|
|
16
|
+
set: (v) => {
|
|
17
|
+
on = v;
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/** Status chip shown while ultra is on (same footer surface as the /mode chip). */
|
|
22
|
+
const ULTRA_STATUS = "◆ ultra";
|
|
23
|
+
const ULTRA_ON_COPY = "Ultra mode ON: meaningful work fans out to parallel subagents, adversarial " +
|
|
24
|
+
"verification agents try to break the result, then the agent synthesizes. " +
|
|
25
|
+
"Expect more subagent spend per task.";
|
|
26
|
+
const ULTRA_OFF_COPY = "Ultra mode OFF: back to delegate-when-useful.";
|
|
27
|
+
/**
|
|
28
|
+
* Wire the /ultra command onto a shared holder. No argument toggles; `on` /
|
|
29
|
+
* `off` set explicitly; `status` reports without changing anything.
|
|
30
|
+
*/
|
|
31
|
+
export function registerUltraCommand(pi, holder) {
|
|
32
|
+
pi.registerCommand("ultra", {
|
|
33
|
+
description: "Toggle ultra mode: aggressive fan-out/verify/synthesize orchestration. /ultra on | off | status.",
|
|
34
|
+
handler: async (args, ctx) => {
|
|
35
|
+
const notify = (m, t) => {
|
|
36
|
+
if (ctx.hasUI)
|
|
37
|
+
ctx.ui.notify(m, t);
|
|
38
|
+
};
|
|
39
|
+
const arg = args.trim().toLowerCase();
|
|
40
|
+
if (arg && arg !== "on" && arg !== "off" && arg !== "status") {
|
|
41
|
+
notify(`Unknown argument "${arg}". Use /ultra, /ultra on, /ultra off, or /ultra status.`, "warning");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (arg === "status") {
|
|
45
|
+
notify(holder.get() ? ULTRA_ON_COPY : ULTRA_OFF_COPY, "info");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const next = arg === "on" ? true : arg === "off" ? false : !holder.get();
|
|
49
|
+
holder.set(next);
|
|
50
|
+
try {
|
|
51
|
+
if (ctx.hasUI)
|
|
52
|
+
ctx.ui.setStatus?.("yagni-ultra", next ? ULTRA_STATUS : undefined);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// The chip is chrome; never let it break /ultra.
|
|
56
|
+
}
|
|
57
|
+
notify(next ? ULTRA_ON_COPY : ULTRA_OFF_COPY, "info");
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=ultra.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yagni-app/code-staging",
|
|
3
|
-
"version": "0.3.0-staging.
|
|
3
|
+
"version": "0.3.0-staging.1088.1",
|
|
4
4
|
"description": "YAGNI Code: a terminal coding agent that already knows your company. One YAGNI login routes the model and grounds the agent in your team's context.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"author": "YAGNI, Inc. <jack@yagni.app> (https://yagni.app)",
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
"@earendil-works/pi-tui": "0.84.1",
|
|
39
39
|
"typebox": "^1.3.11"
|
|
40
40
|
},
|
|
41
|
-
"yagniSourceSha": "
|
|
41
|
+
"yagniSourceSha": "eebceee1a0d1673dd4b2755942aa027a89f18611"
|
|
42
42
|
}
|