@oh-my-pi/pi-coding-agent 16.0.9 → 16.0.11

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 (110) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/dist/cli.js +3402 -3443
  3. package/dist/types/advisor/index.d.ts +1 -0
  4. package/dist/types/advisor/transcript-recorder.d.ts +52 -0
  5. package/dist/types/collab/host.d.ts +2 -2
  6. package/dist/types/collab/protocol.d.ts +4 -5
  7. package/dist/types/commit/agentic/agent.d.ts +1 -1
  8. package/dist/types/config/model-resolver.d.ts +11 -2
  9. package/dist/types/config/settings-schema.d.ts +12 -6
  10. package/dist/types/edit/file-snapshot-store.d.ts +1 -1
  11. package/dist/types/extensibility/extensions/types.d.ts +7 -0
  12. package/dist/types/modes/components/agent-hub.d.ts +6 -1
  13. package/dist/types/modes/components/agent-transcript-viewer.d.ts +39 -0
  14. package/dist/types/modes/components/chat-transcript-builder.d.ts +42 -0
  15. package/dist/types/modes/controllers/command-controller.d.ts +3 -2
  16. package/dist/types/modes/interactive-mode.d.ts +2 -1
  17. package/dist/types/modes/types.d.ts +2 -1
  18. package/dist/types/registry/agent-registry.d.ts +10 -3
  19. package/dist/types/session/agent-session.d.ts +13 -0
  20. package/dist/types/session/compact-modes.d.ts +60 -0
  21. package/dist/types/session/streaming-output.d.ts +0 -2
  22. package/dist/types/slash-commands/builtin-registry.d.ts +1 -1
  23. package/dist/types/slash-commands/helpers/collab-qrcode.d.ts +13 -0
  24. package/dist/types/tools/__tests__/json-tree.test.d.ts +1 -0
  25. package/dist/types/tools/index.d.ts +9 -1
  26. package/dist/types/utils/image-loading.d.ts +12 -0
  27. package/dist/types/utils/qrcode.d.ts +48 -0
  28. package/package.json +12 -12
  29. package/src/advisor/index.ts +1 -0
  30. package/src/advisor/transcript-recorder.ts +136 -0
  31. package/src/cli/args.ts +7 -1
  32. package/src/cli/stats-cli.ts +2 -11
  33. package/src/collab/host.ts +29 -17
  34. package/src/collab/protocol.ts +48 -15
  35. package/src/commit/agentic/agent.ts +2 -1
  36. package/src/commit/agentic/tools/git-file-diff.ts +2 -2
  37. package/src/commit/changelog/index.ts +1 -1
  38. package/src/commit/map-reduce/map-phase.ts +1 -1
  39. package/src/commit/map-reduce/utils.ts +1 -1
  40. package/src/config/config-file.ts +1 -1
  41. package/src/config/keybindings.ts +2 -2
  42. package/src/config/model-registry.ts +16 -4
  43. package/src/config/model-resolver.ts +193 -35
  44. package/src/config/settings-schema.ts +14 -7
  45. package/src/config/settings.ts +3 -9
  46. package/src/edit/file-snapshot-store.ts +1 -1
  47. package/src/edit/renderer.ts +7 -7
  48. package/src/eval/js/tool-bridge.ts +3 -2
  49. package/src/eval/py/prelude.py +3 -2
  50. package/src/export/html/tool-views.generated.js +28 -28
  51. package/src/extensibility/extensions/types.ts +7 -0
  52. package/src/hindsight/mental-models.ts +1 -1
  53. package/src/internal-urls/docs-index.generated.txt +1 -1
  54. package/src/internal-urls/history-protocol.ts +8 -3
  55. package/src/irc/bus.ts +8 -0
  56. package/src/lsp/index.ts +2 -2
  57. package/src/main.ts +6 -3
  58. package/src/modes/acp/acp-agent.ts +63 -0
  59. package/src/modes/components/agent-hub.ts +97 -920
  60. package/src/modes/components/agent-transcript-viewer.ts +461 -0
  61. package/src/modes/components/chat-transcript-builder.ts +462 -0
  62. package/src/modes/components/diff.ts +12 -35
  63. package/src/modes/components/oauth-selector.ts +31 -2
  64. package/src/modes/controllers/command-controller.ts +12 -2
  65. package/src/modes/controllers/event-controller.ts +1 -1
  66. package/src/modes/controllers/input-controller.ts +8 -1
  67. package/src/modes/controllers/selector-controller.ts +4 -1
  68. package/src/modes/interactive-mode.ts +4 -2
  69. package/src/modes/types.ts +2 -1
  70. package/src/prompts/tools/inspect-image.md +1 -1
  71. package/src/prompts/tools/read.md +1 -1
  72. package/src/registry/agent-registry.ts +13 -4
  73. package/src/sdk.ts +27 -8
  74. package/src/session/agent-session.ts +185 -17
  75. package/src/session/compact-modes.ts +105 -0
  76. package/src/session/session-dump-format.ts +1 -1
  77. package/src/session/session-history-format.ts +1 -1
  78. package/src/session/streaming-output.ts +5 -5
  79. package/src/slash-commands/builtin-registry.ts +45 -15
  80. package/src/slash-commands/helpers/collab-qrcode.ts +28 -0
  81. package/src/task/executor.ts +1 -1
  82. package/src/task/output-manager.ts +5 -0
  83. package/src/thinking.ts +25 -5
  84. package/src/tools/__tests__/json-tree.test.ts +35 -0
  85. package/src/tools/approval.ts +1 -1
  86. package/src/tools/bash.ts +0 -1
  87. package/src/tools/browser.ts +0 -1
  88. package/src/tools/eval.ts +1 -1
  89. package/src/tools/gh.ts +1 -1
  90. package/src/tools/index.ts +10 -1
  91. package/src/tools/inspect-image.ts +72 -9
  92. package/src/tools/irc.ts +1 -1
  93. package/src/tools/json-tree.ts +22 -5
  94. package/src/tools/read.ts +5 -6
  95. package/src/utils/file-mentions.ts +5 -2
  96. package/src/utils/image-loading.ts +58 -0
  97. package/src/utils/qrcode.ts +535 -0
  98. package/src/web/scrapers/firefox-addons.ts +1 -1
  99. package/src/web/scrapers/github.ts +1 -1
  100. package/src/web/scrapers/go-pkg.ts +2 -2
  101. package/src/web/scrapers/metacpan.ts +2 -2
  102. package/src/web/scrapers/nvd.ts +2 -2
  103. package/src/web/scrapers/ollama.ts +1 -1
  104. package/src/web/scrapers/opencorporates.ts +1 -1
  105. package/src/web/scrapers/pub-dev.ts +1 -1
  106. package/src/web/scrapers/repology.ts +1 -1
  107. package/src/web/scrapers/sourcegraph.ts +1 -1
  108. package/src/web/scrapers/terraform.ts +6 -6
  109. package/src/web/scrapers/wikidata.ts +2 -2
  110. package/src/workspace-tree.ts +1 -1
