@oh-my-pi/pi-coding-agent 16.2.2 → 16.2.3
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 +49 -0
- package/dist/cli.js +3624 -3568
- package/dist/types/advisor/__tests__/config.test.d.ts +1 -0
- package/dist/types/advisor/advise-tool.d.ts +8 -4
- package/dist/types/advisor/config.d.ts +88 -0
- package/dist/types/advisor/index.d.ts +1 -0
- package/dist/types/advisor/transcript-recorder.d.ts +13 -2
- package/dist/types/advisor/watchdog.d.ts +20 -0
- package/dist/types/collab/guest.d.ts +29 -0
- package/dist/types/config/settings-schema.d.ts +81 -0
- package/dist/types/debug/log-viewer.d.ts +1 -0
- package/dist/types/debug/raw-sse.d.ts +1 -0
- package/dist/types/edit/hashline/diff.d.ts +0 -11
- package/dist/types/extensibility/tool-event-input.d.ts +7 -0
- package/dist/types/extensibility/utils.d.ts +12 -0
- package/dist/types/mcp/transports/index.d.ts +1 -0
- package/dist/types/mcp/transports/sse.d.ts +20 -0
- package/dist/types/modes/components/advisor-config.d.ts +59 -0
- package/dist/types/modes/components/index.d.ts +1 -0
- package/dist/types/modes/components/model-selector.d.ts +9 -1
- package/dist/types/modes/components/settings-selector.d.ts +1 -0
- package/dist/types/modes/components/status-line/component.d.ts +30 -1
- package/dist/types/modes/components/status-line/types.d.ts +13 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +10 -4
- package/dist/types/modes/skill-command.d.ts +32 -0
- package/dist/types/modes/types.d.ts +7 -2
- package/dist/types/session/agent-session.d.ts +58 -10
- package/dist/types/session/indexed-session-storage.d.ts +7 -1
- package/dist/types/session/messages.d.ts +26 -0
- package/dist/types/session/messages.test.d.ts +1 -0
- package/dist/types/session/session-entries.d.ts +31 -3
- package/dist/types/session/session-history-format.d.ts +6 -0
- package/dist/types/session/session-loader.d.ts +9 -1
- package/dist/types/session/session-manager.d.ts +6 -4
- package/dist/types/session/session-storage.d.ts +11 -0
- package/dist/types/session/session-title-slot.d.ts +19 -0
- package/dist/types/ssh/connection-manager.d.ts +47 -0
- package/dist/types/ssh/utils.d.ts +16 -0
- package/dist/types/task/executor.d.ts +3 -16
- package/dist/types/task/render.d.ts +0 -5
- package/dist/types/task/renderer.d.ts +13 -0
- package/dist/types/task/types.d.ts +16 -0
- package/dist/types/task/yield-assembly.d.ts +28 -0
- package/dist/types/tiny/text.d.ts +8 -0
- package/dist/types/tools/render-utils.d.ts +2 -0
- package/dist/types/tools/review.d.ts +6 -4
- package/dist/types/tools/ssh.d.ts +1 -1
- package/dist/types/tools/todo.d.ts +6 -0
- package/dist/types/tools/yield.d.ts +8 -3
- package/dist/types/utils/thinking-display.d.ts +4 -0
- package/package.json +12 -12
- package/src/advisor/__tests__/advisor.test.ts +242 -10
- package/src/advisor/__tests__/config.test.ts +173 -0
- package/src/advisor/advise-tool.ts +11 -6
- package/src/advisor/config.ts +256 -0
- package/src/advisor/index.ts +1 -0
- package/src/advisor/runtime.ts +12 -2
- package/src/advisor/transcript-recorder.ts +25 -2
- package/src/advisor/watchdog.ts +57 -31
- package/src/autoresearch/index.ts +7 -2
- package/src/cli/gc-cli.ts +17 -10
- package/src/collab/guest.ts +43 -7
- package/src/config/model-registry.ts +80 -18
- package/src/config/settings-schema.ts +77 -0
- package/src/debug/index.ts +32 -7
- package/src/debug/log-viewer.ts +111 -53
- package/src/debug/raw-sse.ts +68 -48
- package/src/discovery/codex.ts +13 -5
- package/src/edit/hashline/diff.ts +57 -4
- package/src/eval/js/shared/local-module-loader.ts +23 -1
- package/src/export/html/template.js +13 -7
- package/src/extensibility/extensions/loader.ts +5 -3
- package/src/extensibility/extensions/wrapper.ts +9 -3
- package/src/extensibility/hooks/loader.ts +3 -3
- package/src/extensibility/hooks/tool-wrapper.ts +13 -4
- package/src/extensibility/plugins/manager.ts +2 -1
- package/src/extensibility/tool-event-input.ts +23 -0
- package/src/extensibility/utils.ts +74 -0
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/mcp/client.ts +3 -1
- package/src/mcp/manager.ts +12 -5
- package/src/mcp/transports/index.ts +1 -0
- package/src/mcp/transports/sse.ts +377 -0
- package/src/memories/index.ts +15 -6
- package/src/modes/components/advisor-config.ts +555 -0
- package/src/modes/components/advisor-message.ts +9 -2
- package/src/modes/components/agent-hub.ts +9 -4
- package/src/modes/components/index.ts +2 -0
- package/src/modes/components/model-selector.ts +79 -48
- package/src/modes/components/settings-selector.ts +1 -0
- package/src/modes/components/status-line/component.ts +144 -5
- package/src/modes/components/status-line/segments.ts +46 -21
- package/src/modes/components/status-line/types.ts +13 -1
- package/src/modes/components/tool-execution.ts +47 -6
- package/src/modes/controllers/command-controller.ts +23 -2
- package/src/modes/controllers/event-controller.ts +106 -0
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +61 -61
- package/src/modes/controllers/selector-controller.ts +100 -9
- package/src/modes/interactive-mode.ts +65 -5
- package/src/modes/skill-command.ts +116 -0
- package/src/modes/types.ts +7 -2
- package/src/modes/utils/ui-helpers.ts +41 -23
- package/src/prompts/agents/reviewer.md +11 -10
- package/src/prompts/review-custom-request.md +1 -2
- package/src/prompts/review-request.md +1 -2
- package/src/prompts/system/interrupted-thinking.md +7 -0
- package/src/prompts/system/recap-user.md +9 -0
- package/src/prompts/system/subagent-system-prompt.md +8 -5
- package/src/prompts/system/subagent-yield-reminder.md +6 -5
- package/src/prompts/system/system-prompt.md +0 -1
- package/src/prompts/tools/irc.md +2 -2
- package/src/prompts/tools/read.md +2 -2
- package/src/sdk.ts +28 -24
- package/src/session/agent-session.ts +867 -428
- package/src/session/indexed-session-storage.ts +86 -13
- package/src/session/messages.test.ts +125 -0
- package/src/session/messages.ts +172 -9
- package/src/session/redis-session-storage.ts +49 -2
- package/src/session/session-entries.ts +39 -2
- package/src/session/session-history-format.ts +29 -2
- package/src/session/session-listing.ts +54 -24
- package/src/session/session-loader.ts +66 -3
- package/src/session/session-manager.ts +113 -19
- package/src/session/session-persistence.ts +95 -1
- package/src/session/session-storage.ts +36 -0
- package/src/session/session-title-slot.ts +141 -0
- package/src/session/sql-session-storage.ts +71 -11
- package/src/slash-commands/builtin-registry.ts +16 -3
- package/src/ssh/__tests__/connection-manager-args.test.ts +123 -1
- package/src/ssh/__tests__/file-transfer-posix-guard.test.ts +55 -18
- package/src/ssh/connection-manager.ts +139 -12
- package/src/ssh/file-transfer.ts +23 -18
- package/src/ssh/ssh-executor.ts +2 -13
- package/src/ssh/utils.ts +19 -0
- package/src/task/executor.ts +21 -23
- package/src/task/render.ts +162 -20
- package/src/task/renderer.ts +14 -0
- package/src/task/types.ts +17 -0
- package/src/task/yield-assembly.ts +207 -0
- package/src/tiny/text.ts +23 -0
- package/src/tools/ask.ts +55 -4
- package/src/tools/render-utils.ts +2 -0
- package/src/tools/renderers.ts +8 -2
- package/src/tools/review.ts +17 -7
- package/src/tools/ssh.ts +8 -4
- package/src/tools/todo.ts +17 -1
- package/src/tools/yield.ts +140 -31
- package/src/utils/thinking-display.ts +15 -0
|
@@ -112,6 +112,7 @@ import { renderTreeList } from "../tui/tree-list";
|
|
|
112
112
|
import type { EventBus } from "../utils/event-bus";
|
|
113
113
|
import { getEditorCommand, openInEditor } from "../utils/external-editor";
|
|
114
114
|
import { getSessionAccentAnsi, getSessionAccentHex } from "../utils/session-color";
|
|
115
|
+
import { messageHasDisplayableThinking } from "../utils/thinking-display";
|
|
115
116
|
import { popTerminalTitle, pushTerminalTitle, setSessionTerminalTitle } from "../utils/title-generator";
|
|
116
117
|
import {
|
|
117
118
|
isSearchProviderId,
|
|
@@ -413,14 +414,29 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
413
414
|
#modelCycleClearTimer: NodeJS.Timeout | undefined;
|
|
414
415
|
todoPhases: TodoPhase[] = [];
|
|
415
416
|
hideThinkingBlock = false;
|
|
417
|
+
#sessionsWithDisplayableThinkingContent = new WeakSet<AgentSession>();
|
|
418
|
+
/** Whether the visible session has produced thinking content the user can reveal. */
|
|
419
|
+
get hasDisplayableThinkingContent(): boolean {
|
|
420
|
+
return this.#sessionsWithDisplayableThinkingContent.has(this.viewSession);
|
|
421
|
+
}
|
|
422
|
+
/** Record received reasoning content so Ctrl+T can reveal it even when model metadata says thinking is off. */
|
|
423
|
+
noteDisplayableThinkingContent(message: AgentMessage): boolean {
|
|
424
|
+
if (this.hasDisplayableThinkingContent || !messageHasDisplayableThinking(message, this.proseOnlyThinking)) {
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
this.#sessionsWithDisplayableThinkingContent.add(this.viewSession);
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
416
430
|
/**
|
|
417
|
-
* Effective thinking-block visibility: hidden when the user's setting is on
|
|
418
|
-
*
|
|
419
|
-
*
|
|
420
|
-
*
|
|
431
|
+
* Effective thinking-block visibility: hidden when the user's setting is on,
|
|
432
|
+
* or while thinking is "off" before the session has actually produced
|
|
433
|
+
* displayable thinking content. Some providers return thinking blocks without
|
|
434
|
+
* advertising reasoning support, so observed content unlocks the visibility
|
|
435
|
+
* toggle.
|
|
421
436
|
*/
|
|
422
437
|
get effectiveHideThinkingBlock(): boolean {
|
|
423
|
-
|
|
438
|
+
const thinkingOff = (this.viewSession?.thinkingLevel ?? ThinkingLevel.Off) === ThinkingLevel.Off;
|
|
439
|
+
return this.hideThinkingBlock || (thinkingOff && !this.hasDisplayableThinkingContent);
|
|
424
440
|
}
|
|
425
441
|
proseOnlyThinking = true;
|
|
426
442
|
compactionQueuedMessages: CompactionQueuedMessage[] = [];
|
|
@@ -1417,6 +1433,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1417
1433
|
sessionAccent: settings.get("statusLine.sessionAccent"),
|
|
1418
1434
|
transparent: settings.get("statusLine.transparent"),
|
|
1419
1435
|
segmentOptions: settings.get("statusLine.segmentOptions"),
|
|
1436
|
+
compactThinkingLevel: settings.get("statusLine.compactThinkingLevel"),
|
|
1420
1437
|
});
|
|
1421
1438
|
}
|
|
1422
1439
|
|
|
@@ -1478,11 +1495,47 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1478
1495
|
}
|
|
1479
1496
|
|
|
1480
1497
|
rebuildChatFromMessages(): void {
|
|
1498
|
+
// Mid-stream rebuilds (e.g. `/shake`, theme/setting changes that touch the
|
|
1499
|
+
// transcript) replay only committed `state.messages`. The agent's in-flight
|
|
1500
|
+
// `streamMessage` and its still-pending tool calls live OUTSIDE
|
|
1501
|
+
// `state.messages` until `message_end`, so a plain clear+replay detaches
|
|
1502
|
+
// their UI components while keeping the `streamingComponent` / `pendingTools`
|
|
1503
|
+
// references — subsequent `message_update`/`message_end` events would then
|
|
1504
|
+
// update orphaned components that never re-render and the live LLM output
|
|
1505
|
+
// vanishes from the chat (#3656). Snapshot the in-flight components,
|
|
1506
|
+
// clear+replay, then re-append them in their original chat-container order
|
|
1507
|
+
// and restore the `pendingTools` map so streaming routes back into them.
|
|
1508
|
+
const liveComponents: Component[] = [];
|
|
1509
|
+
const livePendingTools = new Map<string, ToolExecutionHandle>();
|
|
1510
|
+
if (this.viewSession?.isStreaming) {
|
|
1511
|
+
const liveSet = new Set<Component>();
|
|
1512
|
+
if (this.streamingComponent) liveSet.add(this.streamingComponent);
|
|
1513
|
+
for (const [id, component] of this.pendingTools) {
|
|
1514
|
+
livePendingTools.set(id, component);
|
|
1515
|
+
liveSet.add(component as unknown as Component);
|
|
1516
|
+
}
|
|
1517
|
+
if (liveSet.size > 0) {
|
|
1518
|
+
for (const child of this.chatContainer.children) {
|
|
1519
|
+
if (liveSet.has(child)) liveComponents.push(child);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1481
1523
|
this.chatContainer.clear();
|
|
1482
1524
|
// Live display uses the compacted transcript tail; export/resume callers
|
|
1483
1525
|
// can still request the full inline compaction history.
|
|
1484
1526
|
const context = this.viewSession.buildTranscriptSessionContext({ collapseCompactedHistory: true });
|
|
1485
1527
|
this.renderSessionContext(context);
|
|
1528
|
+
for (const child of liveComponents) {
|
|
1529
|
+
this.chatContainer.addChild(child);
|
|
1530
|
+
}
|
|
1531
|
+
// `renderSessionContext` clears `pendingTools` at start AND end so the
|
|
1532
|
+
// reconstructed historical tool components don't leak into live tracking.
|
|
1533
|
+
// Restore the in-flight entries afterwards so the next streamed tool-call
|
|
1534
|
+
// delta is routed into the preserved component instead of stacking a
|
|
1535
|
+
// duplicate ToolExecutionComponent below it.
|
|
1536
|
+
for (const [id, component] of livePendingTools) {
|
|
1537
|
+
this.pendingTools.set(id, component);
|
|
1538
|
+
}
|
|
1486
1539
|
// During the pre-streaming window — after `startPendingSubmission` has
|
|
1487
1540
|
// optimistically rendered the user's message but before the user
|
|
1488
1541
|
// `message_start` event lands it in `session` entries — any rebuild
|
|
@@ -3583,6 +3636,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3583
3636
|
sessionContext: SessionContext,
|
|
3584
3637
|
options?: { updateFooter?: boolean; populateHistory?: boolean },
|
|
3585
3638
|
): void {
|
|
3639
|
+
for (const message of sessionContext.messages) {
|
|
3640
|
+
this.noteDisplayableThinkingContent(message);
|
|
3641
|
+
}
|
|
3586
3642
|
this.#uiHelpers.renderSessionContext(sessionContext, options);
|
|
3587
3643
|
}
|
|
3588
3644
|
|
|
@@ -3844,6 +3900,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3844
3900
|
this.#selectorController.showSettingsSelector();
|
|
3845
3901
|
}
|
|
3846
3902
|
|
|
3903
|
+
showAdvisorConfigure(): void {
|
|
3904
|
+
this.#selectorController.showAdvisorConfigure();
|
|
3905
|
+
}
|
|
3906
|
+
|
|
3847
3907
|
showHistorySearch(): void {
|
|
3848
3908
|
this.#selectorController.showHistorySearch();
|
|
3849
3909
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { ImageContent, TextContent } from "@oh-my-pi/pi-ai";
|
|
2
|
+
import { type CustomMessage, SKILL_PROMPT_MESSAGE_TYPE, type SkillPromptDetails } from "../session/messages";
|
|
3
|
+
import type { InteractiveModeContext } from "./types";
|
|
4
|
+
|
|
5
|
+
type SkillCommandHost = Pick<InteractiveModeContext, "skillCommands" | "session" | "showError">;
|
|
6
|
+
|
|
7
|
+
type SkillPromptMessage = Pick<
|
|
8
|
+
CustomMessage<SkillPromptDetails>,
|
|
9
|
+
"customType" | "content" | "display" | "details" | "attribution"
|
|
10
|
+
> & {
|
|
11
|
+
customType: typeof SKILL_PROMPT_MESSAGE_TYPE;
|
|
12
|
+
content: string | (TextContent | ImageContent)[];
|
|
13
|
+
display: true;
|
|
14
|
+
details: SkillPromptDetails;
|
|
15
|
+
attribution: "user";
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type SkillPromptOptions = {
|
|
19
|
+
streamingBehavior: "steer" | "followUp";
|
|
20
|
+
queueChipText: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
interface ParsedSkillCommand {
|
|
24
|
+
commandName: string;
|
|
25
|
+
args: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface InvokeSkillCommandOptions {
|
|
29
|
+
propagateErrors?: boolean;
|
|
30
|
+
queueOnly?: boolean;
|
|
31
|
+
images?: ImageContent[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Built custom-message payload and delivery options for a `/skill:` command. */
|
|
35
|
+
export interface BuiltSkillCommandPrompt {
|
|
36
|
+
message: SkillPromptMessage;
|
|
37
|
+
options: SkillPromptOptions;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function parseSkillCommand(text: string): ParsedSkillCommand | undefined {
|
|
41
|
+
if (!text.startsWith("/skill:")) return undefined;
|
|
42
|
+
const spaceIndex = text.indexOf(" ");
|
|
43
|
+
const commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);
|
|
44
|
+
const args = spaceIndex === -1 ? "" : text.slice(spaceIndex + 1).trim();
|
|
45
|
+
return { commandName, args };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Return true when `text` names a registered `/skill:<name>` command. */
|
|
49
|
+
export function isKnownSkillCommand(ctx: SkillCommandHost, text: string): boolean {
|
|
50
|
+
const parsed = parseSkillCommand(text);
|
|
51
|
+
if (!parsed) return false;
|
|
52
|
+
return ctx.skillCommands.has(parsed.commandName);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Build the user-attributed custom message for a registered `/skill:<name>` command. */
|
|
56
|
+
export async function buildSkillCommandPrompt(
|
|
57
|
+
ctx: SkillCommandHost,
|
|
58
|
+
text: string,
|
|
59
|
+
streamingBehavior: "steer" | "followUp",
|
|
60
|
+
images?: ImageContent[],
|
|
61
|
+
): Promise<BuiltSkillCommandPrompt | undefined> {
|
|
62
|
+
const parsed = parseSkillCommand(text);
|
|
63
|
+
if (!parsed) return undefined;
|
|
64
|
+
const skillPath = ctx.skillCommands.get(parsed.commandName);
|
|
65
|
+
if (!skillPath) return undefined;
|
|
66
|
+
|
|
67
|
+
const content = await Bun.file(skillPath).text();
|
|
68
|
+
const body = content.replace(/^---\n[\s\S]*?\n---\n/, "").trim();
|
|
69
|
+
const metaLines = [`Skill: ${skillPath}`];
|
|
70
|
+
if (parsed.args) {
|
|
71
|
+
metaLines.push(`User: ${parsed.args}`);
|
|
72
|
+
}
|
|
73
|
+
const message = `${body}\n\n---\n\n${metaLines.join("\n")}`;
|
|
74
|
+
const textBlock: TextContent = { type: "text", text: message };
|
|
75
|
+
const promptContent = images && images.length > 0 ? [textBlock, ...images] : message;
|
|
76
|
+
const skillName = parsed.commandName.slice("skill:".length);
|
|
77
|
+
const details: SkillPromptDetails = {
|
|
78
|
+
name: skillName || parsed.commandName,
|
|
79
|
+
path: skillPath,
|
|
80
|
+
args: parsed.args || undefined,
|
|
81
|
+
lineCount: body ? body.split("\n").length : 0,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
message: {
|
|
86
|
+
customType: SKILL_PROMPT_MESSAGE_TYPE,
|
|
87
|
+
content: promptContent,
|
|
88
|
+
display: true,
|
|
89
|
+
details,
|
|
90
|
+
attribution: "user",
|
|
91
|
+
},
|
|
92
|
+
options: { streamingBehavior, queueChipText: text },
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Invoke a registered `/skill:<name>` command as a user-attributed custom message. */
|
|
97
|
+
export async function invokeSkillCommandFromText(
|
|
98
|
+
ctx: SkillCommandHost,
|
|
99
|
+
text: string,
|
|
100
|
+
streamingBehavior: "steer" | "followUp",
|
|
101
|
+
options?: InvokeSkillCommandOptions,
|
|
102
|
+
): Promise<boolean> {
|
|
103
|
+
try {
|
|
104
|
+
const built = await buildSkillCommandPrompt(ctx, text, streamingBehavior, options?.images);
|
|
105
|
+
if (!built) return false;
|
|
106
|
+
const promptOptions = options?.queueOnly ? { ...built.options, queueOnly: true } : built.options;
|
|
107
|
+
await ctx.session.promptCustomMessage(built.message, promptOptions);
|
|
108
|
+
return true;
|
|
109
|
+
} catch (err) {
|
|
110
|
+
if (options?.propagateErrors) {
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
113
|
+
ctx.showError(`Failed to load skill: ${err instanceof Error ? err.message : String(err)}`);
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
}
|
package/src/modes/types.ts
CHANGED
|
@@ -161,10 +161,14 @@ export interface InteractiveModeContext {
|
|
|
161
161
|
hideThinkingBlock: boolean;
|
|
162
162
|
/**
|
|
163
163
|
* Effective thinking-block visibility: true when hidden by user setting OR
|
|
164
|
-
* thinking level is "off"
|
|
165
|
-
*
|
|
164
|
+
* thinking level is "off" before the session has produced displayable
|
|
165
|
+
* thinking content.
|
|
166
166
|
*/
|
|
167
167
|
readonly effectiveHideThinkingBlock: boolean;
|
|
168
|
+
/** Whether this visible session has produced thinking content the user can reveal. */
|
|
169
|
+
readonly hasDisplayableThinkingContent: boolean;
|
|
170
|
+
/** Record a message whose thinking content makes Ctrl+T meaningful even at thinking level "off"; returns true on first observation. */
|
|
171
|
+
noteDisplayableThinkingContent(message: AgentMessage): boolean;
|
|
168
172
|
proseOnlyThinking: boolean;
|
|
169
173
|
compactionQueuedMessages: CompactionQueuedMessage[];
|
|
170
174
|
pendingTools: Map<string, ToolExecutionHandle>;
|
|
@@ -342,6 +346,7 @@ export interface InteractiveModeContext {
|
|
|
342
346
|
|
|
343
347
|
// Selector handling
|
|
344
348
|
showSettingsSelector(): void;
|
|
349
|
+
showAdvisorConfigure(): void;
|
|
345
350
|
showHistorySearch(): void;
|
|
346
351
|
showExtensionsDashboard(): void;
|
|
347
352
|
showAgentsDashboard(): void;
|
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
type SkillPromptDetails,
|
|
46
46
|
} from "../../session/messages";
|
|
47
47
|
import type { SessionContext } from "../../session/session-context";
|
|
48
|
+
import { buildSkillCommandPrompt, invokeSkillCommandFromText, isKnownSkillCommand } from "../skill-command";
|
|
48
49
|
import { createAssistantMessageComponent } from "./interactive-context-helpers";
|
|
49
50
|
import {
|
|
50
51
|
assistantHasVisibleContent,
|
|
@@ -667,6 +668,15 @@ export class UiHelpers {
|
|
|
667
668
|
}
|
|
668
669
|
|
|
669
670
|
async #deliverQueuedMessage(message: CompactionQueuedMessage): Promise<void> {
|
|
671
|
+
if (
|
|
672
|
+
await invokeSkillCommandFromText(this.ctx, message.text, message.mode, {
|
|
673
|
+
propagateErrors: true,
|
|
674
|
+
queueOnly: true,
|
|
675
|
+
images: message.images,
|
|
676
|
+
})
|
|
677
|
+
) {
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
670
680
|
if (this.ctx.isKnownSlashCommand(message.text)) {
|
|
671
681
|
await this.ctx.session.prompt(message.text);
|
|
672
682
|
return;
|
|
@@ -754,29 +764,37 @@ export class UiHelpers {
|
|
|
754
764
|
await this.#deliverQueuedMessage(message);
|
|
755
765
|
}
|
|
756
766
|
|
|
757
|
-
//
|
|
758
|
-
//
|
|
759
|
-
//
|
|
760
|
-
//
|
|
761
|
-
//
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
767
|
+
// First prompt is fire-and-forget — its rejection is funneled through
|
|
768
|
+
// `restoreQueue` rather than rethrown. Plain prompts use primitive
|
|
769
|
+
// recordLocalSubmission and dispose manually in the catch. Skill prompts
|
|
770
|
+
// are rebuilt as user-attributed custom messages so queued `/skill:` text
|
|
771
|
+
// is not sent as a literal prompt after compaction.
|
|
772
|
+
let promptPromise: Promise<unknown>;
|
|
773
|
+
if (isKnownSkillCommand(this.ctx, firstPrompt.text)) {
|
|
774
|
+
const built = await buildSkillCommandPrompt(
|
|
775
|
+
this.ctx,
|
|
776
|
+
firstPrompt.text,
|
|
777
|
+
firstPrompt.mode,
|
|
778
|
+
firstPrompt.images,
|
|
779
|
+
);
|
|
780
|
+
promptPromise = built
|
|
781
|
+
? this.ctx.session.promptCustomMessage(built.message, built.options).catch(restoreQueue)
|
|
782
|
+
: Promise.resolve();
|
|
783
|
+
} else {
|
|
784
|
+
const disposeFirstPrompt = this.ctx.recordLocalSubmission(
|
|
785
|
+
firstPrompt.text,
|
|
786
|
+
firstPrompt.images?.length ?? 0,
|
|
787
|
+
);
|
|
788
|
+
promptPromise = this.ctx.session
|
|
789
|
+
.prompt(firstPrompt.text, {
|
|
790
|
+
streamingBehavior: firstPrompt.mode === "followUp" ? "followUp" : "steer",
|
|
791
|
+
images: firstPrompt.images,
|
|
792
|
+
})
|
|
793
|
+
.catch((error: unknown) => {
|
|
794
|
+
disposeFirstPrompt();
|
|
795
|
+
restoreQueue(error);
|
|
796
|
+
});
|
|
797
|
+
}
|
|
780
798
|
|
|
781
799
|
for (const message of rest) {
|
|
782
800
|
await this.#deliverQueuedMessage(message);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: reviewer
|
|
3
3
|
description: "Code review specialist for quality/security analysis"
|
|
4
|
-
tools: read, grep, glob, bash, lsp, web_search, ast_grep
|
|
4
|
+
tools: read, grep, glob, bash, lsp, web_search, ast_grep
|
|
5
5
|
spawns: explore
|
|
6
6
|
model: pi/slow
|
|
7
7
|
thinking-level: high
|
|
@@ -22,7 +22,7 @@ output:
|
|
|
22
22
|
optionalProperties:
|
|
23
23
|
findings:
|
|
24
24
|
metadata:
|
|
25
|
-
description:
|
|
25
|
+
description: "Populate via incremental yield sections under type: [\"findings\"]; don't repeat it in a final payload."
|
|
26
26
|
elements:
|
|
27
27
|
properties:
|
|
28
28
|
title:
|
|
@@ -60,8 +60,8 @@ Identify bugs the author would want fixed before merge.
|
|
|
60
60
|
<procedure>
|
|
61
61
|
1. Run `git diff`, `jj diff --git`, or `gh pr diff <number>` to view patch
|
|
62
62
|
2. Read modified files for full context
|
|
63
|
-
3.
|
|
64
|
-
4.
|
|
63
|
+
3. Record each issue with incremental `yield` using `type: ["findings"]`
|
|
64
|
+
4. Record `overall_correctness`, `explanation`, and `confidence` with incremental `yield` sections, then stop so idle finalization assembles the result
|
|
65
65
|
|
|
66
66
|
Bash is read-only: `git diff`, `git log`, `git show`, `jj diff --git`, `gh pr diff`. You NEVER make file edits or trigger builds.
|
|
67
67
|
</procedure>
|
|
@@ -115,7 +115,7 @@ memcpy(buf, data.ptr, data.length);
|
|
|
115
115
|
</example>
|
|
116
116
|
|
|
117
117
|
<output>
|
|
118
|
-
Each `
|
|
118
|
+
Each finding uses incremental `yield` with `type: ["findings"]` and `result.data` containing:
|
|
119
119
|
- `title`: Imperative, ≤80 chars
|
|
120
120
|
- `body`: One paragraph
|
|
121
121
|
- `priority`: 0-3
|
|
@@ -123,11 +123,12 @@ Each `report_finding` requires:
|
|
|
123
123
|
- `file_path`: Path to affected file
|
|
124
124
|
- `line_start`, `line_end`: Range ≤10 lines, must overlap diff
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
- `
|
|
128
|
-
- `
|
|
129
|
-
- `
|
|
130
|
-
|
|
126
|
+
Verdict fields also use incremental `yield` sections:
|
|
127
|
+
- `type: ["overall_correctness"]` with `"correct"` (no bugs/blockers) or `"incorrect"`
|
|
128
|
+
- `type: ["explanation"]` with a plain-text 1-3 sentence verdict summary
|
|
129
|
+
- `type: ["confidence"]` with a 0.0-1.0 confidence value
|
|
130
|
+
|
|
131
|
+
Do not emit a separate submit tool call or duplicate `findings` in another payload. Once all sections are recorded, stop and let idle finalization assemble the result.
|
|
131
132
|
|
|
132
133
|
You NEVER output JSON or code blocks.
|
|
133
134
|
|
|
@@ -14,8 +14,7 @@ Create exactly **1 reviewer task**. Its assignment MUST include the custom instr
|
|
|
14
14
|
Reviewer MUST:
|
|
15
15
|
1. Follow the custom instructions below
|
|
16
16
|
2. Read the referenced files or workspace context needed to evaluate them
|
|
17
|
-
3.
|
|
18
|
-
4. Call `yield` with verdict when done
|
|
17
|
+
3. Use incremental `yield` sections for findings and verdict fields; do NOT call a separate finding tool
|
|
19
18
|
|
|
20
19
|
### Custom Instructions
|
|
21
20
|
|
|
@@ -38,8 +38,7 @@ Reviewer MUST:
|
|
|
38
38
|
1. Focus ONLY on assigned files
|
|
39
39
|
2. {{#if skipDiff}}{{diffInstruction}}{{else}}MUST use diff hunks below (NEVER re-run git diff){{/if}}
|
|
40
40
|
3. {{contextInstruction}}
|
|
41
|
-
4.
|
|
42
|
-
5. Call `yield` with verdict when done
|
|
41
|
+
4. Use incremental `yield` sections for findings and verdict fields; do NOT call a separate finding tool
|
|
43
42
|
|
|
44
43
|
{{#if skipDiff}}
|
|
45
44
|
### Diff Previews
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<system-notice type="interrupted-thinking">
|
|
2
|
+
Your previous turn was interrupted while you were thinking.
|
|
3
|
+
- You MUST treat the preserved reasoning as internal continuity context.
|
|
4
|
+
- You MUST continue the user's task from the relevant unfinished point.
|
|
5
|
+
------
|
|
6
|
+
{{reasoning}}
|
|
7
|
+
</system-notice>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<recap>
|
|
2
|
+
The user stepped away and is coming back. Recap in under 40 words, 1-2 plain sentences, no markdown. Lead with the overall goal and current task, then the one next action. Skip root-cause narrative, fix internals, secondary to-dos, and em-dash tangents.
|
|
3
|
+
{{#if goal}}
|
|
4
|
+
Overall goal: {{goal}}
|
|
5
|
+
{{/if}}
|
|
6
|
+
{{#if task}}
|
|
7
|
+
Active task: {{task}}
|
|
8
|
+
{{/if}}
|
|
9
|
+
</recap>
|
|
@@ -50,13 +50,16 @@ Use `irc` only for quick coordination, never long-form content. Address peers by
|
|
|
50
50
|
COMPLETION
|
|
51
51
|
===================================
|
|
52
52
|
|
|
53
|
-
No TODO tracking, no progress updates. Execute
|
|
53
|
+
No TODO tracking, no progress updates. Execute; report results with `yield`.
|
|
54
54
|
|
|
55
|
-
While work remains, you MUST continue with another tool call — investigate, edit, run, verify. Save narrative for
|
|
55
|
+
While work remains, you MUST continue with another tool call — investigate, edit, run, verify. Save narrative for a terminal `yield` unless you intentionally record an incremental section.
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
Yield protocol:
|
|
58
|
+
- Omit `type` for the normal single terminal structured result in `result.data`.
|
|
59
|
+
- Use non-empty `type: string[]` for incremental, non-terminal sections; calls accumulate by section.
|
|
60
|
+
- Use `type: string` for a terminal result; if data is omitted, your last assistant turn becomes the raw final result.
|
|
58
61
|
|
|
59
|
-
This is your only way to return a result.
|
|
62
|
+
This is your only way to return a final result. For structured results, you NEVER put JSON in plain text or substitute a text summary for `result.data`.
|
|
60
63
|
|
|
61
64
|
{{#if outputSchema}}
|
|
62
65
|
Your result MUST match this TypeScript interface:
|
|
@@ -65,7 +68,7 @@ Your result MUST match this TypeScript interface:
|
|
|
65
68
|
```
|
|
66
69
|
{{/if}}
|
|
67
70
|
|
|
68
|
-
Giving up is a last resort. If truly blocked, you MUST
|
|
71
|
+
Giving up is a last resort. If truly blocked, you MUST terminal-yield `result.error` describing what you tried and the exact blocker.
|
|
69
72
|
You NEVER give up due to uncertainty, missing information obtainable via tools or repo context, or needing a design decision you can derive yourself.
|
|
70
73
|
|
|
71
74
|
You MUST keep going until this ticket is closed. This matters.
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
<system-reminder>
|
|
2
2
|
Your last turn ended without a tool call, so the session went idle. This is reminder {{retryCount}} of {{maxRetries}}.
|
|
3
3
|
|
|
4
|
-
Every turn MUST end with a tool call. Pick
|
|
5
|
-
1. **Resume the work** — if the assignment is not finished, call the next tool you would have called (edit, write, bash, search, etc.). NEVER
|
|
6
|
-
2. **Yield
|
|
7
|
-
3. **Yield with
|
|
4
|
+
Every turn MUST end with a tool call. Pick the first that applies:
|
|
5
|
+
1. **Resume the work** — if the assignment is not finished and you are not recording an incremental section, call the next tool you would have called (edit, write, bash, search, etc.). NEVER treat this reminder as a forced stop.
|
|
6
|
+
2. **Yield an incremental section** — only when useful for the assignment: call `yield` with non-empty `type: string[]`; matching sections accumulate and the task continues.
|
|
7
|
+
3. **Yield with success** — only if the assignment is genuinely complete: call terminal `yield`. Omit `type` for the single final structured result in `result.data`; use `type: string` to finalize from the last assistant turn when data is omitted.
|
|
8
|
+
4. **Yield with error** — only if you hit a real, concrete blocker you can name (missing file, unavailable API, contradictory spec). Describe what you tried and the exact blocker. NEVER fabricate a "forced immediate-yield" or "system reminder required termination" reason — this reminder is not a blocker.
|
|
8
9
|
|
|
9
|
-
Default to option 1 unless the work is actually done or
|
|
10
|
+
Default to option 1 unless the work is actually done, actually blocked, or ready for an incremental section.
|
|
10
11
|
|
|
11
12
|
You NEVER end this turn with text only.
|
|
12
13
|
</system-reminder>
|
|
@@ -58,7 +58,6 @@ Special URLs for internal resources; with most FS/bash tools they auto-resolve t
|
|
|
58
58
|
{{/if}}
|
|
59
59
|
- `agent://<id>`: agent output artifact; `/<path>` extracts a JSON field
|
|
60
60
|
- `artifact://<id>`: artifact content
|
|
61
|
-
- `history://<agentId>`: agent transcript (markdown); bare `history://` lists agents
|
|
62
61
|
- `local://<name>.md`: plan artifacts or shared content for subagents
|
|
63
62
|
{{#if hasObsidian}}
|
|
64
63
|
- `vault://<vault>/<path>`: Obsidian vault (read/edit). `vault://` lists vaults; `vault://_/…` targets the active vault. File ops `?op=outline|backlinks|links|tags|properties|tasks|base|…`; vault ops `?op=search&q=…|daily|tasks|orphans|unresolved|bases|…`.
|
package/src/prompts/tools/irc.md
CHANGED
|
@@ -7,7 +7,7 @@ Send/receive short text messages between agents in this process.
|
|
|
7
7
|
- Messaging an `idle`/`parked` peer wakes it — no separate revive call.
|
|
8
8
|
- `op: "wait"` — block for a message (optionally only `from` one peer); consumes + returns it. Timeout = clean "no message", not an error.
|
|
9
9
|
- `op: "inbox"` — drain pending messages without blocking.
|
|
10
|
-
- Replies arrive only when the recipient sends one
|
|
10
|
+
- Replies arrive only when the recipient sends one; don't interrogate a peer for status.
|
|
11
11
|
</instruction>
|
|
12
12
|
|
|
13
13
|
<when_to_use>
|
|
@@ -24,7 +24,7 @@ NEVER for: routine progress updates, things a tool call can verify, questions yo
|
|
|
24
24
|
Applies to sending + replying.
|
|
25
25
|
- **Plain prose only.** NEVER JSON status payloads like `{"type":"task_completed",…}` — write a normal sentence.
|
|
26
26
|
- **NEVER quote the message you answer.** Lead with the answer; set `replyTo`.
|
|
27
|
-
- **Learn about peers via IRC** — NEVER grep artifacts, read other sessions' JSONL, or shell-poke. DM them
|
|
27
|
+
- **Learn about peers via IRC** — NEVER grep artifacts, read other sessions' JSONL, or shell-poke. DM them.
|
|
28
28
|
- **Send, then keep working.** `wait`/`await: true` only when you cannot proceed. NEVER "did you get my message?". A `failed` receipt = peer unreachable — move on; NEVER retry in a loop.
|
|
29
29
|
- **Answer expected questions** via `irc send` to the sender (finish your current step first).
|
|
30
30
|
- **Stay terse.** One question per send; share files via `local://`/`memory://`/`artifact://` URLs, never pasted blobs.
|
|
@@ -7,7 +7,7 @@ Read files, directories, archives, SQLite, images, documents, internal resources
|
|
|
7
7
|
|
|
8
8
|
## Parameters
|
|
9
9
|
|
|
10
|
-
- `path` — required. Local path, internal URI (`skill://`, `agent://`, `artifact://`, `
|
|
10
|
+
- `path` — required. Local path, internal URI (`skill://`, `agent://`, `artifact://`, `memory://`, `rule://`, `local://`, `vault://`, `mcp://`, `omp://`, `issue://`, `pr://`, `ssh://`), or URL. Append `:<sel>` for ranges/modes (e.g. `src/foo.ts:50-200`, `src/foo.ts:raw`, `db.sqlite:users:42`).
|
|
11
11
|
|
|
12
12
|
## Selectors
|
|
13
13
|
|
|
@@ -67,7 +67,7 @@ For `.sqlite`, `.sqlite3`, `.db`, `.db3`:
|
|
|
67
67
|
|
|
68
68
|
# Internal URIs
|
|
69
69
|
|
|
70
|
-
All URI schemes take the same line selectors. `artifact://<id>` recovers full output a bash/eval/tool result spilled or truncated.
|
|
70
|
+
All URI schemes take the same line selectors. `artifact://<id>` recovers full output a bash/eval/tool result spilled or truncated.
|
|
71
71
|
|
|
72
72
|
`ssh://host/<absolute-path>` reads a remote text file (UTF-8, ≤1 MiB) or lists a directory one level deep, on a pre-configured SSH host or `~/.ssh/config` alias; `ssh://host/` lists the remote root and bare `ssh://` lists the configured hosts. Files are also writable via `write` and searchable via `search`; a directory only lists (`search` refuses a directory, `write` refuses to overwrite one). A literal `:`, `?`, or `#` in the remote path must be percent-encoded (`%3A`/`%3F`/`%23`) — a trailing `:sel` is read as a line selector, and `?`/`#` start a URL query/fragment. Requires a POSIX login shell (`sh`/`bash`/`zsh`); a Windows host or a non-POSIX shell (fish, csh/tcsh) is rejected — use the `ssh` tool there.
|
|
73
73
|
|