@getpaseo/server 0.1.100 → 0.1.101

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 (67) hide show
  1. package/dist/server/executable-resolution/windows.js +3 -0
  2. package/dist/server/server/agent/agent-manager.d.ts +10 -0
  3. package/dist/server/server/agent/agent-manager.js +65 -27
  4. package/dist/server/server/agent/agent-sdk-types.d.ts +8 -0
  5. package/dist/server/server/agent/mcp-server.d.ts +2 -45
  6. package/dist/server/server/agent/mcp-server.js +45 -1985
  7. package/dist/server/server/agent/prompt-attachments.js +6 -2
  8. package/dist/server/server/agent/provider-snapshot-manager.d.ts +4 -0
  9. package/dist/server/server/agent/provider-snapshot-manager.js +58 -13
  10. package/dist/server/server/agent/providers/acp-agent.d.ts +20 -1
  11. package/dist/server/server/agent/providers/acp-agent.js +170 -26
  12. package/dist/server/server/agent/providers/claude/agent.js +60 -10
  13. package/dist/server/server/agent/providers/codex-app-server-agent.js +6 -57
  14. package/dist/server/server/agent/providers/diagnostic-utils.d.ts +1 -0
  15. package/dist/server/server/agent/providers/diagnostic-utils.js +1 -1
  16. package/dist/server/server/agent/providers/generic-acp-agent.d.ts +3 -0
  17. package/dist/server/server/agent/providers/generic-acp-agent.js +41 -23
  18. package/dist/server/server/agent/providers/mock-load-test-agent.js +4 -2
  19. package/dist/server/server/agent/providers/pi/agent.d.ts +2 -1
  20. package/dist/server/server/agent/providers/pi/agent.js +3 -0
  21. package/dist/server/server/agent/providers/provider-image-output.d.ts +5 -0
  22. package/dist/server/server/agent/providers/provider-image-output.js +55 -0
  23. package/dist/server/server/agent/tools/paseo-tools.d.ts +48 -0
  24. package/dist/server/server/agent/tools/paseo-tools.js +2121 -0
  25. package/dist/server/server/agent/tools/types.d.ts +36 -0
  26. package/dist/server/server/agent/tools/types.js +2 -0
  27. package/dist/server/server/bootstrap.js +71 -62
  28. package/dist/server/server/persisted-config.d.ts +5 -0
  29. package/dist/server/server/persisted-config.js +10 -2
  30. package/dist/server/server/session/agent-updates/agent-updates-service.d.ts +59 -0
  31. package/dist/server/server/session/agent-updates/agent-updates-service.js +220 -0
  32. package/dist/server/server/session/checkout/checkout-session.d.ts +13 -15
  33. package/dist/server/server/session/checkout/checkout-session.js +18 -16
  34. package/dist/server/server/session/checkout/git-metadata-generator.d.ts +53 -0
  35. package/dist/server/server/session/checkout/git-metadata-generator.js +159 -0
  36. package/dist/server/server/session/daemon/daemon-session.d.ts +14 -0
  37. package/dist/server/server/session/daemon/daemon-session.js +38 -0
  38. package/dist/server/server/session/daemon/diagnostics.d.ts +41 -0
  39. package/dist/server/server/session/daemon/diagnostics.js +421 -0
  40. package/dist/server/server/session/git-mutation/git-mutation-service.d.ts +34 -0
  41. package/dist/server/server/session/git-mutation/git-mutation-service.js +71 -0
  42. package/dist/server/server/session/workspace-git-observer/workspace-git-observer-service.d.ts +36 -0
  43. package/dist/server/server/session/workspace-git-observer/workspace-git-observer-service.js +134 -0
  44. package/dist/server/server/session/workspace-provisioning/workspace-provisioning-service.d.ts +34 -0
  45. package/dist/server/server/session/workspace-provisioning/workspace-provisioning-service.js +190 -0
  46. package/dist/server/server/session/workspace-scripts/workspace-scripts-service.d.ts +41 -0
  47. package/dist/server/server/session/workspace-scripts/workspace-scripts-service.js +100 -0
  48. package/dist/server/server/session.d.ts +7 -51
  49. package/dist/server/server/session.js +113 -938
  50. package/dist/server/server/speech/providers/openai/config.d.ts +1 -2
  51. package/dist/server/server/speech/providers/openai/config.js +13 -9
  52. package/dist/server/server/speech/providers/openai/runtime.js +2 -16
  53. package/dist/server/server/speech/providers/openai/stt.d.ts +1 -0
  54. package/dist/server/server/speech/providers/openai/stt.js +4 -2
  55. package/dist/server/server/speech/providers/openai/tts.d.ts +1 -0
  56. package/dist/server/server/speech/providers/openai/tts.js +1 -0
  57. package/dist/server/server/websocket/runtime-metrics.d.ts +20 -0
  58. package/dist/server/server/websocket-server.d.ts +1 -2
  59. package/dist/server/server/websocket-server.js +26 -21
  60. package/dist/server/server/worktree-bootstrap.d.ts +1 -1
  61. package/dist/server/server/worktree-branch-name-generator.js +3 -1
  62. package/dist/server/utils/checkout-git.js +51 -26
  63. package/dist/src/executable-resolution/windows.js +3 -0
  64. package/dist/src/server/persisted-config.js +10 -2
  65. package/package.json +5 -5
  66. package/dist/server/server/speech/providers/openai/realtime-transcription-session.d.ts +0 -42
  67. package/dist/server/server/speech/providers/openai/realtime-transcription-session.js +0 -168