@@ -1529,7 +1529,6 @@ export class InputController {
1529
1529
  for (const child of this.ctx.chatContainer.children) {
1530
1530
  if (child instanceof AssistantMessageComponent) {
1531
1531
  child.setHideThinkingBlock(this.ctx.hideThinkingBlock);
1532
- child.invalidate();
1533
1532
  }
1534
1533
  }
1535
1534
 
@@ -1538,6 +1537,14 @@ export class InputController {
1538
1537
  this.ctx.streamingComponent.updateContent(this.ctx.streamingMessage);
1539
1538
  }
1540
1539
 
1540
+ // Every block now carries the new flag, but on ED3-risk terminals the
1541
+ // blocks that scrolled past the live region are frozen snapshots in
1542
+ // committed scrollback — a plain repaint replays them stale, so scrolling
1543
+ // up still shows the old thinking expanded. resetDisplay() retires those
1544
+ // snapshots (it invalidates every block) and forces a full clear + replay
1545
+ // of the whole transcript, matching setToolsExpanded()'s redraw.
1546
+ this.ctx.ui.resetDisplay();
1547
+
1541
1548
  this.ctx.showStatus(`Thinking blocks: ${this.ctx.hideThinkingBlock ? "hidden" : "visible"}`);
1542
1549
  }
1543
1550
 
@@ -320,9 +320,12 @@ export class SelectorController {
320
320
  for (const child of this.ctx.chatContainer.children) {
321
321
  if (child instanceof AssistantMessageComponent) {
322
322
  child.setHideThinkingBlock(value as boolean);
323
- child.invalidate();
324
323
  }
325
324
  }
325
+ // Full clear + replay so blocks frozen in committed scrollback on
326
+ // ED3-risk terminals retire their stale snapshots too (see
327
+ // InputController.toggleThinkingBlockVisibility).
328
+ this.ctx.ui.resetDisplay();
326
329
  break;
327
330
  case "tui.tight":
328
331
  setTuiTight(value as boolean);
@@ -82,6 +82,7 @@ import planModeCompactInstructionsPrompt from "../prompts/system/plan-mode-compa
82
82
  type: "text",
83
83
  };
