@oh-my-pi/pi-coding-agent 16.1.6 → 16.1.8
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 +52 -0
- package/dist/cli.js +3160 -3139
- package/dist/types/cli/session-picker.d.ts +0 -1
- package/dist/types/cli/tiny-models-cli.d.ts +2 -0
- package/dist/types/collab/protocol.d.ts +20 -1
- package/dist/types/config/models-config-schema.d.ts +10 -0
- package/dist/types/config/models-config.d.ts +6 -0
- package/dist/types/config/settings-schema.d.ts +57 -6
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +5 -1
- package/dist/types/internal-urls/filesystem-resource.d.ts +9 -0
- package/dist/types/mcp/loader.d.ts +3 -2
- package/dist/types/mcp/manager.d.ts +4 -3
- package/dist/types/mcp/startup-events.d.ts +21 -4
- package/dist/types/mnemopi/config.d.ts +1 -0
- package/dist/types/modes/components/agent-hub.d.ts +1 -0
- package/dist/types/modes/components/agent-transcript-viewer.d.ts +1 -0
- package/dist/types/modes/components/assistant-message.d.ts +5 -1
- package/dist/types/modes/components/btw-panel.d.ts +2 -0
- package/dist/types/modes/components/chat-transcript-builder.d.ts +1 -0
- package/dist/types/modes/components/session-selector.d.ts +0 -2
- package/dist/types/modes/controllers/btw-controller.d.ts +2 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/controllers/streaming-reveal.d.ts +3 -2
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/types.d.ts +3 -0
- package/dist/types/session/agent-session.d.ts +5 -3
- package/dist/types/session/session-history-format.d.ts +25 -0
- package/dist/types/session/snapcompact-inline.d.ts +1 -1
- package/dist/types/slash-commands/builtin-registry.d.ts +7 -4
- package/dist/types/slash-commands/types.d.ts +2 -0
- package/dist/types/system-prompt.d.ts +1 -0
- package/dist/types/tiny/models.d.ts +11 -7
- package/dist/types/tools/todo.d.ts +1 -0
- package/dist/types/utils/thinking-display.d.ts +2 -0
- package/package.json +12 -12
- package/src/advisor/__tests__/advisor.test.ts +104 -0
- package/src/advisor/runtime.ts +38 -2
- package/src/cli/session-picker.ts +1 -2
- package/src/cli/tiny-models-cli.ts +7 -2
- package/src/collab/guest.ts +172 -20
- package/src/collab/host.ts +47 -5
- package/src/collab/protocol.ts +16 -1
- package/src/config/models-config-schema.ts +1 -0
- package/src/config/settings-schema.ts +59 -5
- package/src/edit/renderer.ts +8 -12
- package/src/extensibility/legacy-pi-ai-shim.ts +16 -4
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/internal-urls/filesystem-resource.ts +34 -0
- package/src/internal-urls/local-protocol.ts +7 -1
- package/src/internal-urls/memory-protocol.ts +5 -1
- package/src/internal-urls/skill-protocol.ts +20 -4
- package/src/internal-urls/vault-protocol.ts +5 -2
- package/src/main.ts +8 -8
- package/src/mcp/loader.ts +4 -3
- package/src/mcp/manager.ts +35 -15
- package/src/mcp/startup-events.ts +106 -11
- package/src/mnemopi/config.ts +2 -0
- package/src/mnemopi/state.ts +3 -1
- package/src/modes/components/agent-hub.ts +4 -0
- package/src/modes/components/agent-transcript-viewer.ts +2 -0
- package/src/modes/components/assistant-message.ts +217 -18
- package/src/modes/components/btw-panel.ts +15 -3
- package/src/modes/components/chat-transcript-builder.ts +8 -2
- package/src/modes/components/model-selector.ts +72 -9
- package/src/modes/components/omfg-panel.ts +1 -1
- package/src/modes/components/session-selector.ts +4 -9
- package/src/modes/components/snapcompact-shape-preview.ts +1 -1
- package/src/modes/components/tool-execution.ts +10 -2
- package/src/modes/controllers/btw-controller.ts +32 -7
- package/src/modes/controllers/event-controller.ts +24 -4
- package/src/modes/controllers/input-controller.ts +43 -21
- package/src/modes/controllers/selector-controller.ts +23 -13
- package/src/modes/controllers/streaming-reveal.ts +78 -20
- package/src/modes/controllers/todo-command-controller.ts +9 -10
- package/src/modes/interactive-mode.ts +83 -8
- package/src/modes/types.ts +3 -0
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/prompts/advisor/system.md +1 -1
- package/src/prompts/system/side-channel-no-tools.md +3 -0
- package/src/prompts/system/system-prompt.md +164 -156
- package/src/sdk.ts +12 -8
- package/src/session/agent-session.ts +349 -84
- package/src/session/history-storage.ts +15 -16
- package/src/session/session-history-format.ts +41 -1
- package/src/session/snapcompact-inline.ts +9 -6
- package/src/slash-commands/builtin-registry.ts +147 -21
- package/src/slash-commands/helpers/todo.ts +12 -6
- package/src/slash-commands/types.ts +2 -0
- package/src/system-prompt.ts +6 -11
- package/src/tiny/models.ts +7 -3
- package/src/tiny/title-client.ts +8 -2
- package/src/tiny/worker.ts +1 -0
- package/src/tools/search.ts +10 -1
- package/src/tools/sqlite-reader.ts +59 -3
- package/src/tools/todo.ts +6 -0
- package/src/tools/write.ts +4 -6
- package/src/utils/thinking-display.ts +78 -0
|
@@ -38,24 +38,23 @@ class AsyncDrain<T> {
|
|
|
38
38
|
let queue = this.#queue;
|
|
39
39
|
if (!queue) {
|
|
40
40
|
this.#queue = queue = [];
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
resolve(hnd(queue!));
|
|
48
|
-
} catch (error) {
|
|
49
|
-
reject(error);
|
|
41
|
+
const { promise, resolve, reject } = Promise.withResolvers<void>();
|
|
42
|
+
const exec = (): void => {
|
|
43
|
+
try {
|
|
44
|
+
if (this.#queue === queue) {
|
|
45
|
+
this.#queue = undefined;
|
|
50
46
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
setTimeout(exec, this.delayMs);
|
|
55
|
-
} else {
|
|
56
|
-
queueMicrotask(exec);
|
|
47
|
+
resolve(hnd(queue!));
|
|
48
|
+
} catch (error) {
|
|
49
|
+
reject(error);
|
|
57
50
|
}
|
|
58
|
-
}
|
|
51
|
+
};
|
|
52
|
+
if (this.delayMs > 0) {
|
|
53
|
+
setTimeout(exec, this.delayMs);
|
|
54
|
+
} else {
|
|
55
|
+
queueMicrotask(exec);
|
|
56
|
+
}
|
|
57
|
+
this.#promise = promise;
|
|
59
58
|
}
|
|
60
59
|
queue.push(value);
|
|
61
60
|
return this.#promise;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
10
10
|
import type { AssistantMessage, ImageContent, TextContent, ToolResultMessage } from "@oh-my-pi/pi-ai";
|
|
11
|
+
import { escapeXmlText } from "@oh-my-pi/pi-utils";
|
|
11
12
|
import { INTENT_FIELD } from "@oh-my-pi/pi-wire";
|
|
12
13
|
import type {
|
|
13
14
|
BashExecutionMessage,
|
|
@@ -28,6 +29,17 @@ export interface HistoryFormatOptions {
|
|
|
28
29
|
includeToolIntent?: boolean;
|
|
29
30
|
/** Render watched-session roles as inline `**agent**:` / `**user**:` labels (collapsing consecutive same-role messages) instead of `## ` headings, so a primary transcript embedded inside an advisor turn stays visually distinct. */
|
|
30
31
|
watchedRoles?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Expand the primary agent's injected constraint context — plan mode's rules
|
|
34
|
+
* (`plan-mode-context`) and the approved plan it implements
|
|
35
|
+
* (`plan-mode-reference`) — verbatim instead of as a truncated one-liner,
|
|
36
|
+
* wrapped in a `<primary-context>` tag so a reviewer reads it as the primary's
|
|
37
|
+
* instructions, not its own. The advisor sets this: a truncated rule (plan
|
|
38
|
+
* mode's "NEVER create files … except the plan file") makes it raise false
|
|
39
|
+
* blockers. See {@link PRIMARY_CONTEXT_CUSTOM_TYPES}. Other custom messages
|
|
40
|
+
* still collapse to a one-liner.
|
|
41
|
+
*/
|
|
42
|
+
expandPrimaryContext?: boolean;
|
|
31
43
|
}
|
|
32
44
|
|
|
33
45
|
/** Max length of the primary-arg summary inside `→ tool(...)` lines. */
|
|
@@ -156,6 +168,21 @@ function executionLine(
|
|
|
156
168
|
return `→ ${kind}! ${oneLine(source)} ⇒ ${status} · ${lines} ${lines === 1 ? "line" : "lines"}`;
|
|
157
169
|
}
|
|
158
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Hidden custom messages that inject the primary agent's operative *constraints*
|
|
173
|
+
* — plan mode's rules and the approved plan it implements. A reviewer (the
|
|
174
|
+
* advisor) must read these verbatim; truncating them hides load-bearing
|
|
175
|
+
* exceptions (e.g. plan mode permits exactly one plan file). Every other custom
|
|
176
|
+
* type stays a one-liner.
|
|
177
|
+
*
|
|
178
|
+
* Deliberately excludes `goal-mode-context`: its body carries live budget
|
|
179
|
+
* counters (tokens/seconds used) that change every turn, so it can neither be
|
|
180
|
+
* deduped against a prior copy nor expanded each turn without flooding the
|
|
181
|
+
* reviewer — and its constraints don't drive the file-write misreads this
|
|
182
|
+
* targets.
|
|
183
|
+
*/
|
|
184
|
+
export const PRIMARY_CONTEXT_CUSTOM_TYPES: ReadonlySet<string> = new Set(["plan-mode-context", "plan-mode-reference"]);
|
|
185
|
+
|
|
159
186
|
/** One-liner for custom/hook messages: `[irc] A → B: body…`. */
|
|
160
187
|
function customOneLiner(msg: CustomMessage | HookMessage): string {
|
|
161
188
|
const details = (msg.details ?? {}) as Record<string, unknown>;
|
|
@@ -279,7 +306,20 @@ export function formatSessionHistoryMarkdown(messages: unknown[], opts?: History
|
|
|
279
306
|
}
|
|
280
307
|
case "custom":
|
|
281
308
|
case "hookMessage": {
|
|
282
|
-
|
|
309
|
+
const custom = msg as CustomMessage | HookMessage;
|
|
310
|
+
if (opts?.expandPrimaryContext && PRIMARY_CONTEXT_CUSTOM_TYPES.has(custom.customType)) {
|
|
311
|
+
const text = contentToText(custom.content).trim();
|
|
312
|
+
if (text) {
|
|
313
|
+
lines.push(
|
|
314
|
+
`<primary-context kind="${custom.customType}">`,
|
|
315
|
+
escapeXmlText(text),
|
|
316
|
+
"</primary-context>",
|
|
317
|
+
"",
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
} else {
|
|
321
|
+
lines.push(customOneLiner(custom), "");
|
|
322
|
+
}
|
|
283
323
|
lastWatchedLabel = undefined;
|
|
284
324
|
break;
|
|
285
325
|
}
|
|
@@ -414,7 +414,7 @@ export class SnapcompactInlineTransformer {
|
|
|
414
414
|
private readonly onToolResultSavings?: SnapcompactSavingsSink,
|
|
415
415
|
) {}
|
|
416
416
|
|
|
417
|
-
transform(context: Context, model: Model): Context {
|
|
417
|
+
async transform(context: Context, model: Model): Promise<Context> {
|
|
418
418
|
// Vision gate: providers silently DROP images on text-only models —
|
|
419
419
|
// rendering would lose the content entirely.
|
|
420
420
|
if (!model.input.includes("image")) return context;
|
|
@@ -482,7 +482,7 @@ export class SnapcompactInlineTransformer {
|
|
|
482
482
|
for (const swap of plan.toolResults) {
|
|
483
483
|
const target = targets.get(swap.id);
|
|
484
484
|
if (!target) continue;
|
|
485
|
-
const frames = this.#framesFor(this.#toolCache, swap.id, target.text, shape);
|
|
485
|
+
const frames = await this.#framesFor(this.#toolCache, swap.id, target.text, shape);
|
|
486
486
|
messages[target.index] = { ...target.message, content: [{ type: "text", text: toolResultNote }, ...frames] };
|
|
487
487
|
changed = true;
|
|
488
488
|
savings.push({
|
|
@@ -506,7 +506,10 @@ export class SnapcompactInlineTransformer {
|
|
|
506
506
|
if (!cached || cached.hash !== hash) {
|
|
507
507
|
cached = {
|
|
508
508
|
hash,
|
|
509
|
-
frames: snapcompact.renderMany(systemPromptTarget.text, {
|
|
509
|
+
frames: await snapcompact.renderMany(systemPromptTarget.text, {
|
|
510
|
+
shape,
|
|
511
|
+
maxFrames: MAX_SYSTEM_PROMPT_FRAMES,
|
|
512
|
+
}),
|
|
510
513
|
};
|
|
511
514
|
this.#systemCache = cached;
|
|
512
515
|
}
|
|
@@ -526,16 +529,16 @@ export class SnapcompactInlineTransformer {
|
|
|
526
529
|
return { ...context, systemPrompt, messages };
|
|
527
530
|
}
|
|
528
531
|
|
|
529
|
-
#framesFor(
|
|
532
|
+
async #framesFor(
|
|
530
533
|
cache: Map<string, FrameCacheEntry>,
|
|
531
534
|
key: string,
|
|
532
535
|
text: string,
|
|
533
536
|
shape: snapcompact.Shape,
|
|
534
|
-
): ImageContent[] {
|
|
537
|
+
): Promise<ImageContent[]> {
|
|
535
538
|
const hash = Bun.hash(text);
|
|
536
539
|
const cached = cache.get(key);
|
|
537
540
|
if (cached && cached.hash === hash) return cached.frames;
|
|
538
|
-
const frames = snapcompact.renderMany(text, { shape });
|
|
541
|
+
const frames = await snapcompact.renderMany(text, { shape });
|
|
539
542
|
cache.set(key, { hash, frames });
|
|
540
543
|
return frames;
|
|
541
544
|
}
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
MarketplaceManager,
|
|
25
25
|
} from "../extensibility/plugins/marketplace";
|
|
26
26
|
import { resolveMemoryBackend } from "../memory-backend";
|
|
27
|
+
import { describeLoopLimitRuntime } from "../modes/loop-limit";
|
|
27
28
|
import { theme } from "../modes/theme/theme";
|
|
28
29
|
import type { InteractiveModeContext } from "../modes/types";
|
|
29
30
|
import type { AgentSession, FreshSessionResult } from "../session/agent-session";
|
|
@@ -58,6 +59,12 @@ export type { BuiltinSlashCommand, SubcommandDef } from "./types";
|
|
|
58
59
|
/** TUI-specific runtime accepted by `executeBuiltinSlashCommand`. */
|
|
59
60
|
export type BuiltinSlashCommandRuntime = TuiSlashCommandRuntime;
|
|
60
61
|
|
|
62
|
+
export interface TuiBuiltinSlashCommand extends BuiltinSlashCommand {
|
|
63
|
+
getArgumentCompletions?: (prefix: string) => AutocompleteItem[] | null;
|
|
64
|
+
getInlineHint?: (argumentText: string) => string | null;
|
|
65
|
+
getAutocompleteDescription?: () => string | undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
61
68
|
function refreshStatusLine(ctx: InteractiveModeContext): void {
|
|
62
69
|
ctx.statusLine.invalidate();
|
|
63
70
|
ctx.updateEditorTopBorder();
|
|
@@ -77,6 +84,17 @@ function formatFastModeStatus(session: AgentSession): string {
|
|
|
77
84
|
}
|
|
78
85
|
}
|
|
79
86
|
|
|
87
|
+
const AUTOCOMPLETE_DETAIL_LIMIT = 48;
|
|
88
|
+
|
|
89
|
+
function shortDetail(value: string, limit = AUTOCOMPLETE_DETAIL_LIMIT): string {
|
|
90
|
+
const singleLine = value.replace(/\s+/g, " ").trim();
|
|
91
|
+
return singleLine.length <= limit ? singleLine : `${singleLine.slice(0, limit - 1)}…`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function formatTokenCount(value: number): string {
|
|
95
|
+
return value.toLocaleString();
|
|
96
|
+
}
|
|
97
|
+
|
|
80
98
|
/** Scheme-less display form of a browser deep link: accent + underline, OSC-8 linked to the full URL. */
|
|
81
99
|
function collabWebLinkClickable(webLink: string): string {
|
|
82
100
|
const display = theme.fg("accent", `\x1b[4m${webLink.replace(/^https?:\/\//, "")}\x1b[24m`);
|
|
@@ -253,6 +271,15 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
253
271
|
description: "Toggle plan mode (agent plans before executing)",
|
|
254
272
|
inlineHint: "[prompt]",
|
|
255
273
|
allowArgs: true,
|
|
274
|
+
getTuiAutocompleteDescription: runtime => {
|
|
275
|
+
if (!runtime.ctx.settings.get("plan.enabled" as SettingPath)) return "Plan: disabled in settings";
|
|
276
|
+
if (runtime.ctx.planModeEnabled) {
|
|
277
|
+
const planFile = runtime.ctx.planModePlanFilePath;
|
|
278
|
+
return `Plan: on${planFile ? ` (${path.basename(planFile)})` : ""}`;
|
|
279
|
+
}
|
|
280
|
+
if (runtime.ctx.goalModeEnabled) return "Plan: blocked by goal mode";
|
|
281
|
+
return "Plan: off";
|
|
282
|
+
},
|
|
256
283
|
handleTui: async (command, runtime) => {
|
|
257
284
|
const hadArgs = !!command.args;
|
|
258
285
|
await runtime.ctx.handlePlanModeCommand(command.args || undefined);
|
|
@@ -265,6 +292,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
265
292
|
{
|
|
266
293
|
name: "plan-review",
|
|
267
294
|
description: "Re-open the plan review for the latest plan (plan mode only)",
|
|
295
|
+
getTuiAutocompleteDescription: runtime =>
|
|
296
|
+
runtime.ctx.planModeEnabled ? "Plan review: available" : "Plan review: plan mode inactive",
|
|
268
297
|
handleTui: async (_command, runtime) => {
|
|
269
298
|
await runtime.ctx.openPlanReview();
|
|
270
299
|
runtime.ctx.editor.setText("");
|
|
@@ -283,6 +312,12 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
283
312
|
],
|
|
284
313
|
inlineHint: "[objective]",
|
|
285
314
|
allowArgs: true,
|
|
315
|
+
getTuiAutocompleteDescription: runtime => {
|
|
316
|
+
if (!runtime.ctx.settings.get("goal.enabled" as SettingPath)) return "Goal: disabled in settings";
|
|
317
|
+
if (runtime.ctx.planModeEnabled) return "Goal: blocked by plan mode";
|
|
318
|
+
const state = runtime.ctx.session.getGoalModeState();
|
|
319
|
+
return state ? `Goal: ${state.goal.status} (${shortDetail(state.goal.objective)})` : "Goal: off";
|
|
320
|
+
},
|
|
286
321
|
handleTui: async (command, runtime) => {
|
|
287
322
|
const hadArgs = !!command.args;
|
|
288
323
|
await runtime.ctx.handleGoalModeCommand(command.args || undefined);
|
|
@@ -308,6 +343,12 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
308
343
|
"Toggle loop mode. While enabled, the next prompt you send re-submits after every yield. Esc cancels the current iteration; /loop again to disable.",
|
|
309
344
|
inlineHint: "[count|duration] [prompt]",
|
|
310
345
|
allowArgs: true,
|
|
346
|
+
getTuiAutocompleteDescription: runtime => {
|
|
347
|
+
if (!runtime.ctx.loopModeEnabled) return "Loop: off";
|
|
348
|
+
if (runtime.ctx.loopLimit) return `Loop: on (${describeLoopLimitRuntime(runtime.ctx.loopLimit)})`;
|
|
349
|
+
if (runtime.ctx.loopPrompt) return "Loop: on (repeating prompt)";
|
|
350
|
+
return "Loop: on (waiting for next prompt)";
|
|
351
|
+
},
|
|
311
352
|
handleTui: async (command, runtime) => {
|
|
312
353
|
const prompt = await runtime.ctx.handleLoopCommand(command.args);
|
|
313
354
|
runtime.ctx.editor.setText("");
|
|
@@ -321,6 +362,10 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
321
362
|
aliases: ["models"],
|
|
322
363
|
description: "Switch model for this session",
|
|
323
364
|
acpDescription: "Show current model selection",
|
|
365
|
+
getTuiAutocompleteDescription: runtime => {
|
|
366
|
+
const model = runtime.ctx.session.model;
|
|
367
|
+
return model ? `Model: ${model.provider}/${model.id}` : "Model: none selected";
|
|
368
|
+
},
|
|
324
369
|
handle: async (command, runtime) => {
|
|
325
370
|
if (command.args) {
|
|
326
371
|
const modelId = command.args.trim();
|
|
@@ -359,6 +404,10 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
359
404
|
{
|
|
360
405
|
name: "switch",
|
|
361
406
|
description: "Switch model for this session (same as alt+p)",
|
|
407
|
+
getTuiAutocompleteDescription: runtime => {
|
|
408
|
+
const model = runtime.ctx.session.model;
|
|
409
|
+
return model ? `Model: ${model.provider}/${model.id}` : "Model: none selected";
|
|
410
|
+
},
|
|
362
411
|
handleTui: (_command, runtime) => {
|
|
363
412
|
runtime.ctx.showModelSelector({ temporaryOnly: true });
|
|
364
413
|
runtime.ctx.editor.setText("");
|
|
@@ -375,6 +424,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
375
424
|
{ name: "status", description: "Show fast mode status" },
|
|
376
425
|
],
|
|
377
426
|
allowArgs: true,
|
|
427
|
+
getTuiAutocompleteDescription: runtime => `Fast: ${formatFastModeStatus(runtime.ctx.session)}`,
|
|
378
428
|
handle: async (command, runtime) => {
|
|
379
429
|
const arg = command.args.toLowerCase();
|
|
380
430
|
if (!arg || arg === "toggle") {
|
|
@@ -442,6 +492,12 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
442
492
|
{ name: "dump", description: "Copy the advisor's transcript to clipboard", usage: "[raw]" },
|
|
443
493
|
],
|
|
444
494
|
allowArgs: true,
|
|
495
|
+
getTuiAutocompleteDescription: runtime => {
|
|
496
|
+
const stats = runtime.ctx.session.getAdvisorStats();
|
|
497
|
+
if (stats.active && stats.model) return `Advisor: on (${stats.model.provider}/${stats.model.id})`;
|
|
498
|
+
if (stats.configured) return "Advisor: configured, no model";
|
|
499
|
+
return "Advisor: off";
|
|
500
|
+
},
|
|
445
501
|
handle: async (command, runtime) => {
|
|
446
502
|
const { verb, rest } = parseSubcommand(command.args);
|
|
447
503
|
if (!verb || verb === "toggle") {
|
|
@@ -603,6 +659,14 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
603
659
|
{ name: "stop", description: "Stop sharing" },
|
|
604
660
|
],
|
|
605
661
|
allowArgs: true,
|
|
662
|
+
getTuiAutocompleteDescription: runtime => {
|
|
663
|
+
if (runtime.ctx.collabHost) {
|
|
664
|
+
return `Collab: hosting (${Math.max(0, runtime.ctx.collabHost.participants.length - 1)} guests)`;
|
|
665
|
+
}
|
|
666
|
+
if (runtime.ctx.collabGuest?.readOnly) return "Collab: read-only guest";
|
|
667
|
+
if (runtime.ctx.collabGuest) return "Collab: guest";
|
|
668
|
+
return "Collab: off";
|
|
669
|
+
},
|
|
606
670
|
handleTui: async (command, runtime) => {
|
|
607
671
|
const ctx = runtime.ctx;
|
|
608
672
|
ctx.editor.setText("");
|
|
@@ -702,6 +766,11 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
702
766
|
{
|
|
703
767
|
name: "leave",
|
|
704
768
|
description: "Leave the collab session",
|
|
769
|
+
getTuiAutocompleteDescription: runtime => {
|
|
770
|
+
if (runtime.ctx.collabHost) return "Leave collab: hosting";
|
|
771
|
+
if (runtime.ctx.collabGuest) return "Leave collab: guest";
|
|
772
|
+
return "Leave collab: not in collab";
|
|
773
|
+
},
|
|
705
774
|
handleTui: async (_command, runtime) => {
|
|
706
775
|
const ctx = runtime.ctx;
|
|
707
776
|
ctx.editor.setText("");
|
|
@@ -726,6 +795,10 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
726
795
|
{ name: "visible", description: "Switch to visible mode" },
|
|
727
796
|
],
|
|
728
797
|
allowArgs: true,
|
|
798
|
+
getTuiAutocompleteDescription: runtime => {
|
|
799
|
+
if (!runtime.ctx.settings.get("browser.enabled" as SettingPath)) return "Browser: disabled";
|
|
800
|
+
return runtime.ctx.settings.get("browser.headless" as SettingPath) ? "Browser: headless" : "Browser: visible";
|
|
801
|
+
},
|
|
729
802
|
handle: async (command, runtime) => {
|
|
730
803
|
const arg = command.args.toLowerCase();
|
|
731
804
|
const enabled = runtime.settings.get("browser.enabled" as SettingPath) as boolean;
|
|
@@ -817,6 +890,14 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
817
890
|
{ name: "rm", description: "Remove task/phase/all (fuzzy-matched)", usage: "[<task|phase>]" },
|
|
818
891
|
],
|
|
819
892
|
allowArgs: true,
|
|
893
|
+
getTuiAutocompleteDescription: runtime => {
|
|
894
|
+
const tasks = runtime.ctx.todoPhases.flatMap(phase => phase.tasks);
|
|
895
|
+
if (tasks.length === 0) return "Todos: none";
|
|
896
|
+
const pending = tasks.filter(task => task.status === "pending").length;
|
|
897
|
+
const inProgress = tasks.filter(task => task.status === "in_progress").length;
|
|
898
|
+
const completed = tasks.filter(task => task.status === "completed").length;
|
|
899
|
+
return `Todos: ${pending + inProgress} open (${inProgress} in progress, ${completed} done)`;
|
|
900
|
+
},
|
|
820
901
|
handle: handleTodoAcp,
|
|
821
902
|
handleTui: async (command, runtime) => {
|
|
822
903
|
await runtime.ctx.handleTodoCommand(command.args);
|
|
@@ -881,6 +962,11 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
881
962
|
name: "jobs",
|
|
882
963
|
description: "Show async background jobs status",
|
|
883
964
|
acpDescription: "Show background jobs",
|
|
965
|
+
getTuiAutocompleteDescription: runtime => {
|
|
966
|
+
const snapshot = runtime.ctx.session.getAsyncJobSnapshot({ recentLimit: 5 });
|
|
967
|
+
if (!snapshot || (snapshot.running.length === 0 && snapshot.recent.length === 0)) return "Jobs: none";
|
|
968
|
+
return `Jobs: ${snapshot.running.length} running, ${snapshot.recent.length} recent`;
|
|
969
|
+
},
|
|
884
970
|
handle: async (_command, runtime) => {
|
|
885
971
|
const snapshot = runtime.session.getAsyncJobSnapshot({ recentLimit: 5 });
|
|
886
972
|
if (!snapshot || (snapshot.running.length === 0 && snapshot.recent.length === 0)) {
|
|
@@ -1016,6 +1102,11 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1016
1102
|
name: "tools",
|
|
1017
1103
|
description: "Show tools currently visible to the agent",
|
|
1018
1104
|
acpDescription: "Show available tools",
|
|
1105
|
+
getTuiAutocompleteDescription: runtime => {
|
|
1106
|
+
const active = runtime.ctx.session.getActiveToolNames().length;
|
|
1107
|
+
const all = runtime.ctx.session.getAllToolNames().length;
|
|
1108
|
+
return all === 0 ? "Tools: none available" : `Tools: ${active} active / ${all} available`;
|
|
1109
|
+
},
|
|
1019
1110
|
handle: async (_command, runtime) => {
|
|
1020
1111
|
const active = runtime.session.getActiveToolNames();
|
|
1021
1112
|
const all = runtime.session.getAllToolNames();
|
|
@@ -1035,6 +1126,11 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1035
1126
|
name: "context",
|
|
1036
1127
|
description: "Show estimated context usage breakdown",
|
|
1037
1128
|
acpDescription: "Show context usage",
|
|
1129
|
+
getTuiAutocompleteDescription: runtime => {
|
|
1130
|
+
const usage = runtime.ctx.session.getContextUsage();
|
|
1131
|
+
if (!usage) return "Context: unavailable";
|
|
1132
|
+
return `Context: ${Math.round(usage.percent)}% (${formatTokenCount(usage.tokens)}/${formatTokenCount(usage.contextWindow)})`;
|
|
1133
|
+
},
|
|
1038
1134
|
handle: async (_command, runtime) => {
|
|
1039
1135
|
await runtime.output(buildContextReportText(runtime));
|
|
1040
1136
|
return commandConsumed();
|
|
@@ -1094,6 +1190,10 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1094
1190
|
description: "Login with OAuth provider",
|
|
1095
1191
|
inlineHint: "[provider|redirect URL]",
|
|
1096
1192
|
allowArgs: true,
|
|
1193
|
+
getTuiAutocompleteDescription: runtime =>
|
|
1194
|
+
runtime.ctx.oauthManualInput.hasPending()
|
|
1195
|
+
? `Login: waiting for ${runtime.ctx.oauthManualInput.pendingProviderId ?? "OAuth"} callback`
|
|
1196
|
+
: "Login: choose provider",
|
|
1097
1197
|
handleTui: (command, runtime) => {
|
|
1098
1198
|
const manualInput = runtime.ctx.oauthManualInput;
|
|
1099
1199
|
const args = command.args.trim();
|
|
@@ -1233,6 +1333,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1233
1333
|
{
|
|
1234
1334
|
name: "fresh",
|
|
1235
1335
|
description: "Reset provider stream state without changing the local transcript",
|
|
1336
|
+
getTuiAutocompleteDescription: runtime =>
|
|
1337
|
+
runtime.ctx.session.isStreaming ? "Fresh: unavailable while streaming" : "Fresh: ready",
|
|
1236
1338
|
handle: async (_command, runtime) => {
|
|
1237
1339
|
const result = runtime.session.freshSession();
|
|
1238
1340
|
if (!result) {
|
|
@@ -1268,6 +1370,10 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1268
1370
|
})),
|
|
1269
1371
|
acpInputHint: `[${COMPACT_MODES.map(mode => mode.name).join("|")}] [focus]`,
|
|
1270
1372
|
allowArgs: true,
|
|
1373
|
+
getTuiAutocompleteDescription: runtime => {
|
|
1374
|
+
const usage = runtime.ctx.session.getContextUsage();
|
|
1375
|
+
return usage ? `Compact: context ${Math.round(usage.percent)}% used` : "Compact: context unavailable";
|
|
1376
|
+
},
|
|
1271
1377
|
handle: async (command, runtime) => {
|
|
1272
1378
|
const parsed = parseCompactArgs(command.args);
|
|
1273
1379
|
if ("error" in parsed) return usage(parsed.error, runtime);
|
|
@@ -1354,6 +1460,9 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1354
1460
|
allowArgs: true,
|
|
1355
1461
|
handleTui: async (command, runtime) => {
|
|
1356
1462
|
const question = command.text.slice(`/${command.name}`.length).trim();
|
|
1463
|
+
if (question) {
|
|
1464
|
+
runtime.ctx.editor.addToHistory(command.text);
|
|
1465
|
+
}
|
|
1357
1466
|
runtime.ctx.editor.setText("");
|
|
1358
1467
|
await runtime.ctx.handleBtwCommand(question);
|
|
1359
1468
|
},
|
|
@@ -1365,6 +1474,9 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1365
1474
|
allowArgs: true,
|
|
1366
1475
|
handleTui: async (command, runtime) => {
|
|
1367
1476
|
const work = command.text.slice(`/${command.name}`.length).trim();
|
|
1477
|
+
if (work) {
|
|
1478
|
+
runtime.ctx.editor.addToHistory(command.text);
|
|
1479
|
+
}
|
|
1368
1480
|
runtime.ctx.editor.setText("");
|
|
1369
1481
|
await runtime.ctx.handleTanCommand(work);
|
|
1370
1482
|
},
|
|
@@ -1376,6 +1488,9 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1376
1488
|
allowArgs: true,
|
|
1377
1489
|
handleTui: async (command, runtime) => {
|
|
1378
1490
|
const complaint = command.text.slice(`/${command.name}`.length).trim();
|
|
1491
|
+
if (complaint) {
|
|
1492
|
+
runtime.ctx.editor.addToHistory(command.text);
|
|
1493
|
+
}
|
|
1379
1494
|
runtime.ctx.editor.setText("");
|
|
1380
1495
|
await runtime.ctx.handleOmfgCommand(complaint);
|
|
1381
1496
|
},
|
|
@@ -1484,6 +1599,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1484
1599
|
}
|
|
1485
1600
|
},
|
|
1486
1601
|
handleTui: async (command, runtime) => {
|
|
1602
|
+
runtime.ctx.editor.addToHistory(command.text);
|
|
1487
1603
|
runtime.ctx.editor.setText("");
|
|
1488
1604
|
await runtime.ctx.handleMemoryCommand(command.text);
|
|
1489
1605
|
},
|
|
@@ -1511,6 +1627,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1511
1627
|
runtime.ctx.editor.setText("");
|
|
1512
1628
|
return;
|
|
1513
1629
|
}
|
|
1630
|
+
runtime.ctx.editor.addToHistory(command.text);
|
|
1514
1631
|
runtime.ctx.editor.setText("");
|
|
1515
1632
|
await runtime.ctx.handleRenameCommand(title);
|
|
1516
1633
|
},
|
|
@@ -1553,6 +1670,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1553
1670
|
runtime.ctx.editor.setText("");
|
|
1554
1671
|
return;
|
|
1555
1672
|
}
|
|
1673
|
+
runtime.ctx.editor.addToHistory(command.text);
|
|
1556
1674
|
runtime.ctx.editor.setText("");
|
|
1557
1675
|
await runtime.ctx.handleMoveCommand(targetPath);
|
|
1558
1676
|
},
|
|
@@ -2097,6 +2215,10 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
2097
2215
|
aliases: ["force:"],
|
|
2098
2216
|
inlineHint: "<tool-name> [prompt]",
|
|
2099
2217
|
allowArgs: true,
|
|
2218
|
+
getTuiAutocompleteDescription: runtime => {
|
|
2219
|
+
const count = runtime.ctx.session.getActiveToolNames().length;
|
|
2220
|
+
return count === 0 ? "Force: no active tools" : `Force: ${count} active tools`;
|
|
2221
|
+
},
|
|
2100
2222
|
handle: async (command, runtime) => {
|
|
2101
2223
|
const spaceIdx = command.args.indexOf(" ");
|
|
2102
2224
|
const toolName = spaceIdx === -1 ? command.args : command.args.slice(0, spaceIdx);
|
|
@@ -2225,34 +2347,38 @@ export const BUILTIN_SLASH_COMMAND_DEFS: ReadonlyArray<BuiltinSlashCommand> = BU
|
|
|
2225
2347
|
description: command.description,
|
|
2226
2348
|
subcommands: command.subcommands,
|
|
2227
2349
|
inlineHint: command.inlineHint,
|
|
2350
|
+
getTuiAutocompleteDescription: command.getTuiAutocompleteDescription,
|
|
2228
2351
|
}),
|
|
2229
2352
|
);
|
|
2230
2353
|
|
|
2354
|
+
function materializeTuiBuiltinSlashCommand(
|
|
2355
|
+
cmd: BuiltinSlashCommand,
|
|
2356
|
+
runtime?: TuiSlashCommandRuntime,
|
|
2357
|
+
): TuiBuiltinSlashCommand {
|
|
2358
|
+
const materialized: TuiBuiltinSlashCommand = { ...cmd };
|
|
2359
|
+
if (cmd.subcommands) {
|
|
2360
|
+
materialized.getArgumentCompletions = buildArgumentCompletions(cmd.subcommands);
|
|
2361
|
+
materialized.getInlineHint = buildSubcommandInlineHint(cmd.subcommands);
|
|
2362
|
+
} else if (cmd.inlineHint) {
|
|
2363
|
+
materialized.getInlineHint = buildStaticInlineHint(cmd.inlineHint);
|
|
2364
|
+
}
|
|
2365
|
+
if (runtime && cmd.getTuiAutocompleteDescription) {
|
|
2366
|
+
materialized.getAutocompleteDescription = () => cmd.getTuiAutocompleteDescription?.(runtime);
|
|
2367
|
+
}
|
|
2368
|
+
return materialized;
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2231
2371
|
/**
|
|
2232
2372
|
* Materialized builtin slash commands with completion functions derived from
|
|
2233
2373
|
* declarative subcommand/hint definitions.
|
|
2234
2374
|
*/
|
|
2235
|
-
export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
return {
|
|
2243
|
-
...cmd,
|
|
2244
|
-
getArgumentCompletions: buildArgumentCompletions(cmd.subcommands),
|
|
2245
|
-
getInlineHint: buildSubcommandInlineHint(cmd.subcommands),
|
|
2246
|
-
};
|
|
2247
|
-
}
|
|
2248
|
-
if (cmd.inlineHint) {
|
|
2249
|
-
return {
|
|
2250
|
-
...cmd,
|
|
2251
|
-
getInlineHint: buildStaticInlineHint(cmd.inlineHint),
|
|
2252
|
-
};
|
|
2253
|
-
}
|
|
2254
|
-
return cmd;
|
|
2255
|
-
});
|
|
2375
|
+
export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<TuiBuiltinSlashCommand> = BUILTIN_SLASH_COMMAND_DEFS.map(cmd =>
|
|
2376
|
+
materializeTuiBuiltinSlashCommand(cmd),
|
|
2377
|
+
);
|
|
2378
|
+
|
|
2379
|
+
export function buildTuiBuiltinSlashCommands(runtime: TuiSlashCommandRuntime): ReadonlyArray<TuiBuiltinSlashCommand> {
|
|
2380
|
+
return BUILTIN_SLASH_COMMAND_DEFS.map(cmd => materializeTuiBuiltinSlashCommand(cmd, runtime));
|
|
2381
|
+
}
|
|
2256
2382
|
|
|
2257
2383
|
/**
|
|
2258
2384
|
* Unified registry exposed for cross-mode tooling. Each spec carries at least
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import * as path from "node:path";
|
|
2
1
|
import type { TodoPhase } from "../../tools/todo";
|
|
3
2
|
import {
|
|
4
3
|
applyOpsToPhases,
|
|
5
4
|
getLatestTodoPhasesFromEntries,
|
|
6
5
|
markdownToPhases,
|
|
7
6
|
phasesToMarkdown,
|
|
7
|
+
resolveTodoMarkdownPath,
|
|
8
8
|
USER_TODO_EDIT_CUSTOM_TYPE,
|
|
9
9
|
} from "../../tools/todo";
|
|
10
10
|
import type { ParsedSlashCommand, SlashCommandResult, SlashCommandRuntime } from "../types";
|
|
11
|
-
import { commandConsumed, parseSubcommand, usage } from "./parse";
|
|
11
|
+
import { commandConsumed, errorMessage, parseSubcommand, usage } from "./parse";
|
|
12
12
|
|
|
13
13
|
type TodoMutationVerb = "done" | "drop" | "rm";
|
|
14
14
|
|
|
@@ -127,19 +127,25 @@ async function handleTodoExportCommand(restArgs: string, runtime: SlashCommandRu
|
|
|
127
127
|
await runtime.output("No todos to export.");
|
|
128
128
|
return commandConsumed();
|
|
129
129
|
}
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
let target: string;
|
|
131
|
+
try {
|
|
132
|
+
target = resolveTodoMarkdownPath(restArgs, runtime.sessionManager.getCwd());
|
|
133
|
+
await Bun.write(target, phasesToMarkdown(phases));
|
|
134
|
+
} catch (err) {
|
|
135
|
+
return usage(`Failed to write todos: ${errorMessage(err)}`, runtime);
|
|
136
|
+
}
|
|
132
137
|
await runtime.output(`Wrote todos to ${target}`);
|
|
133
138
|
return commandConsumed();
|
|
134
139
|
}
|
|
135
140
|
|
|
136
141
|
async function handleTodoImportCommand(restArgs: string, runtime: SlashCommandRuntime): Promise<SlashCommandResult> {
|
|
137
|
-
|
|
142
|
+
let target: string;
|
|
138
143
|
let content: string;
|
|
139
144
|
try {
|
|
145
|
+
target = resolveTodoMarkdownPath(restArgs, runtime.sessionManager.getCwd());
|
|
140
146
|
content = await Bun.file(target).text();
|
|
141
147
|
} catch (err) {
|
|
142
|
-
return usage(`Failed to read
|
|
148
|
+
return usage(`Failed to read todos: ${errorMessage(err)}`, runtime);
|
|
143
149
|
}
|
|
144
150
|
const { phases, errors } = markdownToPhases(content);
|
|
145
151
|
if (errors.length > 0) return usage(`Could not parse ${target}:\n ${errors.join("\n ")}`, runtime);
|
|
@@ -20,6 +20,8 @@ export interface BuiltinSlashCommand {
|
|
|
20
20
|
subcommands?: SubcommandDef[];
|
|
21
21
|
/** Static inline hint when command takes a simple argument (no subcommands). */
|
|
22
22
|
inlineHint?: string;
|
|
23
|
+
/** TUI-only dynamic status text for command-name autocomplete. Static `description` remains canonical for ACP/help. */
|
|
24
|
+
getTuiAutocompleteDescription?: (runtime: TuiSlashCommandRuntime) => string | undefined;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
/** Parsed slash-command text after stripping the leading "/". */
|
package/src/system-prompt.ts
CHANGED
|
@@ -327,6 +327,8 @@ export async function loadSystemPromptFiles(options: LoadContextFilesOptions = {
|
|
|
327
327
|
return userLevel?.content ?? null;
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
+
export const DEFAULT_SYSTEM_PROMPT_TOOL_NAMES = ["read", "bash", "eval", "edit", "write"] as const;
|
|
331
|
+
|
|
330
332
|
export interface SystemPromptToolMetadata {
|
|
331
333
|
label: string;
|
|
332
334
|
description: string;
|
|
@@ -584,18 +586,11 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
584
586
|
const dateTime = date;
|
|
585
587
|
const promptCwd = shortenPath(resolvedCwd.replace(/\\/g, "/"));
|
|
586
588
|
|
|
587
|
-
// Build tool metadata for system prompt rendering
|
|
588
|
-
// Priority: explicit list > tools map >
|
|
589
|
-
// Default includes both bash and python; actual availability determined by settings in createTools
|
|
589
|
+
// Build tool metadata for system prompt rendering.
|
|
590
|
+
// Priority: explicit list > tools map > conservative SDK fallback.
|
|
590
591
|
let toolNames = providedToolNames;
|
|
591
592
|
if (!toolNames) {
|
|
592
|
-
|
|
593
|
-
// Tools map provided
|
|
594
|
-
toolNames = Array.from(tools.keys());
|
|
595
|
-
} else {
|
|
596
|
-
// Use defaults
|
|
597
|
-
toolNames = ["read", "bash", "eval", "edit", "write"]; // TODO: Why?
|
|
598
|
-
}
|
|
593
|
+
toolNames = tools ? Array.from(tools.keys()) : [...DEFAULT_SYSTEM_PROMPT_TOOL_NAMES];
|
|
599
594
|
}
|
|
600
595
|
|
|
601
596
|
// Build tool descriptions for system prompt rendering.
|
|
@@ -625,7 +620,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
625
620
|
// Filter skills for the rendered system prompt:
|
|
626
621
|
// - require the `read` tool so the model can actually fetch skill content;
|
|
627
622
|
// - drop skills with frontmatter `hide: true` (still loadable via skill:// and /skill:<name>).
|
|
628
|
-
const hasRead =
|
|
623
|
+
const hasRead = toolNames.includes("read");
|
|
629
624
|
const filteredSkills = hasRead ? skills.filter(skill => skill.hide !== true) : [];
|
|
630
625
|
|
|
631
626
|
const effectiveSystemPromptCustomization = dedupePromptSource(systemPromptCustomization, [
|
package/src/tiny/models.ts
CHANGED
|
@@ -12,6 +12,8 @@ export interface TinyTitleLocalModelSpec {
|
|
|
12
12
|
contextNote: string;
|
|
13
13
|
/** Model family emits hidden reasoning unless the chat template disables it. */
|
|
14
14
|
reasoning?: boolean;
|
|
15
|
+
/** Reason this model is blocked before loading the ONNX runtime. */
|
|
16
|
+
unsupportedReason?: string;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
export const TINY_TITLE_LOCAL_MODELS = [
|
|
@@ -108,7 +110,7 @@ export function getTinyTitleModelSpec(key: TinyTitleLocalModelKey): (typeof TINY
|
|
|
108
110
|
/** Default memory model: the online path (the configured smol / remote LLM; no local download). */
|
|
109
111
|
export const ONLINE_MEMORY_MODEL_KEY = "online";
|
|
110
112
|
/** Recommended local model for memory tasks when none is named. */
|
|
111
|
-
export const DEFAULT_MEMORY_LOCAL_MODEL_KEY = "
|
|
113
|
+
export const DEFAULT_MEMORY_LOCAL_MODEL_KEY = "lfm2-1.2b";
|
|
112
114
|
|
|
113
115
|
/**
|
|
114
116
|
* Local models for Mnemopi memory tasks (fact extraction + consolidation).
|
|
@@ -123,9 +125,11 @@ export const TINY_MEMORY_LOCAL_MODELS = [
|
|
|
123
125
|
dtype: "q4",
|
|
124
126
|
label: "Qwen3 1.7B",
|
|
125
127
|
description:
|
|
126
|
-
"
|
|
127
|
-
contextNote: "
|
|
128
|
+
"Disabled for local inference: onnxruntime-node cannot run this ONNX export's RotaryEmbedding cache updates.",
|
|
129
|
+
contextNote: "Blocked before load to avoid the unsupported RotaryEmbedding runtime path.",
|
|
128
130
|
reasoning: true,
|
|
131
|
+
unsupportedReason:
|
|
132
|
+
"onnxruntime-node does not support Qwen3 RotaryEmbedding cache updates in onnx-community/Qwen3-1.7B-ONNX",
|
|
129
133
|
},
|
|
130
134
|
{
|
|
131
135
|
key: "gemma-3-1b",
|