@aexol/spectral 0.7.8 → 0.8.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 (190) hide show
  1. package/dist/agent/agents.js +4 -4
  2. package/dist/agent/index.js +8 -8
  3. package/dist/cli.js +1 -1
  4. package/dist/commands/serve.js +1 -1
  5. package/dist/extensions/spectral-vision-fallback.js +81 -44
  6. package/dist/mcp/agent-dir.js +1 -1
  7. package/dist/mcp/config.js +3 -3
  8. package/dist/mcp/sampling-handler.js +1 -1
  9. package/dist/mcp/server-manager.js +5 -1
  10. package/dist/memory/commands/status.js +1 -1
  11. package/dist/memory/compaction.js +2 -2
  12. package/dist/memory/config.js +3 -3
  13. package/dist/memory/debug-log.js +1 -1
  14. package/dist/memory/observer.js +2 -2
  15. package/dist/memory/tokens.js +1 -1
  16. package/dist/memory/tools/read-project-observations.js +2 -2
  17. package/dist/memory/tools/recall-observation.js +2 -2
  18. package/dist/relay/auto-research.js +23 -23
  19. package/dist/relay/dispatcher.js +28 -2
  20. package/dist/relay/models-fetch.js +2 -2
  21. package/dist/{pi → sdk}/coding-agent/cli/args.js +4 -4
  22. package/dist/{pi → sdk}/coding-agent/config.js +9 -9
  23. package/dist/{pi → sdk}/coding-agent/core/agent-session.js +2 -0
  24. package/dist/{pi → sdk}/coding-agent/core/compaction/compaction.js +161 -5
  25. package/dist/{pi → sdk}/coding-agent/core/model-registry.js +11 -4
  26. package/dist/{pi → sdk}/coding-agent/core/package-manager.js +5 -5
  27. package/dist/{pi → sdk}/coding-agent/core/sdk.js +1 -1
  28. package/dist/{pi → sdk}/coding-agent/core/session-manager.js +4 -4
  29. package/dist/{pi → sdk}/coding-agent/core/telemetry.js +1 -1
  30. package/dist/{pi → sdk}/coding-agent/migrations.js +3 -3
  31. package/dist/{pi → sdk}/coding-agent/utils/tools-manager.js +1 -1
  32. package/dist/{pi → sdk}/coding-agent/utils/version-check.js +2 -2
  33. package/dist/{pi → sdk}/coding-agent/utils/windows-self-update.js +1 -1
  34. package/dist/server/{pi-bridge.js → agent-bridge.js} +113 -97
  35. package/dist/server/handlers/sessions.js +21 -0
  36. package/dist/server/session-stream.js +5 -5
  37. package/package.json +6 -3
  38. /package/dist/{pi → sdk}/agent-core/agent-loop.js +0 -0
  39. /package/dist/{pi → sdk}/agent-core/agent.js +0 -0
  40. /package/dist/{pi → sdk}/agent-core/harness/agent-harness.js +0 -0
  41. /package/dist/{pi → sdk}/agent-core/harness/compaction/branch-summarization.js +0 -0
  42. /package/dist/{pi → sdk}/agent-core/harness/compaction/compaction.js +0 -0
  43. /package/dist/{pi → sdk}/agent-core/harness/compaction/utils.js +0 -0
  44. /package/dist/{pi → sdk}/agent-core/harness/env/nodejs.js +0 -0
  45. /package/dist/{pi → sdk}/agent-core/harness/messages.js +0 -0
  46. /package/dist/{pi → sdk}/agent-core/harness/prompt-templates.js +0 -0
  47. /package/dist/{pi → sdk}/agent-core/harness/session/jsonl-repo.js +0 -0
  48. /package/dist/{pi → sdk}/agent-core/harness/session/jsonl-storage.js +0 -0
  49. /package/dist/{pi → sdk}/agent-core/harness/session/memory-repo.js +0 -0
  50. /package/dist/{pi → sdk}/agent-core/harness/session/memory-storage.js +0 -0
  51. /package/dist/{pi → sdk}/agent-core/harness/session/repo-utils.js +0 -0
  52. /package/dist/{pi → sdk}/agent-core/harness/session/session.js +0 -0
  53. /package/dist/{pi → sdk}/agent-core/harness/session/uuid.js +0 -0
  54. /package/dist/{pi → sdk}/agent-core/harness/skills.js +0 -0
  55. /package/dist/{pi → sdk}/agent-core/harness/system-prompt.js +0 -0
  56. /package/dist/{pi → sdk}/agent-core/harness/types.js +0 -0
  57. /package/dist/{pi → sdk}/agent-core/harness/utils/shell-output.js +0 -0
  58. /package/dist/{pi → sdk}/agent-core/harness/utils/truncate.js +0 -0
  59. /package/dist/{pi → sdk}/agent-core/index.js +0 -0
  60. /package/dist/{pi → sdk}/agent-core/node.js +0 -0
  61. /package/dist/{pi → sdk}/agent-core/proxy.js +0 -0
  62. /package/dist/{pi → sdk}/agent-core/types.js +0 -0
  63. /package/dist/{pi → sdk}/ai/api-registry.js +0 -0
  64. /package/dist/{pi → sdk}/ai/cli.js +0 -0
  65. /package/dist/{pi → sdk}/ai/env-api-keys.js +0 -0
  66. /package/dist/{pi → sdk}/ai/image-models.generated.js +0 -0
  67. /package/dist/{pi → sdk}/ai/image-models.js +0 -0
  68. /package/dist/{pi → sdk}/ai/images-api-registry.js +0 -0
  69. /package/dist/{pi → sdk}/ai/images.js +0 -0
  70. /package/dist/{pi → sdk}/ai/index.js +0 -0
  71. /package/dist/{pi → sdk}/ai/models.generated.js +0 -0
  72. /package/dist/{pi → sdk}/ai/models.js +0 -0
  73. /package/dist/{pi → sdk}/ai/oauth.js +0 -0
  74. /package/dist/{pi → sdk}/ai/providers/anthropic.js +0 -0
  75. /package/dist/{pi → sdk}/ai/providers/faux.js +0 -0
  76. /package/dist/{pi → sdk}/ai/providers/github-copilot-headers.js +0 -0
  77. /package/dist/{pi → sdk}/ai/providers/openai-completions.js +0 -0
  78. /package/dist/{pi → sdk}/ai/providers/openai-prompt-cache.js +0 -0
  79. /package/dist/{pi → sdk}/ai/providers/register-builtins.js +0 -0
  80. /package/dist/{pi → sdk}/ai/providers/simple-options.js +0 -0
  81. /package/dist/{pi → sdk}/ai/providers/transform-messages.js +0 -0
  82. /package/dist/{pi → sdk}/ai/session-resources.js +0 -0
  83. /package/dist/{pi → sdk}/ai/stream.js +0 -0
  84. /package/dist/{pi → sdk}/ai/types.js +0 -0
  85. /package/dist/{pi → sdk}/ai/utils/diagnostics.js +0 -0
  86. /package/dist/{pi → sdk}/ai/utils/event-stream.js +0 -0
  87. /package/dist/{pi → sdk}/ai/utils/hash.js +0 -0
  88. /package/dist/{pi → sdk}/ai/utils/headers.js +0 -0
  89. /package/dist/{pi → sdk}/ai/utils/json-parse.js +0 -0
  90. /package/dist/{pi → sdk}/ai/utils/node-http-proxy.js +0 -0
  91. /package/dist/{pi → sdk}/ai/utils/oauth/anthropic.js +0 -0
  92. /package/dist/{pi → sdk}/ai/utils/oauth/device-code.js +0 -0
  93. /package/dist/{pi → sdk}/ai/utils/oauth/github-copilot.js +0 -0
  94. /package/dist/{pi → sdk}/ai/utils/oauth/index.js +0 -0
  95. /package/dist/{pi → sdk}/ai/utils/oauth/oauth-page.js +0 -0
  96. /package/dist/{pi → sdk}/ai/utils/oauth/openai-codex.js +0 -0
  97. /package/dist/{pi → sdk}/ai/utils/oauth/pkce.js +0 -0
  98. /package/dist/{pi → sdk}/ai/utils/oauth/types.js +0 -0
  99. /package/dist/{pi → sdk}/ai/utils/overflow.js +0 -0
  100. /package/dist/{pi → sdk}/ai/utils/sanitize-unicode.js +0 -0
  101. /package/dist/{pi → sdk}/ai/utils/typebox-helpers.js +0 -0
  102. /package/dist/{pi → sdk}/ai/utils/validation.js +0 -0
  103. /package/dist/{pi → sdk}/coding-agent/bun/cli.js +0 -0
  104. /package/dist/{pi → sdk}/coding-agent/bun/restore-sandbox-env.js +0 -0
  105. /package/dist/{pi → sdk}/coding-agent/cli/file-processor.js +0 -0
  106. /package/dist/{pi → sdk}/coding-agent/cli/initial-message.js +0 -0
  107. /package/dist/{pi → sdk}/coding-agent/cli.js +0 -0
  108. /package/dist/{pi → sdk}/coding-agent/core/agent-session-runtime.js +0 -0
  109. /package/dist/{pi → sdk}/coding-agent/core/agent-session-services.js +0 -0
  110. /package/dist/{pi → sdk}/coding-agent/core/auth-guidance.js +0 -0
  111. /package/dist/{pi → sdk}/coding-agent/core/auth-storage.js +0 -0
  112. /package/dist/{pi → sdk}/coding-agent/core/bash-executor.js +0 -0
  113. /package/dist/{pi → sdk}/coding-agent/core/compaction/branch-summarization.js +0 -0
  114. /package/dist/{pi → sdk}/coding-agent/core/compaction/index.js +0 -0
  115. /package/dist/{pi → sdk}/coding-agent/core/compaction/utils.js +0 -0
  116. /package/dist/{pi → sdk}/coding-agent/core/defaults.js +0 -0
  117. /package/dist/{pi → sdk}/coding-agent/core/diagnostics.js +0 -0
  118. /package/dist/{pi → sdk}/coding-agent/core/event-bus.js +0 -0
  119. /package/dist/{pi → sdk}/coding-agent/core/exec.js +0 -0
  120. /package/dist/{pi → sdk}/coding-agent/core/extensions/index.js +0 -0
  121. /package/dist/{pi → sdk}/coding-agent/core/extensions/loader.js +0 -0
  122. /package/dist/{pi → sdk}/coding-agent/core/extensions/runner.js +0 -0
  123. /package/dist/{pi → sdk}/coding-agent/core/extensions/types.js +0 -0
  124. /package/dist/{pi → sdk}/coding-agent/core/extensions/wrapper.js +0 -0
  125. /package/dist/{pi → sdk}/coding-agent/core/footer-data-provider.js +0 -0
  126. /package/dist/{pi → sdk}/coding-agent/core/http-dispatcher.js +0 -0
  127. /package/dist/{pi → sdk}/coding-agent/core/index.js +0 -0
  128. /package/dist/{pi → sdk}/coding-agent/core/keybindings.js +0 -0
  129. /package/dist/{pi → sdk}/coding-agent/core/messages.js +0 -0
  130. /package/dist/{pi → sdk}/coding-agent/core/model-resolver.js +0 -0
  131. /package/dist/{pi → sdk}/coding-agent/core/output-guard.js +0 -0
  132. /package/dist/{pi → sdk}/coding-agent/core/prompt-templates.js +0 -0
  133. /package/dist/{pi → sdk}/coding-agent/core/provider-display-names.js +0 -0
  134. /package/dist/{pi → sdk}/coding-agent/core/resolve-config-value.js +0 -0
  135. /package/dist/{pi → sdk}/coding-agent/core/resource-loader.js +0 -0
  136. /package/dist/{pi → sdk}/coding-agent/core/session-cwd.js +0 -0
  137. /package/dist/{pi → sdk}/coding-agent/core/settings-manager.js +0 -0
  138. /package/dist/{pi → sdk}/coding-agent/core/skills.js +0 -0
  139. /package/dist/{pi → sdk}/coding-agent/core/slash-commands.js +0 -0
  140. /package/dist/{pi → sdk}/coding-agent/core/source-info.js +0 -0
  141. /package/dist/{pi → sdk}/coding-agent/core/system-prompt.js +0 -0
  142. /package/dist/{pi → sdk}/coding-agent/core/timings.js +0 -0
  143. /package/dist/{pi → sdk}/coding-agent/core/tools/bash.js +0 -0
  144. /package/dist/{pi → sdk}/coding-agent/core/tools/edit-diff.js +0 -0
  145. /package/dist/{pi → sdk}/coding-agent/core/tools/edit.js +0 -0
  146. /package/dist/{pi → sdk}/coding-agent/core/tools/file-mutation-queue.js +0 -0
  147. /package/dist/{pi → sdk}/coding-agent/core/tools/find.js +0 -0
  148. /package/dist/{pi → sdk}/coding-agent/core/tools/grep.js +0 -0
  149. /package/dist/{pi → sdk}/coding-agent/core/tools/index.js +0 -0
  150. /package/dist/{pi → sdk}/coding-agent/core/tools/ls.js +0 -0
  151. /package/dist/{pi → sdk}/coding-agent/core/tools/output-accumulator.js +0 -0
  152. /package/dist/{pi → sdk}/coding-agent/core/tools/path-utils.js +0 -0
  153. /package/dist/{pi → sdk}/coding-agent/core/tools/read.js +0 -0
  154. /package/dist/{pi → sdk}/coding-agent/core/tools/render-utils.js +0 -0
  155. /package/dist/{pi → sdk}/coding-agent/core/tools/tool-definition-wrapper.js +0 -0
  156. /package/dist/{pi → sdk}/coding-agent/core/tools/truncate.js +0 -0
  157. /package/dist/{pi → sdk}/coding-agent/core/tools/write.js +0 -0
  158. /package/dist/{pi → sdk}/coding-agent/index.js +0 -0
  159. /package/dist/{pi → sdk}/coding-agent/main.js +0 -0
  160. /package/dist/{pi → sdk}/coding-agent/modes/index.js +0 -0
  161. /package/dist/{pi → sdk}/coding-agent/modes/interactive/components/diff.js +0 -0
  162. /package/dist/{pi → sdk}/coding-agent/modes/interactive/components/keybinding-hints.js +0 -0
  163. /package/dist/{pi → sdk}/coding-agent/modes/interactive/components/visual-truncate.js +0 -0
  164. /package/dist/{pi → sdk}/coding-agent/modes/interactive/interactive-mode.js +0 -0
  165. /package/dist/{pi → sdk}/coding-agent/modes/interactive/theme/theme.js +0 -0
  166. /package/dist/{pi → sdk}/coding-agent/modes/print-mode.js +0 -0
  167. /package/dist/{pi → sdk}/coding-agent/modes/rpc/jsonl.js +0 -0
  168. /package/dist/{pi → sdk}/coding-agent/modes/rpc/rpc-client.js +0 -0
  169. /package/dist/{pi → sdk}/coding-agent/modes/rpc/rpc-mode.js +0 -0
  170. /package/dist/{pi → sdk}/coding-agent/modes/rpc/rpc-types.js +0 -0
  171. /package/dist/{pi → sdk}/coding-agent/utils/ansi.js +0 -0
  172. /package/dist/{pi → sdk}/coding-agent/utils/changelog.js +0 -0
  173. /package/dist/{pi → sdk}/coding-agent/utils/child-process.js +0 -0
  174. /package/dist/{pi → sdk}/coding-agent/utils/clipboard-image.js +0 -0
  175. /package/dist/{pi → sdk}/coding-agent/utils/clipboard-native.js +0 -0
  176. /package/dist/{pi → sdk}/coding-agent/utils/clipboard.js +0 -0
  177. /package/dist/{pi → sdk}/coding-agent/utils/exif-orientation.js +0 -0
  178. /package/dist/{pi → sdk}/coding-agent/utils/frontmatter.js +0 -0
  179. /package/dist/{pi → sdk}/coding-agent/utils/fs-watch.js +0 -0
  180. /package/dist/{pi → sdk}/coding-agent/utils/git.js +0 -0
  181. /package/dist/{pi → sdk}/coding-agent/utils/html.js +0 -0
  182. /package/dist/{pi → sdk}/coding-agent/utils/image-convert.js +0 -0
  183. /package/dist/{pi → sdk}/coding-agent/utils/image-resize.js +0 -0
  184. /package/dist/{pi → sdk}/coding-agent/utils/mime.js +0 -0
  185. /package/dist/{pi → sdk}/coding-agent/utils/paths.js +0 -0
  186. /package/dist/{pi → sdk}/coding-agent/utils/photon.js +0 -0
  187. /package/dist/{pi → sdk}/coding-agent/utils/pi-user-agent.js +0 -0
  188. /package/dist/{pi → sdk}/coding-agent/utils/shell.js +0 -0
  189. /package/dist/{pi → sdk}/coding-agent/utils/sleep.js +0 -0
  190. /package/dist/{pi → sdk}/coding-agent/utils/syntax-highlight.js +0 -0