84
84
  import type { AgentSession, AgentSessionEvent, ResolvedRoleModel } from "../session/agent-session";
85
+ import type { CompactMode } from "../session/compact-modes";
85
86
  import { HistoryStorage } from "../session/history-storage";
86
87
  import type { SessionContext } from "../session/session-context";
87
88
  import { getRecentSessions } from "../session/session-listing";
@@ -2350,7 +2351,7 @@ export class InteractiveMode implements InteractiveModeContext {
2350
2351
  // the try/finally is idempotent and kept for the !compactBeforeExecute
2351
2352
  // branch.
2352
2353
  this.session.setPlanReferencePath(options.planFilePath);
2353
- compactOutcome = await this.handleCompactCommand(compactionPrompt, outcome =>
2354
+ compactOutcome = await this.handleCompactCommand(compactionPrompt, undefined, outcome =>
2354
2355
  this.#applyDeferredPlanModelTransition(outcome, options.executionModel),
2355
2356
  );
2356
2357
  }
@@ -3616,9 +3617,10 @@ export class InteractiveMode implements InteractiveModeContext {
3616
3617
 
3617
3618
  handleCompactCommand(
3618
3619
  customInstructions?: string,
3620
+ mode?: CompactMode,
3619
3621
  beforeFlush?: (outcome: CompactionOutcome) => void | Promise<void>,
3620
3622
  ): Promise<CompactionOutcome> {
3621
- return this.#commandController.handleCompactCommand(customInstructions, beforeFlush);
3623
+ return this.#commandController.handleCompactCommand(customInstructions, mode, beforeFlush);
3622
3624
  }
3623
3625
 
3624
3626
  handleHandoffCommand(customInstructions?: string): Promise<void> {
@@ -17,6 +17,7 @@ import type { CompactOptions } from "../extensibility/extensions/types";
17
17
  import type { MCPManager } from "../mcp";
18
18
  import type { PlanApprovalDetails } from "../plan-mode/approved-plan";
19
19
  import type { AgentSession } from "../session/agent-session";
20
+ import type { CompactMode } from "../session/compact-modes";
20
21
  import type { HistoryStorage } from "../session/history-storage";
21
22
  import type { SessionContext } from "../session/session-context";
22
23
  import type { SessionManager } from "../session/session-manager";
@@ -293,7 +294,7 @@ export interface InteractiveModeContext {
293
294
  handlePythonCommand(code: string, excludeFromContext?: boolean): Promise<void>;
294
295
  handleMCPCommand(text: string): Promise<void>;
295
296
  handleSSHCommand(text: string): Promise<void>;
296
- handleCompactCommand(customInstructions?: string): Promise<CompactionOutcome>;
297
+ handleCompactCommand(customInstructions?: string, mode?: CompactMode): Promise<CompactionOutcome>;
297
298
  handleHandoffCommand(customInstructions?: string): Promise<void>;
298
299
  handleShakeCommand(mode: ShakeMode): Promise<void>;
299
300
  handleMoveCommand(targetPath: string): Promise<void>;
@@ -2,7 +2,7 @@ Inspects an image file with a vision-capable model and returns compact text anal
2
2
 
3
3
  <instruction>
4
4
  - Use this for image understanding tasks (OCR, UI/screenshot debugging, scene/object questions)
5
- - Provide `path` to the local image file
5
+ - Provide `path` as a local image file path, `Image #N` attachment label, or `attachment://N` URI
6
6
  - Write a specific `question`:
7
7
  - what to inspect
8
8
  - constraints (for example: "quote visible text verbatim", "only report confirmed findings")
@@ -33,7 +33,7 @@ Append `:<sel>` to `path`; bare path = default mode.
33
33
  - File with explicit selector → lines prefixed with numbers: `41|def alpha():`.
34
34
  {{/if}}
35
35
  {{/if}}
36
- - Parseable code without selector → **structural summary**: declarations kept, bodies collapsed to `..` (merged brace pair) or `…` (standalone). The footer shows the recovery selector: `[NN lines elided; re-read needed ranges, e.g. <path>:5-16,40-80]`. Re-issue ONLY the ranges you need via the multi-range selector. `..`/`…` carry no content — NEVER guess what's inside; NEVER re-read the whole file or `:raw` when ranges suffice.
36
+ - Parseable code without selector → **structural summary**: declarations kept, body elided with `…`. The footer shows the recovery selector. Re-issue ONLY the ranges you need via the multi-range selector.
37
37
 
38
38
  # Documents & Notebooks
39
39
 
@@ -22,7 +22,13 @@ export const MAIN_AGENT_ID = "Main";
22
22
  * - `aborted`: hard-killed, terminal.
23
23
  */
24
24
  export type AgentStatus = "running" | "idle" | "parked" | "aborted";
25
- export type AgentKind = "main" | "sub";
25
+ /**
26
+ * - `main`/`sub`: the user-facing agent tree (driving agent + task subagents).
27
+ * - `advisor`: a passive review transcript persisted like a subagent for usage
28
+ * attribution and Agent Hub observability, but never a peer — hidden from
29
+ * agent-facing rosters (`irc`, `history://`) and not messageable/revivable.
30
+ */
31
+ export type AgentKind = "main" | "sub" | "advisor";
26
32
 
27
33
  export interface AgentRef {
28
34
  id: string;
@@ -157,11 +163,14 @@ export class AgentRegistry {
157
163
  }
158
164
 
159
165
  /**
160
- * Returns every alive agent (running | idle) except the caller.
161
- * Flat namespace: every agent can see every other agent.
166
+ * Returns every alive agent (running | idle) except the caller. Advisor refs
167
+ * are observability-only transcripts, never peers, so they are excluded.
168
+ * Flat namespace: every other agent is visible.
162
169
  */
163
170
  listVisibleTo(id: string): AgentRef[] {
164
- return this.list().filter(ref => ref.id !== id && (ref.status === "running" || ref.status === "idle"));
171
+ return this.list().filter(
172
+ ref => ref.id !== id && ref.kind !== "advisor" && (ref.status === "running" || ref.status === "idle"),
173
+ );
165
174
  }
166
175
 
167
176
  onChange(listener: RegistryListener): () => void {
package/src/sdk.ts CHANGED
@@ -5,7 +5,6 @@ import {
5
5
  type AgentTelemetryConfig,
6
6
  type AgentTool,
7
7
  AppendOnlyContextManager,
8
- INTENT_FIELD,
9
8
  type ThinkingLevel,
10
9
  } from "@oh-my-pi/pi-agent-core";
11
10
  import {
@@ -35,6 +34,7 @@ import {
35
34
  prompt,
36
35
  Snowflake,
37
36
  } from "@oh-my-pi/pi-utils";
37
+ import { INTENT_FIELD } from "@oh-my-pi/pi-wire";
38
38
  import { ADVISOR_READONLY_TOOL_NAMES, discoverWatchdogFiles } from "./advisor";
39
39
  import { type AsyncJob, AsyncJobManager } from "./async";
40
40
  import { AutoLearnController, buildAutoLearnInstructions } from "./autolearn/controller";
@@ -144,6 +144,7 @@ import { AgentOutputManager } from "./task/output-manager";
144
144
  import {
145
145
  AUTO_THINKING,
146
146
  type ConfiguredThinkingLevel,
147
+ parseConfiguredThinkingLevel,
147
148
  parseThinkingLevel,
148
149
  resolveProvisionalAutoLevel,
149
150
  resolveThinkingLevelForModel,
@@ -1266,12 +1267,16 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1266
1267
  ? getRestorableSessionModels(existingSession.models, sessionManager.getLastModelChangeRole())
1267
1268
  : [];
1268
1269
  let restoredSessionModelIndex = -1;
1270
+ let restoredSessionThinkingLevel: ThinkingLevel | undefined;
1269
1271
  if (!hasExplicitModel && !model && sessionModelStrings.length > 0) {
1270
1272
  logger.time("restoreSessionModel", () => {
1271
1273
  let failedSessionModel: string | undefined;
1272
1274
  for (let i = 0; i < sessionModelStrings.length; i++) {
1273
1275
  const sessionModelStr = sessionModelStrings[i];
1274
- const parsedModel = parseModelString(sessionModelStr);
1276
+ const parsedModel = parseModelString(sessionModelStr, {
1277
+ allowMaxAlias: true,
1278
+ isLiteralModelId: (provider, id) => modelRegistry.find(provider, id) !== undefined,
1279
+ });
1275
1280
  if (!parsedModel) {
1276
1281
  failedSessionModel ??= sessionModelStr;
1277
1282
  continue;
@@ -1281,6 +1286,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1281
1286
  if (restoredModel && hasModelAuth(restoredModel)) {
1282
1287
  model = restoredModel;
1283
1288
  restoredSessionModelIndex = i;
1289
+ restoredSessionThinkingLevel = parsedModel.thinkingLevel;
1284
1290
  break;
1285
1291
  }
1286
1292
  failedSessionModel ??= sessionModelStr;
@@ -1305,15 +1311,19 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1305
1311
  const taskDepth = options.taskDepth ?? 0;
1306
1312
 
1307
1313
  // Resolves the session/agent thinking level using the same precedence we
1308
- // apply at startup: explicit option → persisted session entry → default
1309
- // role's explicit selector → selected model's defaultLevelglobal
1310
- // settings default. Run again after extension role reclaim so the final
1311
- // model's own defaults aren't masked by an earlier fallback model's.
1314
+ // apply at startup: explicit option → persisted session entry → restored
1315
+ // model selector suffix default role's explicit selector selected
1316
+ // model's defaultLevel → global settings default. Run again after extension
1317
+ // role reclaim so the final model's own defaults aren't masked by an earlier
1318
+ // fallback model's.
1312
1319
  const pickInitialThinkingLevel = (selectedModel: Model | undefined): ConfiguredThinkingLevel | undefined => {
1313
1320
  let level = options.thinkingLevel;
1314
1321
  if (level === undefined && hasExistingSession && hasThinkingEntry) {
1315
1322
  level = parseThinkingLevel(existingSession.thinkingLevel);
1316
1323
  }
1324
+ if (level === undefined && !hasThinkingEntry && restoredSessionThinkingLevel !== undefined) {
1325
+ level = restoredSessionThinkingLevel;
1326
+ }
1317
1327
  if (level === undefined && !hasExplicitModel && !hasThinkingEntry && defaultRoleSpec.explicitThinkingLevel) {
1318
1328
  level = defaultRoleSpec.thinkingLevel;
1319
1329
  }
@@ -1321,7 +1331,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1321
1331
  level = selectedModel.thinking.defaultLevel;
1322
1332
  }
1323
1333
  if (level === undefined) {
1324
- level = settings.get("defaultThinkingLevel");
1334
+ level = parseConfiguredThinkingLevel(settings.get("defaultThinkingLevel"));
1325
1335
  }
1326
1336
  return level;
1327
1337
  };
@@ -1533,6 +1543,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1533
1543
  getModelString: () => (hasExplicitModel && model ? formatModelString(model) : undefined),
1534
1544
  getActiveModelString,
1535
1545
  getActiveModel: () => agent?.state.model ?? model,
1546
+ getImageAttachments: () => session?.getImageAttachments() ?? [],
1536
1547
  getPlanModeState: () => session?.getPlanModeState(),
1537
1548
  getPlanReferencePath: () => session?.getPlanReferencePath() ?? "local://PLAN.md",
1538
1549
  getGoalModeState: () => session?.getGoalModeState(),
@@ -1905,13 +1916,17 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1905
1916
  if (!hasExplicitModel && sessionRetryLimit > 0) {
1906
1917
  for (let i = 0; i < sessionRetryLimit; i++) {
1907
1918
  const sessionModelStr = sessionModelStrings[i];
1908
- const parsedModel = parseModelString(sessionModelStr);
1919
+ const parsedModel = parseModelString(sessionModelStr, {
1920
+ allowMaxAlias: true,
1921
+ isLiteralModelId: (provider, id) => modelRegistry.find(provider, id) !== undefined,
1922
+ });
1909
1923
  if (!parsedModel) continue;
1910
1924
  const restoredModel = modelRegistry.find(parsedModel.provider, parsedModel.id);
1911
1925
  if (restoredModel && hasModelAuth(restoredModel)) {
1912
1926
  model = restoredModel;
1913
1927
  modelFallbackMessage = undefined;
1914
1928
  restoredSessionModelIndex = i;
1929
+ restoredSessionThinkingLevel = parsedModel.thinkingLevel;
1915
1930
  // Recompute thinking-level from scratch against the reclaimed
1916
1931
  // model: any value derived from the earlier fallback model's
1917
1932
  // `thinking.defaultLevel` must not become sticky.
@@ -2585,6 +2600,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2585
2600
  if (watchdogFiles && watchdogFiles.length > 0) {
2586
2601
  advisorWatchdogPrompt = watchdogFiles.join("\n\n");
2587
2602
  }
2603
+ // Owned only when this session created the manager; subagents receive a
2604
+ // parent's manager via `options.mcpManager` and MUST NOT disconnect it.
2605
+ const ownedMcpManager = options.mcpManager ? undefined : mcpManager;
2588
2606
  session = new AgentSession({
2589
2607
  advisorWatchdogPrompt,
2590
2608
  agent,
@@ -2630,6 +2648,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2630
2648
  return out;
2631
2649
  }
2632
2650
  : undefined,
2651
+ disconnectOwnedMcpManager: ownedMcpManager ? () => ownedMcpManager.disconnectAll() : undefined,
2633
2652
  mcpDiscoveryEnabled,
2634
2653
  initialSelectedMCPToolNames,
2635
2654
  defaultSelectedMCPToolNames,