@oh-my-pi/pi-coding-agent 17.0.0 → 17.0.2

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 (202) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/dist/cli.js +4006 -3966
  3. package/dist/types/advisor/advise-tool.d.ts +5 -2
  4. package/dist/types/advisor/config.d.ts +14 -6
  5. package/dist/types/advisor/runtime.d.ts +20 -11
  6. package/dist/types/autolearn/controller.d.ts +1 -0
  7. package/dist/types/cli-commands.d.ts +14 -1
  8. package/dist/types/config/model-discovery.d.ts +17 -0
  9. package/dist/types/config/model-registry.d.ts +4 -0
  10. package/dist/types/config/model-resolver.d.ts +6 -2
  11. package/dist/types/config/models-config-schema.d.ts +10 -0
  12. package/dist/types/config/models-config.d.ts +6 -0
  13. package/dist/types/config/settings-schema.d.ts +32 -7
  14. package/dist/types/config/settings.d.ts +50 -0
  15. package/dist/types/cursor.d.ts +11 -0
  16. package/dist/types/discovery/helpers.d.ts +5 -2
  17. package/dist/types/discovery/substitute-plugin-root.d.ts +20 -7
  18. package/dist/types/exec/bash-executor.d.ts +2 -0
  19. package/dist/types/extensibility/extensions/managed-timers.d.ts +15 -0
  20. package/dist/types/extensibility/extensions/runner.d.ts +7 -0
  21. package/dist/types/extensibility/extensions/types.d.ts +18 -0
  22. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +40 -0
  23. package/dist/types/extensibility/shared-events.d.ts +6 -0
  24. package/dist/types/extensibility/utils.d.ts +2 -2
  25. package/dist/types/internal-urls/agent-protocol.d.ts +1 -0
  26. package/dist/types/lib/xai-http.d.ts +11 -0
  27. package/dist/types/mcp/transports/stdio.d.ts +13 -1
  28. package/dist/types/modes/acp/acp-agent.d.ts +3 -0
  29. package/dist/types/modes/acp/acp-mode.d.ts +6 -2
  30. package/dist/types/modes/components/advisor-config.d.ts +8 -1
  31. package/dist/types/modes/components/assistant-message.d.ts +2 -0
  32. package/dist/types/modes/components/custom-editor-plugin-ctor.test.d.ts +1 -0
  33. package/dist/types/modes/components/custom-editor.d.ts +23 -1
  34. package/dist/types/modes/components/hook-editor.d.ts +16 -2
  35. package/dist/types/modes/components/login-dialog.test.d.ts +1 -0
  36. package/dist/types/modes/components/model-hub.d.ts +5 -2
  37. package/dist/types/modes/components/session-selector.d.ts +4 -0
  38. package/dist/types/modes/components/tool-execution.d.ts +8 -1
  39. package/dist/types/modes/components/tool-execution.test.d.ts +1 -0
  40. package/dist/types/modes/controllers/command-controller.d.ts +8 -0
  41. package/dist/types/modes/controllers/selector-controller.d.ts +3 -1
  42. package/dist/types/modes/interactive-mode.d.ts +6 -0
  43. package/dist/types/modes/noninteractive-dispose.test.d.ts +1 -0
  44. package/dist/types/modes/print-mode.d.ts +1 -1
  45. package/dist/types/modes/types.d.ts +10 -0
  46. package/dist/types/modes/utils/transcript-render-helpers.d.ts +2 -2
  47. package/dist/types/modes/warp-events.d.ts +24 -0
  48. package/dist/types/modes/warp-events.test.d.ts +1 -0
  49. package/dist/types/plan-mode/model-transition.d.ts +47 -0
  50. package/dist/types/plan-mode/model-transition.test.d.ts +1 -0
  51. package/dist/types/registry/agent-lifecycle.d.ts +26 -1
  52. package/dist/types/sdk.d.ts +13 -2
  53. package/dist/types/session/agent-session.d.ts +43 -11
  54. package/dist/types/session/session-history-format.d.ts +10 -0
  55. package/dist/types/session/session-manager.d.ts +14 -0
  56. package/dist/types/session/streaming-output.d.ts +2 -0
  57. package/dist/types/slash-commands/helpers/active-oauth-account.d.ts +9 -0
  58. package/dist/types/slash-commands/types.d.ts +4 -4
  59. package/dist/types/system-prompt.d.ts +1 -1
  60. package/dist/types/task/label.d.ts +1 -1
  61. package/dist/types/telemetry-export.d.ts +34 -9
  62. package/dist/types/tools/approval.d.ts +8 -0
  63. package/dist/types/tools/bash.d.ts +2 -0
  64. package/dist/types/tools/browser/registry.d.ts +7 -3
  65. package/dist/types/tools/browser/tab-supervisor.d.ts +2 -0
  66. package/dist/types/tools/essential-tools.d.ts +29 -0
  67. package/dist/types/tools/image-gen.d.ts +2 -1
  68. package/dist/types/tools/index.d.ts +4 -1
  69. package/dist/types/tools/manage-skill.d.ts +2 -0
  70. package/dist/types/tools/xdev.d.ts +11 -2
  71. package/dist/types/utils/title-generator.d.ts +2 -1
  72. package/dist/types/web/search/index.d.ts +2 -0
  73. package/dist/types/web/search/providers/base.d.ts +3 -0
  74. package/dist/types/web/search/providers/kimi.d.ts +4 -1
  75. package/dist/types/web/search/types.d.ts +1 -1
  76. package/package.json +21 -16
  77. package/src/advisor/__tests__/advisor.test.ts +1358 -86
  78. package/src/advisor/__tests__/config.test.ts +58 -2
  79. package/src/advisor/advise-tool.ts +7 -3
  80. package/src/advisor/config.ts +76 -24
  81. package/src/advisor/runtime.ts +445 -92
  82. package/src/autolearn/controller.ts +23 -28
  83. package/src/cli/bench-cli.ts +4 -1
  84. package/src/cli/grep-cli.ts +2 -1
  85. package/src/cli-commands.ts +57 -23
  86. package/src/cli.ts +5 -1
  87. package/src/config/model-discovery.ts +81 -21
  88. package/src/config/model-registry.ts +34 -6
  89. package/src/config/model-resolver.ts +57 -12
  90. package/src/config/models-config-schema.ts +1 -0
  91. package/src/config/settings-schema.ts +43 -6
  92. package/src/config/settings.ts +405 -25
  93. package/src/cursor.ts +20 -3
  94. package/src/debug/report-bundle.ts +40 -4
  95. package/src/discovery/codex.ts +14 -4
  96. package/src/discovery/helpers.ts +28 -5
  97. package/src/discovery/substitute-plugin-root.ts +23 -7
  98. package/src/exec/bash-executor.ts +14 -5
  99. package/src/export/html/template.js +2 -0
  100. package/src/extensibility/custom-tools/loader.ts +3 -3
  101. package/src/extensibility/custom-tools/wrapper.ts +2 -1
  102. package/src/extensibility/extensions/loader.ts +3 -3
  103. package/src/extensibility/extensions/managed-timers.ts +83 -0
  104. package/src/extensibility/extensions/runner.ts +26 -0
  105. package/src/extensibility/extensions/types.ts +18 -0
  106. package/src/extensibility/extensions/wrapper.ts +2 -1
  107. package/src/extensibility/hooks/loader.ts +3 -3
  108. package/src/extensibility/legacy-pi-coding-agent-shim.ts +96 -1
  109. package/src/extensibility/plugins/legacy-pi-compat.ts +225 -22
  110. package/src/extensibility/plugins/manager.ts +2 -2
  111. package/src/extensibility/shared-events.ts +6 -0
  112. package/src/extensibility/utils.ts +91 -25
  113. package/src/internal-urls/__tests__/agent-protocol-nested.test.ts +73 -0
  114. package/src/internal-urls/agent-protocol.ts +73 -39
  115. package/src/irc/bus.ts +22 -3
  116. package/src/launch/broker.ts +3 -2
  117. package/src/launch/client.ts +2 -2
  118. package/src/launch/presence.ts +2 -2
  119. package/src/lib/xai-http.ts +28 -2
  120. package/src/lsp/client.ts +1 -1
  121. package/src/main.ts +11 -8
  122. package/src/mcp/manager.ts +9 -3
  123. package/src/mcp/transports/stdio.ts +103 -23
  124. package/src/modes/acp/acp-agent.ts +28 -9
  125. package/src/modes/acp/acp-event-mapper.ts +8 -0
  126. package/src/modes/acp/acp-mode.ts +18 -4
  127. package/src/modes/components/advisor-config.ts +65 -3
  128. package/src/modes/components/ask-dialog.ts +1 -1
  129. package/src/modes/components/assistant-message.ts +35 -14
  130. package/src/modes/components/chat-transcript-builder.ts +2 -0
  131. package/src/modes/components/custom-editor-plugin-ctor.test.ts +36 -0
  132. package/src/modes/components/custom-editor.ts +62 -1
  133. package/src/modes/components/hook-editor.ts +35 -5
  134. package/src/modes/components/login-dialog.test.ts +56 -0
  135. package/src/modes/components/login-dialog.ts +7 -3
  136. package/src/modes/components/model-hub.ts +138 -42
  137. package/src/modes/components/plan-review-overlay.ts +51 -10
  138. package/src/modes/components/session-selector.ts +10 -0
  139. package/src/modes/components/status-line/component.test.ts +1 -0
  140. package/src/modes/components/status-line/component.ts +2 -4
  141. package/src/modes/components/status-line/segments.ts +21 -6
  142. package/src/modes/components/tool-execution.test.ts +101 -0
  143. package/src/modes/components/tool-execution.ts +117 -9
  144. package/src/modes/components/transcript-container.ts +30 -2
  145. package/src/modes/controllers/command-controller-shared.ts +1 -1
  146. package/src/modes/controllers/command-controller.ts +168 -48
  147. package/src/modes/controllers/event-controller.ts +6 -0
  148. package/src/modes/controllers/extension-ui-controller.ts +4 -22
  149. package/src/modes/controllers/input-controller.ts +12 -12
  150. package/src/modes/controllers/selector-controller.ts +208 -46
  151. package/src/modes/interactive-mode.ts +205 -65
  152. package/src/modes/noninteractive-dispose.test.ts +60 -0
  153. package/src/modes/print-mode.ts +10 -5
  154. package/src/modes/rpc/host-tools.ts +2 -1
  155. package/src/modes/rpc/rpc-mode.ts +31 -7
  156. package/src/modes/theme/theme.ts +2 -2
  157. package/src/modes/types.ts +10 -0
  158. package/src/modes/utils/interactive-context-helpers.ts +3 -1
  159. package/src/modes/utils/transcript-render-helpers.ts +3 -2
  160. package/src/modes/warp-events.test.ts +794 -0
  161. package/src/modes/warp-events.ts +232 -0
  162. package/src/plan-mode/model-transition.test.ts +60 -0
  163. package/src/plan-mode/model-transition.ts +51 -0
  164. package/src/prompts/system/system-prompt.md +1 -1
  165. package/src/registry/agent-lifecycle.ts +133 -18
  166. package/src/sdk.ts +227 -44
  167. package/src/session/agent-session.ts +1364 -382
  168. package/src/session/session-history-format.ts +20 -5
  169. package/src/session/session-listing.ts +6 -5
  170. package/src/session/session-loader.ts +9 -0
  171. package/src/session/session-manager.ts +48 -0
  172. package/src/session/session-persistence.ts +11 -0
  173. package/src/session/streaming-output.ts +3 -1
  174. package/src/slash-commands/builtin-registry.ts +9 -0
  175. package/src/slash-commands/helpers/active-oauth-account.ts +16 -0
  176. package/src/slash-commands/types.ts +4 -4
  177. package/src/system-prompt.ts +2 -2
  178. package/src/task/executor.ts +1 -1
  179. package/src/task/label.ts +2 -0
  180. package/src/telemetry-export.ts +453 -97
  181. package/src/tools/approval.ts +11 -0
  182. package/src/tools/bash.ts +71 -38
  183. package/src/tools/browser/registry.ts +9 -2
  184. package/src/tools/browser/tab-supervisor.ts +65 -10
  185. package/src/tools/browser.ts +4 -3
  186. package/src/tools/essential-tools.ts +45 -0
  187. package/src/tools/eval.ts +3 -0
  188. package/src/tools/gh.ts +169 -2
  189. package/src/tools/image-gen.ts +624 -513
  190. package/src/tools/index.ts +10 -6
  191. package/src/tools/manage-skill.ts +5 -3
  192. package/src/tools/output-meta.ts +7 -0
  193. package/src/tools/path-utils.ts +8 -5
  194. package/src/tools/read.ts +48 -3
  195. package/src/tools/write.ts +22 -4
  196. package/src/tools/xdev.ts +14 -3
  197. package/src/utils/title-generator.ts +15 -4
  198. package/src/web/search/index.ts +21 -6
  199. package/src/web/search/providers/base.ts +3 -0
  200. package/src/web/search/providers/kimi.ts +18 -12
  201. package/src/web/search/providers/xai.ts +40 -9
  202. package/src/web/search/types.ts +6 -1
