@oh-my-pi/pi-coding-agent 16.2.5 → 16.2.6
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/CHANGELOG.md +18 -1
- package/dist/cli.js +2991 -3011
- package/dist/types/cli/bench-cli.d.ts +6 -1
- package/dist/types/commands/bench.d.ts +8 -0
- package/dist/types/config/model-discovery.d.ts +6 -1
- package/dist/types/config/settings-schema.d.ts +1 -1
- package/dist/types/edit/index.d.ts +1 -0
- package/dist/types/edit/renderer.d.ts +4 -0
- package/dist/types/edit/snapshot-details.d.ts +33 -0
- package/dist/types/mcp/oauth-flow.d.ts +4 -6
- package/dist/types/modes/controllers/tool-args-reveal.d.ts +9 -2
- package/dist/types/session/agent-session.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +3 -4
- package/dist/types/web/search/providers/duckduckgo.d.ts +2 -2
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +12 -12
- package/src/cli/args.ts +32 -1
- package/src/cli/bench-cli.ts +89 -21
- package/src/cli/web-search-cli.ts +6 -1
- package/src/commands/bench.ts +10 -1
- package/src/config/mcp-schema.json +1 -1
- package/src/config/model-discovery.ts +66 -8
- package/src/config/model-registry.ts +13 -6
- package/src/edit/hashline/execute.ts +15 -9
- package/src/edit/index.ts +19 -6
- package/src/edit/modes/patch.ts +3 -2
- package/src/edit/modes/replace.ts +3 -2
- package/src/edit/renderer.ts +4 -0
- package/src/edit/snapshot-details.ts +77 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +2 -2
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/mcp/oauth-flow.ts +10 -8
- package/src/mcp/transports/stdio.ts +9 -17
- package/src/modes/controllers/event-controller.ts +17 -8
- package/src/modes/controllers/tool-args-reveal.ts +100 -22
- package/src/prompts/bench.md +4 -10
- package/src/prompts/tools/irc.md +19 -29
- package/src/prompts/tools/job.md +8 -14
- package/src/prompts/tools/lsp.md +19 -30
- package/src/prompts/tools/task.md +42 -62
- package/src/sdk.ts +1 -0
- package/src/session/agent-session.ts +10 -0
- package/src/session/session-listing.ts +9 -8
- package/src/session/session-loader.ts +98 -3
- package/src/session/session-manager.ts +34 -4
- package/src/subprocess/worker-client.ts +12 -4
- package/src/tools/path-utils.ts +4 -2
- package/src/web/search/index.ts +14 -8
- package/src/web/search/providers/duckduckgo.ts +136 -78
- package/src/web/search/providers/gemini.ts +268 -185
- package/src/web/search/types.ts +1 -1
package/src/mcp/oauth-flow.ts
CHANGED
|
@@ -75,6 +75,10 @@ export interface MCPStoredOAuthCredential extends OAuthCredential {
|
|
|
75
75
|
const DEFAULT_PORT = 3000;
|
|
76
76
|
const CALLBACK_PATH = "/callback";
|
|
77
77
|
|
|
78
|
+
function hasOAuthScope(scopes: string | null | undefined, scope: string): boolean {
|
|
79
|
+
return !!scopes && scopes.split(/\s+/).includes(scope);
|
|
80
|
+
}
|
|
81
|
+
|
|
78
82
|
function isLoopbackHostname(hostname: string): boolean {
|
|
79
83
|
return hostname === "localhost" || hostname === "127.0.0.1";
|
|
80
84
|
}
|
|
@@ -225,12 +229,10 @@ export interface MCPOAuthConfig {
|
|
|
225
229
|
/** OAuth scopes (space-separated) */
|
|
226
230
|
scopes?: string;
|
|
227
231
|
/**
|
|
228
|
-
* `prompt` parameter for the authorization request.
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
* (RFC 6749 §3.1 requires servers to ignore the param when unsupported).
|
|
233
|
-
* Set to `""` to omit the parameter entirely.
|
|
232
|
+
* `prompt` parameter for the authorization request. By default the parameter
|
|
233
|
+
* is omitted, matching the reference MCP SDK, except for `offline_access`
|
|
234
|
+
* requests where OIDC Core requires `prompt=consent` to issue refresh-token
|
|
235
|
+
* access. Set to `""` to omit the parameter entirely.
|
|
234
236
|
*/
|
|
235
237
|
prompt?: string;
|
|
236
238
|
/** Exact redirect URI to advertise to the provider */
|
|
@@ -325,7 +327,7 @@ export class MCPOAuthFlow extends OAuthCallbackFlow {
|
|
|
325
327
|
if (this.config.scopes && !params.get("scope")) {
|
|
326
328
|
params.set("scope", this.config.scopes);
|
|
327
329
|
}
|
|
328
|
-
const prompt = this.config.prompt ?? "consent";
|
|
330
|
+
const prompt = this.config.prompt ?? (hasOAuthScope(params.get("scope"), "offline_access") ? "consent" : "");
|
|
329
331
|
if (prompt && !params.get("prompt")) {
|
|
330
332
|
params.set("prompt", prompt);
|
|
331
333
|
}
|
|
@@ -494,7 +496,7 @@ export class MCPOAuthFlow extends OAuthCallbackFlow {
|
|
|
494
496
|
Accept: "application/json",
|
|
495
497
|
},
|
|
496
498
|
body: JSON.stringify({
|
|
497
|
-
client_name: "
|
|
499
|
+
client_name: "oh-my-pi",
|
|
498
500
|
redirect_uris: [redirectUri],
|
|
499
501
|
grant_types: ["authorization_code", "refresh_token"],
|
|
500
502
|
response_types: ["code"],
|
|
@@ -147,21 +147,6 @@ function resolveWindowsShimPath(value: string, shimDir: string): string | null {
|
|
|
147
147
|
return path.join(shimDir, ...suffix.split(/[\\/]+/).filter(Boolean));
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
function extractWindowsNpmShimTarget(content: string): string | null {
|
|
151
|
-
const match = /"%_prog%"\s+"([^"]+)"\s+%\*/i.exec(content);
|
|
152
|
-
return match?.[1] ?? null;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Extract the shim's PATH-fallback interpreter (`SET "_prog=node"`). The
|
|
157
|
-
* `IF EXIST` branch assigns a `%dp0%`-prefixed value, so requiring a
|
|
158
|
-
* non-`%`-leading value picks the bare program name.
|
|
159
|
-
*/
|
|
160
|
-
function extractWindowsNpmShimProg(content: string): string | null {
|
|
161
|
-
const match = /SET\s+"_prog=([^%"][^"]*)"/i.exec(content);
|
|
162
|
-
return match?.[1] ?? null;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
150
|
async function resolveWindowsNpmShimCommand(
|
|
166
151
|
command: string,
|
|
167
152
|
args: readonly string[],
|
|
@@ -171,6 +156,11 @@ async function resolveWindowsNpmShimCommand(
|
|
|
171
156
|
if (!isWindowsBatchCommand(command)) return null;
|
|
172
157
|
if (!hasPathSegment(command)) return null;
|
|
173
158
|
const commandPath = path.resolve(cwd, command);
|
|
159
|
+
const commandName = path
|
|
160
|
+
.basename(commandPath)
|
|
161
|
+
.replace(/\.cmd$/i, "")
|
|
162
|
+
.toLowerCase();
|
|
163
|
+
if (commandName === "npx") return null;
|
|
174
164
|
|
|
175
165
|
let content: string;
|
|
176
166
|
try {
|
|
@@ -181,7 +171,9 @@ async function resolveWindowsNpmShimCommand(
|
|
|
181
171
|
|
|
182
172
|
// cmd-shim emits the same invocation line for every interpreter; only
|
|
183
173
|
// bypass cmd.exe when the shim's fallback interpreter is actually node.
|
|
184
|
-
|
|
174
|
+
// The IF EXIST branch assigns a %dp0%-prefixed value, so requiring a
|
|
175
|
+
// non-%-leading SET value picks the bare PATH-fallback program name.
|
|
176
|
+
const prog = /SET\s+"_prog=([^%"][^"]*)"/i.exec(content)?.[1];
|
|
185
177
|
if (
|
|
186
178
|
!prog ||
|
|
187
179
|
path
|
|
@@ -191,7 +183,7 @@ async function resolveWindowsNpmShimCommand(
|
|
|
191
183
|
)
|
|
192
184
|
return null;
|
|
193
185
|
|
|
194
|
-
const rawTarget =
|
|
186
|
+
const rawTarget = /"%_prog%"\s+"([^"]+)"\s+%\*/i.exec(content)?.[1];
|
|
195
187
|
if (!rawTarget) return null;
|
|
196
188
|
|
|
197
189
|
const target = resolveWindowsShimPath(rawTarget, path.dirname(commandPath));
|
|
@@ -50,6 +50,15 @@ const MAX_LIVE_IRC_CARDS = 4;
|
|
|
50
50
|
const IDLE_RECAP_MIN_SECONDS = 1;
|
|
51
51
|
const IDLE_RECAP_MAX_SECONDS = 3600;
|
|
52
52
|
|
|
53
|
+
const RAW_PARTIAL_JSON_RENDERERS: Record<string, true> = { bash: true, edit: true, apply_patch: true };
|
|
54
|
+
|
|
55
|
+
function exposesRawPartialJson(toolName: string, rawInput: boolean, tool: unknown): boolean {
|
|
56
|
+
if (rawInput) return true;
|
|
57
|
+
if (RAW_PARTIAL_JSON_RENDERERS[toolName]) return true;
|
|
58
|
+
if (tool === null || typeof tool !== "object" || !("renderCall" in tool)) return false;
|
|
59
|
+
return typeof tool.renderCall === "function";
|
|
60
|
+
}
|
|
61
|
+
|
|
53
62
|
type AgentSessionEventHandlers = {
|
|
54
63
|
[E in AgentSessionEventKind]: (event: Extract<AgentSessionEvent, { type: E }>) => Promise<void>;
|
|
55
64
|
};
|
|
@@ -623,7 +632,7 @@ export class EventController {
|
|
|
623
632
|
// Internal URL read falls through to ToolExecutionComponent below.
|
|
624
633
|
}
|
|
625
634
|
|
|
626
|
-
// Preserve the raw partial JSON for renderers that need to surface fields before the JSON object closes.
|
|
635
|
+
// Preserve the raw partial JSON only for renderers that need to surface fields before the JSON object closes.
|
|
627
636
|
// Bash uses this to show inline env assignments during streaming instead of popping them in at completion.
|
|
628
637
|
// While the JSON is still open, ToolArgsRevealController paces the
|
|
629
638
|
// reveal (write/edit/bash previews grow smoothly when a slow provider
|
|
@@ -631,13 +640,14 @@ export class EventController {
|
|
|
631
640
|
// as-is — mirroring how assistant text snaps at message_end.
|
|
632
641
|
let renderArgs: Record<string, unknown>;
|
|
633
642
|
const partialJson = getStreamingPartialJson(content);
|
|
643
|
+
const rawInput = content.customWireName !== undefined;
|
|
644
|
+
const tool = this.ctx.viewSession.getToolByName(content.name);
|
|
634
645
|
if (partialJson) {
|
|
635
|
-
renderArgs = this.#toolArgsReveal.setTarget(
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
content.
|
|
639
|
-
|
|
640
|
-
);
|
|
646
|
+
renderArgs = this.#toolArgsReveal.setTarget(content.id, partialJson, {
|
|
647
|
+
rawInput,
|
|
648
|
+
exposeRawPartialJson: exposesRawPartialJson(content.name, rawInput, tool),
|
|
649
|
+
fullArgs: content.arguments,
|
|
650
|
+
});
|
|
641
651
|
} else {
|
|
642
652
|
this.#toolArgsReveal.finish(content.id);
|
|
643
653
|
renderArgs = content.arguments;
|
|
@@ -645,7 +655,6 @@ export class EventController {
|
|
|
645
655
|
if (!this.ctx.pendingTools.has(content.id)) {
|
|
646
656
|
this.#resolveDisplaceablePoll(content.name);
|
|
647
657
|
this.#resetReadGroup();
|
|
648
|
-
const tool = this.ctx.viewSession.getToolByName(content.name);
|
|
649
658
|
const component = new ToolExecutionComponent(
|
|
650
659
|
content.name,
|
|
651
660
|
renderArgs,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseStreamingJson } from "@oh-my-pi/pi-utils";
|
|
1
|
+
import { parseStreamingJson, parseStreamingJsonThrottled, STREAMING_JSON_PARSE_MIN_GROWTH } from "@oh-my-pi/pi-utils";
|
|
2
2
|
import { nextStep, STREAMING_REVEAL_FRAME_MS } from "./streaming-reveal";
|
|
3
3
|
|
|
4
4
|
/** Minimal component surface the reveal pushes frames into. */
|
|
@@ -19,6 +19,16 @@ type RevealEntry = {
|
|
|
19
19
|
revealed: number;
|
|
20
20
|
/** Custom-tool raw input: display args are `{ input: prefix }`, never parsed as JSON. */
|
|
21
21
|
rawInput: boolean;
|
|
22
|
+
/** Whether the renderer observes fresh raw JSON prefixes directly. */
|
|
23
|
+
exposeRawPartialJson: boolean;
|
|
24
|
+
/** Last parsed JSON args from the revealed prefix. */
|
|
25
|
+
parsedArgs: Record<string, unknown>;
|
|
26
|
+
/** Prefix length covered by `parsedArgs`. */
|
|
27
|
+
parsedLen: number;
|
|
28
|
+
/** Last object handed to a component; reused when visible args have not changed. */
|
|
29
|
+
displayArgs: Record<string, unknown>;
|
|
30
|
+
/** Raw prefix carried by `displayArgs.__partialJson`. */
|
|
31
|
+
displayPrefix: string;
|
|
22
32
|
};
|
|
23
33
|
|
|
24
34
|
/** Clamp a slice end into `text`, never splitting a surrogate pair: a prefix
|
|
@@ -32,15 +42,64 @@ function clampSliceEnd(text: string, end: number): number {
|
|
|
32
42
|
return code >= 0xd800 && code <= 0xdbff ? end + 1 : end;
|
|
33
43
|
}
|
|
34
44
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
type ToolArgsRevealTarget = {
|
|
46
|
+
rawInput: boolean;
|
|
47
|
+
exposeRawPartialJson: boolean;
|
|
48
|
+
fullArgs: Record<string, unknown>;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
type DisplayArgsStep = {
|
|
52
|
+
args: Record<string, unknown>;
|
|
53
|
+
changed: boolean;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
function initialDisplayArgs(): Record<string, unknown> {
|
|
57
|
+
return { __partialJson: "" };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function resetDisplayState(entry: RevealEntry): void {
|
|
61
|
+
entry.parsedArgs = {};
|
|
62
|
+
entry.parsedLen = 0;
|
|
63
|
+
entry.displayArgs = initialDisplayArgs();
|
|
64
|
+
entry.displayPrefix = "";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Display args for a revealed prefix. Function-tool JSON is parsed at the same
|
|
68
|
+
* growth-throttled cadence providers use, so a long `write` payload cannot make
|
|
69
|
+
* the reveal loop re-parse the whole growing buffer every frame. Renderers that
|
|
70
|
+
* read raw JSON directly still receive fresh `__partialJson` prefixes; other
|
|
71
|
+
* renderers get a stable object reference while parsed fields are unchanged. */
|
|
72
|
+
function displayArgsForPrefix(entry: RevealEntry, prefix: string, forceParse = false): DisplayArgsStep {
|
|
73
|
+
if (entry.rawInput) {
|
|
74
|
+
if (prefix === entry.displayPrefix) return { args: entry.displayArgs, changed: false };
|
|
75
|
+
const args = { input: prefix, __partialJson: prefix };
|
|
76
|
+
entry.displayArgs = args;
|
|
77
|
+
entry.displayPrefix = prefix;
|
|
78
|
+
return { args, changed: true };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let parsedChanged = false;
|
|
82
|
+
if (forceParse || (prefix.length > 0 && prefix.length < STREAMING_JSON_PARSE_MIN_GROWTH)) {
|
|
83
|
+
entry.parsedArgs = parseStreamingJson<Record<string, unknown>>(prefix);
|
|
84
|
+
entry.parsedLen = prefix.length;
|
|
85
|
+
parsedChanged = true;
|
|
86
|
+
} else {
|
|
87
|
+
const throttled = parseStreamingJsonThrottled<Record<string, unknown>>(prefix, entry.parsedLen);
|
|
88
|
+
if (throttled) {
|
|
89
|
+
entry.parsedArgs = throttled.value;
|
|
90
|
+
entry.parsedLen = throttled.parsedLen;
|
|
91
|
+
parsedChanged = true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const rawPrefixChanged = entry.exposeRawPartialJson && prefix !== entry.displayPrefix;
|
|
96
|
+
if (!parsedChanged && !rawPrefixChanged) return { args: entry.displayArgs, changed: false };
|
|
97
|
+
|
|
98
|
+
const displayPrefix = entry.exposeRawPartialJson || parsedChanged ? prefix : entry.displayPrefix;
|
|
99
|
+
const args = { ...entry.parsedArgs, __partialJson: displayPrefix };
|
|
100
|
+
entry.displayArgs = args;
|
|
101
|
+
entry.displayPrefix = displayPrefix;
|
|
102
|
+
return { args, changed: true };
|
|
44
103
|
}
|
|
45
104
|
|
|
46
105
|
/**
|
|
@@ -49,7 +108,9 @@ function buildDisplayArgs(prefix: string, rawInput: boolean): Record<string, unk
|
|
|
49
108
|
* (or throttle their partial parses) would otherwise make write/edit/bash
|
|
50
109
|
* streaming previews jump in chunks. Each pending tool call reveals its raw
|
|
51
110
|
* argument stream at the shared 30fps cadence with the same adaptive
|
|
52
|
-
* catch-up step
|
|
111
|
+
* catch-up step. JSON prefixes are parsed only when enough new bytes arrive to
|
|
112
|
+
* change renderer-visible fields, while raw-prefix consumers still receive
|
|
113
|
+
* fresh `__partialJson` on every reveal frame.
|
|
53
114
|
*
|
|
54
115
|
* Reveal units are UTF-16 code units of the raw stream, not graphemes —
|
|
55
116
|
* the prefix goes through a JSON parser rather than straight to the screen,
|
|
@@ -71,12 +132,8 @@ export class ToolArgsRevealController {
|
|
|
71
132
|
* args to render right now. With smoothing disabled the full target passes
|
|
72
133
|
* through in the caller's legacy shape (`{ ...args, __partialJson }`).
|
|
73
134
|
*/
|
|
74
|
-
setTarget(
|
|
75
|
-
|
|
76
|
-
partialJson: string,
|
|
77
|
-
rawInput: boolean,
|
|
78
|
-
fullArgs: Record<string, unknown>,
|
|
79
|
-
): Record<string, unknown> {
|
|
135
|
+
setTarget(id: string, partialJson: string, target: ToolArgsRevealTarget): Record<string, unknown> {
|
|
136
|
+
const { rawInput, exposeRawPartialJson, fullArgs } = target;
|
|
80
137
|
if (!this.#getSmoothStreaming()) {
|
|
81
138
|
// Toggle may flip mid-call: drop any live entry so ticks stop.
|
|
82
139
|
this.#entries.delete(id);
|
|
@@ -84,18 +141,34 @@ export class ToolArgsRevealController {
|
|
|
84
141
|
}
|
|
85
142
|
let entry = this.#entries.get(id);
|
|
86
143
|
if (!entry) {
|
|
87
|
-
entry = {
|
|
144
|
+
entry = {
|
|
145
|
+
component: undefined,
|
|
146
|
+
target: partialJson,
|
|
147
|
+
revealed: 0,
|
|
148
|
+
rawInput,
|
|
149
|
+
exposeRawPartialJson,
|
|
150
|
+
parsedArgs: {},
|
|
151
|
+
parsedLen: 0,
|
|
152
|
+
displayArgs: initialDisplayArgs(),
|
|
153
|
+
displayPrefix: "",
|
|
154
|
+
};
|
|
88
155
|
this.#entries.set(id, entry);
|
|
89
156
|
} else {
|
|
157
|
+
if (entry.rawInput !== rawInput || entry.exposeRawPartialJson !== exposeRawPartialJson) {
|
|
158
|
+
entry.rawInput = rawInput;
|
|
159
|
+
entry.exposeRawPartialJson = exposeRawPartialJson;
|
|
160
|
+
resetDisplayState(entry);
|
|
161
|
+
}
|
|
90
162
|
// Streams only append; a non-prefix target means a rewind — snap into range.
|
|
91
163
|
if (!partialJson.startsWith(entry.target)) {
|
|
92
164
|
entry.revealed = Math.min(entry.revealed, partialJson.length);
|
|
165
|
+
resetDisplayState(entry);
|
|
93
166
|
}
|
|
94
167
|
entry.target = partialJson;
|
|
95
168
|
}
|
|
96
169
|
entry.revealed = clampSliceEnd(entry.target, entry.revealed);
|
|
97
170
|
this.#syncTimer();
|
|
98
|
-
return
|
|
171
|
+
return displayArgsForPrefix(entry, entry.target.slice(0, entry.revealed)).args;
|
|
99
172
|
}
|
|
100
173
|
|
|
101
174
|
/** Attach the component future ticks push frames into. */
|
|
@@ -118,7 +191,7 @@ export class ToolArgsRevealController {
|
|
|
118
191
|
flushAll(): void {
|
|
119
192
|
for (const [id, entry] of this.#entries) {
|
|
120
193
|
if (entry.component && entry.revealed < entry.target.length) {
|
|
121
|
-
entry.component.updateArgs(
|
|
194
|
+
entry.component.updateArgs(displayArgsForPrefix(entry, entry.target, true).args, id);
|
|
122
195
|
}
|
|
123
196
|
}
|
|
124
197
|
this.#entries.clear();
|
|
@@ -157,15 +230,20 @@ export class ToolArgsRevealController {
|
|
|
157
230
|
|
|
158
231
|
#tick(): void {
|
|
159
232
|
let advanced = false;
|
|
233
|
+
let rendered = false;
|
|
160
234
|
for (const [id, entry] of this.#entries) {
|
|
161
235
|
const backlog = entry.target.length - entry.revealed;
|
|
162
236
|
if (backlog <= 0 || !entry.component) continue;
|
|
163
237
|
entry.revealed = clampSliceEnd(entry.target, entry.revealed + nextStep(backlog));
|
|
164
|
-
|
|
238
|
+
const display = displayArgsForPrefix(entry, entry.target.slice(0, entry.revealed));
|
|
239
|
+
if (display.changed) {
|
|
240
|
+
entry.component.updateArgs(display.args, id);
|
|
241
|
+
rendered = true;
|
|
242
|
+
}
|
|
165
243
|
advanced = true;
|
|
166
244
|
}
|
|
167
245
|
if (advanced) {
|
|
168
|
-
this.#requestRender();
|
|
246
|
+
if (rendered) this.#requestRender();
|
|
169
247
|
} else {
|
|
170
248
|
// Every entry caught up (or unbound); setTarget restarts on growth.
|
|
171
249
|
this.#stopTimer();
|
package/src/prompts/bench.md
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
Schema and statistics: orders(id, customer_id, status, total) holds 50,000,000 rows with 5 distinct status values; customers(id, country, segment) holds 4,000,000 rows across 200 countries; line_items(order_id, product_id, qty) holds 300,000,000 rows; products(id, category, price) holds 80,000 rows across 600 categories. The query reports total revenue per product category for shipped orders placed by customers in one given country.
|
|
4
|
-
|
|
5
|
-
Reason step by step and keep going: estimate the selectivity and output cardinality of each predicate and each join, then enumerate every join order over the four tables and derive the cost of each under both nested-loop and hash-join operators, weigh index access against full scans for each table, decide where the aggregation belongs and whether a partial pre-aggregation or a semi-join reduction earns its keep, and account for a memory limit that forces a hash build side to spill to disk. Compute the number behind every decision before you commit to it; when you finish one candidate plan, move on to the next and derive its cost too, and choose a winner only after you have costed the whole field. Never assert a choice you have not justified with an estimate.
|
|
1
|
+
Write a detailed, four-paragraph explanation of how a web browser renders a webpage. Cover the process from receiving the initial HTML payload to painting pixels on the screen. Include the construction of the DOM and CSSOM, the render tree, layout, and painting.
|
|
6
2
|
|
|
7
3
|
Form:
|
|
8
|
-
- Plain paragraphs only: no headings, no lists, no code fences, no
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
Output only the analysis.
|
|
4
|
+
- Plain paragraphs only: no headings, no lists, no code fences, no preamble.
|
|
5
|
+
- Do not summarize early; keep explaining until you reach the token limit.
|
|
6
|
+
- Output only the explanation.
|
package/src/prompts/tools/irc.md
CHANGED
|
@@ -1,33 +1,23 @@
|
|
|
1
|
-
Send
|
|
1
|
+
Send and receive short text messages between the agents running in this process.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
- `op: "list"` — peers with status (`running` | `idle` | `parked`), unread count, parent, last activity. Use when unsure who exists.
|
|
6
|
-
- `op: "send"` — fire-and-forget; returns per-recipient receipts immediately, NEVER waits for the recipient to act. Outcomes: delivered, or `failed` (unreachable). `to: "all"` broadcasts to live peers.
|
|
7
|
-
- Messaging an `idle`/`parked` peer wakes it — no separate revive call.
|
|
8
|
-
- `op: "wait"` — block for a message (optionally only `from` one peer); consumes + returns it. Timeout = clean "no message", not an error.
|
|
9
|
-
- `op: "inbox"` — drain pending messages without blocking.
|
|
10
|
-
- Replies arrive only when the recipient sends one; don't interrogate a peer for status.
|
|
11
|
-
</instruction>
|
|
3
|
+
# Addressing and Discovery
|
|
4
|
+
The main agent is always `Main`. Subagents inherit their task ID (e.g., `AuthLoader`). If you don't know who is currently running, use `op: "list"` to view all peers alongside their status, unread message count, and recent activity. Address peers by their exact ID from the roster; NEVER invent names.
|
|
12
5
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
6
|
+
# Messaging Rules
|
|
7
|
+
Use `op: "send"` to deliver a message to a specific peer or broadcast to `"all"`.
|
|
8
|
+
- **Fire and forget:** Sending NEVER blocks. You get delivery receipts immediately (`delivered` or `failed`). Do not wait around—send your message and keep working. If a receipt says `failed`, the peer is gone; do not retry.
|
|
9
|
+
- **Waking peers:** Sending a message to an `idle` or `parked` agent automatically wakes them up.
|
|
10
|
+
- **Answering:** When replying to a question, use `op: "send"`, lead directly with your answer (NEVER quote the original message), and set `replyTo` so the recipient can correlate it.
|
|
11
|
+
- **Format:** Messages MUST be plain prose. NEVER send JSON status objects. Keep it terse and share paths via `local://` or `artifact://` URLs, not pasted blobs.
|
|
19
12
|
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
# Waiting and Inboxes
|
|
14
|
+
Messages only arrive when the peer actively sends one—do not interrogate a peer for status.
|
|
15
|
+
- If you are completely blocked and MUST wait for an answer, use `op: "wait"` (or `await: true` on a send). This blocks your turn until a message arrives. If it times out, that just means "no message arrived", not a failure.
|
|
16
|
+
- To check for messages without blocking, use `op: "inbox"` to drain your queue.
|
|
22
17
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
- **Answer expected questions** via `irc send` to the sender (finish your current step first).
|
|
30
|
-
- **Stay terse.** One question per send; share files via `local://`/`memory://`/`artifact://` URLs, never pasted blobs.
|
|
31
|
-
- **Address peers by exact id** from `op: "list"` (e.g. `AuthLoader`, `Main`). NEVER invent friendly names.
|
|
32
|
-
- **NEVER IRC what a tool answers.** A `read`, grep, or build resolves it? Do that first.
|
|
33
|
-
</etiquette>
|
|
18
|
+
# When to Coordinate
|
|
19
|
+
Message peers instead of guessing, duplicating work, or spying.
|
|
20
|
+
- Use IRC when you hit an unexpected state (e.g., missing files) or an out-of-scope decision. DM `Main` or your spawner for guidance.
|
|
21
|
+
- If you overlap with another agent's work or need a file they are touching, DM them before editing.
|
|
22
|
+
- NEVER use shell tools, grep, or read other sessions' files to figure out what a peer is doing. Message them directly.
|
|
23
|
+
- NEVER use IRC for something a tool can answer (e.g., grepping codebase, running a build).
|
package/src/prompts/tools/job.md
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
Manages async background tasks (e.g. bash scripts, subagents).
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Background tasks deliver their results automatically the moment they finish. You NEVER need to poll to retrieve output. Only use this tool if you need to intervene in the lifecycle of a task.
|
|
4
4
|
|
|
5
|
-
#
|
|
5
|
+
# Interventions
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- Use only when genuinely blocked with no other work.
|
|
13
|
-
- Completed jobs include final output.
|
|
14
|
-
|
|
15
|
-
## `cancel: [id, …]`
|
|
16
|
-
Stop running jobs.
|
|
17
|
-
- Use when a job is stalled, hung, or no longer needed.
|
|
7
|
+
- **Block and wait:** Pass `poll` with specific job IDs when you are completely blocked and cannot do any other work. The call returns as soon as one watched job finishes or the wait window elapses — NOT when all of them finish; re-issue to keep waiting.
|
|
8
|
+
- To watch EVERY running job, issue a call with NO fields at all (no `poll`, no `cancel`, no `list`). NEVER pass an array of every running ID.
|
|
9
|
+
- A finished job's output is included in the wait result.
|
|
10
|
+
- **Stop execution:** Pass `cancel` with job IDs to kill jobs that have hung, stalled, or are no longer needed. A cancel-only call returns immediately.
|
|
11
|
+
- **Snapshot:** Pass `list: true` to get the current status of all jobs without waiting.
|
package/src/prompts/tools/lsp.md
CHANGED
|
@@ -1,39 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
Symbol-aware code intelligence from language servers — the accurate path for navigation, refactors, and diagnostics where text search or edits would miss callsites.
|
|
2
2
|
|
|
3
3
|
<operations>
|
|
4
|
-
- `
|
|
5
|
-
- `definition
|
|
6
|
-
- `
|
|
7
|
-
- `
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- `
|
|
11
|
-
- `
|
|
12
|
-
- `rename_file
|
|
13
|
-
- `code_actions`: list quick-fixes/refactors/import actions; apply one when `apply: true` + `query` matches title or index
|
|
14
|
-
- `status`: active language servers
|
|
15
|
-
- `capabilities`: per-server capabilities
|
|
16
|
-
- `request`: raw LSP request — `query` = method name (e.g. `rust-analyzer/expandMacro`, `workspace/executeCommand`); `payload` = JSON params
|
|
17
|
-
- `reload`: restart one server (via `file`) or all (`file: "*"`)
|
|
18
|
-
</operations>
|
|
4
|
+
Position-based — pass `file` + `line` + `symbol` (substring on that line; append `#N` for the Nth match, e.g. `kind#2`):
|
|
5
|
+
- `definition`, `type_definition`, `implementation`, `references`, `hover` — standard LSP lookups
|
|
6
|
+
- `rename` — rename the symbol everywhere; **applies by default**, `apply: false` previews; needs `new_name`
|
|
7
|
+
- `code_actions` — quick-fixes/refactors/imports at that position; lists by default (`query` filters by kind, e.g. `quickfix`, `source.organizeImports`), **applies one only with `apply: true` + `query`** (then `query` = action title substring or numeric index)
|
|
8
|
+
|
|
9
|
+
File / workspace:
|
|
10
|
+
- `diagnostics` — errors/warnings for a path, a glob (`src/**/*.ts`), or the whole workspace (`file: "*"`)
|
|
11
|
+
- `symbols` — `file` lists that file's symbols; `file: "*"` + `query` searches the workspace
|
|
12
|
+
- `rename_file` — move `file` → `new_name` on disk AND rewrite imports/references through the server; applies by default
|
|
19
13
|
|
|
20
|
-
|
|
21
|
-
- `
|
|
22
|
-
- `
|
|
23
|
-
- `
|
|
24
|
-
|
|
25
|
-
- `new_name`: required for `rename` (new identifier) and `rename_file` (destination path)
|
|
26
|
-
- `apply`: apply edits for rename/rename_file/code_actions (default true for rename/rename_file; code_actions list mode unless true)
|
|
27
|
-
- `payload`: JSON params for `action: request`
|
|
28
|
-
- `timeout`: seconds
|
|
29
|
-
</parameters>
|
|
14
|
+
Servers:
|
|
15
|
+
- `status`, `capabilities` — what's running / per-server capabilities (one via `file`, all via `*`)
|
|
16
|
+
- `reload` — restart one server (`file`) or all (`*`); `reload *` also re-reads project LSP config
|
|
17
|
+
- `request` — raw escape hatch: `query` = method (`rust-analyzer/expandMacro`, `workspace/executeCommand`), `payload` = JSON params (else auto-built from `file`/`line`/`symbol`)
|
|
18
|
+
</operations>
|
|
30
19
|
|
|
31
20
|
<caution>
|
|
32
|
-
-
|
|
21
|
+
- `line` is 1-indexed. Project-aware `definition`/`references`/`rename` ERROR without `symbol` rather than guess the wrong identifier; a missing match or out-of-range `#N` is an explicit error, never a silent fallback.
|
|
33
22
|
</caution>
|
|
34
23
|
|
|
35
24
|
<critical>
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
25
|
+
- Symbol-aware work (rename, references, definition/type/impl, code actions) MUST use `lsp` whenever a server is available — it follows shadowing, re-exports, and cross-file usages that text tools miss.
|
|
26
|
+
- NEVER do a cross-file rename with `ast_edit`, `sed`, or hand edits when `lsp` `rename`/`rename_file` can — text renames silently drop callsites.
|
|
27
|
+
- Reach for `code_actions` on imports, quick-fixes, and server-known refactors before editing by hand.
|
|
39
28
|
</critical>
|