@@ -1,7 +1,6 @@
1
1
  import { getAgentStreamEventTurnId, } from "../agent-sdk-types.js";
2
2
  import { importSessionFromPersistence } from "../provider-session-import.js";
3
3
  import { randomUUID } from "node:crypto";
4
- import * as fsSync from "node:fs";
5
4
  import fs from "node:fs/promises";
6
5
  import os from "node:os";
7
6
  import path from "node:path";
@@ -18,7 +17,7 @@ import { extractCodexTerminalSessionId, nonEmptyString } from "./tool-call-mappe
18
17
  import { buildCodexFeatures, codexModelSupportsFastMode } from "./codex-feature-definitions.js";
19
18
  import { CodexAppServerClient, parseCodexThreadForkResponse, parseCodexThreadRollbackResponse, } from "./codex/app-server-transport.js";
20
19
  import { revertCodexConversation } from "./codex/rewind.js";
21
- import { renderProviderImageOutputAsAssistantMarkdown, } from "./provider-image-output.js";
20
+ import { materializeProviderImage, renderProviderImageOutputAsAssistantMarkdown, } from "./provider-image-output.js";
22
21
  import { normalizeProviderReplayTimestamp } from "../provider-history-timestamps.js";
23
22
  import { formatProviderDiagnostic, formatProviderDiagnosticError, buildBinaryDiagnosticRows, buildCommandResolutionDiagnosticRows, resolveBinaryVersion, } from "./diagnostic-utils.js";
24
23
  import { runProviderTurn } from "./provider-runner.js";
@@ -38,7 +37,6 @@ function isCodexAlreadyUnarchivedError(error, threadId) {
38
37
  const TURN_START_TIMEOUT_MS = 90 * 1000;
39
38
  const INTERRUPT_TIMEOUT_MS = 2000;
40
39
  const CODEX_PROVIDER = "codex";
41
- const CODEX_IMAGE_ATTACHMENT_DIR = "paseo-attachments";
42
40
  // Codex treats most app-server client names as the model-request originator.
43
41
  // This reserved Codex name is non-originating, so requests keep Codex's default
44
42
  // CLI identity instead of showing up as Paseo in provider usage logs.
@@ -1220,21 +1218,6 @@ function codexImageOutputFromResult(result) {
1220
1218
  mimeType: firstStringField(resultRecord, ["mimeType", "mime_type"]),
1221
1219
  };
1222
1220
  }