@@ -46,6 +46,15 @@ export interface HistoryFormatOptions {
46
46
  * this so it sees what changed without re-reading the file.
47
47
  */
48
48
  expandEditDiffs?: boolean;
49
+ /**
50
+ * Chunked rendering support: a caller formatting one logical transcript in
51
+ * several calls (the advisor's chunked delta render) passes a result index
52
+ * built over the WHOLE delta plus one shared consumed-id set, so a toolCall
53
+ * finds its toolResult across chunk boundaries and the result is never
54
+ * re-rendered as an orphan in a later chunk.
55
+ */
56
+ toolResultIndex?: ReadonlyMap<string, ToolResultMessage>;
57
+ consumedToolCallIds?: Set<string>;
49
58
  }
50
59
 
51
60
  /** Max length of the primary-arg summary inside `→ tool(...)` lines. */
@@ -273,13 +282,19 @@ export function formatSessionHistoryMarkdown(messages: unknown[], opts?: History
273
282
  }
274
283
 
275
284
  // Index tool results by call id so each toolCall collapses to one line.
276
- const resultsByCallId = new Map<string, ToolResultMessage>();
277
- for (const msg of typed) {
278
- if (msg.role === "toolResult") {
279
- resultsByCallId.set(msg.toolCallId, msg);
285
+ // Chunked callers supply a whole-delta index + shared consumed set so
286
+ // call/result pairs resolve across chunk boundaries.
287
+ let resultsByCallId = opts?.toolResultIndex;
288
+ if (!resultsByCallId) {
289
+ const local = new Map<string, ToolResultMessage>();
290
+ for (const msg of typed) {
291
+ if (msg.role === "toolResult") {
292
+ local.set(msg.toolCallId, msg);
293
+ }
280
294
  }
295
+ resultsByCallId = local;
281
296
  }
282
- const consumed = new Set<string>();
297
+ const consumed = opts?.consumedToolCallIds ?? new Set<string>();
283
298
  // In watched mode, consecutive same-role messages collapse under one label
284
299
  // (the watched agent emits one assistant message per tool call, so otherwise
285
300
  // every call repeats `**agent**:`). Cleared whenever a
@@ -1,6 +1,6 @@
1
1
  import * as os from "node:os";
2
2
  import * as path from "node:path";
3
- import type { Message, TextContent } from "@oh-my-pi/pi-ai";
3
+ import type { Message } from "@oh-my-pi/pi-ai";
4
4
  import { getAgentDir as getDefaultAgentDir, logger, parseJsonlLenient, toError } from "@oh-my-pi/pi-utils";
5
5
  import { computeDefaultSessionDir } from "./session-paths";
6
6
  import { FileSessionStorage, type SessionStorage } from "./session-storage";
@@ -108,10 +108,11 @@ function sessionDisplayName(info: SessionInfo): string {
108
108
 
109
109
  function extractTextFromContent(content: Message["content"]): string {
110
110
  if (typeof content === "string") return content;
111
- return content
112
- .filter((block): block is TextContent => block.type === "text")
113
- .map(block => block.text)
114
- .join(" ");
111
+ const text: string[] = [];
112
+ for (const block of content) {
113
+ if (block.type === "text") text.push(block.text);
114
+ }
115
+ return text.join(" ");
115
116
  }
116
117
 
117
118
  /**
@@ -252,6 +252,15 @@ async function resolvePersistedBlobRefs(value: unknown, blobStore: BlobStore, ke
252
252
  }
253
253
 
254
254
  if (typeof value !== "object" || value === null) return;
255
+ if (
256
+ "type" in value &&
257
+ value.type === "image_generation_call" &&
258
+ "result" in value &&
259
+ typeof value.result === "string" &&
260
+ isBlobRef(value.result)
261
+ ) {
262
+ value.result = await resolveImageData(blobStore, value.result);
263
+ }
255
264
 
256
265
  if (hasImageUrl(value) && isBlobRef(value.image_url)) {
257
266
  value.image_url = await resolveImageDataUrl(blobStore, value.image_url);
@@ -935,6 +935,26 @@ export class SessionManager {
935
935
  };
936
936
  }
937
937
 
938
+ /**
939
+ * Create an independent manager for the current logical session and branch.
940
+ * The clone shares the storage backend but owns its entry index and writer, so
941
+ * callers can finish session-owned work after this manager switches elsewhere.
942
+ * Set `persist` false when the original session is intentionally being dropped.
943
+ */
944
+ cloneCurrentSession(options?: { persist?: boolean }): SessionManager {
945
+ const persist = options?.persist ?? this.#persist;
946
+ const clone = new SessionManager(this.#cwd, this.#sessionDir, persist, this.#storage);
947
+ clone.#suppressBreadcrumb = true;
948
+ clone.restoreState(this.captureState());
949
+ if (!persist) {
950
+ clone.#sessionFile = undefined;
951
+ clone.#fileIsCurrent = false;
952
+ clone.#rewriteRequired = false;
953
+ clone.#forceFileCreation = false;
954
+ }
955
+ return clone;
956
+ }
957
+
938
958
  restoreState(snapshot: SessionManagerStateSnapshot): void {
939
959
  this.#closeWriterEventually();
940
960
  this.#diskTail = Promise.resolve();
@@ -1470,6 +1490,34 @@ export class SessionManager {
1470
1490
  return entry.id;
1471
1491
  }
1472
1492
 
1493
+ /**
1494
+ * Append to a non-active branch without changing the current leaf.
1495
+ * Used by work that retains ownership of a branch across tree navigation.
1496
+ */
1497
+ appendMessageToBranch(
1498
+ message:
1499
+ | Message
1500
+ | CustomMessage
1501
+ | HookMessage
1502
+ | BashExecutionMessage
1503
+ | PythonExecutionMessage
1504
+ | FileMentionMessage,
1505
+ parentId: string | null,
1506
+ ): string {
1507
+ if (parentId !== null && !this.#index.has(parentId)) throw new Error(`Entry ${parentId} not found`);
1508
+ const activeLeafId = this.#index.leafId();
1509
+ const entry: SessionMessageEntry = {
1510
+ type: "message",
1511
+ id: generateId(this.#index),
1512
+ parentId,
1513
+ timestamp: nowIso(),
1514
+ message,
1515
+ };
1516
+ this.#recordEntry(entry);
1517
+ this.#index.setLeaf(activeLeafId);
1518
+ return entry.id;
1519
+ }
1520
+
1473
1521
  /** Append a thinking level change as child of current leaf, then advance leaf. Returns entry id. */
1474
1522
  appendThinkingLevelChange(thinkingLevel?: string, configured?: string): string {
1475
1523
  const entry: ThinkingLevelChangeEntry = {
@@ -79,6 +79,17 @@ function isNonEmptyString(value: unknown): value is string {
79
79
  */
80
80
  function truncateForPersistence(obj: unknown, blobStore: BlobStore, key?: string): unknown {
81
81
  if (obj === null || obj === undefined) return obj;
82
+ if (
83
+ typeof obj === "object" &&
84
+ "type" in obj &&
85
+ obj.type === "image_generation_call" &&
86
+ "result" in obj &&
87
+ typeof obj.result === "string" &&
88
+ !isBlobRef(obj.result) &&
89
+ obj.result.length >= BLOB_EXTERNALIZE_THRESHOLD
90
+ ) {
91
+ return { ...obj, result: externalizeImageDataSync(blobStore, obj.result) };
92
+ }
82
93
  if (shouldExternalizeImagePayload(obj, key)) {
83
94
  return { ...obj, data: externalizeImageDataSync(blobStore, obj.data, obj.mimeType) };
84
95
  }
@@ -43,6 +43,8 @@ export interface OutputSummary {
43
43
  columnDroppedBytes?: number;
44
44
  /** Number of distinct lines that hit the per-line column cap. */
45
45
  columnTruncatedLines?: number;
46
+ /** Configured per-line column cap in effect (chars), when > 0. */
47
+ columnMax?: number;
46
48
  /** Artifact ID for internal URL access (artifact://<id>) when truncated */
47
49
  artifactId?: string;
48
50
  }
@@ -836,7 +838,6 @@ export class OutputSink {
836
838
  const capped = this.#maxColumns > 0 ? this.#applyColumnCap(chunk) : chunk;
837
839
  const cappedBytes = capped === chunk ? rawBytes : Buffer.byteLength(capped, "utf-8");
838
840
  const cappedThisChunk = cappedBytes < rawBytes;
839
- if (cappedThisChunk) this.#truncated = true;
840
841
 
841
842
  // Mirror RAW chunk to the artifact file so the on-disk record is the full
842
843
  // uncapped stream. Mirror triggers on: in-memory overflow OR this chunk's
@@ -1276,6 +1277,7 @@ export class OutputSink {
1276
1277
  elidedLines,
1277
1278
  columnDroppedBytes: this.#columnDroppedBytes > 0 ? this.#columnDroppedBytes : undefined,
1278
1279
  columnTruncatedLines: this.#columnTruncatedLines > 0 ? this.#columnTruncatedLines : undefined,
1280
+ columnMax: this.#columnTruncatedLines > 0 ? this.#maxColumns : undefined,
1279
1281
  artifactId: this.#file?.artifactId,
1280
1282
  };
1281
1283
  }
@@ -1389,6 +1389,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
1389
1389
  },
1390
1390
  {
1391
1391
  name: "new",
1392
+ aliases: ["clear"],
1392
1393
  description: "Start a new session",
1393
1394
  handleTui: async (_command, runtime) => {
1394
1395
  runtime.ctx.editor.setText("");
@@ -1704,6 +1705,11 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
1704
1705
  } catch {
1705
1706
  return usage(`Directory does not exist: ${resolvedPath}`, runtime);
1706
1707
  }
1708
+ try {
1709
+ await runtime.settings.flush();
1710
+ } catch (err) {
1711
+ return usage(`Failed to save pending settings: ${errorMessage(err)}`, runtime);
1712
+ }
1707
1713
  try {
1708
1714
  await runtime.sessionManager.moveTo(resolvedPath);
1709
1715
  } catch (err) {
@@ -2254,6 +2260,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
2254
2260
  // listClaudePluginRoots re-reads from disk on next access.
2255
2261
  const projectPath = await resolveActiveProjectRegistryPath(runtime.ctx.sessionManager.getCwd());
2256
2262
  clearPluginRootsAndCaches(projectPath ? [projectPath] : undefined);
2263
+ await runtime.ctx.refreshSkillState();
2257
2264
  await runtime.ctx.refreshSlashCommandState();
2258
2265
  resetCapabilities();
2259
2266
  runtime.ctx.showStatus("Plugins reloaded.");
@@ -2319,6 +2326,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
2319
2326
  },
2320
2327
  {
2321
2328
  name: "quit",
2329
+ aliases: ["q"],
2322
2330
  description: "Quit the application",
2323
2331
  handleTui: shutdownHandlerTui,
2324
2332
  },
@@ -2601,6 +2609,7 @@ export async function executeBuiltinSlashCommand(
2601
2609
  reloadPlugins: async () => {
2602
2610
  const projectPath = await resolveActiveProjectRegistryPath(ctx.sessionManager.getCwd());
2603
2611
  clearPluginRootsAndCaches(projectPath ? [projectPath] : undefined);
2612
+ await ctx.refreshSkillState();
2604
2613
  await ctx.refreshSlashCommandState();
2605
2614
  resetCapabilities();
2606
2615
  },
@@ -5,6 +5,22 @@ function normalizeIdentityValue(value: unknown): string | undefined {
5
5
  return typeof value === "string" && value.trim() ? value.trim().toLowerCase() : undefined;
6
6
  }
7
7
 
8
+ /**
9
+ * Session marker label for an active OAuth identity: the base identifier
10
+ * (email → accountId → projectId) suffixed with the organization when present
11
+ * and distinct. Same-email Anthropic multi-org accounts share the base, so the
12
+ * org suffix is the only field that tells the session's quota pool apart —
13
+ * mirrors the account-list rows (`formatUsageReportAccount`) and login success.
14
+ * Returns `undefined` when no identifier is recoverable.
15
+ */
16
+ export function formatActiveAccountLabel(identity: OAuthAccountIdentity | undefined): string | undefined {
17
+ if (!identity) return undefined;
18
+ const base = identity.email || identity.accountId || identity.projectId;
19
+ if (!base) return undefined;
20
+ const org = identity.orgName || identity.orgId;
21
+ return org && org !== base ? `${base} (${org})` : base;
22
+ }
23
+
8
24
  /**
9
25
  * True when a single usage-limit column belongs to the given OAuth identity.
10
26
  *
@@ -64,10 +64,10 @@ export interface SlashCommandRuntime {
64
64
  /** Re-advertise the available command list (no-op outside ACP). */
65
65
  refreshCommands: () => Promise<void> | void;
66
66
  /**
67
- * Reload plugin state (caches, slash command registry, project registries)
68
- * and re-emit available commands. Used by `/reload-plugins`, `/move`, and
69
- * `/marketplace`/`/plugins` mutations so the session sees a consistent view
70
- * after plugin or project-scope changes.
67
+ * Reload plugin state (caches, skills, slash command registry, project
68
+ * registries) and re-emit available commands. Used by `/reload-plugins`,
69
+ * `/move`, and `/marketplace`/`/plugins` mutations so the session sees a
70
+ * consistent view after plugin or project-scope changes.
71
71
  */
72
72
  reloadPlugins: () => Promise<void>;
73
73
  notifyTitleChanged?: () => Promise<void> | void;
@@ -470,7 +470,7 @@ export interface BuildSystemPromptOptions {
470
470
  /** Pre-loaded context files (skips discovery if provided). */
471
471
  contextFiles?: Array<{ path: string; content: string; depth?: number }>;
472
472
  /** Skills provided directly to system prompt construction. */
473
- skills?: Skill[];
473
+ skills?: readonly Skill[];
474
474
  /** Pre-loaded rulebook rules (descriptions, excluding TTSR and always-apply). */
475
475
  rules?: Array<{ name: string; description?: string; path: string; globs?: string[] }>;
476
476
  /** Intent field name injected into every tool schema. If set, explains the field in the prompt. */
@@ -637,7 +637,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
637
637
  totalLines: 0,
638
638
  agentsMdFiles: [],
639
639
  });
640
- const skillsPromise: Promise<Skill[]> =
640
+ const skillsPromise: Promise<readonly Skill[]> =
641
641
  providedSkills !== undefined
642
642
  ? Promise.resolve(providedSkills)
643
643
  : skillsSettings?.enabled !== false
@@ -1052,7 +1052,7 @@ function createSubagentRunMonitor(args: RunMonitorArgs): SubagentRunMonitor {
1052
1052
  // failures just leave the label unset.
1053
1053
  const labelSource = assignment?.trim();
1054
1054
  if (!args.description && args.modelRegistry && args.settings && labelSource) {
1055
- generateTaskLabel(labelSource, args.modelRegistry, args.settings, id)
1055
+ generateTaskLabel(labelSource, args.modelRegistry, args.settings, id, abortSignal)
1056
1056
  .then(label => {
1057
1057
  if (!label || abortSignal.aborted || progress.description) return;
1058
1058
  progress.description = label;
package/src/task/label.ts CHANGED
@@ -15,6 +15,7 @@ export async function generateTaskLabel(
15
15
  registry: ModelRegistry,
16
16
  settings: Settings,
17
17
  sessionId?: string,
18
+ signal?: AbortSignal,
18
19
  ): Promise<string | null> {
19
20
  const text = assignment.trim();
20
21
  if (!text) return null;
@@ -27,6 +28,7 @@ export async function generateTaskLabel(
27
28
  undefined,
28
29
  undefined,
29
30
  TASK_LABEL_SYSTEM_PROMPT,
31
+ signal,
30
32
  );
31
33
  } catch (err) {
32
34
  logger.debug("task-label: generation failed", {