@oh-my-pi/pi-coding-agent 14.9.8 → 15.0.0

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.
Files changed (138) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/package.json +7 -7
  3. package/scripts/build-binary.ts +11 -0
  4. package/scripts/format-prompts.ts +1 -1
  5. package/src/cli/args.ts +2 -2
  6. package/src/cli/stats-cli.ts +2 -0
  7. package/src/cli.ts +24 -1
  8. package/src/commands/acp.ts +24 -0
  9. package/src/commands/launch.ts +6 -4
  10. package/src/commit/agentic/prompts/system.md +1 -1
  11. package/src/config/model-resolver.ts +30 -0
  12. package/src/config/settings-schema.ts +61 -9
  13. package/src/config/settings.ts +18 -1
  14. package/src/edit/index.ts +22 -1
  15. package/src/edit/modes/patch.ts +10 -0
  16. package/src/edit/modes/replace.ts +3 -0
  17. package/src/edit/renderer.ts +10 -0
  18. package/src/edit/streaming.ts +1 -1
  19. package/src/eval/js/context-manager.ts +10 -9
  20. package/src/eval/js/shared/rewrite-imports.ts +120 -48
  21. package/src/eval/js/shared/runtime.ts +31 -4
  22. package/src/eval/js/tool-bridge.ts +43 -21
  23. package/src/extensibility/extensions/runner.ts +54 -1
  24. package/src/extensibility/extensions/types.ts +11 -0
  25. package/src/extensibility/skills.ts +33 -1
  26. package/src/hashline/grammar.lark +1 -1
  27. package/src/hashline/input.ts +11 -5
  28. package/src/internal-urls/docs-index.generated.ts +7 -7
  29. package/src/internal-urls/index.ts +1 -0
  30. package/src/internal-urls/issue-pr-protocol.ts +577 -0
  31. package/src/internal-urls/router.ts +6 -3
  32. package/src/internal-urls/types.ts +22 -1
  33. package/src/main.ts +13 -9
  34. package/src/modes/acp/acp-agent.ts +361 -54
  35. package/src/modes/acp/acp-client-bridge.ts +152 -0
  36. package/src/modes/acp/acp-event-mapper.ts +180 -15
  37. package/src/modes/acp/terminal-auth.ts +37 -0
  38. package/src/modes/components/read-tool-group.ts +29 -1
  39. package/src/modes/controllers/command-controller.ts +14 -6
  40. package/src/modes/controllers/event-controller.ts +24 -11
  41. package/src/modes/controllers/extension-ui-controller.ts +8 -2
  42. package/src/modes/controllers/input-controller.ts +72 -39
  43. package/src/modes/interactive-mode.ts +71 -7
  44. package/src/modes/rpc/rpc-mode.ts +17 -2
  45. package/src/modes/types.ts +6 -2
  46. package/src/modes/utils/ui-helpers.ts +15 -3
  47. package/src/prompts/agents/designer.md +5 -5
  48. package/src/prompts/agents/explore.md +7 -7
  49. package/src/prompts/agents/init.md +9 -9
  50. package/src/prompts/agents/librarian.md +14 -14
  51. package/src/prompts/agents/plan.md +4 -4
  52. package/src/prompts/agents/reviewer.md +5 -5
  53. package/src/prompts/agents/task.md +10 -10
  54. package/src/prompts/commands/orchestrate.md +2 -2
  55. package/src/prompts/compaction/branch-summary.md +3 -3
  56. package/src/prompts/compaction/compaction-short-summary.md +7 -7
  57. package/src/prompts/compaction/compaction-summary-context.md +1 -1
  58. package/src/prompts/compaction/compaction-summary.md +5 -5
  59. package/src/prompts/compaction/compaction-turn-prefix.md +3 -3
  60. package/src/prompts/compaction/compaction-update-summary.md +11 -11
  61. package/src/prompts/memories/consolidation.md +2 -2
  62. package/src/prompts/memories/read-path.md +1 -1
  63. package/src/prompts/memories/stage_one_input.md +1 -1
  64. package/src/prompts/memories/stage_one_system.md +5 -5
  65. package/src/prompts/review-request.md +4 -4
  66. package/src/prompts/system/agent-creation-architect.md +17 -17
  67. package/src/prompts/system/agent-creation-user.md +2 -2
  68. package/src/prompts/system/commit-message-system.md +2 -2
  69. package/src/prompts/system/custom-system-prompt.md +2 -2
  70. package/src/prompts/system/eager-todo.md +6 -6
  71. package/src/prompts/system/handoff-document.md +1 -1
  72. package/src/prompts/system/plan-mode-active.md +22 -21
  73. package/src/prompts/system/plan-mode-approved.md +4 -4
  74. package/src/prompts/system/plan-mode-compact-instructions.md +16 -0
  75. package/src/prompts/system/plan-mode-reference.md +2 -2
  76. package/src/prompts/system/plan-mode-subagent.md +8 -8
  77. package/src/prompts/system/plan-mode-tool-decision-reminder.md +2 -2
  78. package/src/prompts/system/project-prompt.md +4 -4
  79. package/src/prompts/system/subagent-system-prompt.md +7 -7
  80. package/src/prompts/system/subagent-yield-reminder.md +4 -4
  81. package/src/prompts/system/system-prompt.md +72 -71
  82. package/src/prompts/system/ttsr-interrupt.md +1 -1
  83. package/src/prompts/tools/apply-patch.md +1 -1
  84. package/src/prompts/tools/ast-edit.md +3 -3
  85. package/src/prompts/tools/ast-grep.md +3 -3
  86. package/src/prompts/tools/browser.md +3 -3
  87. package/src/prompts/tools/checkpoint.md +3 -3
  88. package/src/prompts/tools/exit-plan-mode.md +2 -2
  89. package/src/prompts/tools/find.md +3 -3
  90. package/src/prompts/tools/github.md +2 -5
  91. package/src/prompts/tools/hashline.md +20 -20
  92. package/src/prompts/tools/image-gen.md +3 -3
  93. package/src/prompts/tools/irc.md +1 -1
  94. package/src/prompts/tools/lsp.md +2 -2
  95. package/src/prompts/tools/patch.md +6 -6
  96. package/src/prompts/tools/read.md +7 -7
  97. package/src/prompts/tools/replace.md +5 -5
  98. package/src/prompts/tools/retain.md +1 -1
  99. package/src/prompts/tools/rewind.md +2 -2
  100. package/src/prompts/tools/search.md +2 -2
  101. package/src/prompts/tools/ssh.md +2 -2
  102. package/src/prompts/tools/task.md +12 -6
  103. package/src/prompts/tools/web-search.md +2 -2
  104. package/src/prompts/tools/write.md +3 -3
  105. package/src/sdk.ts +69 -12
  106. package/src/session/agent-session.ts +231 -22
  107. package/src/session/client-bridge.ts +81 -0
  108. package/src/session/compaction/errors.ts +31 -0
  109. package/src/session/compaction/index.ts +1 -0
  110. package/src/slash-commands/acp-builtins.ts +46 -0
  111. package/src/slash-commands/builtin-registry.ts +699 -116
  112. package/src/slash-commands/helpers/context-report.ts +39 -0
  113. package/src/slash-commands/helpers/format.ts +23 -0
  114. package/src/slash-commands/helpers/marketplace-manager.ts +25 -0
  115. package/src/slash-commands/helpers/mcp.ts +532 -0
  116. package/src/slash-commands/helpers/parse.ts +85 -0
  117. package/src/slash-commands/helpers/ssh.ts +193 -0
  118. package/src/slash-commands/helpers/todo.ts +279 -0
  119. package/src/slash-commands/helpers/usage-report.ts +91 -0
  120. package/src/slash-commands/types.ts +126 -0
  121. package/src/task/executor.ts +10 -3
  122. package/src/task/index.ts +29 -51
  123. package/src/task/render.ts +6 -3
  124. package/src/task/worktree.ts +170 -239
  125. package/src/tools/bash.ts +176 -2
  126. package/src/tools/browser/tab-supervisor.ts +13 -13
  127. package/src/tools/conflict-detect.ts +6 -6
  128. package/src/tools/fetch.ts +15 -4
  129. package/src/tools/find.ts +19 -1
  130. package/src/tools/gh-renderer.ts +0 -12
  131. package/src/tools/gh.ts +682 -176
  132. package/src/tools/github-cache.ts +548 -0
  133. package/src/tools/index.ts +3 -0
  134. package/src/tools/read.ts +110 -27
  135. package/src/tools/write.ts +23 -1
  136. package/src/tui/code-cell.ts +70 -2
  137. package/src/utils/git.ts +5 -0
  138. package/src/task/isolation-backend.ts +0 -94