@@ -12,12 +12,12 @@
12
12
  * System prompt for the agent goes here.
13
13
  *
14
14
  * Locations:
15
- * ~/.pi/agent/agents/*.md — User-level (always loaded)
16
- * .pi/agents/*.md — Project-level (opt-in via agentScope)
15
+ * ~/.spectral/agent/agents/*.md — User-level (always loaded)
16
+ * .spectral/agents/*.md — Project-level (opt-in via agentScope)
17
17
  */
18
18
  import * as fs from "node:fs";
19
19
  import * as path from "node:path";
20
- import { getAgentDir, parseFrontmatter } from "../pi/coding-agent/index.js";
20
+ import { getAgentDir, parseFrontmatter } from "../sdk/coding-agent/index.js";
21
21
  function loadAgentsFromDir(dir, source) {
22
22
  const agents = [];
23
23
  if (!fs.existsSync(dir)) {
@@ -74,7 +74,7 @@ function isDirectory(p) {
74
74
  function findNearestProjectAgentsDir(cwd) {
75
75
  let currentDir = cwd;
76
76
  while (true) {
77
- const candidate = path.join(currentDir, ".pi", "agents");
77
+ const candidate = path.join(currentDir, ".spectral", "agents");
78
78
  if (isDirectory(candidate))
79
79
  return candidate;
80
80
  const parentDir = path.dirname(currentDir);
@@ -13,8 +13,8 @@
13
13
  * - Chain: { chain: [{ agent, task: "... {previous} ..." }, ...] }
14
14
  *
15
15
  * Agent definitions live as markdown files with YAML frontmatter:
16
- * - ~/.pi/agent/agents/*.md (user-level, always loaded)
17
- * - .pi/agents/*.md (project-level, opt-in via agentScope)
16
+ * - ~/.spectral/agent/agents/*.md (user-level, always loaded)
17
+ * - .spectral/agents/*.md (project-level, opt-in via agentScope)
18
18
  *
19
19
  * Subagents use an in-process `agentLoop()` for:
20
20
  * - Clean context isolation (fresh messages array)
@@ -24,15 +24,15 @@
24
24
  * - Signal propagation (abort passthrough)
25
25
  * - Access to observational memory via the recall tool
26
26
  */
27
- import { agentLoop } from "../pi/agent-core/index.js";
28
- import { StringEnum } from "../pi/ai/index.js";
27
+ import { agentLoop } from "../sdk/agent-core/index.js";
28
+ import { StringEnum } from "../sdk/ai/index.js";
29
29
  import { Type } from "typebox";
30
30
  import { discoverAgents } from "./agents.js";
31
31
  // ---------------------------------------------------------------------------
32
32
  // Built-in tool resolution (no spawn needed — tools are registered in-process)
33
33
  // ---------------------------------------------------------------------------
34
- import { createTool, allToolNames } from "../pi/coding-agent/core/tools/index.js";
35
- import { wrapToolDefinition } from "../pi/coding-agent/core/tools/tool-definition-wrapper.js";
34
+ import { createTool, allToolNames } from "../sdk/coding-agent/core/tools/index.js";
35
+ import { wrapToolDefinition } from "../sdk/coding-agent/core/tools/tool-definition-wrapper.js";
36
36
  // ---------------------------------------------------------------------------
37
37
  // Constants
38
38
  // ---------------------------------------------------------------------------
@@ -386,8 +386,8 @@ export default function subagentExtension(pi) {
386
386
  description: [
387
387
  "Delegate tasks to specialized subagents with isolated context.",
388
388
  "Modes: single (agent + task), parallel (tasks array), chain (sequential with {previous} placeholder).",
389
- 'Default agent scope is "user" (from ~/.pi/agent/agents).',
390
- 'To enable project-local agents in .pi/agents, set agentScope: "both" (or "project").',
389
+ 'Default agent scope is "user" (from ~/.spectral/agent/agents).',
390
+ 'To enable project-local agents in .spectral/agents, set agentScope: "both" (or "project").',
391
391
  ].join(" "),
392
392
  promptSnippet: "Run a subagent to investigate, plan, review, or implement code changes",
393
393
  promptGuidelines: [
package/dist/cli.js CHANGED
@@ -46,7 +46,7 @@ function printHeader() {
46
46
  ].join("\n"));
47
47
  }
48
48
  // ---- Disable interactive git editors ----------------------------------------
49
- // Set at process level for serve mode (PiBridge in-process → getShellEnv()
49
+ // Set at process level for serve mode (AgentBridge in-process → getShellEnv()
50
50
  // returns process.env). Without this, `git rebase --continue`, `git commit`
51
51
  // (without -m), `git merge`, etc. open an editor that hangs forever — the
52
52
  // agent's bash tool runs with `stdio: ["ignore", ...]`, so there is no TTY
@@ -146,7 +146,7 @@ export async function runServe(opts = {}) {
146
146
  const backendUrl = opts.backendUrlOverride ?? process.env.SPECTRAL_BACKEND_URL ?? DEFAULT_BACKEND_URL;
147
147
  const relayUrl = opts.relayUrlOverride ?? process.env.SPECTRAL_RELAY_URL ?? deriveRelayUrl(backendUrl);
148
148
  // Register BEFORE constructing the SessionStreamManager. The manager
149
- // (and every PiBridge it spawns) needs the machine JWT to authenticate
149
+ // (and every AgentBridge it spawns) needs the machine JWT to authenticate
150
150
  // backend-proxied inference calls, so registration must succeed first.
151
151
  // Fail-fast on error — a clear message beats a hang.
152
152
  let registration;
@@ -1,55 +1,87 @@
1
1
  /**
2
2
  * Spectral Vision Extension
3
3
  *
4
- * Automatically describes images using a vision-capable model and replaces
5
- * raw image data with text descriptions. This saves context for the main agent
6
- * and allows non-vision models to "see" images.
4
+ * Automatically switches to a vision-capable model for image processing
5
+ * when images are attached. This allows non-vision models to "see" images
6
+ * and saves main agent context by using a potentially smaller/cheaper
7
+ * vision model for image understanding.
7
8
  *
8
9
  * Logic:
9
10
  * - ALWAYS intercepts images (even when main model supports vision),
10
- * replacing them with text descriptions to save main agent context.
11
- * - Uses the main model for vision descriptions if it supports images.
12
- * - Falls back to the admin-configured default vision model (isVisionDefault
13
- * flag from backend via SettingsManager).
14
- * - If no admin default is configured, falls back to the first available
11
+ * replacing them with text descriptions.
12
+ * - Prefers the admin-configured default vision model (isVisionDefault
13
+ * flag from backend via SettingsManager) for image descriptions.
14
+ * - Falls back to the main model for vision if no admin default is
15
+ * configured and the main model supports images.
16
+ * - If neither is available, falls back to the first available
15
17
  * vision-capable model by provider priority.
16
18
  *
17
19
  * Hooks into the `context` event to intercept ALL images before they reach
18
20
  * the LLM (covers user-attached images and tool-result images alike).
19
21
  */
20
- import { streamSimple } from "../pi/ai/index.js";
22
+ import { streamSimple } from "../sdk/ai/index.js";
21
23
  // ---------------------------------------------------------------------------
22
24
  // Helpers
23
25
  // ---------------------------------------------------------------------------
24
- /** Find the best available vision-capable model from the registry */
25
- function findVisionModel(ctx) {
26
+ /**
27
+ * Check if the SettingsManager has an admin-configured default vision model
28
+ * with working auth. Returns the model if found and auth-ready, or undefined.
29
+ */
30
+ function getAdminVisionModel(ctx) {
31
+ const settings = ctx.settingsManager;
32
+ if (!settings)
33
+ return undefined;
34
+ const defaultVisionProvider = settings.getDefaultVisionProvider();
35
+ const defaultVisionModel = settings.getDefaultVisionModel();
36
+ if (!defaultVisionProvider || !defaultVisionModel)
37
+ return undefined;
26
38
  const allModels = ctx.modelRegistry.getAll();
27
- const availableModels = allModels.filter((m) => ctx.modelRegistry.hasConfiguredAuth(m));
28
- // Filter to models that support images
29
- const visionModels = availableModels.filter((m) => m.input.includes("image"));
30
- if (visionModels.length === 0)
39
+ const match = allModels.find((m) => m.provider === defaultVisionProvider && m.id === defaultVisionModel);
40
+ if (!match || !match.input.includes("image"))
31
41
  return undefined;
32
- // 1. Try admin-configured default vision model from settings
33
- const settings = ctx.settingsManager;
34
- if (settings) {
35
- const defaultVisionProvider = settings.getDefaultVisionProvider();
36
- const defaultVisionModel = settings.getDefaultVisionModel();
37
- if (defaultVisionProvider && defaultVisionModel) {
38
- const match = visionModels.find((m) => m.provider === defaultVisionProvider && m.id === defaultVisionModel);
39
- if (match) {
40
- process.stderr.write(`[spectral-vision] Using admin-configured default: ${match.provider}/${match.id}\n`);
41
- return match;
42
- }
43
- }
42
+ if (!ctx.modelRegistry.hasConfiguredAuth(match))
43
+ return undefined;
44
+ return match;
45
+ }
46
+ /**
47
+ * Resolve the best vision model to use for image descriptions.
48
+ *
49
+ * Priority:
50
+ * 1. Admin-configured default vision model (isVisionDefault from backend)
51
+ * 2. Current main model if it supports images
52
+ * 3. Best available vision model by provider priority
53
+ * 4. First available vision model
54
+ */
55
+ function resolveVisionModel(ctx) {
56
+ // 1. Admin-configured default vision model (always preferred)
57
+ const adminModel = getAdminVisionModel(ctx);
58
+ if (adminModel) {
59
+ process.stderr.write(`[spectral-vision] Using admin-configured default: ${adminModel.provider}/${adminModel.id}\n`);
60
+ return adminModel;
44
61
  }
45
- // 2. Fall back to provider priority
62
+ // 2. Current main model if it supports images
63
+ const currentModel = ctx.model;
64
+ if (currentModel?.input.includes("image") && ctx.modelRegistry.hasConfiguredAuth(currentModel)) {
65
+ process.stderr.write(`[spectral-vision] Using main model for vision: ${currentModel.provider}/${currentModel.id}\n`);
66
+ return currentModel;
67
+ }
68
+ // 3. Fall back to best available vision model by provider priority
69
+ const allModels = ctx.modelRegistry.getAll();
70
+ const visionModels = allModels.filter((m) => m.input.includes("image") && ctx.modelRegistry.hasConfiguredAuth(m));
71
+ if (visionModels.length === 0)
72
+ return undefined;
46
73
  const providerPriority = ["anthropic", "openai", "google", "openrouter"];
47
74
  for (const provider of providerPriority) {
48
75
  const match = visionModels.find((m) => m.provider === provider);
49
- if (match)
76
+ if (match) {
77
+ process.stderr.write(`[spectral-vision] Using fallback vision model: ${match.provider}/${match.id}\n`);
50
78
  return match;
79
+ }
51
80
  }
52
- return visionModels[0];
81
+ // 4. First available vision model
82
+ const fallback = visionModels[0];
83
+ process.stderr.write(`[spectral-vision] Using first available vision model: ${fallback.provider}/${fallback.id}\n`);
84
+ return fallback;
53
85
  }
54
86
  /** Check if any content block in an array is an image */
55
87
  function hasImageContent(content) {
@@ -111,6 +143,13 @@ async function describeImages(visionModel, content, contextText, ctx) {
111
143
  const textNote = description.trim()
112
144
  ? `[Image description from ${visionModel.provider}/${visionModel.id} (${imageCount} image(s)):\n${description}\n]`
113
145
  : `[${imageCount} image(s) — vision model returned empty description]`;
146
+ // Notify the frontend that the vision extension analyzed the image(s)
147
+ try {
148
+ ctx.ui.notify(`[spectral-vision] Analyzed ${imageCount} image(s) with ${visionModel.provider}/${visionModel.id}`, "info");
149
+ }
150
+ catch {
151
+ // UI context may not be available in headless modes
152
+ }
114
153
  return [
115
154
  ...textPrefixBlocks,
116
155
  { type: "text", text: textNote },
@@ -119,6 +158,13 @@ async function describeImages(visionModel, content, contextText, ctx) {
119
158
  catch (err) {
120
159
  const msg = err instanceof Error ? err.message : String(err);
121
160
  process.stderr.write(`[spectral-vision] Vision call failed: ${msg}\n`);
161
+ // Notify frontend about the failure
162
+ try {
163
+ ctx.ui.notify(`[spectral-vision] Failed to analyze ${imageCount} image(s): ${msg}`, "warning");
164
+ }
165
+ catch {
166
+ // UI context may not be available in headless modes
167
+ }
122
168
  return [
123
169
  ...textPrefixBlocks,
124
170
  {
@@ -134,7 +180,7 @@ async function describeImages(visionModel, content, contextText, ctx) {
134
180
  export default function spectralVisionExtension(pi) {
135
181
  let visionModel;
136
182
  pi.on("session_start", (_event, ctx) => {
137
- visionModel = findVisionModel(ctx);
183
+ visionModel = resolveVisionModel(ctx);
138
184
  if (visionModel) {
139
185
  process.stderr.write(`[spectral-vision] Ready — using ${visionModel.provider}/${visionModel.id} for image descriptions.\n`);
140
186
  }
@@ -151,19 +197,10 @@ export default function spectralVisionExtension(pi) {
151
197
  }
152
198
  if (totalImages === 0)
153
199
  return;
154
- // Resolve vision model
155
- const currentModel = ctx.model;
156
- // If main model supports images, use it for vision (saves auth setup)
157
- if (currentModel?.input.includes("image")) {
158
- visionModel = currentModel;
159
- }
160
- else {
161
- // Refresh vision model
162
- if (!visionModel ||
163
- !ctx.modelRegistry.hasConfiguredAuth(visionModel)) {
164
- visionModel = findVisionModel(ctx);
165
- }
166
- }
200
+ // Re-resolve vision model each time images are encountered.
201
+ // This ensures automatic switching to the admin-configured default
202
+ // vision model even when the main model supports vision.
203
+ visionModel = resolveVisionModel(ctx);
167
204
  if (!visionModel) {
168
205
  process.stderr.write("[spectral-vision] No vision model available, images will be omitted\n");
169
206
  return;
@@ -3,7 +3,7 @@ import { join, resolve } from "node:path";
3
3
  export function getAgentDir() {
4
4
  const configured = process.env.PI_CODING_AGENT_DIR?.trim();
5
5
  if (!configured) {
6
- return join(homedir(), ".pi", "agent");
6
+ return join(homedir(), ".spectral", "agent");
7
7
  }
8
8
  if (configured === "~") {
9
9
  return homedir();
@@ -5,7 +5,7 @@ import { dirname, join, resolve } from "node:path";
5
5
  import { getAgentPath } from "./agent-dir.js";
6
6
  const GENERIC_GLOBAL_CONFIG_PATH = join(homedir(), ".config", "mcp", "mcp.json");
7
7
  const PROJECT_CONFIG_NAME = ".mcp.json";
8
- const PROJECT_PI_CONFIG_NAME = ".pi/mcp.json";
8
+ const PROJECT_MCP_CONFIG_NAME = ".spectral/mcp.json";
9
9
  const REPOPROMPT_BINARY_CANDIDATES = [
10
10
  join(homedir(), "RepoPrompt", "repoprompt_cli"),
11
11
  "/Applications/Repo Prompt.app/Contents/MacOS/repoprompt-mcp",
@@ -32,7 +32,7 @@ export function getProjectConfigPath(cwd = process.cwd()) {
32
32
  return resolve(cwd, PROJECT_CONFIG_NAME);
33
33
  }
34
34
  export function getProjectPiConfigPath(cwd = process.cwd()) {
35
- return resolve(cwd, PROJECT_PI_CONFIG_NAME);
35
+ return resolve(cwd, PROJECT_MCP_CONFIG_NAME);
36
36
  }
37
37
  export function getConfigDiscoveryPaths(overridePath) {
38
38
  return getConfigSources(overridePath).map((source) => ({
@@ -368,7 +368,7 @@ function findProjectRoot(cwd = process.cwd()) {
368
368
  if (existsSync(join(current, ".git"))
369
369
  || existsSync(join(current, "package.json"))
370
370
  || existsSync(join(current, PROJECT_CONFIG_NAME))
371
- || existsSync(join(current, ".pi"))) {
371
+ || existsSync(join(current, ".spectral"))) {
372
372
  return current;
373
373
  }
374
374
  const parent = dirname(current);
@@ -1,4 +1,4 @@
1
- import { complete } from "../pi/ai/index.js";
1
+ import { complete } from "../sdk/ai/index.js";
2
2
  import { truncateAtWord } from "./utils.js";
3
3
  import { CreateMessageRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
4
4
  export function registerSamplingHandler(client, options) {
@@ -96,6 +96,8 @@ export class McpServerManager {
96
96
  }
97
97
  // Check for UnauthorizedError — server requires OAuth.
98
98
  // This can fire from createHttpTransport probe or client.connect.
99
+ // transport may be undefined if createHttpTransport threw during the probe
100
+ // before returning a transport; close() handles this safely.
99
101
  if (error instanceof UnauthorizedError && supportsOAuth(definition)) {
100
102
  return {
101
103
  client,
@@ -239,7 +241,9 @@ export class McpServerManager {
239
241
  connection.status = "closed";
240
242
  this.connections.delete(name);
241
243
  await connection.client.close().catch(() => { });
242
- await connection.transport.close().catch(() => { });
244
+ if (connection.transport) {
245
+ await connection.transport.close().catch(() => { });
246
+ }
243
247
  }
244
248
  async closeAll() {
245
249
  const names = [...this.connections.keys()];
@@ -1,4 +1,4 @@
1
- import { SettingsManager } from "../../pi/coding-agent/index.js";
1
+ import { SettingsManager } from "../../sdk/coding-agent/index.js";
2
2
  import { getMemoryState, rawTokensSinceLastBound, rawTokensSinceLastCompaction, } from "../branch.js";
3
3
  import { observationPoolTokens as estimateObservationPoolTokens } from "../compaction.js";
4
4
  import { countByRelevance, formatRelevanceHistogram } from "../relevance.js";
@@ -1,5 +1,5 @@
1
- import { agentLoop } from "../pi/agent-core/index.js";
2
- import { Type } from "../pi/ai/index.js";
1
+ import { agentLoop } from "../sdk/agent-core/index.js";
2
+ import { Type } from "../sdk/ai/index.js";
3
3
  import { debugLog, isDebugLogEnabled } from "./debug-log.js";
4
4
  import { hashId } from "./ids.js";
5
5
  import { AGENT_LOOP_MAX_TOKENS, boundedMaxTokens } from "./model-budget.js";
@@ -1,10 +1,10 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { getAgentDir } from "../pi/coding-agent/index.js";
3
+ import { getAgentDir } from "../sdk/coding-agent/index.js";
4
4
  export const DEFAULTS = {
5
5
  observationThresholdTokens: 1_000,
6
6
  compactionThresholdTokens: 50_000,
7
- reflectionThresholdTokens: 30_000,
7
+ reflectionThresholdTokens: 10_000,
8
8
  passive: false,
9
9
  debugLog: false,
10
10
  observerMaxChunkTokens: 30_000,
@@ -72,7 +72,7 @@ function readNamespacedConfig(path) {
72
72
  }
73
73
  export function loadConfig(cwd, env = process.env) {
74
74
  const globalPath = join(getAgentDir(), "settings.json");
75
- const projectPath = join(cwd, ".pi", "settings.json");
75
+ const projectPath = join(cwd, ".spectral", "settings.json");
76
76
  return {
77
77
  ...DEFAULTS,
78
78
  ...readNamespacedConfig(globalPath),
@@ -1,7 +1,7 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
2
  import { existsSync, mkdirSync, renameSync, statSync, unlinkSync, appendFileSync } from "node:fs";
3
3
  import { dirname, join } from "node:path";
4
- import { getAgentDir } from "../pi/coding-agent/index.js";
4
+ import { getAgentDir } from "../sdk/coding-agent/index.js";
5
5
  export const DEBUG_LOG_MAX_BYTES = 10 * 1024 * 1024;
6
6
  export const DEBUG_LOG_RELATIVE_PATH = join("observational-memory", "debug.ndjson");
7
7
  const storage = new AsyncLocalStorage();
@@ -1,5 +1,5 @@
1
- import { agentLoop } from "../pi/agent-core/index.js";
2
- import { Type } from "../pi/ai/index.js";
1
+ import { agentLoop } from "../sdk/agent-core/index.js";
2
+ import { Type } from "../sdk/ai/index.js";
3
3
  import { hashId } from "./ids.js";
4
4
  import { AGENT_LOOP_MAX_TOKENS, boundedMaxTokens } from "./model-budget.js";
5
5
  import { OBSERVER_SYSTEM } from "./prompts.js";
@@ -1,4 +1,4 @@
1
- import { estimateTokens as estimateMessageTokens } from "../pi/coding-agent/index.js";
1
+ import { estimateTokens as estimateMessageTokens } from "../sdk/coding-agent/index.js";
2
2
  export function estimateStringTokens(text) {
3
3
  return Math.ceil(text.length / 4);
4
4
  }
@@ -1,5 +1,5 @@
1
- import { Type } from "../../pi/ai/index.js";
2
- import { defineTool } from "../../pi/coding-agent/index.js";
1
+ import { Type } from "../../sdk/ai/index.js";
2
+ import { defineTool } from "../../sdk/coding-agent/index.js";
3
3
  import { getProjectObsStore } from "../project-observations-store.js";
4
4
  export const READ_PROJECT_OBSERVATIONS_TOOL_NAME = "read_project_observations";
5
5
  export const readProjectObservationsTool = defineTool({
@@ -1,5 +1,5 @@
1
- import { Type } from "../../pi/ai/index.js";
2
- import { defineTool } from "../../pi/coding-agent/index.js";
1
+ import { Type } from "../../sdk/ai/index.js";
2
+ import { defineTool } from "../../sdk/coding-agent/index.js";
3
3
  import { recallMemorySources, } from "../branch.js";
4
4
  import { renderRecallSourceEntries, renderRecallSourceEntry } from "../serialize.js";
5
5
  import { estimateEntryTokens } from "../tokens.js";
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-research handler — sends an auto-research task through the existing
3
- * PiBridge (backend proxy) instead of spawning a separate pi process.
3
+ * AgentBridge (backend proxy) instead of spawning a separate pi process.
4
4
  *
5
5
  * This ensures auto-research uses the same model and API keys as the active
6
6
  * session — no separate subprocess, no missing API key errors.
@@ -47,12 +47,12 @@ function makeRelaySubscriber(sessionId, relay) {
47
47
  };
48
48
  }
49
49
  /**
50
- * Scan the project's .pi/extensions/auto-research/ directory for generated
50
+ * Scan the project's .spectral/extensions/auto-research/ directory for generated
51
51
  * extension directories. Each subdirectory with .ts files counts as one
52
52
  * extension.
53
53
  */
54
54
  function scanGeneratedExtensions(projectPath) {
55
- const arDir = path.join(projectPath, ".pi", "extensions", "auto-research");
55
+ const arDir = path.join(projectPath, ".spectral", "extensions", "auto-research");
56
56
  const extensions = [];
57
57
  try {
58
58
  if (!fs.existsSync(arDir))
@@ -105,7 +105,7 @@ function scanGeneratedExtensions(projectPath) {
105
105
  }
106
106
  extensions.push({
107
107
  name: entry.name,
108
- path: `.pi/extensions/auto-research/${entry.name}`,
108
+ path: `.spectral/extensions/auto-research/${entry.name}`,
109
109
  description,
110
110
  usesLLM,
111
111
  fileCount,
@@ -137,7 +137,7 @@ function hasAgentsMdUpdate(projectPath) {
137
137
  */
138
138
  function readManifest(projectPath) {
139
139
  try {
140
- const mPath = path.join(projectPath, ".pi", "extensions", "auto-research", "manifest.json");
140
+ const mPath = path.join(projectPath, ".spectral", "extensions", "auto-research", "manifest.json");
141
141
  if (!fs.existsSync(mPath))
142
142
  return null;
143
143
  const raw = fs.readFileSync(mPath, "utf-8");
@@ -164,7 +164,7 @@ function gatherPreRunContext(projectPath) {
164
164
  const isIncremental = manifest !== null;
165
165
  const existingExtensions = [];
166
166
  try {
167
- const arDir = path.join(projectPath, ".pi", "extensions", "auto-research");
167
+ const arDir = path.join(projectPath, ".spectral", "extensions", "auto-research");
168
168
  if (fs.existsSync(arDir)) {
169
169
  for (const entry of fs.readdirSync(arDir, { withFileTypes: true })) {
170
170
  if (entry.isDirectory() && entry.name !== "node_modules") {
@@ -209,7 +209,7 @@ function buildIncrementalSection(ctx) {
209
209
  if (ctx.existingExtensions.length > 0) {
210
210
  lines.push("### Existing extensions to review:");
211
211
  for (const name of ctx.existingExtensions) {
212
- lines.push(` - \`.pi/extensions/auto-research/${name}/\``);
212
+ lines.push(` - \`.spectral/extensions/auto-research/${name}/\``);
213
213
  }
214
214
  lines.push("");
215
215
  }
@@ -224,7 +224,7 @@ function buildIncrementalSection(ctx) {
224
224
  lines.push(" was removed from the project, delete the extension directory entirely.", "");
225
225
  lines.push("4. **Update AGENTS.md** — The AUTO-RESEARCH section should reflect the CURRENT");
226
226
  lines.push(" set of extensions (remove stale entries, add new ones).", "");
227
- lines.push("5. **Save manifest.json** — Write/update .pi/extensions/auto-research/manifest.json");
227
+ lines.push("5. **Save manifest.json** — Write/update .spectral/extensions/auto-research/manifest.json");
228
228
  lines.push(` with: lastRun (ISO), lastCommit (git HEAD), runCount (${ctx.manifest ? ctx.manifest.runCount + 1 : 1}),`);
229
229
  lines.push(" and extensions array with name/path/category for each generated extension.", "");
230
230
  return lines.join("\n");
@@ -234,7 +234,7 @@ function buildIncrementalSection(ctx) {
234
234
  */
235
235
  function writeManifest(projectPath, extensions) {
236
236
  try {
237
- const arDir = path.join(projectPath, ".pi", "extensions", "auto-research");
237
+ const arDir = path.join(projectPath, ".spectral", "extensions", "auto-research");
238
238
  if (!fs.existsSync(arDir))
239
239
  fs.mkdirSync(arDir, { recursive: true });
240
240
  let currentCommit = "unknown";
@@ -257,7 +257,7 @@ function writeManifest(projectPath, extensions) {
257
257
  }
258
258
  /**
259
259
  * Build the auto-research task prompt. This is sent as a user message
260
- * through the existing PiBridge, so the agent uses the session's model
260
+ * through the existing AgentBridge, so the agent uses the session's model
261
261
  * and backend proxy.
262
262
  *
263
263
  * @param projectPath Absolute path to the project root
@@ -297,8 +297,8 @@ function buildAutoResearchTask(projectPath, projectName, preRunContext = null) {
297
297
  "}",
298
298
  "```",
299
299
  "",
300
- "Extensions live in: `.pi/extensions/` (project-local) or `~/.pi/agent/extensions/` (user-global).",
301
- "Auto-research generates extensions into `.pi/extensions/auto-research/<name>/`.",
300
+ "Extensions live in: `.spectral/extensions/` (project-local) or `~/.spectral/agent/extensions/` (user-global).",
301
+ "Auto-research generates extensions into `.spectral/extensions/auto-research/<name>/`.",
302
302
  "",
303
303
  "### Tools (pi.registerTool)",
304
304
  "",
@@ -400,10 +400,10 @@ function buildAutoResearchTask(projectPath, projectName, preRunContext = null) {
400
400
  "",
401
401
  "### Extension File Structure",
402
402
  "",
403
- "Each extension is a directory under `.pi/extensions/auto-research/`:",
403
+ "Each extension is a directory under `.spectral/extensions/auto-research/`:",
404
404
  "",
405
405
  "```",
406
- ".pi/extensions/auto-research/",
406
+ ".spectral/extensions/auto-research/",
407
407
  " <extension-name>/",
408
408
  " index.ts # Entry point — default export activate(pi)",
409
409
  " utils.ts # [optional] Helper functions",
@@ -440,7 +440,7 @@ function buildAutoResearchTask(projectPath, projectName, preRunContext = null) {
440
440
  "",
441
441
  "1. **Context Collection** — Explore the project structure:",
442
442
  " - Read package.json, tsconfig.json, deno.json (if present)",
443
- " - Check existing extensions under .pi/extensions/",
443
+ " - Check existing extensions under .spectral/extensions/",
444
444
  " - Review key source files to understand architecture",
445
445
  " - Check git log for recent changes and patterns",
446
446
  "",
@@ -454,9 +454,9 @@ function buildAutoResearchTask(projectPath, projectName, preRunContext = null) {
454
454
  " - Use `pi.registerTool()` for simple tools with TypeBox validation",
455
455
  " - Use `pi.registerCommand()` for custom slash commands",
456
456
  " - For LLM-powered extensions, use `ctx.modelRegistry` to call models",
457
- " - Create files under `.pi/extensions/auto-research/<name>/`",
457
+ " - Create files under `.spectral/extensions/auto-research/<name>/`",
458
458
  " - Each extension needs an `index.ts` that registers its tools/commands",
459
- " - Read `.pi/agents/auto-research-templates.md` for proven extension templates",
459
+ " - Read `.spectral/agents/auto-research-templates.md` for proven extension templates",
460
460
  "",
461
461
  "4. **Validation** — Verify generated extensions:",
462
462
  " - Ensure all imports resolve to available packages (see reference above)",
@@ -478,7 +478,7 @@ function buildAutoResearchTask(projectPath, projectName, preRunContext = null) {
478
478
  "",
479
479
  "### Important Rules",
480
480
  "",
481
- "- Write each extension as TypeScript files under `.pi/extensions/auto-research/<name>/`",
481
+ "- Write each extension as TypeScript files under `.spectral/extensions/auto-research/<name>/`",
482
482
  "- Every extension directory MUST have an `index.ts` entry point",
483
483
  "- Use proper TypeScript with type annotations and error handling",
484
484
  "- Extensions must handle errors gracefully (never crash the agent)",
@@ -491,7 +491,7 @@ function buildAutoResearchTask(projectPath, projectName, preRunContext = null) {
491
491
  "agent sessions automatically know about the new capabilities.",
492
492
  "",
493
493
  "**How AGENTS.md works:**",
494
- "- Pi loads AGENTS.md at startup from the project root and parent directories",
494
+ "- Spectral loads AGENTS.md at startup from the project root and parent directories",
495
495
  "- All found AGENTS.md files are concatenated and injected into the system prompt",
496
496
  "- This means documented extensions get discovered by the agent automatically",
497
497
  "",
@@ -502,7 +502,7 @@ function buildAutoResearchTask(projectPath, projectName, preRunContext = null) {
502
502
  "## Auto-Generated Extensions",
503
503
  "",
504
504
  "These extensions were generated by auto-research. They are available",
505
- "in every session. Pi loads them automatically from `.pi/extensions/`.",
505
+ "in every session. Spectral loads them automatically from `.spectral/extensions/`.",
506
506
  "",
507
507
  "### `<extension-name>`",
508
508
  "",
@@ -539,7 +539,7 @@ function buildAutoResearchTask(projectPath, projectName, preRunContext = null) {
539
539
  // ---------------------------------------------------------------------------
540
540
  const AR_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes — generous for LLM-based analysis
541
541
  /**
542
- * Execute auto-research for a project through the existing PiBridge.
542
+ * Execute auto-research for a project through the existing AgentBridge.
543
543
  *
544
544
  * Caller (dispatcher) is fire-and-forget — this function is `void` and
545
545
  * all errors are surfaced as `auto_research_error` events on the wire
@@ -547,7 +547,7 @@ const AR_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes — generous for LLM-based a
547
547
  *
548
548
  * This replaces the old subprocess-spawning approach. Instead of launching
549
549
  * a separate pi process (which lacks backend proxy credentials), we send
550
- * the auto-research task through the session's existing PiBridge. The agent
550
+ * the auto-research task through the session's existing AgentBridge. The agent
551
551
  * uses the session's model, backend proxy, and all available tools.
552
552
  */
553
553
  export function handleAutoResearch(input, deps) {
@@ -700,7 +700,7 @@ export function handleAutoResearch(input, deps) {
700
700
  // then timeout fires and calls finalize() again — but finalize() is
701
701
  // gated by watcherFired, so it's a no-op. The only issue is we don't
702
702
  // clearTimeout on watcher fire — but that's fine, the timer is harmless.
703
- // --- Send the prompt through the existing PiBridge (backend proxy) ---
703
+ // --- Send the prompt through the existing AgentBridge (backend proxy) ---
704
704
  manager.prompt(sessionId, taskContent, storedModelId).catch((err) => {
705
705
  if (watcherFired)
706
706
  return; // already handled by watcher