1223
- function writeImageAttachmentSync(mimeType, data) {
1224
- const attachmentsDir = path.join(os.tmpdir(), CODEX_IMAGE_ATTACHMENT_DIR);
1225
- fsSync.mkdirSync(attachmentsDir, { recursive: true });
1226
- const normalized = normalizeImageData(mimeType, data);
1227
- const extension = getImageExtension(normalized.mimeType);
1228
- const filename = `${randomUUID()}.${extension}`;
1229
- const filePath = path.join(attachmentsDir, filename);
1230
- fsSync.writeFileSync(filePath, Buffer.from(normalized.data, "base64"));
1231
- return filePath;
1232
- }
1233
- function materializeCodexImageOutput(image) {
1234
- return {
1235
- path: writeImageAttachmentSync(image.mimeType ?? "image/png", image.data),
1236
- };
1237
- }
1238
1221
  function mapCodexThreadImageItem(normalizedType, normalizedItem) {
1239
1222
  if (normalizedType === "imageView") {
1240
1223
  return renderProviderImageOutputAsAssistantMarkdown({
@@ -1248,7 +1231,7 @@ function mapCodexThreadImageItem(normalizedType, normalizedItem) {
1248
1231
  url: result?.url ?? null,
1249
1232
  data: result?.data ?? null,
1250
1233
  mimeType: result?.mimeType ?? null,
1251
- }, { materialize: materializeCodexImageOutput });
1234
+ }, { materialize: materializeProviderImage });
1252
1235
  }
1253
1236
  export function threadItemToTimeline(item, options) {
1254
1237
  const itemRecord = toObjectRecord(item);
@@ -1357,33 +1340,6 @@ function toSandboxPolicy(type, networkAccess) {
1357
1340
  return { type: "workspaceWrite", networkAccess: networkAccess ?? false };
1358
1341
  }
1359
1342
  }
1360
- function getImageExtension(mimeType) {
1361
- switch (mimeType) {
1362
- case "image/jpeg":
1363
- return "jpg";
1364
- case "image/png":
1365
- return "png";
1366
- case "image/webp":
1367
- return "webp";
1368
- case "image/gif":
1369
- return "gif";
1370
- case "image/bmp":
1371
- return "bmp";
1372
- case "image/tiff":
1373
- return "tiff";
1374
- default:
1375
- return "bin";
1376
- }
1377
- }
1378
- function normalizeImageData(mimeType, data) {
1379
- if (data.startsWith("data:")) {
1380
- const match = data.match(/^data:([^;]+);base64,(.*)$/);
1381
- if (match) {
1382
- return { mimeType: match[1], data: match[2] };
1383
- }
1384
- }
1385
- return { mimeType, data };
1386
- }
1387
1343
  const ThreadStartedNotificationSchema = z