@@ -240,45 +240,12 @@ export class InputController {
240
240
  text = slashResult;
241
241
  }
242
242
 
243
- // Handle skill commands (/skill:name [args])
244
- if (text.startsWith("/skill:")) {
245
- const spaceIndex = text.indexOf(" ");
246
- const commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);
247
- const args = spaceIndex === -1 ? "" : text.slice(spaceIndex + 1).trim();
248
- const skillPath = this.ctx.skillCommands?.get(commandName);
249
- if (skillPath) {
250
- this.ctx.editor.addToHistory(text);
251
- this.ctx.editor.setText("");
252
- try {
253
- const content = await Bun.file(skillPath).text();
254
- const body = content.replace(/^---\n[\s\S]*?\n---\n/, "").trim();
255
- const metaLines = [`Skill: ${skillPath}`];
256
- if (args) {
257
- metaLines.push(`User: ${args}`);
258
- }
259
- const message = `${body}\n\n---\n\n${metaLines.join("\n")}`;
260
- const skillName = commandName.slice("skill:".length);
261
- const details: SkillPromptDetails = {
262
- name: skillName || commandName,
263
- path: skillPath,
264
- args: args || undefined,
265
- lineCount: body ? body.split("\n").length : 0,
266
- };
267
- await this.ctx.session.promptCustomMessage(
268
- {
269
- customType: SKILL_PROMPT_MESSAGE_TYPE,
270
- content: message,
271
- display: true,
272
- details,
273
- attribution: "user",
274
- },
275
- { streamingBehavior: "followUp" },
276
- );
277
- } catch (err) {
278
- this.ctx.showError(`Failed to load skill: ${err instanceof Error ? err.message : String(err)}`);
279
- }
280
- return;
281
- }
243
+ // Handle skill commands (/skill:name [args]). Enter ⇒ steer (matches the
244
+ // free-text Enter semantics applied a few lines below at the streaming
245
+ // branch). Ctrl+Enter routes through `handleFollowUp` and dispatches the
246
+ // same helper with `"followUp"`.
247
+ if (await this.#invokeSkillCommand(text, "steer")) {
248
+ return;
282
249
  }
283
250
 
284
251
  // Handle bash command (! for normal, !! for excluded from context)
@@ -439,16 +406,82 @@ export class InputController {
439
406
  }
440
407
  }
441
408
 
