@camerontaylor/paseo-protocol 0.12.0-beta.1.fork.1 → 0.12.0-fork.1

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.
@@ -0,0 +1,315 @@
1
+ /**
2
+ * One entry per Paseo MCP tool: the timeline label, the context shown after it,
3
+ * and how the expanded details lay out the call.
4
+ *
5
+ * Labels are fixed text so truncation never hides them. Actions read verb-first
6
+ * ("Send message"); the context goes in the summary.
7
+ */
8
+ const WORKSPACE_FIELDS = [
9
+ "title",
10
+ "workspaceId",
11
+ "projectId",
12
+ "isolation",
13
+ "path",
14
+ "mode",
15
+ "worktreeSlug",
16
+ "branchName",
17
+ "baseBranch",
18
+ "branch",
19
+ "prNumber",
20
+ "forge",
21
+ ];
22
+ const AGENT_FIELDS = [
23
+ "title",
24
+ "agentId",
25
+ "provider",
26
+ "workspaceId",
27
+ "cwd",
28
+ "sessionMode",
29
+ "modeId",
30
+ "background",
31
+ "notifyOnFinish",
32
+ "internal",
33
+ "settings",
34
+ "labels",
35
+ ];
36
+ const AUTOMATION_FIELDS = [
37
+ "name",
38
+ "id",
39
+ "cron",
40
+ "timezone",
41
+ "provider",
42
+ "cwd",
43
+ "isolation",
44
+ "maxRuns",
45
+ "expiresIn",
46
+ "clearExpires",
47
+ ];
48
+ const BROWSER_FIELDS = [
49
+ "browserId",
50
+ "url",
51
+ "ref",
52
+ "sourceRef",
53
+ "targetRef",
54
+ "value",
55
+ "text",
56
+ "key",
57
+ "button",
58
+ "doubleClick",
59
+ "modifiers",
60
+ "filePaths",
61
+ "fullPage",
62
+ "maxEntries",
63
+ "timeoutMs",
64
+ "deltaX",
65
+ "deltaY",
66
+ "width",
67
+ "height",
68
+ "function",
69
+ ];
70
+ const AUTOMATION_OUTPUT = ["id", "status", "nextRunAt", "expiresAt"];
71
+ function field(...keys) {
72
+ return (input) => keys.map((key) => input[key]).find(isPresent);
73
+ }
74
+ function targetAgent() {
75
+ return (_input, agent) => agent;
76
+ }
77
+ function fieldOrTargetAgent(key) {
78
+ return (input, agent) => (isPresent(input[key]) ? input[key] : agent);
79
+ }
80
+ function workspaceContext() {
81
+ return (input) => [input.title, input.branchName, input.branch, baseName(input.path)].find(isPresent);
82
+ }
83
+ function viewportContext() {
84
+ return (input) => typeof input.width === "number" && typeof input.height === "number"
85
+ ? `${input.width}×${input.height}`
86
+ : undefined;
87
+ }
88
+ function isPresent(value) {
89
+ if (typeof value === "string")
90
+ return value.trim().length > 0;
91
+ if (Array.isArray(value))
92
+ return value.length > 0;
93
+ return typeof value === "number";
94
+ }
95
+ function baseName(value) {
96
+ if (typeof value !== "string")
97
+ return undefined;
98
+ return value.split(/[\\/]/).findLast((segment) => segment.length > 0);
99
+ }
100
+ function browser(label, context) {
101
+ return { label, ...(context ? { context } : {}), detail: { inputOrder: BROWSER_FIELDS } };
102
+ }
103
+ export const PASEO_TOOL_CATALOG = {
104
+ // Agents
105
+ create_agent: {
106
+ label: "Create agent",
107
+ context: field("title", "initialPrompt"),
108
+ detail: {
109
+ promptField: "initialPrompt",
110
+ inputOrder: AGENT_FIELDS,
111
+ outputFields: ["agentId", "status", "currentModeId", "cwd"],
112
+ },
113
+ },
114
+ send_agent_prompt: {
115
+ label: "Send message",
116
+ context: field("prompt"),
117
+ detail: {
118
+ promptField: "prompt",
119
+ inputOrder: AGENT_FIELDS,
120
+ outputFields: ["status", "lastMessage", "permission"],
121
+ },
122
+ },
123
+ get_agent_status: {
124
+ label: "Check agent",
125
+ context: targetAgent(),
126
+ detail: { inputOrder: ["agentId"] },
127
+ },
128
+ get_agent_activity: {
129
+ label: "Read agent activity",
130
+ context: targetAgent(),
131
+ detail: { inputOrder: ["agentId", "limit"] },
132
+ },
133
+ list_agents: {
134
+ label: "List agents",
135
+ detail: { inputOrder: ["cwd", "statuses", "sinceHours", "limit", "includeArchived"] },
136
+ },
137
+ update_agent: {
138
+ label: "Update agent",
139
+ context: fieldOrTargetAgent("name"),
140
+ detail: { inputOrder: AGENT_FIELDS },
141
+ },
142
+ set_agent_mode: {
143
+ label: "Set agent mode",
144
+ context: field("modeId"),
145
+ detail: { inputOrder: ["agentId", "modeId"] },
146
+ },
147
+ cancel_agent: {
148
+ label: "Cancel agent",
149
+ context: targetAgent(),
150
+ detail: { inputOrder: ["agentId"] },
151
+ },
152
+ archive_agent: {
153
+ label: "Archive agent",
154
+ context: targetAgent(),
155
+ detail: { inputOrder: ["agentId"] },
156
+ },
157
+ kill_agent: { label: "Kill agent", context: targetAgent(), detail: { inputOrder: ["agentId"] } },
158
+ list_pending_permissions: { label: "List permissions" },
159
+ respond_to_permission: {
160
+ label: "Respond to permission",
161
+ context: targetAgent(),
162
+ detail: { inputOrder: ["agentId", "requestId", "response"] },
163
+ },
164
+ // Workspaces and scripts
165
+ create_workspace: {
166
+ label: "Create workspace",
167
+ context: workspaceContext(),
168
+ detail: { inputOrder: WORKSPACE_FIELDS, outputFields: ["workspaceId", "projectId"] },
169
+ },
170
+ list_workspaces: { label: "List workspaces" },
171
+ rename_workspace: {
172
+ label: "Rename workspace",
173
+ context: field("title"),
174
+ detail: { inputOrder: ["title", "workspaceId"] },
175
+ },
176
+ archive_workspace: {
177
+ label: "Archive workspace",
178
+ context: field("workspaceId"),
179
+ detail: {
180
+ inputOrder: ["workspaceId"],
181
+ outputFields: ["workspaceId", "archivedAgentIds", "removedDirectory"],
182
+ },
183
+ },
184
+ list_workspace_scripts: { label: "List scripts", detail: { inputOrder: ["workspaceId"] } },
185
+ start_workspace_script: {
186
+ label: "Start script",
187
+ context: field("scriptName"),
188
+ detail: { inputOrder: ["workspaceId", "scriptName"] },
189
+ },
190
+ stop_workspace_script: {
191
+ label: "Stop script",
192
+ context: field("scriptName"),
193
+ detail: { inputOrder: ["workspaceId", "scriptName"] },
194
+ },
195
+ // Terminals
196
+ create_terminal: {
197
+ label: "Create terminal",
198
+ context: field("name", "command"),
199
+ detail: { inputOrder: ["cwd", "workspaceId", "name", "command"] },
200
+ },
201
+ list_terminals: { label: "List terminals", detail: { inputOrder: ["cwd", "all"] } },
202
+ capture_terminal: {
203
+ label: "Read terminal",
204
+ context: field("terminalId"),
205
+ detail: { inputOrder: ["terminalId", "lines"] },
206
+ },
207
+ send_terminal_keys: {
208
+ label: "Send keys",
209
+ context: field("keys"),
210
+ detail: { inputOrder: ["terminalId", "keys", "literal"] },
211
+ },
212
+ kill_terminal: {
213
+ label: "Kill terminal",
214
+ context: field("terminalId"),
215
+ detail: { inputOrder: ["terminalId"] },
216
+ },
217
+ // Schedules and heartbeats
218
+ create_schedule: {
219
+ label: "Create schedule",
220
+ context: field("name", "cron"),
221
+ detail: {
222
+ promptField: "prompt",
223
+ inputOrder: AUTOMATION_FIELDS,
224
+ outputFields: AUTOMATION_OUTPUT,
225
+ },
226
+ },
227
+ create_heartbeat: {
228
+ label: "Create heartbeat",
229
+ context: field("name", "cron"),
230
+ detail: {
231
+ promptField: "prompt",
232
+ inputOrder: AUTOMATION_FIELDS,
233
+ outputFields: AUTOMATION_OUTPUT,
234
+ },
235
+ },
236
+ update_schedule: {
237
+ label: "Update schedule",
238
+ context: field("name", "id"),
239
+ detail: { promptField: "prompt", inputOrder: AUTOMATION_FIELDS },
240
+ },
241
+ list_schedules: { label: "List schedules" },
242
+ inspect_schedule: {
243
+ label: "Inspect schedule",
244
+ context: field("id"),
245
+ detail: { inputOrder: ["id"] },
246
+ },
247
+ schedule_logs: {
248
+ label: "Read schedule logs",
249
+ context: field("id"),
250
+ detail: { inputOrder: ["id"] },
251
+ },
252
+ run_schedule_once: {
253
+ label: "Run schedule",
254
+ context: field("id"),
255
+ detail: { inputOrder: ["id"] },
256
+ },
257
+ pause_schedule: { label: "Pause schedule", context: field("id"), detail: { inputOrder: ["id"] } },
258
+ resume_schedule: {
259
+ label: "Resume schedule",
260
+ context: field("id"),
261
+ detail: { inputOrder: ["id"] },
262
+ },
263
+ delete_schedule: {
264
+ label: "Delete schedule",
265
+ context: field("id"),
266
+ detail: { inputOrder: ["id"] },
267
+ },
268
+ delete_heartbeat: {
269
+ label: "Delete heartbeat",
270
+ context: field("id"),
271
+ detail: { inputOrder: ["id"] },
272
+ },
273
+ // Providers
274
+ list_providers: { label: "List providers" },
275
+ list_models: {
276
+ label: "List models",
277
+ context: field("provider"),
278
+ detail: { inputOrder: ["provider"] },
279
+ },
280
+ list_profiles: { label: "List profiles" },
281
+ inspect_provider: {
282
+ label: "Inspect provider",
283
+ context: field("provider"),
284
+ detail: { inputOrder: ["provider", "cwd", "settings"] },
285
+ },
286
+ // Browser
287
+ browser_list_tabs: browser("List browser tabs"),
288
+ browser_new_tab: browser("Open browser tab", field("url")),
289
+ browser_close_tab: browser("Close browser tab", field("browserId")),
290
+ browser_navigate: browser("Navigate", field("url")),
291
+ browser_back: browser("Go back"),
292
+ browser_forward: browser("Go forward"),
293
+ browser_reload: browser("Reload page"),
294
+ browser_snapshot: browser("Snapshot page"),
295
+ browser_screenshot: browser("Take screenshot"),
296
+ browser_click: browser("Click element", field("ref")),
297
+ browser_hover: browser("Hover element", field("ref")),
298
+ browser_drag: browser("Drag element", field("sourceRef")),
299
+ browser_fill: browser("Fill field", field("ref")),
300
+ browser_select: browser("Select option", field("value", "ref")),
301
+ browser_type: browser("Type text", field("text")),
302
+ browser_keypress: browser("Press key", field("key")),
303
+ browser_upload: browser("Upload files", field("filePaths")),
304
+ browser_wait: browser("Wait for page", field("text", "url")),
305
+ browser_scroll: browser("Scroll page"),
306
+ browser_resize: browser("Resize browser", viewportContext()),
307
+ browser_logs: browser("Read browser logs"),
308
+ browser_evaluate: browser("Run JavaScript", field("function")),
309
+ };
310
+ export function getPaseoToolEntry(leafName) {
311
+ return Object.prototype.hasOwnProperty.call(PASEO_TOOL_CATALOG, leafName)
312
+ ? PASEO_TOOL_CATALOG[leafName]
313
+ : undefined;
314
+ }
315
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1,15 @@
1
+ export interface PaseoToolDetailField {
2
+ label: string;
3
+ value: string;
4
+ }
5
+ export type PaseoToolDetailSection = {
6
+ kind: "prose";
7
+ title: string;
8
+ text: string;
9
+ } | {
10
+ kind: "fields";
11
+ title: string;
12
+ fields: PaseoToolDetailField[];
13
+ };
14
+ export declare function buildPaseoToolDetailSections(toolName: string, input: unknown, output: unknown): PaseoToolDetailSection[] | null;
15
+ //# sourceMappingURL=detail-sections.d.ts.map
@@ -0,0 +1,161 @@
1
+ import { getPaseoToolLeafName } from "../tool-name-normalization.js";
2
+ import { getPaseoToolEntry } from "./catalog.js";
3
+ const FIELD_LABELS = {
4
+ agentId: "Agent",
5
+ archivedAgentIds: "Archived agents",
6
+ baseBranch: "Base branch",
7
+ branchName: "New branch",
8
+ browserId: "Browser tab",
9
+ clearExpires: "Clear expiry",
10
+ currentModeId: "Current mode",
11
+ cwd: "Working directory",
12
+ deltaX: "Horizontal delta",
13
+ deltaY: "Vertical delta",
14
+ doubleClick: "Double click",
15
+ expiresIn: "Expires in",
16
+ expiresAt: "Expires",
17
+ filePaths: "Files",
18
+ fullPage: "Full page",
19
+ initialPrompt: "Prompt",
20
+ id: "ID",
21
+ lastMessage: "Last message",
22
+ maxEntries: "Maximum entries",
23
+ maxRuns: "Maximum runs",
24
+ modeId: "Mode",
25
+ newMode: "New mode",
26
+ nextRunAt: "Next run",
27
+ notifyOnFinish: "Notify on finish",
28
+ internal: "Internal",
29
+ prNumber: "Change request",
30
+ projectId: "Project",
31
+ removedDirectory: "Removed directory",
32
+ requestId: "Request",
33
+ scriptName: "Script",
34
+ sessionMode: "Session mode",
35
+ sinceHours: "Since (hours)",
36
+ sourceRef: "Source",
37
+ targetRef: "Target",
38
+ terminalId: "Terminal",
39
+ thinkingOptionId: "Thinking",
40
+ timeoutMs: "Timeout (ms)",
41
+ updateCount: "Updates",
42
+ workspaceId: "Workspace",
43
+ worktreeSlug: "Worktree",
44
+ };
45
+ function isRecord(value) {
46
+ return typeof value === "object" && value !== null && !Array.isArray(value);
47
+ }
48
+ function humanizeKey(key) {
49
+ const known = FIELD_LABELS[key];
50
+ if (known)
51
+ return known;
52
+ const words = key
53
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
54
+ .replace(/[._-]+/g, " ")
55
+ .split(" ")
56
+ .filter(Boolean);
57
+ const sentence = words.join(" ").toLowerCase();
58
+ return `${sentence[0]?.toUpperCase() ?? ""}${sentence.slice(1)}`;
59
+ }
60
+ function formatValue(value, depth = 0) {
61
+ if (value === null)
62
+ return "None";
63
+ if (value === undefined)
64
+ return "";
65
+ if (typeof value === "boolean")
66
+ return value ? "Yes" : "No";
67
+ if (typeof value === "string" || typeof value === "number" || typeof value === "bigint") {
68
+ return String(value);
69
+ }
70
+ if (Array.isArray(value)) {
71
+ if (value.length === 0)
72
+ return "None";
73
+ return value.map((item) => `• ${indentMultiline(formatValue(item, depth + 1), 2)}`).join("\n");
74
+ }
75
+ if (isRecord(value)) {
76
+ const entries = Object.entries(value).filter(([, child]) => child !== undefined);
77
+ if (entries.length === 0)
78
+ return "None";
79
+ return entries
80
+ .map(([key, child]) => {
81
+ const formatted = formatValue(child, depth + 1);
82
+ return `${humanizeKey(key)}: ${indentMultiline(formatted, 2)}`;
83
+ })
84
+ .join("\n");
85
+ }
86
+ return String(value);
87
+ }
88
+ function indentMultiline(value, spaces) {
89
+ const indentation = " ".repeat(spaces);
90
+ return value.replace(/\n/g, `\n${indentation}`);
91
+ }
92
+ function orderedEntries(value, order = [], omittedKey, includedKeys) {
93
+ const included = includedKeys ? new Set(includedKeys) : null;
94
+ const keys = Object.keys(value).filter((key) => key !== omittedKey && value[key] !== undefined && (!included || included.has(key)));
95
+ const rank = new Map(order.map((key, index) => [key, index]));
96
+ keys.sort((left, right) => {
97
+ const leftRank = rank.get(left) ?? Number.MAX_SAFE_INTEGER;
98
+ const rightRank = rank.get(right) ?? Number.MAX_SAFE_INTEGER;
99
+ return leftRank - rightRank || left.localeCompare(right);
100
+ });
101
+ return keys.map((key) => [key, value[key]]);
102
+ }
103
+ function fieldsFromValue(value, order, omittedKey, includedKeys) {
104
+ if (value === null || value === undefined)
105
+ return [];
106
+ if (!isRecord(value)) {
107
+ const formatted = formatValue(value);
108
+ return formatted ? [{ label: "Value", value: formatted }] : [];
109
+ }
110
+ return orderedEntries(value, order, omittedKey, includedKeys).map(([key, child]) => ({
111
+ label: humanizeKey(key),
112
+ value: formatValue(child),
113
+ }));
114
+ }
115
+ function parseJsonText(value) {
116
+ if (typeof value !== "string")
117
+ return undefined;
118
+ try {
119
+ return JSON.parse(value);
120
+ }
121
+ catch {
122
+ return undefined;
123
+ }
124
+ }
125
+ function unwrapMcpResult(output) {
126
+ if (!isRecord(output))
127
+ return output;
128
+ if (output.structuredContent !== undefined) {
129
+ return output.structuredContent;
130
+ }
131
+ if (Array.isArray(output.content) && output.content.length === 1) {
132
+ const item = output.content[0];
133
+ if (isRecord(item) && item.type === "text") {
134
+ return parseJsonText(item.text) ?? item.text;
135
+ }
136
+ }
137
+ return output;
138
+ }
139
+ export function buildPaseoToolDetailSections(toolName, input, output) {
140
+ const leafName = getPaseoToolLeafName(toolName);
141
+ if (!leafName)
142
+ return null;
143
+ const spec = getPaseoToolEntry(leafName)?.detail ?? {};
144
+ const sections = [];
145
+ if (spec.promptField && isRecord(input)) {
146
+ const prompt = input[spec.promptField];
147
+ if (typeof prompt === "string" && prompt.length > 0) {
148
+ sections.push({ kind: "prose", title: "Prompt", text: prompt });
149
+ }
150
+ }
151
+ const inputFields = fieldsFromValue(input, spec.inputOrder, spec.promptField);
152
+ if (inputFields.length > 0) {
153
+ sections.push({ kind: "fields", title: "Details", fields: inputFields });
154
+ }
155
+ const outputFields = fieldsFromValue(unwrapMcpResult(output), spec.outputFields, undefined, spec.outputFields);
156
+ if (outputFields.length > 0) {
157
+ sections.push({ kind: "fields", title: "Result", fields: outputFields });
158
+ }
159
+ return sections;
160
+ }
161
+ //# sourceMappingURL=detail-sections.js.map
@@ -0,0 +1,22 @@
1
+ import type { ToolCallDetail } from "../agent-types.js";
2
+ import { type PaseoToolName } from "./catalog.js";
3
+ export { buildAgentMessageDisplay, type AgentMessageDisplay } from "./agent-message.js";
4
+ export { buildPaseoToolDetailSections, type PaseoToolDetailField, type PaseoToolDetailSection, } from "./detail-sections.js";
5
+ export { MAX_SUMMARY_CHARS } from "./summary-text.js";
6
+ export type { PaseoToolName };
7
+ export interface PaseoToolDisplayInput {
8
+ toolName: string;
9
+ detail: ToolCallDetail;
10
+ /** Title of the agent named by the call's `agentId`, when the caller knows it. */
11
+ targetAgentTitle?: string;
12
+ }
13
+ export interface PaseoToolDisplay {
14
+ displayName: string;
15
+ summary?: string;
16
+ }
17
+ export declare const PASEO_TOOL_NAMES: readonly PaseoToolName[];
18
+ /** Returns null for tools outside the Paseo namespace. */
19
+ export declare function buildPaseoToolDisplay(input: PaseoToolDisplayInput): PaseoToolDisplay | null;
20
+ /** The agent a Paseo call acts on, so callers can resolve its title. */
21
+ export declare function getPaseoToolTargetAgentId(toolName: string, detail: ToolCallDetail | undefined): string | null;
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,62 @@
1
+ import { getPaseoToolLeafName, isPaseoToolName } from "../tool-name-normalization.js";
2
+ import { getPaseoToolEntry, PASEO_TOOL_CATALOG } from "./catalog.js";
3
+ import { toSummaryText } from "./summary-text.js";
4
+ export { buildAgentMessageDisplay } from "./agent-message.js";
5
+ export { buildPaseoToolDetailSections, } from "./detail-sections.js";
6
+ export { MAX_SUMMARY_CHARS } from "./summary-text.js";
7
+ export const PASEO_TOOL_NAMES = Object.keys(PASEO_TOOL_CATALOG);
8
+ /** Returns null for tools outside the Paseo namespace. */
9
+ export function buildPaseoToolDisplay(input) {
10
+ const leafName = paseoLeafName(input.toolName);
11
+ if (!leafName)
12
+ return null;
13
+ const entry = getPaseoToolEntry(leafName);
14
+ if (!entry)
15
+ return { displayName: humanizeLeafName(leafName) };
16
+ const toolInput = readToolInput(input.detail);
17
+ const targetAgent = input.targetAgentTitle ?? readString(toolInput.agentId);
18
+ const summary = formatContext(entry.context?.(toolInput, targetAgent));
19
+ return { displayName: entry.label, ...(summary ? { summary } : {}) };
20
+ }
21
+ /** The agent a Paseo call acts on, so callers can resolve its title. */
22
+ export function getPaseoToolTargetAgentId(toolName, detail) {
23
+ if (!detail || !paseoLeafName(toolName))
24
+ return null;
25
+ return readString(readToolInput(detail).agentId) ?? null;
26
+ }
27
+ function paseoLeafName(toolName) {
28
+ return isPaseoToolName(toolName) ? getPaseoToolLeafName(toolName) : null;
29
+ }
30
+ function readToolInput(detail) {
31
+ if (detail.type !== "unknown")
32
+ return {};
33
+ const value = detail.input;
34
+ return typeof value === "object" && value !== null && !Array.isArray(value)
35
+ ? value
36
+ : {};
37
+ }
38
+ function readString(value) {
39
+ return typeof value === "string" && value.length > 0 ? value : undefined;
40
+ }
41
+ function formatContext(value) {
42
+ if (typeof value === "string")
43
+ return toSummaryText(value);
44
+ if (typeof value === "number")
45
+ return String(value);
46
+ if (Array.isArray(value)) {
47
+ const [first, ...rest] = value;
48
+ const head = typeof first === "string" ? toSummaryText(first) : undefined;
49
+ if (!head)
50
+ return undefined;
51
+ return rest.length > 0 ? `${head} +${rest.length}` : head;
52
+ }
53
+ return undefined;
54
+ }
55
+ function humanizeLeafName(leafName) {
56
+ const sentence = leafName
57
+ .replace(/[._-]+/g, " ")
58
+ .trim()
59
+ .toLowerCase();
60
+ return `${sentence[0]?.toUpperCase() ?? ""}${sentence.slice(1)}`;
61
+ }
62
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Prompts and results can be one enormous line. Summaries are capped here so no
3
+ * consumer relies on its renderer to clip them.
4
+ */
5
+ export declare const MAX_SUMMARY_CHARS = 120;
6
+ export declare function toSummaryText(value: string): string | undefined;
7
+ //# sourceMappingURL=summary-text.d.ts.map
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Prompts and results can be one enormous line. Summaries are capped here so no
3
+ * consumer relies on its renderer to clip them.
4
+ */
5
+ export const MAX_SUMMARY_CHARS = 120;
6
+ // Bounds the whitespace scan: inputs can be megabytes.
7
+ const SCAN_WINDOW_CHARS = MAX_SUMMARY_CHARS * 4;
8
+ export function toSummaryText(value) {
9
+ const isPartial = value.length > SCAN_WINDOW_CHARS;
10
+ const collapsed = value.slice(0, SCAN_WINDOW_CHARS).replace(/\s+/g, " ").trim();
11
+ if (!collapsed)
12
+ return undefined;
13
+ if (collapsed.length <= MAX_SUMMARY_CHARS && !isPartial)
14
+ return collapsed;
15
+ const kept = dropTrailingHighSurrogate(collapsed.slice(0, MAX_SUMMARY_CHARS - 1)).trimEnd();
16
+ return `${kept}…`;
17
+ }
18
+ function dropTrailingHighSurrogate(text) {
19
+ const last = text.charCodeAt(text.length - 1);
20
+ return last >= 0xd800 && last <= 0xdbff ? text.slice(0, -1) : text;
21
+ }
22
+ //# sourceMappingURL=summary-text.js.map
@@ -1,4 +1,5 @@
1
1
  export const BUILTIN_PROVIDER_ICON_NAMES = [
2
+ "antigravity",
2
3
  "claude",
3
4
  "codex",
4
5
  "copilot",
@@ -21,6 +22,7 @@ export const ACP_PROVIDER_ICON_NAMES = [
21
22
  "crow-cli",
22
23
  "cursor",
23
24
  "deepagents",
25
+ "devin",
24
26
  "dimcode",
25
27
  "dirac",
26
28
  "factory-droid",
@@ -30,6 +32,7 @@ export const ACP_PROVIDER_ICON_NAMES = [
30
32
  "glm-acp-agent",
31
33
  "goose",
32
34
  "grok",
35
+ "hermes",
33
36
  "hoplite",
34
37
  "junie",
35
38
  "kilo",
@@ -1,6 +1,8 @@
1
1
  import type { ToolCallTimelineItem } from "./agent-types.js";
2
2
  export type ToolCallDisplayInput = Pick<ToolCallTimelineItem, "name" | "status" | "error" | "metadata" | "detail" | "agentMessage"> & {
3
3
  cwd?: string;
4
+ /** Title of the agent a Paseo call targets, when the caller knows it. */
5
+ targetAgentTitle?: string;
4
6
  };
5
7
  export interface ToolCallDisplayModel {
6
8
  displayName: string;