@fungi.computer/parakeet 0.0.0-stage → 0.1.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 (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +186 -2
  3. package/dist/account-commands.d.ts +102 -0
  4. package/dist/account-commands.js +239 -0
  5. package/dist/agent-head.d.ts +4 -0
  6. package/dist/agent-head.js +2 -0
  7. package/dist/client-contributions.d.ts +20 -0
  8. package/dist/client-contributions.js +41 -0
  9. package/dist/commands/advertised.d.ts +23 -0
  10. package/dist/commands/advertised.js +72 -0
  11. package/dist/commands/builtins.d.ts +14 -0
  12. package/dist/commands/builtins.js +91 -0
  13. package/dist/commands/hints.d.ts +36 -0
  14. package/dist/commands/hints.js +64 -0
  15. package/dist/commands/interactions.d.ts +55 -0
  16. package/dist/commands/interactions.js +101 -0
  17. package/dist/commands/run.d.ts +12 -0
  18. package/dist/commands/run.js +64 -0
  19. package/dist/commands/slash.d.ts +126 -0
  20. package/dist/commands/slash.js +190 -0
  21. package/dist/commands/use-slash-commands.d.ts +57 -0
  22. package/dist/commands/use-slash-commands.js +207 -0
  23. package/dist/components/AgentHead.d.ts +22 -0
  24. package/dist/components/AgentHead.js +20 -0
  25. package/dist/components/ChatComposer.d.ts +43 -0
  26. package/dist/components/ChatComposer.js +209 -0
  27. package/dist/components/ChatMessage.d.ts +8 -0
  28. package/dist/components/ChatMessage.js +16 -0
  29. package/dist/components/ClientRows.d.ts +11 -0
  30. package/dist/components/ClientRows.js +46 -0
  31. package/dist/components/CodeBlock.d.ts +11 -0
  32. package/dist/components/CodeBlock.js +38 -0
  33. package/dist/components/InteractionPanel.d.ts +15 -0
  34. package/dist/components/InteractionPanel.js +42 -0
  35. package/dist/components/MarkdownRenderer.d.ts +6 -0
  36. package/dist/components/MarkdownRenderer.js +48 -0
  37. package/dist/components/RunFailureNotice.d.ts +7 -0
  38. package/dist/components/RunFailureNotice.js +13 -0
  39. package/dist/components/ShiitakeAvatar.d.ts +21 -0
  40. package/dist/components/ShiitakeAvatar.js +22 -0
  41. package/dist/components/SlashMenu.d.ts +8 -0
  42. package/dist/components/SlashMenu.js +50 -0
  43. package/dist/components/ThinkingBlock.d.ts +3 -0
  44. package/dist/components/ThinkingBlock.js +18 -0
  45. package/dist/components/TimelineItem.d.ts +15 -0
  46. package/dist/components/TimelineItem.js +35 -0
  47. package/dist/components/ToolGroup.d.ts +12 -0
  48. package/dist/components/ToolGroup.js +90 -0
  49. package/dist/components/WaitingNotice.d.ts +7 -0
  50. package/dist/components/WaitingNotice.js +8 -0
  51. package/dist/components/avatar-presentation.d.ts +16 -0
  52. package/dist/components/avatar-presentation.js +9 -0
  53. package/dist/components/sprite-service.d.ts +2 -0
  54. package/dist/components/sprite-service.js +9 -0
  55. package/dist/conversation.d.ts +165 -0
  56. package/dist/conversation.js +762 -0
  57. package/dist/core/assistant-visibility.d.ts +3 -0
  58. package/dist/core/assistant-visibility.js +4 -0
  59. package/dist/core/author-labels.d.ts +16 -0
  60. package/dist/core/author-labels.js +16 -0
  61. package/dist/core/build-timeline.d.ts +36 -0
  62. package/dist/core/build-timeline.js +328 -0
  63. package/dist/core/client-actions.d.ts +35 -0
  64. package/dist/core/client-actions.js +128 -0
  65. package/dist/core/client-contributions.d.ts +24 -0
  66. package/dist/core/client-contributions.js +115 -0
  67. package/dist/core/client-timeline.d.ts +33 -0
  68. package/dist/core/client-timeline.js +72 -0
  69. package/dist/core/format-context.d.ts +11 -0
  70. package/dist/core/format-context.js +20 -0
  71. package/dist/core/format-tokens.d.ts +2 -0
  72. package/dist/core/format-tokens.js +6 -0
  73. package/dist/core/model-identity.d.ts +3 -0
  74. package/dist/core/model-identity.js +7 -0
  75. package/dist/core/session-projection.d.ts +11 -0
  76. package/dist/core/session-projection.js +191 -0
  77. package/dist/core/session-query.d.ts +118 -0
  78. package/dist/core/session-query.js +187 -0
  79. package/dist/core/session-view-state.d.ts +66 -0
  80. package/dist/core/session-view-state.js +114 -0
  81. package/dist/core/tool-contract.d.ts +15 -0
  82. package/dist/core/tool-contract.js +3 -0
  83. package/dist/core/tool-groups.d.ts +36 -0
  84. package/dist/core/tool-groups.js +151 -0
  85. package/dist/core/tool-view.d.ts +182 -0
  86. package/dist/core/tool-view.js +618 -0
  87. package/dist/headless.d.ts +28 -0
  88. package/dist/headless.js +24 -0
  89. package/dist/index.d.ts +9 -0
  90. package/dist/index.js +4 -0
  91. package/dist/lib/failure.d.ts +2 -0
  92. package/dist/lib/failure.js +9 -0
  93. package/dist/markdown.d.ts +3 -0
  94. package/dist/markdown.js +6 -0
  95. package/dist/model-commands.d.ts +44 -0
  96. package/dist/model-commands.js +232 -0
  97. package/dist/parakeet.d.ts +77 -0
  98. package/dist/parakeet.js +276 -0
  99. package/dist/provider-choices.d.ts +8 -0
  100. package/dist/provider-choices.js +46 -0
  101. package/dist/reload-command.d.ts +43 -0
  102. package/dist/reload-command.js +72 -0
  103. package/dist/session-commands.d.ts +53 -0
  104. package/dist/session-commands.js +259 -0
  105. package/dist/session.d.ts +24 -0
  106. package/dist/session.js +1 -0
  107. package/dist/store/types.d.ts +163 -0
  108. package/dist/store/types.js +1 -0
  109. package/dist/styles.css +19129 -0
  110. package/docs/api/README.md +15 -0
  111. package/docs/api/commands/slash.md +321 -0
  112. package/docs/api/headless.md +2510 -0
  113. package/docs/api/index.md +1249 -0
  114. package/package.json +79 -3
  115. package/src/styles.css +272 -0
@@ -0,0 +1,618 @@
1
+ import { parseCodexPatch, } from "@fungi.computer/knapsack/patch";
2
+ import { filesEditFailureSchema } from "@fungi.computer/knapsack";
3
+ import { formatPatch, parsePatch, structuredPatch, } from "diff";
4
+ import { z } from "zod";
5
+ import stripAnsi from "strip-ansi";
6
+ import { reportFailure } from "../lib/failure.js";
7
+ /** Shared tool status meaning, used by the browser and native presentations. */
8
+ export function toolStatusTone(status) {
9
+ if (status === "error")
10
+ return "error";
11
+ if (status === "unknown")
12
+ return "neutral";
13
+ return status === "running" ? "info" : "success";
14
+ }
15
+ /** Shared artwork identity for browser and native tool rows. */
16
+ export const toolKindSigils = {
17
+ /** Artwork for code execution. */
18
+ code: "computer",
19
+ /** Artwork for shell commands. */
20
+ command: "run",
21
+ /** Artwork for file edits. */
22
+ edit: "write",
23
+ /** Artwork for other tool calls. */
24
+ other: "working",
25
+ /** Artwork for file reads. */
26
+ read: "read",
27
+ /** Artwork for search results. */
28
+ search: "search",
29
+ };
30
+ /** Items shown before "more" for excerpts, matches, and output. */
31
+ export const toolPreviewLineLimit = 12;
32
+ /** Diff lines shown before "more". */
33
+ const toolDiffPreviewLimit = 40;
34
+ /** Script lines shown before "more". */
35
+ const toolScriptPreviewLimit = 6;
36
+ /** Hard bound on any one section, independent of preview. */
37
+ const toolSectionLimit = 400;
38
+ const labelLimit = 72;
39
+ const kindByName = new Map([
40
+ ["apply_patch", "edit"],
41
+ ["bash", "command"],
42
+ ["edit", "edit"],
43
+ ["execute", "code"],
44
+ ["find", "search"],
45
+ ["grep", "search"],
46
+ ["ls", "search"],
47
+ ["read", "read"],
48
+ ["write", "edit"],
49
+ ]);
50
+ const verbs = {
51
+ code: ["Ran script", "Running script"],
52
+ command: ["Ran", "Running"],
53
+ edit: ["Updated", "Updating"],
54
+ other: ["Called", "Calling"],
55
+ read: ["Read", "Reading"],
56
+ search: ["Searched", "Searching"],
57
+ };
58
+ const pathArgs = z.looseObject({ path: z.string() });
59
+ const filePathArgs = z.looseObject({ file_path: z.string() });
60
+ const commandArgs = z.looseObject({ command: z.string() });
61
+ const codeArgs = z.looseObject({ code: z.string() });
62
+ const patchArgs = z.looseObject({ patch: z.string() });
63
+ const patternArgs = z.looseObject({ pattern: z.string() });
64
+ const editArgs = z.looseObject({ oldText: z.string(), newText: z.string() });
65
+ const writeArgs = z.looseObject({ content: z.string() });
66
+ const jsonText = z.string();
67
+ /** Knapsack Files read page; `offset` is a byte offset. */
68
+ const readResult = z.looseObject({
69
+ content: z.string(),
70
+ offset: z.number().int().min(0).optional(),
71
+ });
72
+ /** Knapsack Files grep result. */
73
+ const grepResult = z.looseObject({
74
+ matches: z.array(z.looseObject({
75
+ path: z.string(),
76
+ line: z.number().int(),
77
+ text: z.string(),
78
+ })),
79
+ });
80
+ /** Process output shared by shell tools and the Files patch engine. */
81
+ const processResult = z
82
+ .looseObject({
83
+ stdout: z.string().optional(),
84
+ stderr: z.string().optional(),
85
+ exitCode: z.number().int().nullable().optional(),
86
+ })
87
+ .refine((value) => value.stdout !== undefined ||
88
+ value.stderr !== undefined ||
89
+ value.exitCode !== undefined);
90
+ const filesEditResult = z.strictObject({
91
+ path: z.string(),
92
+ bytes: z.number().int().nonnegative(),
93
+ diff: z.string(),
94
+ firstChangedLine: z.number().int().positive().nullable(),
95
+ unifiedPatch: z.string().nullable(),
96
+ unifiedPatchBytes: z.number().int().nonnegative(),
97
+ unifiedPatchAddedLines: z.number().int().nonnegative(),
98
+ unifiedPatchRemovedLines: z.number().int().nonnegative(),
99
+ unifiedPatchTruncated: z.boolean(),
100
+ });
101
+ /** Plain-text grep hit: `path:line:text` or `path:line- text`. */
102
+ const hitPattern = /^(.+?:\d+)[:-]\s?(.*)$/u;
103
+ /** Typed kind for one Pi tool name. */
104
+ function toolKind(name) {
105
+ return kindByName.get(name) ?? "other";
106
+ }
107
+ function firstLine(text) {
108
+ const line = text
109
+ .split("\n")
110
+ .map((candidate) => candidate.trim())
111
+ .find((candidate) => candidate !== "") ?? "";
112
+ return line.length > labelLimit ? `${line.slice(0, labelLimit - 1)}…` : line;
113
+ }
114
+ function bounded(items) {
115
+ return items.slice(0, toolSectionLimit);
116
+ }
117
+ function textLines(text) {
118
+ return bounded(stripAnsi(text).replace(/\n$/u, "").split("\n"));
119
+ }
120
+ function argumentPath(args) {
121
+ const path = pathArgs.safeParse(args);
122
+ if (path.success)
123
+ return path.data.path;
124
+ const filePath = filePathArgs.safeParse(args);
125
+ return filePath.success ? filePath.data.file_path : undefined;
126
+ }
127
+ function parseJsonText(text) {
128
+ try {
129
+ return { kind: "parsed", value: JSON.parse(text) };
130
+ }
131
+ catch (cause) {
132
+ return { kind: "invalid", cause };
133
+ }
134
+ }
135
+ function resultValue(result) {
136
+ if (result === undefined)
137
+ return { kind: "none" };
138
+ const text = result.content
139
+ .flatMap((part) => (part.type === "text" ? [part.text] : []))
140
+ .join("\n");
141
+ if (text.trim() === "")
142
+ return { kind: "none" };
143
+ const parsed = parseJsonText(text);
144
+ if (parsed.kind === "invalid")
145
+ return { kind: "text", text };
146
+ const json = parsed.value;
147
+ const string = jsonText.safeParse(json);
148
+ if (string.success)
149
+ return { kind: "text", text: string.data };
150
+ const value = z.json().safeParse(json);
151
+ return value.success
152
+ ? { kind: "json", value: value.data }
153
+ : { kind: "text", text };
154
+ }
155
+ function excerpt(content, offset) {
156
+ const numbered = offset === undefined || offset === 0;
157
+ return {
158
+ kind: "excerpt",
159
+ lines: textLines(content).map((text, index) => numbered ? { number: index + 1, text } : { text }),
160
+ preview: toolPreviewLineLimit,
161
+ };
162
+ }
163
+ function matchesFromText(text) {
164
+ return {
165
+ kind: "matches",
166
+ matches: textLines(text)
167
+ .filter((line) => line.trim() !== "")
168
+ .map((line) => {
169
+ const hit = hitPattern.exec(line);
170
+ return hit === null
171
+ ? { location: "", text: line }
172
+ : { location: hit[1] ?? "", text: hit[2] ?? "" };
173
+ }),
174
+ preview: toolPreviewLineLimit,
175
+ };
176
+ }
177
+ function outputSection(stdout, stderr) {
178
+ const lines = [];
179
+ if (stdout !== undefined && stdout.trim() !== "")
180
+ for (const text of textLines(stdout))
181
+ lines.push({ stream: "stdout", text });
182
+ if (stderr !== undefined && stderr.trim() !== "")
183
+ for (const text of textLines(stderr))
184
+ lines.push({ stream: "stderr", text });
185
+ return lines.length === 0
186
+ ? undefined
187
+ : { kind: "output", lines: bounded(lines), preview: toolPreviewLineLimit };
188
+ }
189
+ function jsonSection(value, label) {
190
+ return {
191
+ kind: "text",
192
+ label,
193
+ language: "json",
194
+ lines: textLines(JSON.stringify(value, null, 2)),
195
+ preview: toolPreviewLineLimit,
196
+ };
197
+ }
198
+ /**
199
+ * Recognize the typed result shapes (Files read and grep pages, process
200
+ * output) and drop success envelopes; anything else is shown as-is.
201
+ */
202
+ function resultBody(value, kind) {
203
+ if (value.kind === "none")
204
+ return { sections: [] };
205
+ if (value.kind === "text") {
206
+ if (kind === "read")
207
+ return { sections: [excerpt(value.text, undefined)] };
208
+ if (kind === "search")
209
+ return { sections: [matchesFromText(value.text)] };
210
+ if (kind === "command") {
211
+ const output = outputSection(value.text, undefined);
212
+ return {
213
+ failure: firstLine(value.text),
214
+ sections: output === undefined ? [] : [output],
215
+ };
216
+ }
217
+ return {
218
+ failure: firstLine(value.text),
219
+ sections: [
220
+ {
221
+ kind: "text",
222
+ lines: textLines(value.text),
223
+ preview: toolPreviewLineLimit,
224
+ },
225
+ ],
226
+ };
227
+ }
228
+ const read = readResult.safeParse(value.value);
229
+ if (read.success)
230
+ return { sections: [excerpt(read.data.content, read.data.offset)] };
231
+ const grep = grepResult.safeParse(value.value);
232
+ if (grep.success)
233
+ return {
234
+ sections: [
235
+ {
236
+ kind: "matches",
237
+ matches: bounded(grep.data.matches).map((match) => ({
238
+ location: `${match.path}:${String(match.line)}`,
239
+ text: stripAnsi(match.text),
240
+ })),
241
+ preview: toolPreviewLineLimit,
242
+ },
243
+ ],
244
+ };
245
+ const editFailure = filesEditFailureSchema.safeParse(value.value);
246
+ if (editFailure.success)
247
+ return {
248
+ failure: editFailure.data.error.message,
249
+ sections: [
250
+ {
251
+ kind: "text",
252
+ lines: [editFailure.data.error.message],
253
+ preview: toolPreviewLineLimit,
254
+ },
255
+ ],
256
+ };
257
+ const edit = filesEditResult.safeParse(value.value);
258
+ if (edit.success) {
259
+ const { unifiedPatch, unifiedPatchBytes, unifiedPatchTruncated } = edit.data;
260
+ if (unifiedPatchTruncated && unifiedPatch === null)
261
+ return {
262
+ sections: [
263
+ {
264
+ kind: "text",
265
+ lines: [
266
+ `Diff omitted (${String(unifiedPatchBytes)} bytes, ${String(edit.data.unifiedPatchAddedLines)} added, ${String(edit.data.unifiedPatchRemovedLines)} removed).`,
267
+ ],
268
+ preview: toolPreviewLineLimit,
269
+ },
270
+ ],
271
+ };
272
+ if (!unifiedPatchTruncated &&
273
+ unifiedPatch !== null &&
274
+ unifiedPatchBytes <= 65_536 &&
275
+ new TextEncoder().encode(unifiedPatch).byteLength === unifiedPatchBytes) {
276
+ try {
277
+ const parsed = parsePatch(unifiedPatch);
278
+ const file = parsed[0];
279
+ if (parsed.length === 1 &&
280
+ file?.oldFileName === edit.data.path &&
281
+ file.newFileName === edit.data.path) {
282
+ return {
283
+ sections: [
284
+ diffSection([
285
+ {
286
+ operation: "update",
287
+ path: edit.data.path,
288
+ moveTo: undefined,
289
+ lineNumbers: true,
290
+ unified: unifiedPatch,
291
+ hunks: file.hunks.map((hunk) => ({
292
+ header: `-${String(hunk.oldStart)},${String(hunk.oldLines)} +${String(hunk.newStart)},${String(hunk.newLines)}`,
293
+ lines: hunk.lines.flatMap((line) => {
294
+ const kind = diffMarkers.get(line.charAt(0));
295
+ return kind === undefined
296
+ ? []
297
+ : [{ kind, text: line.slice(1) }];
298
+ }),
299
+ })),
300
+ },
301
+ ]),
302
+ ],
303
+ };
304
+ }
305
+ }
306
+ catch (cause) {
307
+ reportFailure("render tool patch", cause);
308
+ }
309
+ }
310
+ }
311
+ const process = processResult.safeParse(value.value);
312
+ if (process.success) {
313
+ const { stdout, stderr, exitCode } = process.data;
314
+ const output = outputSection(stdout, stderr);
315
+ const failed = exitCode !== undefined && exitCode !== null && exitCode !== 0;
316
+ return {
317
+ exitCode: failed ? exitCode : undefined,
318
+ failure: stderr !== undefined && stderr.trim() !== ""
319
+ ? firstLine(stripAnsi(stderr))
320
+ : failed
321
+ ? `Exited with code ${String(exitCode)}`
322
+ : undefined,
323
+ sections: output === undefined ? [] : [output],
324
+ };
325
+ }
326
+ return { sections: [jsonSection(value.value)] };
327
+ }
328
+ const operationLabels = {
329
+ add: "Add",
330
+ delete: "Delete",
331
+ update: "Update",
332
+ };
333
+ function diffRows(files) {
334
+ const named = files.length > 1;
335
+ return files.flatMap((file) => [
336
+ ...(named
337
+ ? [
338
+ {
339
+ kind: "file",
340
+ text: `${operationLabels[file.operation]} ${file.path ?? ""}${file.moveTo === undefined ? "" : ` → ${file.moveTo}`}`,
341
+ },
342
+ ]
343
+ : []),
344
+ ...file.hunks.flatMap((hunk) => [
345
+ ...(hunk.header === undefined
346
+ ? []
347
+ : [{ kind: "hunk", text: hunk.header }]),
348
+ ...hunk.lines,
349
+ ]),
350
+ ]);
351
+ }
352
+ function diffSection(files) {
353
+ return {
354
+ kind: "diff",
355
+ files,
356
+ lines: bounded(diffRows(files)),
357
+ preview: toolDiffPreviewLimit,
358
+ };
359
+ }
360
+ const markerByKind = {
361
+ add: "+",
362
+ context: " ",
363
+ remove: "-",
364
+ };
365
+ /** Codex patch files, read by Knapsack's owner-side grammar reader. */
366
+ function patchFiles(patch) {
367
+ return patch.map((file) => {
368
+ // No real line numbers exist; number hunks consecutively so the unified
369
+ // form stays well-formed, and mark it so renderers hide the numbers.
370
+ let oldStart = 1;
371
+ let newStart = 1;
372
+ const hunks = file.hunks.map((hunk) => {
373
+ const count = (kind) => hunk.lines.filter((line) => line.kind === kind).length;
374
+ const oldLines = count("context") + count("remove");
375
+ const newLines = count("context") + count("add");
376
+ const numbered = {
377
+ oldStart,
378
+ oldLines,
379
+ newStart,
380
+ newLines,
381
+ lines: hunk.lines.map((line) => `${markerByKind[line.kind]}${line.text}`),
382
+ };
383
+ oldStart += oldLines;
384
+ newStart += newLines;
385
+ return numbered;
386
+ });
387
+ const structured = {
388
+ oldFileName: file.path,
389
+ newFileName: file.moveTo ?? file.path,
390
+ oldHeader: undefined,
391
+ newHeader: undefined,
392
+ hunks,
393
+ };
394
+ return {
395
+ hunks: file.hunks.map((hunk) => ({
396
+ header: hunk.context,
397
+ lines: hunk.lines,
398
+ })),
399
+ lineNumbers: false,
400
+ moveTo: file.moveTo,
401
+ operation: file.operation,
402
+ path: file.path,
403
+ unified: structured.hunks.length === 0 ? "" : formatPatch(structured),
404
+ };
405
+ });
406
+ }
407
+ const diffMarkers = new Map([
408
+ ["+", "add"],
409
+ ["-", "remove"],
410
+ [" ", "context"],
411
+ ]);
412
+ /** Line diff of a replacement, computed by jsdiff. */
413
+ function replacementFile(path, oldText, newText) {
414
+ const name = path ?? "file";
415
+ const patch = structuredPatch(name, name, oldText, newText, "", "", {
416
+ context: 3,
417
+ });
418
+ return {
419
+ lineNumbers: true,
420
+ unified: formatPatch(patch),
421
+ hunks: patch.hunks.map((hunk) => ({
422
+ header: `-${String(hunk.oldStart)},${String(hunk.oldLines)} +${String(hunk.newStart)},${String(hunk.newLines)}`,
423
+ lines: hunk.lines.flatMap((line) => {
424
+ const kind = diffMarkers.get(line.charAt(0));
425
+ return kind === undefined ? [] : [{ kind, text: line.slice(1) }];
426
+ }),
427
+ })),
428
+ operation: oldText === "" ? "add" : "update",
429
+ path,
430
+ };
431
+ }
432
+ function editTarget(item, path) {
433
+ const patch = patchArgs.safeParse(item.args);
434
+ if (patch.success) {
435
+ const parsed = parseCodexPatch(patch.data.patch);
436
+ const paths = [
437
+ ...new Set(parsed.flatMap((file) => (file.path ? [file.path] : []))),
438
+ ];
439
+ const label = paths.length <= 1
440
+ ? (paths[0] ?? item.name)
441
+ : `${paths[0] ?? ""} +${String(paths.length - 1)}`;
442
+ return {
443
+ get input() {
444
+ return [diffSection(patchFiles(parsed))];
445
+ },
446
+ label,
447
+ paths,
448
+ };
449
+ }
450
+ const target = {
451
+ label: path ?? item.name,
452
+ paths: path === undefined ? [] : [path],
453
+ };
454
+ const edit = editArgs.safeParse(item.args);
455
+ if (edit.success)
456
+ return {
457
+ ...target,
458
+ get input() {
459
+ return [
460
+ diffSection([
461
+ replacementFile(path, edit.data.oldText, edit.data.newText),
462
+ ]),
463
+ ];
464
+ },
465
+ };
466
+ const write = writeArgs.safeParse(item.args);
467
+ return {
468
+ ...target,
469
+ get input() {
470
+ return write.success
471
+ ? [diffSection([replacementFile(path, "", write.data.content)])]
472
+ : [];
473
+ },
474
+ };
475
+ }
476
+ /** Arguments that miss the tool's typed shape are shown as they are. */
477
+ function untypedTarget(item) {
478
+ return {
479
+ get input() {
480
+ return Object.keys(item.args).length === 0
481
+ ? []
482
+ : [jsonSection(item.args, "Arguments")];
483
+ },
484
+ label: item.name,
485
+ paths: [],
486
+ };
487
+ }
488
+ function callTarget(item, kind) {
489
+ const path = argumentPath(item.args);
490
+ switch (kind) {
491
+ case "code": {
492
+ const code = codeArgs.safeParse(item.args);
493
+ return code.success
494
+ ? {
495
+ get input() {
496
+ return [
497
+ {
498
+ kind: "script",
499
+ lines: textLines(code.data.code),
500
+ preview: toolScriptPreviewLimit,
501
+ },
502
+ ];
503
+ },
504
+ label: firstLine(code.data.code),
505
+ paths: [],
506
+ }
507
+ : untypedTarget(item);
508
+ }
509
+ case "command": {
510
+ const command = commandArgs.safeParse(item.args);
511
+ return command.success
512
+ ? {
513
+ get input() {
514
+ return [
515
+ {
516
+ kind: "command",
517
+ lines: textLines(command.data.command),
518
+ preview: toolScriptPreviewLimit,
519
+ },
520
+ ];
521
+ },
522
+ label: firstLine(command.data.command),
523
+ paths: [],
524
+ }
525
+ : untypedTarget(item);
526
+ }
527
+ case "edit":
528
+ return editTarget(item, path);
529
+ case "read":
530
+ return {
531
+ input: [],
532
+ label: path ?? item.name,
533
+ paths: path === undefined ? [] : [path],
534
+ };
535
+ case "search": {
536
+ const pattern = patternArgs.safeParse(item.args);
537
+ const label = pattern.success
538
+ ? path === undefined
539
+ ? pattern.data.pattern
540
+ : `${pattern.data.pattern} in ${path}`
541
+ : (path ?? ".");
542
+ return { input: [], label: firstLine(label), paths: [] };
543
+ }
544
+ case "other":
545
+ return untypedTarget(item);
546
+ }
547
+ }
548
+ /** Build the renderer-neutral view of one call. */
549
+ export function toolCallView(item) {
550
+ const kind = toolKind(item.name);
551
+ const target = callTarget(item, kind);
552
+ const [verb, activeVerb] = verbs[kind];
553
+ // A Files edit refusal can arrive inside a successful execute result and
554
+ // must set the group status before its body is opened.
555
+ const statusValue = item.status === "completed" ? resultValue(item.result) : undefined;
556
+ const refused = statusValue?.kind === "json" &&
557
+ filesEditFailureSchema.safeParse(statusValue.value).success;
558
+ // Most completed tools are collapsed. Decode their potentially large result
559
+ // body and diff only when a visible renderer asks for those fields. Failed
560
+ // edits still inspect the result here because their status can be encoded in
561
+ // a successful transport envelope.
562
+ let details;
563
+ const getDetails = () => {
564
+ if (details !== undefined)
565
+ return details;
566
+ const value = resultValue(item.result);
567
+ const body = resultBody(value, kind);
568
+ const failed = item.status === "error" || refused;
569
+ const error = failed
570
+ ? (body.failure ?? "The tool reported an error.")
571
+ : undefined;
572
+ const resultHasDiff = body.sections.some((section) => section.kind === "diff");
573
+ const resultSections = (kind === "edit" && !failed && !resultHasDiff ? [] : body.sections).filter((section) => !(section.kind === "text" &&
574
+ section.lines.length === 1 &&
575
+ section.lines[0] === error));
576
+ details = {
577
+ error,
578
+ exitCode: body.exitCode,
579
+ sections: [
580
+ ...(kind === "edit" && resultHasDiff ? [] : target.input),
581
+ ...resultSections,
582
+ ],
583
+ };
584
+ return details;
585
+ };
586
+ const failed = item.status === "error" || refused;
587
+ return {
588
+ activeVerb,
589
+ durationMs: item.startedAt === undefined || item.result === undefined
590
+ ? undefined
591
+ : Math.max(0, item.result.timestamp - item.startedAt),
592
+ get error() {
593
+ return failed ? getDetails().error : undefined;
594
+ },
595
+ get exitCode() {
596
+ return getDetails().exitCode;
597
+ },
598
+ id: item.id,
599
+ kind,
600
+ label: target.label,
601
+ name: item.name,
602
+ paths: target.paths,
603
+ get sections() {
604
+ return getDetails().sections;
605
+ },
606
+ status: failed ? "error" : item.status,
607
+ verb,
608
+ };
609
+ }
610
+ /** Compact duration: `340ms`, `1.2s`, `2m 5s`. */
611
+ export function formatToolDuration(milliseconds) {
612
+ if (milliseconds < 1_000)
613
+ return `${String(Math.round(milliseconds))}ms`;
614
+ if (milliseconds < 60_000)
615
+ return `${(milliseconds / 1_000).toFixed(1)}s`;
616
+ const seconds = Math.round(milliseconds / 1_000);
617
+ return `${String(Math.floor(seconds / 60))}m ${String(seconds % 60)}s`;
618
+ }
@@ -0,0 +1,28 @@
1
+ export { formatModelIdentity } from "./core/model-identity.js";
2
+ export { formatContextUsage, formatTokenCount } from "./core/format-context.js";
3
+ export { providerChoices } from "./provider-choices.js";
4
+ export { ParakeetProvider, useComposer, useClientContributions, useParakeetInteractions, useExtensionSlots, useParakeetSession, useStatus, useTimeline, type ComposerController, type ParakeetModel, type ParakeetObservation, type ParakeetReconnectOutcome, type ParakeetStatus, type ParakeetSessionViewState, type ParakeetUsage, type StatusController, type TimelineController, } from "./conversation.js";
5
+ export { sessionViewState, queuedSessionLabel, sessionActivityStatus, type SessionActivityStatus, type SessionViewFacts, type SessionViewState as ParakeetOwnerViewState, } from "./core/session-view-state.js";
6
+ export { mergeTerminals } from "./core/session-projection.js";
7
+ export { seedSessionQuery, sessionQueryOptions, subscribeSessionActivity, } from "./core/session-query.js";
8
+ export { accountCommandDefinitions, contributeAccountCommands, loginRegistry, type AccountCommandOptions, type AccountOwner, type ProviderAccess, type ModelAccountContext, type LoginHost, type LoginMethod, type LoginMethodKind, type LoginPolicy, type LoginProvider, type LoginScope, } from "./account-commands.js";
9
+ export { contributeModelCommands, sessionModelCommandDefinitions, type SessionModelChoice, type SessionModelCommandOptions, } from "./model-commands.js";
10
+ export { contributeReloadCommand, reloadCommandDefinitions, reloadSources, type ReloadCommandOptions, type ReloadSource, } from "./reload-command.js";
11
+ export { contributeSessionCommands, type ParakeetSessionCommandOptions, } from "./session-commands.js";
12
+ export { completeSlash, fuzzyMatch, keyBindings, keyLabel, parseSlashDraft, slashRegistry, type FuzzyMatch, type KeyBindingRow, type SlashCommand, type SlashCommandItem, type SlashCompletion, type SlashParse, type SlashWordItem, } from "./commands/slash.js";
13
+ export { composerHints, fitHints, formatHints, type ComposerHintContext, type ComposerKeys, type KeyHint, } from "./commands/hints.js";
14
+ export { isCancelledOutcome, runSlashDraft, type SlashRuntime, } from "./commands/run.js";
15
+ export { createParakeetInteractions, type ParakeetInteractions, type ParakeetInteractionState, type ParakeetNotice, type ParakeetOpenInteraction, } from "./commands/interactions.js";
16
+ export { parakeetBuiltinCommands, type ParakeetBuiltinOptions, } from "./commands/builtins.js";
17
+ export { advertisedCommands, type AdvertisedCatalogError, type AdvertisedCommands, } from "./commands/advertised.js";
18
+ export { useSlashCommands, type ParakeetCommandRuntime, type SlashCommandController, type SlashCommandOptions, } from "./commands/use-slash-commands.js";
19
+ export type { ParakeetSession, ParakeetSessionCommand, ParakeetPromptResult, } from "./session.js";
20
+ export type { AssistantMessageItem as ParakeetAssistantMessageItem, TimelineItem as ParakeetTimelineItem, ToolGroupItem as ParakeetToolGroupItem, ToolItem as ParakeetToolItem, UserMessageItem as ParakeetUserMessageItem, } from "./store/types.js";
21
+ export type { ToolArguments as ParakeetToolArguments } from "./core/tool-contract.js";
22
+ export { useMarkdown, type MarkdownBlock, type MarkdownInline, } from "./markdown.js";
23
+ export { formatToolDuration, toolCallView, toolKindSigils, toolStatusTone, type ToolCallSection as ParakeetToolCallSection, type ToolCallView as ParakeetToolCallView, type ToolDiffFile as ParakeetToolDiffFile, type ToolDiffHunk as ParakeetToolDiffHunk, type ToolDiffLine as ParakeetToolDiffLine, type ToolDiffRow as ParakeetToolDiffRow, type ToolExcerptLine as ParakeetToolExcerptLine, type ToolKind as ParakeetToolKind, type ToolMatch as ParakeetToolMatch, type ToolOutputLine as ParakeetToolOutputLine, } from "./core/tool-view.js";
24
+ export { assistantMessageVisible } from "./core/assistant-visibility.js";
25
+ export { disclosureOpen, initialToolDisclosure, toggleAllDisclosure, toggleDisclosure, toolCallDefaultOpen, toolGroupDefaultOpen, visibleToolCalls, type ToolDisclosureState, } from "./core/tool-groups.js";
26
+ export { useClientActions } from "./client-contributions.js";
27
+ export { placeClientTimeline, type PlacedClientCell, } from "./core/client-timeline.js";
28
+ export type { ClientContribution } from "./core/client-contributions.js";
@@ -0,0 +1,24 @@
1
+ export { formatModelIdentity } from "./core/model-identity.js";
2
+ export { formatContextUsage, formatTokenCount } from "./core/format-context.js";
3
+ export { providerChoices } from "./provider-choices.js";
4
+ export { ParakeetProvider, useComposer, useClientContributions, useParakeetInteractions, useExtensionSlots, useParakeetSession, useStatus, useTimeline, } from "./conversation.js";
5
+ export { sessionViewState, queuedSessionLabel, sessionActivityStatus, } from "./core/session-view-state.js";
6
+ export { mergeTerminals } from "./core/session-projection.js";
7
+ export { seedSessionQuery, sessionQueryOptions, subscribeSessionActivity, } from "./core/session-query.js";
8
+ export { accountCommandDefinitions, contributeAccountCommands, loginRegistry, } from "./account-commands.js";
9
+ export { contributeModelCommands, sessionModelCommandDefinitions, } from "./model-commands.js";
10
+ export { contributeReloadCommand, reloadCommandDefinitions, reloadSources, } from "./reload-command.js";
11
+ export { contributeSessionCommands, } from "./session-commands.js";
12
+ export { completeSlash, fuzzyMatch, keyBindings, keyLabel, parseSlashDraft, slashRegistry, } from "./commands/slash.js";
13
+ export { composerHints, fitHints, formatHints, } from "./commands/hints.js";
14
+ export { isCancelledOutcome, runSlashDraft, } from "./commands/run.js";
15
+ export { createParakeetInteractions, } from "./commands/interactions.js";
16
+ export { parakeetBuiltinCommands, } from "./commands/builtins.js";
17
+ export { advertisedCommands, } from "./commands/advertised.js";
18
+ export { useSlashCommands, } from "./commands/use-slash-commands.js";
19
+ export { useMarkdown, } from "./markdown.js";
20
+ export { formatToolDuration, toolCallView, toolKindSigils, toolStatusTone, } from "./core/tool-view.js";
21
+ export { assistantMessageVisible } from "./core/assistant-visibility.js";
22
+ export { disclosureOpen, initialToolDisclosure, toggleAllDisclosure, toggleDisclosure, toolCallDefaultOpen, toolGroupDefaultOpen, visibleToolCalls, } from "./core/tool-groups.js";
23
+ export { useClientActions } from "./client-contributions.js";
24
+ export { placeClientTimeline, } from "./core/client-timeline.js";
@@ -0,0 +1,9 @@
1
+ export { ParakeetProvider, useComposer, useStatus, useTimeline, useMarkdown, disclosureOpen, formatToolDuration, formatModelIdentity, formatContextUsage, formatTokenCount, initialToolDisclosure, toggleAllDisclosure, toggleDisclosure, toolCallDefaultOpen, toolGroupDefaultOpen, visibleToolCalls, } from "./headless.js";
2
+ export { Parakeet, ParakeetView } from "./parakeet.js";
3
+ export type { ParakeetVisitor } from "./parakeet.js";
4
+ export { InteractionPanel, type InteractionPanelProps, } from "./components/InteractionPanel.js";
5
+ export type { ParakeetAssistantMessageItem, ComposerController, ParakeetModel, ParakeetSession, ParakeetStatus, ParakeetTimelineItem, ParakeetToolArguments, ParakeetToolCallSection, ParakeetToolCallView, ParakeetToolDiffFile, ParakeetToolDiffHunk, ParakeetToolDiffLine, ParakeetToolDiffRow, ParakeetToolExcerptLine, ParakeetToolMatch, ParakeetToolOutputLine, ParakeetToolGroupItem, ParakeetToolItem, ParakeetToolKind, ToolDisclosureState, ParakeetUsage, ParakeetUserMessageItem, StatusController, TimelineController, MarkdownBlock, MarkdownInline, } from "./headless.js";
6
+ export type { ParakeetProps, ParakeetViewProps } from "./parakeet.js";
7
+ export type { ParakeetAuthor, ParakeetAuthorOf } from "./core/author-labels.js";
8
+ export type { ParakeetSessionCommand, ParakeetPromptResult, } from "./session.js";
9
+ export * from "./agent-head.js";