409
+ /**
410
+ * Dispatch a `/skill:<name> [args]` invocation through `promptCustomMessage`
411
+ * using the supplied `streamingBehavior`. Returns true if the text was a
412
+ * recognised skill command and was dispatched. A failure to load the skill
413
+ * file is surfaced via `showError` but still returns true — the editor was
414
+ * already cleared on the success path, so falling through to plain-text
415
+ * handling at that point would double-submit. Returns false when the text
416
+ * isn't a `/skill:` prefix or the command name isn't a registered skill,
417
+ * so the caller can fall through to plain-text handling (this branch
418
+ * leaves the editor state untouched). `streamingBehavior` is only consulted
419
+ * while the agent is streaming; the idle path of `promptCustomMessage`
420
+ * ignores it.
421
+ */
422
+ async #invokeSkillCommand(text: string, streamingBehavior: "steer" | "followUp"): Promise<boolean> {
423
+ if (!text.startsWith("/skill:")) return false;
424
+ const spaceIndex = text.indexOf(" ");
425
+ const commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex);
426
+ const args = spaceIndex === -1 ? "" : text.slice(spaceIndex + 1).trim();
427
+ const skillPath = this.ctx.skillCommands?.get(commandName);
428
+ if (!skillPath) return false;
429
+ this.ctx.editor.addToHistory(text);
430
+ this.ctx.editor.setText("");
431
+ try {
432
+ const content = await Bun.file(skillPath).text();
433
+ const body = content.replace(/^---\n[\s\S]*?\n---\n/, "").trim();
434
+ const metaLines = [`Skill: ${skillPath}`];
435
+ if (args) {
436
+ metaLines.push(`User: ${args}`);
437
+ }
438
+ const message = `${body}\n\n---\n\n${metaLines.join("\n")}`;
439
+ const skillName = commandName.slice("skill:".length);
440
+ const details: SkillPromptDetails = {
441
+ name: skillName || commandName,
442
+ path: skillPath,
443
+ args: args || undefined,
444
+ lineCount: body ? body.split("\n").length : 0,
445
+ };
446
+ await this.ctx.session.promptCustomMessage(
447
+ {
448
+ customType: SKILL_PROMPT_MESSAGE_TYPE,
449
+ content: message,
450
+ display: true,
451
+ details,
452
+ attribution: "user",
453
+ },
454
+ { streamingBehavior },
455
+ );
456
+ } catch (err) {
457
+ this.ctx.showError(`Failed to load skill: ${err instanceof Error ? err.message : String(err)}`);
458
+ }
459
+ return true;
460
+ }
461
+
442
462
  /** Send editor text as a follow-up message (queued behind current stream). */