1388
1344
  .object({
1389
1345
  thread: z.object({ id: z.string() }).passthrough(),
@@ -2008,16 +1964,6 @@ const CodexNotificationSchema = z.union([
2008
1964
  .object({ method: z.string(), params: z.unknown() })
2009
1965
  .transform(({ method, params }) => ({ kind: "unknown_method", method, params })),
2010
1966
  ]);
2011
- async function writeImageAttachment(mimeType, data) {
2012
- const attachmentsDir = path.join(os.tmpdir(), CODEX_IMAGE_ATTACHMENT_DIR);
2013
- await fs.mkdir(attachmentsDir, { recursive: true });
2014
- const normalized = normalizeImageData(mimeType, data);
2015
- const extension = getImageExtension(normalized.mimeType);
2016
- const filename = `${randomUUID()}.${extension}`;
2017
- const filePath = path.join(attachmentsDir, filename);
2018
- await fs.writeFile(filePath, Buffer.from(normalized.data, "base64"));
2019
- return filePath;
2020
- }
2021
1967
  async function readCodexConfiguredDefaults(client, logger) {
2022
1968
  let savedConfigDefaults = {};
2023
1969
  try {
@@ -2071,7 +2017,10 @@ export async function codexAppServerTurnInputFromPrompt(prompt, logger) {
2071
2017
  }
2072
2018
  if (block.type === "image") {
2073
2019
  try {
2074
- const filePath = await writeImageAttachment(block.mimeType, block.data);
2020
+ const filePath = materializeProviderImage({
2021
+ data: block.data,
2022
+ mimeType: block.mimeType,
2023
+ }).path;
2075
2024
  output.push({ type: "localImage", path: filePath });
2076
2025
  }
2077
2026
  catch (error) {
@@ -10,6 +10,7 @@ export declare function formatDiagnosticStatus(available: boolean, error?: {
10
10
  source: string;
11
11
  cause: unknown;
12
12
  }): string;
13
+ export declare function truncateForDiagnostic(value: string): string;
13
14
  export declare function toDiagnosticErrorMessage(error: unknown): string;
14
15
  export declare function resolveBinaryVersion(binaryPath: string): Promise<string>;
15
16
  export interface BinaryDiagnosticVersionCommand {
@@ -24,7 +24,7 @@ export function formatDiagnosticStatus(available, error) {
24
24
  return formatAvailabilityStatus(available);
25
25
  }
26
26
  const DIAGNOSTIC_OUTPUT_CAP = 4096;
27
- function truncateForDiagnostic(value) {
27
+ export function truncateForDiagnostic(value) {
28
28
  const trimmed = value.trim();
29
29
  if (trimmed.length <= DIAGNOSTIC_OUTPUT_CAP) {
30
30
  return trimmed;
@@ -13,11 +13,13 @@ interface GenericACPAgentClientOptions {
13
13
  providerParams?: unknown;
14
14
  waitForInitialCommands?: boolean;
15
15
  initialCommandsWaitTimeoutMs?: number;
16
+ diagnosticPhaseTimeoutMs?: number;
16
17
  }
17
18
  export declare class GenericACPAgentClient extends ACPAgentClient {
18
19
  private readonly command;
19
20
  private readonly providerId?;
20
21
  private readonly label?;
22
+ private readonly diagnosticPhaseTimeoutMs?;
21
23
  constructor(options: GenericACPAgentClientOptions);
22
24
  protected resolveLaunchCommand(): Promise<{
23
25
  command: string;
@@ -28,6 +30,7 @@ export declare class GenericACPAgentClient extends ACPAgentClient {
28
30
  diagnostic: string;
29
31
  }>;
30
32
  private resolveConfiguredLaunch;
33
+ private getACPProbeRowsForDiagnostic;
31
34
  }
32
35
  export interface CommandInvocation {
33
36
  command: string;
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { checkProviderLaunchAvailable, resolveProviderLaunch } from "../provider-launch-config.js";
3
3
  import { ACPAgentClient, DEFAULT_ACP_CAPABILITIES } from "./acp-agent.js";
4
- import { formatProviderDiagnostic, formatProviderDiagnosticError, buildBinaryDiagnosticRows, } from "./diagnostic-utils.js";
4
+ import { buildBinaryDiagnosticRows, formatProviderDiagnostic, toDiagnosticErrorMessage, } from "./diagnostic-utils.js";
5
5
  export const GenericACPProviderParamsSchema = z
6
6
  .object({
7
7
  supportsMcpServers: z.boolean().optional(),
@@ -23,6 +23,7 @@ export class GenericACPAgentClient extends ACPAgentClient {
23
23
  this.command = options.command;
24
24
  this.providerId = options.providerId;
25
25
  this.label = options.label;
26
+ this.diagnosticPhaseTimeoutMs = options.diagnosticPhaseTimeoutMs;
26
27
  }
27
28
  async resolveLaunchCommand() {
28
29
  return {
@@ -37,34 +38,36 @@ export class GenericACPAgentClient extends ACPAgentClient {
37
38
  }
38
39
  async getDiagnostic() {
39
40
  const providerName = formatProviderName(this.label, this.providerId);
41
+ const entries = [
42
+ { label: "Provider ID", value: this.providerId ?? "unknown" },
43
+ { label: "Configured command", value: this.command.join(" ") },
44
+ ];
45
+ const versionProbe = buildVersionProbeCommand(this.command);
40
46
  try {
41
47
  const launch = await this.resolveConfiguredLaunch();
42
48
  const availability = await checkProviderLaunchAvailable(launch);
43
- const versionProbe = buildVersionProbeCommand(this.command);
44
- return {
45
- diagnostic: formatProviderDiagnostic(providerName, [
46
- { label: "Provider ID", value: this.providerId ?? "unknown" },
47
- { label: "Configured command", value: this.command.join(" ") },
48
- ...(await buildBinaryDiagnosticRows(launch, availability, {
49
- binaryLabel: "Launcher binary",
50
- versionCommand: {
51
- command: versionProbe.command,
52
- args: versionProbe.args,
53
- env: this.runtimeSettings?.env,
54
- },
55
- })),
56
- {
57
- label: "Version command",
58
- value: formatCommand(versionProbe.command, versionProbe.args),
59
- },
60
- ]),
61
- };
49
+ entries.push(...(await buildBinaryDiagnosticRows(launch, availability, {
50
+ binaryLabel: "Launcher binary",
51
+ versionCommand: {
52
+ command: versionProbe.command,
53
+ args: versionProbe.args,
54
+ env: this.runtimeSettings?.env,
55
+ },
56
+ })));
62
57
  }
63
58
  catch (error) {
64
- return {
65
- diagnostic: formatProviderDiagnosticError(providerName, error),
66
- };
59
+ entries.push({
60
+ label: "Launcher binary",
61
+ value: `error: ${toDiagnosticErrorMessage(error)}`,
62
+ });
67
63
  }
64
+ entries.push({
65
+ label: "Version command",
66
+ value: formatCommand(versionProbe.command, versionProbe.args),
67
+ }, ...(await this.getACPProbeRowsForDiagnostic()));
68
+ return {
69
+ diagnostic: formatProviderDiagnostic(providerName, entries),
70
+ };
68
71
  }
69
72
  async resolveConfiguredLaunch() {
70
73
  return resolveProviderLaunch({
@@ -72,6 +75,21 @@ export class GenericACPAgentClient extends ACPAgentClient {
72
75
  defaultBinary: this.command[0],
73
76
  });
74
77
  }
78
+ async getACPProbeRowsForDiagnostic() {
79
+ try {
80
+ return await this.buildACPProbeDiagnosticRows({
81
+ phaseTimeoutMs: this.diagnosticPhaseTimeoutMs,
82
+ });
83
+ }
84
+ catch (error) {
85
+ return [
86
+ {
87
+ label: "ACP probe",
88
+ value: `error: ${toDiagnosticErrorMessage(error)}`,
89
+ },
90
+ ];
91
+ }
92
+ }
75
93
  }
76
94
  function buildGenericACPCapabilities(options) {
77
95
  const params = parseGenericACPProviderParams(options.providerParams);
@@ -167,7 +167,7 @@ function parseAgentStreamStressPrompt(prompt) {
167
167
  }
168
168
  function parseStructuredBranchNamePrompt(prompt) {
169
169
  const text = promptToText(prompt);
170
- const hasBranchNamePrompt = text.includes("Generate a git branch name for a coding agent") &&
170
+ const hasBranchNamePrompt = text.includes("Generate a title and a git branch name for a coding agent") &&
171
171
  (text.includes("Return JSON only with fields 'title' and 'branch'.") ||
172
172
  text.includes('"title"') ||
173
173
  text.includes('"branch"'));
@@ -177,7 +177,9 @@ function parseStructuredBranchNamePrompt(prompt) {
177
177
  text.includes('"branch"'))) {
178
178
  return null;
179
179
  }
180
- const seed = text.split("User context:\n").at(-1)?.trim() ?? "";
180
+ const seed = text.match(/<user-prompt>\n([\s\S]*?)\n<\/user-prompt>/)?.[1]?.trim() ??
181
+ text.match(/<attachments>\n([\s\S]*?)\n<\/attachments>/)?.[1]?.trim() ??
182
+ "";
181
183
  const firstLine = seed
182
184
  .split("\n")
183
185
  .find((line) => line.trim().length > 0)
@@ -1,6 +1,6 @@
1
1
  import type { Logger } from "pino";
2
2
  import { z } from "zod";
3
- import { type AgentCapabilityFlags, type AgentClient, type AgentLaunchContext, type AgentMode, type AgentModelDefinition, type AgentPermissionRequest, type AgentPermissionResponse, type AgentPersistenceHandle, type AgentPromptInput, type AgentRunOptions, type AgentRunResult, type AgentRuntimeInfo, type AgentSession, type AgentSessionConfig, type AgentSlashCommand, type AgentStreamEvent, type FetchCatalogOptions, type ImportableProviderSession, type ImportProviderSessionContext, type ImportProviderSessionInput, type ListImportableSessionsOptions, type ProviderCatalog } from "../../agent-sdk-types.js";
3
+ import { type AgentCapabilityFlags, type AgentClient, type AgentFeature, type AgentLaunchContext, type AgentMode, type AgentModelDefinition, type AgentPermissionRequest, type AgentPermissionResponse, type AgentPersistenceHandle, type AgentPromptInput, type AgentRunOptions, type AgentRunResult, type AgentRuntimeInfo, type AgentSession, type AgentSessionConfig, type AgentSlashCommand, type AgentStreamEvent, type FetchCatalogOptions, type ImportableProviderSession, type ImportProviderSessionContext, type ImportProviderSessionInput, type ListImportableSessionsOptions, type ProviderCatalog } from "../../agent-sdk-types.js";
4
4
  import { type ProviderRuntimeSettings } from "../../provider-launch-config.js";
5
5
  import type { PiRuntime, PiRuntimeSession } from "./runtime.js";
6
6
  import type { PiCommandsRpcType, PiSessionState } from "./rpc-types.js";
@@ -114,6 +114,7 @@ export declare class PiRpcAgentClient implements AgentClient {
114
114
  createSession(config: AgentSessionConfig, launchContext?: AgentLaunchContext): Promise<AgentSession>;
115
115
  resumeSession(handle: AgentPersistenceHandle, overrides?: Partial<AgentSessionConfig>, _launchContext?: AgentLaunchContext): Promise<AgentSession>;
116
116
  fetchCatalog(options: FetchCatalogOptions): Promise<ProviderCatalog>;
117
+ listFeatures(_config: AgentSessionConfig): Promise<AgentFeature[]>;
117
118
  listImportableSessions(options?: ListImportableSessionsOptions): Promise<ImportableProviderSession[]>;
118
119
  importSession(input: ImportProviderSessionInput, context: ImportProviderSessionContext): Promise<import("../../agent-sdk-types.js").ImportedProviderSession>;
119
120
  isAvailable(): Promise<boolean>;
@@ -1550,6 +1550,9 @@ export class PiRpcAgentClient {
1550
1550
  await runtimeSession.close();
1551
1551
  }
1552
1552
  }
1553
+ async listFeatures(_config) {
1554
+ return [];
1555
+ }
1553
1556
  async listImportableSessions(options) {
1554
1557
  return await listPiImportableSessions({
1555
1558
  ...options,
@@ -9,6 +9,11 @@ export interface ProviderImageOutput {
9
9
  export interface MaterializedProviderImage {
10
10
  path: string;
11
11
  }
12
+ export declare function materializeProviderImage(image: {
13
+ data: string;
14
+ mimeType: string | null;
15
+ }): MaterializedProviderImage;
16
+ export declare function isProviderImageMarkdown(text: string): boolean;
12
17
  interface RenderProviderImageOutputOptions {
13
18
  materialize?: (image: {
14
19
  data: string;
@@ -1,3 +1,58 @@
1
+ import { createHash } from "node:crypto";
2
+ import * as fsSync from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ const PROVIDER_IMAGE_ATTACHMENT_DIR = "paseo-attachments";
6
+ function getImageExtension(mimeType) {
7
+ switch (mimeType) {
8
+ case "image/jpeg":
9
+ return "jpg";
10
+ case "image/png":
11
+ return "png";
12
+ case "image/webp":
13
+ return "webp";
14
+ case "image/gif":
15
+ return "gif";
16
+ case "image/bmp":
17
+ return "bmp";
18
+ case "image/tiff":
19
+ return "tiff";
20
+ default:
21
+ return "bin";
22
+ }
23
+ }
24
+ function normalizeImageData(mimeType, data) {
25
+ if (data.startsWith("data:")) {
26
+ const match = data.match(/^data:([^;]+);base64,(.*)$/);
27
+ if (match) {
28
+ return { mimeType: match[1], data: match[2] };
29
+ }
30
+ }
31
+ return { mimeType, data };
32
+ }
33
+ // Filenames are a content hash of the bytes so re-materializing the same image
34
+ // is idempotent: history replay reuses the existing temp file instead of leaking
35
+ // a fresh one on every load.
36
+ export function materializeProviderImage(image) {
37
+ const attachmentsDir = path.join(os.tmpdir(), PROVIDER_IMAGE_ATTACHMENT_DIR);
38
+ fsSync.mkdirSync(attachmentsDir, { recursive: true });
39
+ const normalized = normalizeImageData(image.mimeType ?? "image/png", image.data);
40
+ const bytes = Buffer.from(normalized.data, "base64");
41
+ const extension = getImageExtension(normalized.mimeType);
42
+ const hash = createHash("sha256").update(bytes).digest("hex");
43
+ const filePath = path.join(attachmentsDir, `${hash}.${extension}`);
44
+ fsSync.writeFileSync(filePath, bytes);
45
+ return { path: filePath };
46
+ }
47
+ // Recognizes the markdown renderProviderImageOutputAsAssistantMarkdown emits for a materialized
48
+ // provider image: its source is a content-hashed file in the attachments dir. Matching the full
49
+ // <hash>.<ext> shape (not just a leading "![") keeps user-authored text from being mistaken for a
50
+ // provider image when it reaches the history-replay filter. The separator class allows one-or-more
51
+ // because on Windows the path uses "\\" and escapeMarkdownImageSource doubles each backslash.
52
+ const PROVIDER_IMAGE_MARKDOWN = new RegExp(`^!\\[[^\\]]*\\]\\([^)]*${PROVIDER_IMAGE_ATTACHMENT_DIR}[/\\\\]+[0-9a-f]{64}\\.[a-z0-9]+\\)`);
53
+ export function isProviderImageMarkdown(text) {
54
+ return PROVIDER_IMAGE_MARKDOWN.test(text);
55
+ }
1
56
  function nonEmptyString(value) {
2
57
  const trimmed = value?.trim();
3
58
  return trimmed ? trimmed : null;
@@ -0,0 +1,48 @@
1
+ import type { Logger } from "pino";
2
+ import type { AgentManager } from "../agent-manager.js";
3
+ import type { AgentStorage } from "../agent-storage.js";
4
+ import { type ArchiveDependencies } from "../../workspace-archive-service.js";
5
+ import type { VoiceCallerContext, VoiceSpeakHandler } from "../../voice-types.js";
6
+ import type { TerminalManager } from "../../../terminal/terminal-manager.js";
7
+ import type { CreatePaseoWorktreeWorkflowFn } from "../../worktree-session.js";
8
+ import type { ScheduleService } from "../../schedule/service.js";
9
+ import { type ProviderSnapshotManager } from "../provider-snapshot-manager.js";
10
+ import type { GitHubService } from "../../../services/github-service.js";
11
+ import type { WorkspaceGitService } from "../../workspace-git-service.js";
12
+ import type { PaseoToolCatalog } from "./types.js";
13
+ export interface PaseoToolHostDependencies {
14
+ agentManager: AgentManager;
15
+ agentStorage: AgentStorage;
16
+ terminalManager?: TerminalManager | null;
17
+ getDaemonTcpPort?: () => number | null;
18
+ scheduleService?: ScheduleService | null;
19
+ providerSnapshotManager: ProviderSnapshotManager;
20
+ github?: GitHubService;
21
+ workspaceGitService?: Pick<WorkspaceGitService, "getSnapshot" | "listWorktrees" | "resolveRepoRoot">;
22
+ findWorkspaceIdForCwd?: ArchiveDependencies["findWorkspaceIdForCwd"];
23
+ listActiveWorkspaces?: ArchiveDependencies["listActiveWorkspaces"];
24
+ archiveWorkspaceRecord?: ArchiveDependencies["archiveWorkspaceRecord"];
25
+ emitWorkspaceUpdatesForWorkspaceIds?: ArchiveDependencies["emitWorkspaceUpdatesForWorkspaceIds"];
26
+ markWorkspaceArchiving?: ArchiveDependencies["markWorkspaceArchiving"];
27
+ clearWorkspaceArchiving?: ArchiveDependencies["clearWorkspaceArchiving"];
28
+ createPaseoWorktree?: CreatePaseoWorktreeWorkflowFn;
29
+ ensureWorkspaceForCreate?: (cwd: string) => Promise<string>;
30
+ paseoHome?: string;
31
+ worktreesRoot?: string;
32
+ /**
33
+ * ID of the agent that is using this tool catalog.
34
+ * Used for cwd/mode inheritance when agents spawn child agents.
35
+ */
36
+ callerAgentId?: string;
37
+ /**
38
+ * Optional resolver for session-bound speak handlers.
39
+ * Used by hidden voice agents to narrate through daemon-managed TTS.
40
+ */
41
+ resolveSpeakHandler?: (callerAgentId: string) => VoiceSpeakHandler | null;
42
+ resolveCallerContext?: (callerAgentId: string) => VoiceCallerContext | null;
43
+ enableVoiceTools?: boolean;
44
+ voiceOnly?: boolean;
45
+ logger: Logger;
46
+ }
47
+ export declare function createPaseoToolCatalog(options: PaseoToolHostDependencies): PaseoToolCatalog;
48
+ //# sourceMappingURL=paseo-tools.d.ts.map