@oh-my-pi/pi-coding-agent 16.2.1 → 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 +72 -0
- package/dist/cli.js +3621 -3579
- 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/runtime.d.ts +15 -1
- 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/model-roles.d.ts +1 -1
- package/dist/types/config/settings-schema.d.ts +113 -12
- package/dist/types/debug/log-viewer.d.ts +1 -0
- package/dist/types/debug/raw-sse.d.ts +1 -0
- package/dist/types/discovery/omp-extension-roots.d.ts +3 -3
- package/dist/types/edit/hashline/diff.d.ts +0 -11
- package/dist/types/edit/index.d.ts +18 -0
- package/dist/types/edit/streaming.d.ts +30 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -0
- package/dist/types/extensibility/shared-events.d.ts +1 -0
- package/dist/types/extensibility/tool-event-input.d.ts +7 -0
- package/dist/types/extensibility/utils.d.ts +12 -0
- package/dist/types/mcp/oauth-discovery.d.ts +0 -11
- 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 -3
- 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/sdk.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +84 -12
- package/dist/types/session/indexed-session-storage.d.ts +7 -1
- package/dist/types/session/messages.d.ts +32 -7
- 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/session/settings-stream-fn.d.ts +21 -0
- package/dist/types/session/turn-persistence.d.ts +88 -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 +438 -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 +77 -4
- package/src/advisor/transcript-recorder.ts +25 -2
- package/src/advisor/watchdog.ts +57 -31
- package/src/auto-thinking/classifier.ts +2 -2
- 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/model-resolver.ts +5 -1
- package/src/config/model-roles.ts +3 -3
- package/src/config/settings-schema.ts +107 -8
- 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/discovery/omp-extension-roots.ts +38 -13
- package/src/edit/hashline/diff.ts +57 -4
- package/src/edit/index.ts +21 -0
- package/src/edit/streaming.ts +170 -0
- package/src/eval/js/shared/local-module-loader.ts +23 -1
- package/src/export/html/template.js +13 -7
- package/src/extensibility/custom-tools/types.ts +1 -0
- 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/legacy-pi-bundled-keys.ts +18 -1
- package/src/extensibility/plugins/legacy-pi-bundled-registry.ts +59 -2
- package/src/extensibility/plugins/manager.ts +76 -5
- package/src/extensibility/shared-events.ts +1 -0
- 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/oauth-discovery.ts +5 -29
- package/src/mcp/transports/http.ts +3 -1
- 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/mnemopi/backend.ts +2 -2
- package/src/modes/acp/acp-agent.ts +1 -1
- 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/assistant-message.ts +5 -5
- 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 +145 -14
- package/src/modes/components/status-line/segments.ts +47 -22
- 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 +114 -11
- 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 -10
- package/src/modes/print-mode.ts +1 -1
- package/src/modes/skill-command.ts +116 -0
- package/src/modes/types.ts +7 -2
- package/src/modes/utils/transcript-render-helpers.ts +2 -2
- package/src/modes/utils/ui-helpers.ts +46 -27
- 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 +40 -50
- package/src/session/agent-session.ts +1139 -600
- package/src/session/indexed-session-storage.ts +86 -13
- package/src/session/messages.test.ts +125 -0
- package/src/session/messages.ts +192 -21
- 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 +96 -3
- package/src/session/session-storage.ts +36 -0
- package/src/session/session-title-slot.ts +141 -0
- package/src/session/settings-stream-fn.ts +49 -0
- package/src/session/sql-session-storage.ts +71 -11
- package/src/session/turn-persistence.ts +142 -0
- package/src/session/unexpected-stop-classifier.ts +2 -2
- package/src/slash-commands/builtin-registry.ts +16 -3
- package/src/slash-commands/helpers/mcp.ts +2 -1
- 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/models.ts +8 -6
- package/src/tiny/text.ts +37 -7
- package/src/tools/ask.ts +55 -4
- package/src/tools/image-gen.ts +2 -1
- 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/tts.ts +2 -1
- package/src/tools/yield.ts +140 -31
- package/src/utils/thinking-display.ts +15 -0
- package/src/utils/title-generator.ts +1 -1
- package/src/web/search/providers/tavily.ts +36 -19
|
@@ -4,8 +4,15 @@ import { getOAuthProviders } from "@oh-my-pi/pi-ai/oauth";
|
|
|
4
4
|
import type { OAuthProvider } from "@oh-my-pi/pi-ai/oauth/types";
|
|
5
5
|
import type { Component, OverlayHandle } from "@oh-my-pi/pi-tui";
|
|
6
6
|
import { Input, Loader, Spacer, setTuiTight, Text } from "@oh-my-pi/pi-tui";
|
|
7
|
-
import { getAgentDbPath, getProjectDir, normalizePathForComparison } from "@oh-my-pi/pi-utils";
|
|
8
|
-
import {
|
|
7
|
+
import { getAgentDbPath, getAgentDir, getProjectDir, normalizePathForComparison } from "@oh-my-pi/pi-utils";
|
|
8
|
+
import {
|
|
9
|
+
type AdvisorConfigScope,
|
|
10
|
+
discoverAdvisorConfigs,
|
|
11
|
+
loadWatchdogConfigFile,
|
|
12
|
+
resolveAdvisorConfigEditPath,
|
|
13
|
+
saveWatchdogConfigFile,
|
|
14
|
+
} from "../../advisor";
|
|
15
|
+
import { formatModelSelectorValue, resolveAdvisorRoleSelection } from "../../config/model-resolver";
|
|
9
16
|
import { getRoleInfo } from "../../config/model-roles";
|
|
10
17
|
import { settings } from "../../config/settings";
|
|
11
18
|
import { disableProvider, enableProvider } from "../../discovery";
|
|
@@ -49,7 +56,9 @@ import {
|
|
|
49
56
|
} from "../../tools";
|
|
50
57
|
import { shortenPath } from "../../tools/render-utils";
|
|
51
58
|
import { copyToClipboard } from "../../utils/clipboard";
|
|
59
|
+
import { repo } from "../../utils/git";
|
|
52
60
|
import { setSessionTerminalTitle } from "../../utils/title-generator";
|
|
61
|
+
import { type AdvisorConfigDeps, AdvisorConfigOverlayComponent } from "../components/advisor-config";
|
|
53
62
|
import { AgentDashboard } from "../components/agent-dashboard";
|
|
54
63
|
import { AgentHubOverlayComponent } from "../components/agent-hub";
|
|
55
64
|
import { AssistantMessageComponent } from "../components/assistant-message";
|
|
@@ -163,6 +172,7 @@ export class SelectorController {
|
|
|
163
172
|
showHookStatus: settings.get("statusLine.showHookStatus"),
|
|
164
173
|
sessionAccent: settings.get("statusLine.sessionAccent"),
|
|
165
174
|
transparent: settings.get("statusLine.transparent"),
|
|
175
|
+
compactThinkingLevel: settings.get("statusLine.compactThinkingLevel"),
|
|
166
176
|
...previewSettings,
|
|
167
177
|
});
|
|
168
178
|
this.ctx.updateEditorTopBorder();
|
|
@@ -191,6 +201,7 @@ export class SelectorController {
|
|
|
191
201
|
showHookStatus: settings.get("statusLine.showHookStatus"),
|
|
192
202
|
sessionAccent: settings.get("statusLine.sessionAccent"),
|
|
193
203
|
transparent: settings.get("statusLine.transparent"),
|
|
204
|
+
compactThinkingLevel: settings.get("statusLine.compactThinkingLevel"),
|
|
194
205
|
});
|
|
195
206
|
this.ctx.updateEditorTopBorder();
|
|
196
207
|
this.ctx.ui.requestRender();
|
|
@@ -209,6 +220,78 @@ export class SelectorController {
|
|
|
209
220
|
});
|
|
210
221
|
}
|
|
211
222
|
|
|
223
|
+
showAdvisorConfigure(): void {
|
|
224
|
+
const cwd = this.ctx.sessionManager.getCwd();
|
|
225
|
+
const agentDir = getAgentDir() ?? getProjectDir();
|
|
226
|
+
const initialScope: AdvisorConfigScope = "project";
|
|
227
|
+
void (async () => {
|
|
228
|
+
// "Project" scope edits the repo-root WATCHDOG.yml (the project-level file
|
|
229
|
+
// discovery walks), not the launch subdir — `getProjectDir()` is only cwd.
|
|
230
|
+
let projectDir = cwd;
|
|
231
|
+
try {
|
|
232
|
+
projectDir = (await repo.root(cwd)) ?? cwd;
|
|
233
|
+
} catch {
|
|
234
|
+
projectDir = cwd;
|
|
235
|
+
}
|
|
236
|
+
const dirs = { projectDir, agentDir };
|
|
237
|
+
const initialDoc = await loadWatchdogConfigFile(await resolveAdvisorConfigEditPath(initialScope, dirs));
|
|
238
|
+
// Fullscreen editor on the alternate screen (the /settings idiom): the
|
|
239
|
+
// overlay holds the alt buffer + mouse tracking; the transcript stays put.
|
|
240
|
+
let overlayHandle: OverlayHandle | undefined;
|
|
241
|
+
const done = () => {
|
|
242
|
+
overlayHandle?.hide();
|
|
243
|
+
this.focusActiveEditorArea();
|
|
244
|
+
this.ctx.ui.requestRender();
|
|
245
|
+
};
|
|
246
|
+
// Label the seeded implicit-default row with the actual advisor-role model
|
|
247
|
+
// (NOT the first live advisor, which may be a named advisor from another scope).
|
|
248
|
+
const advisorRoleSel = resolveAdvisorRoleSelection(
|
|
249
|
+
this.ctx.settings,
|
|
250
|
+
this.ctx.session.modelRegistry.getAvailable(),
|
|
251
|
+
this.ctx.session.modelRegistry,
|
|
252
|
+
);
|
|
253
|
+
const defaultAdvisorModel = advisorRoleSel?.model;
|
|
254
|
+
const deps: AdvisorConfigDeps = {
|
|
255
|
+
modelRegistry: this.ctx.session.modelRegistry,
|
|
256
|
+
settings: this.ctx.settings,
|
|
257
|
+
scopedModels: this.ctx.session.scopedModels,
|
|
258
|
+
availableToolNames: this.ctx.session.getAdvisorAvailableToolNames(),
|
|
259
|
+
defaultModelLabel: defaultAdvisorModel
|
|
260
|
+
? `${defaultAdvisorModel.provider}/${defaultAdvisorModel.id}`
|
|
261
|
+
: undefined,
|
|
262
|
+
};
|
|
263
|
+
const overlay = new AdvisorConfigOverlayComponent(this.ctx.ui, deps, initialScope, initialDoc, {
|
|
264
|
+
loadDoc: async scope => loadWatchdogConfigFile(await resolveAdvisorConfigEditPath(scope, dirs)),
|
|
265
|
+
save: async (scope, doc) => {
|
|
266
|
+
await saveWatchdogConfigFile(await resolveAdvisorConfigEditPath(scope, dirs), doc);
|
|
267
|
+
// Re-discover the merged roster (project + user) so the live advisors
|
|
268
|
+
// reflect cross-level precedence, not just the edited file.
|
|
269
|
+
const discovered = await discoverAdvisorConfigs(cwd, agentDir);
|
|
270
|
+
const count = this.ctx.session.applyAdvisorConfigs(discovered.advisors, discovered.sharedInstructions);
|
|
271
|
+
this.ctx.statusLine.invalidate();
|
|
272
|
+
this.ctx.showStatus(
|
|
273
|
+
count > 0
|
|
274
|
+
? `Saved ${scope} WATCHDOG.yml — ${count} advisor${count === 1 ? "" : "s"} active.`
|
|
275
|
+
: `Saved ${scope} WATCHDOG.yml. Run /advisor on to activate the configured advisors.`,
|
|
276
|
+
);
|
|
277
|
+
this.ctx.ui.requestRender();
|
|
278
|
+
},
|
|
279
|
+
close: done,
|
|
280
|
+
requestRender: () => this.ctx.ui.requestRender(),
|
|
281
|
+
notify: message => this.ctx.showStatus(message),
|
|
282
|
+
});
|
|
283
|
+
overlayHandle = this.ctx.ui.showOverlay(overlay, {
|
|
284
|
+
anchor: "bottom-center",
|
|
285
|
+
width: "100%",
|
|
286
|
+
maxHeight: "100%",
|
|
287
|
+
margin: 0,
|
|
288
|
+
fullscreen: true,
|
|
289
|
+
});
|
|
290
|
+
this.ctx.ui.setFocus(overlay);
|
|
291
|
+
this.ctx.ui.requestRender();
|
|
292
|
+
})();
|
|
293
|
+
}
|
|
294
|
+
|
|
212
295
|
showHistorySearch(): void {
|
|
213
296
|
const historyStorage = this.ctx.historyStorage;
|
|
214
297
|
if (!historyStorage) return;
|
|
@@ -451,6 +534,7 @@ export class SelectorController {
|
|
|
451
534
|
case "statusLine.showHookStatus":
|
|
452
535
|
case "statusLine.sessionAccent":
|
|
453
536
|
case "statusLine.transparent":
|
|
537
|
+
case "statusLine.compactThinkingLevel":
|
|
454
538
|
case "statusLineSegments":
|
|
455
539
|
case "statusLineModelThinking":
|
|
456
540
|
case "statusLinePathAbbreviate":
|
|
@@ -471,6 +555,7 @@ export class SelectorController {
|
|
|
471
555
|
sessionAccent: settings.get("statusLine.sessionAccent"),
|
|
472
556
|
transparent: settings.get("statusLine.transparent"),
|
|
473
557
|
segmentOptions: settings.get("statusLine.segmentOptions"),
|
|
558
|
+
compactThinkingLevel: settings.get("statusLine.compactThinkingLevel"),
|
|
474
559
|
};
|
|
475
560
|
this.ctx.statusLine.updateSettings(statusLineSettings);
|
|
476
561
|
this.ctx.updateEditorTopBorder();
|
|
@@ -536,19 +621,25 @@ export class SelectorController {
|
|
|
536
621
|
done();
|
|
537
622
|
this.ctx.ui.requestRender();
|
|
538
623
|
} else if (role === "default") {
|
|
539
|
-
|
|
540
|
-
await this.ctx.session.setModel(model, role, {
|
|
624
|
+
const { switched } = await this.ctx.session.setModel(model, role, {
|
|
541
625
|
selector,
|
|
542
626
|
thinkingLevel: concreteThinking,
|
|
543
627
|
persist: true,
|
|
628
|
+
currentContextTokens,
|
|
544
629
|
});
|
|
545
630
|
if (isAuto) {
|
|
546
|
-
|
|
547
|
-
|
|
631
|
+
if (switched) {
|
|
632
|
+
this.ctx.session.setThinkingLevel(AUTO_THINKING, true);
|
|
633
|
+
} else {
|
|
634
|
+
this.ctx.settings.set("defaultThinkingLevel", AUTO_THINKING);
|
|
635
|
+
}
|
|
636
|
+
} else if (switched && concreteThinking && concreteThinking !== ThinkingLevel.Inherit) {
|
|
548
637
|
this.ctx.session.setThinkingLevel(concreteThinking);
|
|
549
638
|
}
|
|
550
|
-
|
|
551
|
-
|
|
639
|
+
if (switched) {
|
|
640
|
+
this.ctx.statusLine.invalidate();
|
|
641
|
+
this.ctx.updateEditorBorderColor();
|
|
642
|
+
}
|
|
552
643
|
this.ctx.showStatus(`Default model: ${selector ?? model.id}`);
|
|
553
644
|
// Don't call done() - selector stays open for role assignment
|
|
554
645
|
} else {
|
|
@@ -933,7 +1024,7 @@ export class SelectorController {
|
|
|
933
1024
|
|
|
934
1025
|
this.ctx.clearTransientSessionUi();
|
|
935
1026
|
this.ctx.statusLine.invalidate();
|
|
936
|
-
this.ctx.statusLine.
|
|
1027
|
+
this.ctx.statusLine.resetActiveTime();
|
|
937
1028
|
this.ctx.updateEditorTopBorder();
|
|
938
1029
|
this.ctx.updateEditorBorderColor();
|
|
939
1030
|
this.ctx.renderInitialMessages({ clearTerminalHistory: true });
|
|
@@ -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[] = [];
|
|
@@ -858,11 +874,6 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
858
874
|
this.#observerRegistry.subscribeToEventBus(this.#eventBus);
|
|
859
875
|
}
|
|
860
876
|
this.#observerRegistry.setMainSession(this.sessionManager.getSessionFile() ?? undefined);
|
|
861
|
-
this.statusLine.setSubagentHubHint(
|
|
862
|
-
this.keybindings.getDisplayString("app.agents.hub") ||
|
|
863
|
-
this.keybindings.getDisplayString("app.session.observe") ||
|
|
864
|
-
undefined,
|
|
865
|
-
);
|
|
866
877
|
this.syncRunningSubagentBadge();
|
|
867
878
|
this.#observerRegistry.onChange(() => {
|
|
868
879
|
this.syncRunningSubagentBadge();
|
|
@@ -1422,6 +1433,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1422
1433
|
sessionAccent: settings.get("statusLine.sessionAccent"),
|
|
1423
1434
|
transparent: settings.get("statusLine.transparent"),
|
|
1424
1435
|
segmentOptions: settings.get("statusLine.segmentOptions"),
|
|
1436
|
+
compactThinkingLevel: settings.get("statusLine.compactThinkingLevel"),
|
|
1425
1437
|
});
|
|
1426
1438
|
}
|
|
1427
1439
|
|
|
@@ -1483,11 +1495,47 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
1483
1495
|
}
|
|
1484
1496
|
|
|
1485
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
|
+
}
|
|
1486
1523
|
this.chatContainer.clear();
|
|
1487
1524
|
// Live display uses the compacted transcript tail; export/resume callers
|
|
1488
1525
|
// can still request the full inline compaction history.
|
|
1489
1526
|
const context = this.viewSession.buildTranscriptSessionContext({ collapseCompactedHistory: true });
|
|
1490
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
|
+
}
|
|
1491
1539
|
// During the pre-streaming window — after `startPendingSubmission` has
|
|
1492
1540
|
// optimistically rendered the user's message but before the user
|
|
1493
1541
|
// `message_start` event lands it in `session` entries — any rebuild
|
|
@@ -3588,6 +3636,9 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3588
3636
|
sessionContext: SessionContext,
|
|
3589
3637
|
options?: { updateFooter?: boolean; populateHistory?: boolean },
|
|
3590
3638
|
): void {
|
|
3639
|
+
for (const message of sessionContext.messages) {
|
|
3640
|
+
this.noteDisplayableThinkingContent(message);
|
|
3641
|
+
}
|
|
3591
3642
|
this.#uiHelpers.renderSessionContext(sessionContext, options);
|
|
3592
3643
|
}
|
|
3593
3644
|
|
|
@@ -3849,6 +3900,10 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3849
3900
|
this.#selectorController.showSettingsSelector();
|
|
3850
3901
|
}
|
|
3851
3902
|
|
|
3903
|
+
showAdvisorConfigure(): void {
|
|
3904
|
+
this.#selectorController.showAdvisorConfigure();
|
|
3905
|
+
}
|
|
3906
|
+
|
|
3852
3907
|
showHistorySearch(): void {
|
|
3853
3908
|
this.#selectorController.showHistorySearch();
|
|
3854
3909
|
}
|
package/src/modes/print-mode.ts
CHANGED
|
@@ -83,7 +83,7 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti
|
|
|
83
83
|
// Check for error/aborted — skip silent-abort (plan-mode compaction transition)
|
|
84
84
|
if (
|
|
85
85
|
(assistantMsg.stopReason === "error" || assistantMsg.stopReason === "aborted") &&
|
|
86
|
-
!isSilentAbort(assistantMsg
|
|
86
|
+
!isSilentAbort(assistantMsg)
|
|
87
87
|
) {
|
|
88
88
|
const errorLine = sanitizeText(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);
|
|
89
89
|
// Flush before this hard exit — it bypasses the awaited postmortem.quit()
|
|
@@ -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;
|
|
@@ -146,11 +146,11 @@ export function resolveAssistantErrorMessage(
|
|
|
146
146
|
message: AssistantAgentMessage,
|
|
147
147
|
retryAttempt = 0,
|
|
148
148
|
): { hasErrorStop: boolean; errorMessage: string | null } {
|
|
149
|
-
const isAbortedSilently = message.stopReason === "aborted" && isSilentAbort(message
|
|
149
|
+
const isAbortedSilently = message.stopReason === "aborted" && isSilentAbort(message);
|
|
150
150
|
const hasErrorStop = !isAbortedSilently && (message.stopReason === "aborted" || message.stopReason === "error");
|
|
151
151
|
const errorMessage = hasErrorStop
|
|
152
152
|
? message.stopReason === "aborted"
|
|
153
|
-
? resolveAbortLabel(message
|
|
153
|
+
? resolveAbortLabel(message, retryAttempt)
|
|
154
154
|
: message.errorMessage || "Error"
|
|
155
155
|
: null;
|
|
156
156
|
return { hasErrorStop, errorMessage };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import type { AssistantMessage, ImageContent, Message, Usage } from "@oh-my-pi/pi-ai";
|
|
3
|
+
import { getStreamingPartialJson } from "@oh-my-pi/pi-ai/utils/block-symbols";
|
|
3
4
|
import { type Component, Spacer, Text, TruncatedText } from "@oh-my-pi/pi-tui";
|
|
4
5
|
import type { AdvisorMessageDetails } from "../../advisor";
|
|
5
6
|
import { COLLAB_PROMPT_MESSAGE_TYPE, type CollabPromptDetails } from "../../collab/protocol";
|
|
@@ -44,6 +45,7 @@ import {
|
|
|
44
45
|
type SkillPromptDetails,
|
|
45
46
|
} from "../../session/messages";
|
|
46
47
|
import type { SessionContext } from "../../session/session-context";
|
|
48
|
+
import { buildSkillCommandPrompt, invokeSkillCommandFromText, isKnownSkillCommand } from "../skill-command";
|
|
47
49
|
import { createAssistantMessageComponent } from "./interactive-context-helpers";
|
|
48
50
|
import {
|
|
49
51
|
assistantHasVisibleContent,
|
|
@@ -409,10 +411,10 @@ export class UiHelpers {
|
|
|
409
411
|
readGroup?.seal();
|
|
410
412
|
readGroup = null;
|
|
411
413
|
const tool = this.ctx.viewSession.getToolByName(content.name);
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
414
|
+
const partialJson = getStreamingPartialJson(content);
|
|
415
|
+
const renderArgs = partialJson
|
|
416
|
+
? { ...content.arguments, __partialJson: partialJson }
|
|
417
|
+
: content.arguments;
|
|
416
418
|
const component = new ToolExecutionComponent(
|
|
417
419
|
content.name,
|
|
418
420
|
renderArgs,
|
|
@@ -666,6 +668,15 @@ export class UiHelpers {
|
|
|
666
668
|
}
|
|
667
669
|
|
|
668
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
|
+
}
|
|
669
680
|
if (this.ctx.isKnownSlashCommand(message.text)) {
|
|
670
681
|
await this.ctx.session.prompt(message.text);
|
|
671
682
|
return;
|
|
@@ -753,29 +764,37 @@ export class UiHelpers {
|
|
|
753
764
|
await this.#deliverQueuedMessage(message);
|
|
754
765
|
}
|
|
755
766
|
|
|
756
|
-
//
|
|
757
|
-
//
|
|
758
|
-
//
|
|
759
|
-
//
|
|
760
|
-
//
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
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
|
+
}
|
|
779
798
|
|
|
780
799
|
for (const message of rest) {
|
|
781
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
|