@fungi.computer/parakeet 0.0.0-stage → 0.0.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.
- package/LICENSE +21 -0
- package/README.md +186 -2
- package/dist/account-commands.d.ts +102 -0
- package/dist/account-commands.js +239 -0
- package/dist/agent-head.d.ts +4 -0
- package/dist/agent-head.js +2 -0
- package/dist/client-contributions.d.ts +20 -0
- package/dist/client-contributions.js +41 -0
- package/dist/commands/advertised.d.ts +23 -0
- package/dist/commands/advertised.js +72 -0
- package/dist/commands/builtins.d.ts +14 -0
- package/dist/commands/builtins.js +91 -0
- package/dist/commands/hints.d.ts +36 -0
- package/dist/commands/hints.js +64 -0
- package/dist/commands/interactions.d.ts +55 -0
- package/dist/commands/interactions.js +101 -0
- package/dist/commands/run.d.ts +11 -0
- package/dist/commands/run.js +61 -0
- package/dist/commands/slash.d.ts +124 -0
- package/dist/commands/slash.js +182 -0
- package/dist/commands/use-slash-commands.d.ts +57 -0
- package/dist/commands/use-slash-commands.js +207 -0
- package/dist/components/AgentHead.d.ts +27 -0
- package/dist/components/AgentHead.js +53 -0
- package/dist/components/ChatComposer.d.ts +43 -0
- package/dist/components/ChatComposer.js +209 -0
- package/dist/components/ChatMessage.d.ts +8 -0
- package/dist/components/ChatMessage.js +16 -0
- package/dist/components/ClientRows.d.ts +11 -0
- package/dist/components/ClientRows.js +46 -0
- package/dist/components/CodeBlock.d.ts +11 -0
- package/dist/components/CodeBlock.js +38 -0
- package/dist/components/InteractionPanel.d.ts +15 -0
- package/dist/components/InteractionPanel.js +42 -0
- package/dist/components/MarkdownRenderer.d.ts +6 -0
- package/dist/components/MarkdownRenderer.js +48 -0
- package/dist/components/RunFailureNotice.d.ts +7 -0
- package/dist/components/RunFailureNotice.js +13 -0
- package/dist/components/ShiitakeAvatar.d.ts +19 -0
- package/dist/components/ShiitakeAvatar.js +20 -0
- package/dist/components/SlashMenu.d.ts +8 -0
- package/dist/components/SlashMenu.js +50 -0
- package/dist/components/ThinkingBlock.d.ts +3 -0
- package/dist/components/ThinkingBlock.js +18 -0
- package/dist/components/TimelineItem.d.ts +15 -0
- package/dist/components/TimelineItem.js +35 -0
- package/dist/components/ToolGroup.d.ts +12 -0
- package/dist/components/ToolGroup.js +90 -0
- package/dist/components/WaitingNotice.d.ts +7 -0
- package/dist/components/WaitingNotice.js +8 -0
- package/dist/components/avatar-presentation.d.ts +23 -0
- package/dist/components/avatar-presentation.js +16 -0
- package/dist/components/sprite-service.d.ts +2 -0
- package/dist/components/sprite-service.js +9 -0
- package/dist/components/still-frame-cache.d.ts +16 -0
- package/dist/components/still-frame-cache.js +63 -0
- package/dist/conversation.d.ts +164 -0
- package/dist/conversation.js +760 -0
- package/dist/core/assistant-visibility.d.ts +3 -0
- package/dist/core/assistant-visibility.js +4 -0
- package/dist/core/author-labels.d.ts +16 -0
- package/dist/core/author-labels.js +16 -0
- package/dist/core/build-timeline.d.ts +36 -0
- package/dist/core/build-timeline.js +328 -0
- package/dist/core/client-actions.d.ts +35 -0
- package/dist/core/client-actions.js +128 -0
- package/dist/core/client-contributions.d.ts +24 -0
- package/dist/core/client-contributions.js +115 -0
- package/dist/core/client-timeline.d.ts +33 -0
- package/dist/core/client-timeline.js +72 -0
- package/dist/core/format-context.d.ts +11 -0
- package/dist/core/format-context.js +20 -0
- package/dist/core/format-tokens.d.ts +2 -0
- package/dist/core/format-tokens.js +6 -0
- package/dist/core/model-identity.d.ts +3 -0
- package/dist/core/model-identity.js +7 -0
- package/dist/core/session-projection.d.ts +11 -0
- package/dist/core/session-projection.js +191 -0
- package/dist/core/session-query.d.ts +118 -0
- package/dist/core/session-query.js +187 -0
- package/dist/core/session-view-state.d.ts +66 -0
- package/dist/core/session-view-state.js +114 -0
- package/dist/core/tool-contract.d.ts +15 -0
- package/dist/core/tool-contract.js +3 -0
- package/dist/core/tool-groups.d.ts +36 -0
- package/dist/core/tool-groups.js +151 -0
- package/dist/core/tool-view.d.ts +182 -0
- package/dist/core/tool-view.js +618 -0
- package/dist/headless.d.ts +28 -0
- package/dist/headless.js +24 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +4 -0
- package/dist/lib/failure.d.ts +2 -0
- package/dist/lib/failure.js +9 -0
- package/dist/markdown.d.ts +3 -0
- package/dist/markdown.js +6 -0
- package/dist/model-commands.d.ts +44 -0
- package/dist/model-commands.js +232 -0
- package/dist/parakeet.d.ts +77 -0
- package/dist/parakeet.js +276 -0
- package/dist/provider-choices.d.ts +8 -0
- package/dist/provider-choices.js +43 -0
- package/dist/reload-command.d.ts +43 -0
- package/dist/reload-command.js +72 -0
- package/dist/session-commands.d.ts +53 -0
- package/dist/session-commands.js +259 -0
- package/dist/session.d.ts +24 -0
- package/dist/session.js +1 -0
- package/dist/store/types.d.ts +163 -0
- package/dist/store/types.js +1 -0
- package/dist/styles.css +19156 -0
- package/docs/api/README.md +15 -0
- package/docs/api/commands/slash.md +321 -0
- package/docs/api/headless.md +2510 -0
- package/docs/api/index.md +1253 -0
- package/package.json +79 -3
- package/src/styles.css +268 -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: "list",
|
|
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";
|
package/dist/headless.js
ADDED
|
@@ -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";
|
package/dist/index.d.ts
ADDED
|
@@ -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";
|