443
463
  async handleFollowUp(): Promise<void> {
444
464
  const text = this.ctx.editor.getText().trim();
445
465
  if (!text) return;
446
466
 
467
+ // Compaction first: while compacting, free text gets queued via
468
+ // `queueCompactionMessage`, and `/skill:*` rides the same queue so a
469
+ // skill typed during compaction is not lost or short-circuited through
470
+ // `promptCustomMessage`. The skill text is queued verbatim; whether
471
+ // the queued entry is later re-parsed into a skill invocation is a
472
+ // separate concern owned by the compaction-resume path.
447
473
  if (this.ctx.session.isCompacting) {
448
474
  this.ctx.queueCompactionMessage(text, "followUp");
449
475
  return;
450
476
  }
451
477
 
478
+ // Skill commands invoke through the custom-message path regardless of
479
+ // which keybinding submitted them. Enter routes them as `steer`;
480
+ // Ctrl+Enter (this handler) routes them as `followUp`.
481
+ if (await this.#invokeSkillCommand(text, "followUp")) {
482
+ return;
483
+ }
484
+
452
485
  if (this.ctx.session.isStreaming) {
453
486
  this.ctx.editor.addToHistory(text);
454
487
  this.ctx.editor.setText("");
@@ -41,7 +41,11 @@ import { resolveLocalUrlToPath } from "../internal-urls";
41
41
  import { LSP_STARTUP_EVENT_CHANNEL, type LspStartupEvent } from "../lsp/startup-events";
42
42
  import { renameApprovedPlanFile } from "../plan-mode/approved-plan";
43
43
  import planModeApprovedPrompt from "../prompts/system/plan-mode-approved.md" with { type: "text" };
44
+ import planModeCompactInstructionsPrompt from "../prompts/system/plan-mode-compact-instructions.md" with {
45
+ type: "text",
46
+ };
44
47
  import type { AgentSession, AgentSessionEvent } from "../session/agent-session";
48
+ import type { CompactionOutcome } from "../session/compaction";
45
49
  import { HistoryStorage } from "../session/history-storage";
46
50
  import type { SessionContext, SessionManager } from "../session/session-manager";
47
51
  import { getRecentSessions } from "../session/session-manager";
@@ -1109,7 +1113,12 @@ export class InteractiveMode implements InteractiveModeContext {
1109
1113
 
1110
1114
  async #approvePlan(
1111
1115
  planContent: string,
1112
- options: { planFilePath: string; finalPlanFilePath: string; preserveContext?: boolean },
1116
+ options: {
1117
+ planFilePath: string;
1118
+ finalPlanFilePath: string;
1119
+ preserveContext?: boolean;
1120
+ compactBeforeExecute?: boolean;
1121
+ },
1113
1122
  ): Promise<void> {
1114
1123
  await renameApprovedPlanFile({
1115
1124
  planFilePath: options.planFilePath,
@@ -1119,6 +1128,8 @@ export class InteractiveMode implements InteractiveModeContext {
1119
1128
  });
1120
1129
  const previousTools = this.#planModePreviousTools ?? this.session.getActiveToolNames();
1121
1130
  await this.#exitPlanMode({ silent: true, paused: false });
1131
+
1132
+ let compactOutcome: CompactionOutcome | undefined;
1122
1133
  if (!options.preserveContext) {
1123
1134
  await this.handleClearCommand();
1124
1135
  // The new session has a fresh local:// root — persist the approved plan there
@@ -1128,11 +1139,50 @@ export class InteractiveMode implements InteractiveModeContext {
1128
1139
  getSessionId: () => this.sessionManager.getSessionId(),
1129
1140
  });
1130
1141
  await Bun.write(newLocalPath, planContent);
1131
- }
1142
+ } else if (options.compactBeforeExecute) {
1143
+ // Distill the plan-mode transcript before the execution turn is queued so
1144
+ // the plan-approved synthetic prompt lands as a fresh cache anchor.
1145
+ // Outcome is consumed after tool-restoration and plan-reference-path
1146
+ // bookkeeping below; `markPlanReferenceSent` is intentionally deferred
1147
+ // past the cancel guard — see the comment at the cancel branch.
1148
+ // Cancellation skips the synthetic-prompt dispatch (operator's explicit
1149
+ // abort is honored); failure proceeds best-effort — approval intent stands.
1150
+ const compactionPrompt = prompt.render(planModeCompactInstructionsPrompt, {
1151
+ planFilePath: options.finalPlanFilePath,
1152
+ });
1153
+ // Pin the plan reference path BEFORE compaction so any user messages
1154
+ // queued during the compaction await (which `handleCompactCommand`
1155
+ // flushes via `flushCompactionQueue` before returning) see the
1156
+ // approved plan in `#buildPlanReferenceMessage`. Reassignment at
1157
+ // line 1161 below is idempotent and kept for the !compactBeforeExecute
1158
+ // branch.
1159
+ this.session.setPlanReferencePath(options.finalPlanFilePath);
1160
+ compactOutcome = await this.handleCompactCommand(compactionPrompt);
1161
+ }
1162
+
1163
+ // Tool restoration runs on every path — the plan mode tools must be
1164
+ // retired regardless of whether the synthetic prompt fires.
1132
1165
  if (previousTools.length > 0) {
1133
1166
  await this.session.setActiveToolsByName(previousTools);
1134
1167
  }
1135
1168
  this.session.setPlanReferencePath(options.finalPlanFilePath);
1169
+
1170
+ if (compactOutcome === "cancelled") {
1171
+ // Explicit abort: honor it. `executeCompaction` already surfaced
1172
+ // `showError("Compaction cancelled")` to the operator; we add the
1173
+ // deferred-dispatch warning and exit. `markPlanReferenceSent` is
1174
+ // intentionally skipped here: `#planReferenceSent` stays false, so
1175
+ // `AgentSession.#buildPlanReferenceMessage` will inject the plan
1176
+ // reference on the operator's next `prompt()` call. If we marked it
1177
+ // sent here, the executor's first turn would have no plan context.
1178
+ this.showWarning(
1179
+ "Plan approved, but compaction was cancelled — execution not dispatched. Submit a turn to continue.",
1180
+ );
1181
+ return;
1182
+ }
1183
+
1184
+ // markPlanReferenceSent fires only on the dispatch path so the synthetic
1185
+ // plan-approved prompt is the source of the reference injection.
1136
1186
  this.session.markPlanReferenceSent();
1137
1187
  const planModePrompt = prompt.render(planModeApprovedPrompt, {
1138
1188
  planContent,
@@ -1185,14 +1235,24 @@ export class InteractiveMode implements InteractiveModeContext {
1185
1235
  this.#renderPlanPreview(planContent, { append: true });
1186
1236
  const choice = await this.showHookSelector(
1187
1237
  "Plan mode - next step",
1188
- ["Approve and execute", "Approve and keep context", "Refine plan", "Stay in plan mode"],
1238
+ [
1239
+ "Approve and execute",
1240
+ "Approve and compact context",
1241
+ "Approve and keep context",
1242
+ "Refine plan",
1243
+ "Stay in plan mode",
1244
+ ],
1189
1245
  {
1190
1246
  helpText: this.#getPlanReviewHelpText(),
1191
1247
  onExternalEditor: () => void this.#openPlanInExternalEditor(planFilePath),
1192
1248
  },
1193
1249
  );
1194
1250
 
1195
- if (choice === "Approve and execute" || choice === "Approve and keep context") {
1251
+ if (
1252
+ choice === "Approve and execute" ||
1253
+ choice === "Approve and compact context" ||
1254
+ choice === "Approve and keep context"
1255
+ ) {
1196
1256
  const finalPlanFilePath = details.finalPlanFilePath || planFilePath;
1197
1257
  try {
1198
1258
  const latestPlanContent = await this.#readPlanFile(planFilePath);
@@ -1203,7 +1263,8 @@ export class InteractiveMode implements InteractiveModeContext {
1203
1263
  await this.#approvePlan(latestPlanContent, {
1204
1264
  planFilePath,
1205
1265
  finalPlanFilePath,
1206
- preserveContext: choice === "Approve and keep context",
1266
+ preserveContext: choice !== "Approve and execute",
1267
+ compactBeforeExecute: choice === "Approve and compact context",
1207
1268
  });
1208
1269
  } catch (error) {
1209
1270
  this.showError(
@@ -1727,7 +1788,7 @@ export class InteractiveMode implements InteractiveModeContext {
1727
1788
  await controller.handle(text);
1728
1789
  }
1729
1790
 
1730
- handleCompactCommand(customInstructions?: string): Promise<void> {
1791
+ handleCompactCommand(customInstructions?: string): Promise<CompactionOutcome> {
1731
1792
  return this.#commandController.handleCompactCommand(customInstructions);
1732
1793
  }
1733
1794
 
@@ -1735,7 +1796,10 @@ export class InteractiveMode implements InteractiveModeContext {
1735
1796
  return this.#commandController.handleHandoffCommand(customInstructions);
1736
1797
  }
1737
1798
 
1738
- executeCompaction(customInstructionsOrOptions?: string | CompactOptions, isAuto?: boolean): Promise<void> {
1799
+ executeCompaction(
1800
+ customInstructionsOrOptions?: string | CompactOptions,
1801
+ isAuto?: boolean,
1802
+ ): Promise<CompactionOutcome> {
1739
1803
  return this.#commandController.executeCompaction(customInstructionsOrOptions, isAuto);
1740
1804
  }
1741
1805
 
@@ -154,8 +154,17 @@ export function requestRpcEditor(
154
154
  * Run in RPC mode.
155
155
  * Listens for JSON commands on stdin, outputs events and responses on stdout.
156
156
  */
157
- export async function runRpcMode(session: AgentSession): Promise<never> {
157
+ export async function runRpcMode(
158
+ session: AgentSession,
159
+ setToolUIContext?: (uiContext: ExtensionUIContext, hasUI: boolean) => void,
160
+ ): Promise<never> {
158
161
  // Signal to RPC clients that the server is ready to accept commands
162
+ // Suppress terminal notifications: they write \x07 (BEL) or OSC sequences directly to
163
+ // process.stdout with no newline, which the reader merges with the next JSON line and
164
+ // breaks JSON.parse. In RPC mode stdout is the JSON protocol channel — nothing else
165
+ // may write there.
166
+ process.env.PI_NOTIFICATIONS = "off";
167
+
159
168
  process.stdout.write(`${JSON.stringify({ type: "ready" })}\n`);
160
169
  const output = (obj: RpcResponse | RpcExtensionUIRequest | object) => {
161
170
  process.stdout.write(`${JSON.stringify(obj)}\n`);
@@ -405,6 +414,12 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
405
414
  }
406
415
  }
407
416
 
417
+ // Wire up UI context for tool execution (ask tool, etc.) and extensions.
418
+ // A single shared instance routes all responses received on stdin to the
419
+ // correct waiting promise regardless of which code path created the request.
420
+ const rpcUiContext = new RpcExtensionUIContext(pendingExtensionRequests, output);
421
+ setToolUIContext?.(rpcUiContext, true);
422
+
408
423
  // Set up extensions with RPC-based UI context
409
424
  const extensionRunner = session.extensionRunner;
410
425
  if (extensionRunner) {
@@ -481,7 +496,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
481
496
  },
482
497
  compact: instructionsOrOptions => runExtensionCompact(session, instructionsOrOptions),
483
498
  },
484
- new RpcExtensionUIContext(pendingExtensionRequests, output),
499
+ rpcUiContext,
485
500
  );
486
501
  extensionRunner.onError(err => {
487
502
  output({ type: "extension_error", extensionPath: err.extensionPath, event: err.event, error: err.error });
@@ -12,6 +12,7 @@ import type {
12
12
  import type { CompactOptions } from "../extensibility/extensions/types";
13
13
  import type { MCPManager } from "../mcp";
14
14
  import type { AgentSession, AgentSessionEvent } from "../session/agent-session";
15
+ import type { CompactionOutcome } from "../session/compaction";
15
16
  import type { HistoryStorage } from "../session/history-storage";
16
17
  import type { SessionContext, SessionManager } from "../session/session-manager";
17
18
  import type { ExitPlanModeDetails, LspStartupServerInfo } from "../tools";
@@ -207,13 +208,16 @@ export interface InteractiveModeContext {
207
208
  handlePythonCommand(code: string, excludeFromContext?: boolean): Promise<void>;
208
209
  handleMCPCommand(text: string): Promise<void>;
209
210
  handleSSHCommand(text: string): Promise<void>;
210
- handleCompactCommand(customInstructions?: string): Promise<void>;
211
+ handleCompactCommand(customInstructions?: string): Promise<CompactionOutcome>;
211
212
  handleHandoffCommand(customInstructions?: string): Promise<void>;
212
213
  handleMoveCommand(targetPath: string): Promise<void>;
213
214
  handleRenameCommand(title: string): Promise<void>;
214
215
  handleMemoryCommand(text: string): Promise<void>;
215
216
  handleSTTToggle(): Promise<void>;
216
- executeCompaction(customInstructionsOrOptions?: string | CompactOptions, isAuto?: boolean): Promise<void>;
217
+ executeCompaction(
218
+ customInstructionsOrOptions?: string | CompactOptions,
219
+ isAuto?: boolean,
220
+ ): Promise<CompactionOutcome>;
217
221
  openInBrowser(urlOrPath: string): void;
218
222
  refreshSlashCommandState(cwd?: string): Promise<void>;
219
223
 
@@ -9,7 +9,11 @@ import { CompactionSummaryMessageComponent } from "../../modes/components/compac
9
9
  import { CustomMessageComponent } from "../../modes/components/custom-message";
10
10
  import { DynamicBorder } from "../../modes/components/dynamic-border";
11
11
  import { EvalExecutionComponent } from "../../modes/components/eval-execution";
12
- import { ReadToolGroupComponent } from "../../modes/components/read-tool-group";
12
+ import {
13
+ ReadToolGroupComponent,
14
+ readArgsHaveTarget,
15
+ readArgsTargetInternalUrl,
16
+ } from "../../modes/components/read-tool-group";
13
17
  import { SkillMessageComponent } from "../../modes/components/skill-message";
14
18
  import { ToolExecutionComponent } from "../../modes/components/tool-execution";
15
19
  import { UserMessageComponent } from "../../modes/components/user-message";
@@ -302,7 +306,11 @@ export class UiHelpers {
302
306
  continue;
303
307
  }
304
308
 
305
- if (content.name === "read") {
309
+ if (
310
+ content.name === "read" &&
311
+ readArgsHaveTarget(content.arguments) &&
312
+ !readArgsTargetInternalUrl(content.arguments)
313
+ ) {
306
314
  if (hasErrorStop && errorMessage) {
307
315
  if (!readGroup) {
308
316
  readGroup = new ReadToolGroupComponent({
@@ -364,7 +372,11 @@ export class UiHelpers {
364
372
  }
365
373
  }
366
374
  } else if (message.role === "toolResult") {
367
- if (message.toolName === "read") {
375
+ const pendingReadComponent = this.ctx.pendingTools.get(message.toolCallId);
376
+ const isReadGroupResult =
377
+ message.toolName === "read" &&
378
+ (!pendingReadComponent || pendingReadComponent instanceof ReadToolGroupComponent);
379
+ if (isReadGroupResult) {
368
380
  const assistantComponent = readToolCallAssistantComponents.get(message.toolCallId);
369
381
  const images: ImageContent[] = message.content.filter(
370
382
  (content): content is ImageContent => content.type === "image",
@@ -30,9 +30,9 @@ Implement and review UI designs. Edit files, create components, run commands whe
30
30
  </procedure>
31
31
 
32
32
  <directives>
33
- - You **SHOULD** prefer editing existing files over creating new ones
34
- - Changes **MUST** be minimal and consistent with existing code style
35
- - You **MUST NOT** create documentation files (*.md) unless explicitly requested
33
+ - You SHOULD prefer editing existing files over creating new ones
34
+ - Changes MUST be minimal and consistent with existing code style
35
+ - You NEVER create documentation files (*.md) unless explicitly requested
36
36
  </directives>
37
37
 
38
38
  <avoid>
@@ -61,6 +61,6 @@ Implement and review UI designs. Edit files, create components, run commands whe
61
61
 
62
62
  <critical>
63
63
  Every interface should prompt "how was this made?" not "which AI made this?"
64
- You **MUST** commit to clear aesthetic direction and execute with precision.
65
- You **MUST** keep going until implementation is complete.
64
+ You MUST commit to clear aesthetic direction and execute with precision.
65
+ You MUST keep going until implementation is complete.
66
66
  </critical>
@@ -32,13 +32,13 @@ output:
32
32
  Investigate the codebase rapidly. Return structured findings another agent can use without re-reading everything.
33
33
 
34
34
  <directives>
35
- - You **MUST** use tools for broad pattern matching / code search as much as possible.
36
- - You **SHOULD** invoke tools in parallel—this is a short investigation, and you are supposed to finish in a few seconds.
37
- - If a search returns empty results, you **MUST** try at least one alternate strategy (different pattern, broader path, or AST search) before concluding the target doesn't exist.
35
+ - You MUST use tools for broad pattern matching / code search as much as possible.
36
+ - You SHOULD invoke tools in parallel—this is a short investigation, and you are supposed to finish in a few seconds.
37
+ - If a search returns empty results, you MUST try at least one alternate strategy (different pattern, broader path, or AST search) before concluding the target doesn't exist.
38
38
  </directives>
39
39
 
40
40
  <thoroughness>
41
- You **MUST** infer the thoroughness from the task; default to medium:
41
+ You MUST infer the thoroughness from the task; default to medium:
42
42
  - **Quick**: Targeted lookups, key files only
43
43
  - **Medium**: Follow imports, read critical sections
44
44
  - **Thorough**: Trace all dependencies, check tests/types.
@@ -46,12 +46,12 @@ You **MUST** infer the thoroughness from the task; default to medium:
46
46
 
47
47
  <procedure>
48
48
  1. Locate relevant code using tools.
49
- 2. Read key sections (You **MUST NOT** read full files unless they're tiny)
49
+ 2. Read key sections (You NEVER read full files unless they're tiny)
50
50
  3. Identify types/interfaces/key functions.
51
51
  4. Note dependencies between files.
52
52
  </procedure>
53
53
 
54
54
  <critical>
55
- You **MUST** operate as read-only. You **MUST NOT** write, edit, or modify files, nor execute any state-changing commands, via git, build system, package manager, etc.
56
- You **MUST** keep going until complete.
55
+ You MUST operate as read-only. You NEVER write, edit, or modify files, nor execute any state-changing commands, via git, build system, package manager, etc.
56
+ You MUST keep going until complete.
57
57
  </critical>
@@ -18,16 +18,16 @@ Generate AGENTS.md by launching multiple `explore` agents in parallel (via `task
18
18
  </structure>
19
19
 
20
20
  <directives>
21
- - You **MUST** title the document "Repository Guidelines"
22
- - You **MUST** use Markdown headings for structure
23
- - You **MUST** be concise and practical
24
- - You **MUST** focus on what an AI assistant needs to help with the codebase
25
- - You **SHOULD** include examples where helpful (commands, paths, naming patterns)
26
- - You **SHOULD** include file paths where relevant
27
- - You **MUST** call out architecture and code patterns explicitly
28
- - You **SHOULD** omit information obvious from code structure
21
+ - You MUST title the document "Repository Guidelines"
22
+ - You MUST use Markdown headings for structure
23
+ - You MUST be concise and practical
24
+ - You MUST focus on what an AI assistant needs to help with the codebase
25
+ - You SHOULD include examples where helpful (commands, paths, naming patterns)
26
+ - You SHOULD include file paths where relevant
27
+ - You MUST call out architecture and code patterns explicitly
28
+ - You SHOULD omit information obvious from code structure
29
29
  </directives>
30
30
 
31
31
  <output>
32
- After analysis, you **MUST** write AGENTS.md to the project root.
32
+ After analysis, you MUST write AGENTS.md to the project root.
33
33
  </output>
@@ -68,8 +68,8 @@ output:
68
68
  Answer questions about external libraries, frameworks, and APIs by reading source code and official documentation.
69
69
 
70
70
  <critical>
71
- You **MUST** ground every claim in source code or official documentation. You **MUST NOT** rely on training data for API details — it may be stale or wrong.
72
- You **MUST** operate as read-only on the user's project. You **MUST NOT** modify any project files.
71
+ You MUST ground every claim in source code or official documentation. You NEVER rely on training data for API details — it may be stale or wrong.
72
+ You MUST operate as read-only on the user's project. You NEVER modify any project files.
73
73
  </critical>
74
74
 
75
75
  <procedure>
@@ -93,27 +93,27 @@ You **MUST** operate as read-only on the user's project. You **MUST NOT** modify
93
93
  ## 4. Verify
94
94
  - Cross-reference at least two locations (types + implementation, or source + tests).
95
95
  - If the answer involves defaults, find where the default is actually set in code — not where the docs say it is.
96
- - For API signatures: copy verbatim from source. You **MUST NOT** paraphrase or reconstruct from memory.
96
+ - For API signatures: copy verbatim from source. You NEVER paraphrase or reconstruct from memory.
97
97
 
98
98
  ## 5. Report
99
99
  - Call `yield` with structured findings.
100
- - Every `sources` entry **MUST** include a verbatim excerpt.
101
- - The `api` array **MUST** contain exact signatures copied from source.
100
+ - Every `sources` entry MUST include a verbatim excerpt.
101
+ - The `api` array MUST contain exact signatures copied from source.
102
102
  - Clean up cloned repos: `rm -rf /tmp/librarian-*`.
103
103
  </procedure>
104
104
 
105
105
  <directives>
106
- - You **SHOULD** invoke tools in parallel — search multiple paths simultaneously.
107
- - You **MUST** include the exact version you investigated in the `version` field.
108
- - If the library has breaking changes between versions relevant to the question, you **MUST** populate `breaking_changes`.
109
- - If you discover undocumented behavior or gotchas, you **MUST** populate `caveats`.
110
- - When local `node_modules` has the package, you **SHOULD** prefer it over cloning — it reflects the version the project actually uses.
111
- - You **SHOULD** use `web_search` to find the canonical repo URL and to check for known issues, but the definitive answer **MUST** come from reading source code.
112
- - If a search or lookup returns empty or unexpectedly few results, you **MUST** try at least 2 fallback strategies (broader query, alternate path, different source) before concluding nothing exists.
113
- - If the package is absent from local `node_modules` and cloning fails, you **MUST** fall back to `web_search` for official API documentation before reporting failure.
106
+ - You SHOULD invoke tools in parallel — search multiple paths simultaneously.
107
+ - You MUST include the exact version you investigated in the `version` field.
108
+ - If the library has breaking changes between versions relevant to the question, you MUST populate `breaking_changes`.
109
+ - If you discover undocumented behavior or gotchas, you MUST populate `caveats`.
110
+ - When local `node_modules` has the package, you SHOULD prefer it over cloning — it reflects the version the project actually uses.
111
+ - You SHOULD use `web_search` to find the canonical repo URL and to check for known issues, but the definitive answer MUST come from reading source code.
112
+ - If a search or lookup returns empty or unexpectedly few results, you MUST try at least 2 fallback strategies (broader query, alternate path, different source) before concluding nothing exists.
113
+ - If the package is absent from local `node_modules` and cloning fails, you MUST fall back to `web_search` for official API documentation before reporting failure.
114
114
  </directives>
115
115
 
116
116
  <critical>
117
117
  Source code is truth. Documentation is aspiration. Training data is history.
118
- You **MUST** keep going until you have a definitive, source-verified answer.
118
+ You MUST keep going until you have a definitive, source-verified answer.
119
119
  </critical>
@@ -20,7 +20,7 @@ Analyze the codebase and the user's request. Produce a detailed implementation p
20
20
  4. Identify types, interfaces, contracts
21
21
  5. Note dependencies between components
22
22
 
23
- You **MUST** spawn `explore` agents for independent areas and synthesize findings.
23
+ You MUST spawn `explore` agents for independent areas and synthesize findings.
24
24
 
25
25
  ## Phase 3: Design
26
26
  1. List concrete changes (files, functions, types)
@@ -31,7 +31,7 @@ You **MUST** spawn `explore` agents for independent areas and synthesize finding
31
31
 
32
32
  ## Phase 4: Produce Plan
33
33
 
34
- You **MUST** write a plan executable without re-exploration.
34
+ You MUST write a plan executable without re-exploration.
35
35
 
36
36
  <structure>
37
37
  - **Summary**: What to build and why (one paragraph).
@@ -43,6 +43,6 @@ You **MUST** write a plan executable without re-exploration.
43
43
  </structure>
44
44
 
45
45
  <critical>
46
- You **MUST** operate as read-only. You **MUST NOT** write, edit, or modify files, nor execute any state-changing commands, via git, build system, package manager, etc.
47
- You **MUST** keep going until complete.
46
+ You MUST operate as read-only. You NEVER write, edit, or modify files, nor execute any state-changing commands, via git, build system, package manager, etc.
47
+ You MUST keep going until complete.
48
48
  </critical>
@@ -64,7 +64,7 @@ Identify bugs the author would want fixed before merge.
64
64
  3. Call `report_finding` per issue
65
65
  4. Call `yield` with verdict
66
66
 
67
- Bash is read-only: `git diff`, `git log`, `git show`, `gh pr diff`. You **MUST NOT** make file edits or trigger builds.
67
+ Bash is read-only: `git diff`, `git log`, `git show`, `gh pr diff`. You NEVER make file edits or trigger builds.
68
68
  </procedure>
69
69
 
70
70
  <criteria>
@@ -86,7 +86,7 @@ For every new type, variant, or value introduced by the patch that crosses a fun
86
86
  3. If the new type falls through to a silent drop, no-op, or discard (e.g. an unmatched `if`/`switch`
87
87
  that simply returns without processing), report it as a defect.
88
88
 
89
- The dispatch point is frequently **outside the diff**. You **MUST** read it before concluding
89
+ The dispatch point is frequently **outside the diff**. You MUST read it before concluding
90
90
  the producing side is correct. Tracing only the emitting code while skipping the consuming
91
91
  routing logic is the single most common source of missed integration bugs in reviews.
92
92
  </cross-boundary>
@@ -128,13 +128,13 @@ Final `yield` call (payload under `result.data`):
128
128
  - `result.data.overall_correctness`: "correct" (no bugs/blockers) or "incorrect"
129
129
  - `result.data.explanation`: Plain text, 1-3 sentences summarizing verdict. Don't repeat findings (captured via `report_finding`).
130
130
  - `result.data.confidence`: 0.0-1.0
131
- - `result.data.findings`: Optional; **MUST** omit (auto-populated from `report_finding`)
131
+ - `result.data.findings`: Optional; MUST omit (auto-populated from `report_finding`)
132
132
 
133
- You **MUST NOT** output JSON or code blocks.
133
+ You NEVER output JSON or code blocks.
134
134
 
135
135
  Correctness ignores non-blocking issues (style, docs, nits).
136
136
  </output>
137
137
 
138
138
  <critical>
139
- Every finding **MUST** be patch-anchored and evidence-backed.
139
+ Every finding MUST be patch-anchored and evidence-backed.
140
140
  </critical>
@@ -1,16 +1,16 @@
1
1
  You are a worker agent for delegated tasks.
2
2
 
3
- You have FULL access to all tools (edit, write, bash, search, read, etc.) and you **MUST** use them as needed to complete your task.
3
+ You have FULL access to all tools (edit, write, bash, search, read, etc.) and you MUST use them as needed to complete your task.
4
4
 
5
- You **MUST** maintain hyperfocus on the task at hand, do not deviate from what was assigned to you.
5
+ You MUST maintain hyperfocus on the task at hand, do not deviate from what was assigned to you.
6
6
 
7
7
  <directives>
8
- - You **MUST** finish only the assigned work and return the minimum useful result. Do not repeat what you have written to the filesystem.
9
- - You **MAY** make file edits, run commands, and create files when your task requires it—and **SHOULD** do so.
10
- - You **MUST** be concise. You **MUST NOT** include filler, repetition, or tool transcripts. User cannot even see you. Your result is just the notes you are leaving for yourself.
11
- - You **SHOULD** prefer narrow lookups (`search`/`find`) then read only needed ranges. Do not bother yourself with anything beyond your current scope.
12
- - You **SHOULD NOT** do full-file reads unless necessary.
13
- - You **SHOULD** prefer edits to existing files over creating new ones.
14
- - You **MUST NOT** create documentation files (*.md) unless explicitly requested.
15
- - You **MUST** follow the assignment and the instructions given to you. You gave them for a reason.
8
+ - You MUST finish only the assigned work and return the minimum useful result. Do not repeat what you have written to the filesystem.
9
+ - You MAY make file edits, run commands, and create files when your task requires it—and SHOULD do so.
10
+ - You MUST be concise. You NEVER include filler, repetition, or tool transcripts. User cannot even see you. Your result is just the notes you are leaving for yourself.
11
+ - You SHOULD prefer narrow lookups (`search`/`find`) then read only needed ranges. Do not bother yourself with anything beyond your current scope.
12
+ - AVOID full-file reads unless necessary.
13
+ - You SHOULD prefer edits to existing files over creating new ones.
14
+ - You NEVER create documentation files (*.md) unless explicitly requested.
15
+ - You MUST follow the assignment and the instructions given to you. You gave them for a reason.
16
16
  </directives>
@@ -20,13 +20,13 @@ You decompose, dispatch, verify, and iterate. You do **not** edit code. Every fi
20
20
  <rules>
21
21
  1. **Do not yield until everything is closed.** A phase finishing is *not* a yield point — launch the next phase in the same turn. Stop only when every requested item is verifiably done, or you hit a concrete [blocked] state that genuinely requires the user.
22
22
  2. **Enumerate the full surface before dispatching.** If the task references audits, plans, checklists, phase lists, or file lists, expand them into a flat set of items in `todo_write`. "Most of them" or "the important ones" is failure. Re-read the source documents — do not work from memory.
23
- 3. **Parallelize maximally.** Every set of edits with disjoint file scope **MUST** ship as one `task` batch. Serialize only when one subagent produces a contract (types, schema, shared module) the next consumes — and state the dependency when you do.
23
+ 3. **Parallelize maximally.** Every set of edits with disjoint file scope MUST ship as one `task` batch. Serialize only when one subagent produces a contract (types, schema, shared module) the next consumes — and state the dependency when you do.
24
24
  4. **Each `task` assignment is self-contained.** Subagents have no shared context. Spell out: target files (≤3–5 explicit paths, no globs), the change with APIs and patterns, edge cases, and observable acceptance criteria. Do not assume they read the same plan you did.
25
25
  5. **Verify after every phase before launching the next.** Run the appropriate gate: `bun check` for types, package-scoped `bun test` for behavior, `lsp diagnostics` for changed files. If a phase introduced breakage, dispatch fix-up subagents *before* moving on. Never declare a phase done on a red tree.
26
26
  6. **Commit policy.** If the task asks for commits or the repo workflow expects them, commit after each green phase with a focused message. Never commit a red tree. Never commit work the user did not ask to commit.
27
27
  7. **Respawn, do not absorb.** If a subagent returns incomplete or wrong work, spawn a corrective subagent with the specific gap — do not silently fix it yourself.
28
28
  8. **No scope creep, no scope shrink.** Do not add work the user did not ask for. Do not relabel unfinished items as "follow-up", "v1", or "MVP" to imply completion.
29
- 9. **Subagents do not verify, lint, or format.** Every `task` assignment **MUST** instruct the subagent to skip all gates and formatters. Their job is the edit only. You — the orchestrator — run verification and formatting **once** at the end of the phase across the union of changed files. Avoids redundant runs and racing formatter passes.
29
+ 9. **Subagents do not verify, lint, or format.** Every `task` assignment MUST instruct the subagent to skip all gates and formatters. Their job is the edit only. You — the orchestrator — run verification and formatting **once** at the end of the phase across the union of changed files. Avoids redundant runs and racing formatter passes.
30
30
  </rules>
31
31
 
32
32
  <workflow>
@@ -1,6 +1,6 @@
1
- You **MUST** create a structured summary of the conversation branch for context when returning.
1
+ You MUST create a structured summary of the conversation branch for context when returning.
2
2
 
3
- You **MUST** use EXACT format:
3
+ You MUST use EXACT format:
4
4
 
5
5
  ## Goal
6
6
 
@@ -27,4 +27,4 @@ You **MUST** use EXACT format:
27
27
  ## Next Steps
28
28
  1. [What should happen next to continue]
29
29
 
30
- Sections **MUST** be kept concise. You **MUST** preserve exact file paths, function names, error messages.
30
+ Sections MUST be kept concise. You MUST preserve exact file paths, function names, error messages.