@openharness/core 0.3.2 → 0.4.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.
- package/dist/__tests__/messages.test.d.ts +2 -0
- package/dist/__tests__/messages.test.d.ts.map +1 -0
- package/dist/__tests__/messages.test.js +66 -0
- package/dist/__tests__/messages.test.js.map +1 -0
- package/dist/__tests__/skills.test.d.ts +2 -0
- package/dist/__tests__/skills.test.d.ts.map +1 -0
- package/dist/__tests__/skills.test.js +293 -0
- package/dist/__tests__/skills.test.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/messages.d.ts +10 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +53 -0
- package/dist/messages.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/messages.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { extractUserInput } from "../messages.js";
|
|
3
|
+
function makeTextMessage(text, role = "user") {
|
|
4
|
+
return {
|
|
5
|
+
id: "msg-1",
|
|
6
|
+
role,
|
|
7
|
+
parts: [{ type: "text", text }],
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function makeFileMessage(mediaType, url, filename, text) {
|
|
11
|
+
const parts = [];
|
|
12
|
+
if (text) {
|
|
13
|
+
parts.push({ type: "text", text });
|
|
14
|
+
}
|
|
15
|
+
parts.push({ type: "file", mediaType, url, filename });
|
|
16
|
+
return {
|
|
17
|
+
id: "msg-2",
|
|
18
|
+
role: "user",
|
|
19
|
+
parts,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
describe("extractUserInput", () => {
|
|
23
|
+
it("returns string for text-only message", async () => {
|
|
24
|
+
const result = await extractUserInput([makeTextMessage("hello world")]);
|
|
25
|
+
expect(result).toBe("hello world");
|
|
26
|
+
});
|
|
27
|
+
it("concatenates multiple text parts", async () => {
|
|
28
|
+
const msg = {
|
|
29
|
+
id: "msg-1",
|
|
30
|
+
role: "user",
|
|
31
|
+
parts: [
|
|
32
|
+
{ type: "text", text: "hello " },
|
|
33
|
+
{ type: "text", text: "world" },
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
const result = await extractUserInput([msg]);
|
|
37
|
+
expect(result).toBe("hello world");
|
|
38
|
+
});
|
|
39
|
+
it("returns ModelMessage[] for message with file", async () => {
|
|
40
|
+
const msg = makeFileMessage("image/png", "data:image/png;base64,iVBOR", "test.png", "describe this image");
|
|
41
|
+
const result = await extractUserInput([msg]);
|
|
42
|
+
expect(Array.isArray(result)).toBe(true);
|
|
43
|
+
const messages = result;
|
|
44
|
+
expect(messages.length).toBeGreaterThan(0);
|
|
45
|
+
expect(messages[0].role).toBe("user");
|
|
46
|
+
});
|
|
47
|
+
it("returns ModelMessage[] for file-only (no text)", async () => {
|
|
48
|
+
const msg = makeFileMessage("image/jpeg", "data:image/jpeg;base64,/9j/4A");
|
|
49
|
+
const result = await extractUserInput([msg]);
|
|
50
|
+
expect(Array.isArray(result)).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
it("uses only the last message", async () => {
|
|
53
|
+
const result = await extractUserInput([
|
|
54
|
+
makeTextMessage("first message"),
|
|
55
|
+
makeTextMessage("second message"),
|
|
56
|
+
]);
|
|
57
|
+
expect(result).toBe("second message");
|
|
58
|
+
});
|
|
59
|
+
it("throws on empty array", async () => {
|
|
60
|
+
await expect(extractUserInput([])).rejects.toThrow("messages array is empty");
|
|
61
|
+
});
|
|
62
|
+
it("throws when last message is not from user", async () => {
|
|
63
|
+
await expect(extractUserInput([makeTextMessage("hi", "assistant")])).rejects.toThrow('expected "user"');
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
//# sourceMappingURL=messages.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.test.js","sourceRoot":"","sources":["../../src/__tests__/messages.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/skills.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { discoverSkills, scanSkillFiles } from "../skills.js";
|
|
6
|
+
import { createSkillTool } from "../tools/skill.js";
|
|
7
|
+
// ── Helpers ─────────────────────────────────────────────────────────
|
|
8
|
+
let tmpDir;
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "oh-skills-test-"));
|
|
11
|
+
});
|
|
12
|
+
afterEach(async () => {
|
|
13
|
+
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
14
|
+
});
|
|
15
|
+
/** Write a SKILL.md file under `tmpDir/<name>/SKILL.md`. */
|
|
16
|
+
async function writeSkill(name, content, extraFiles) {
|
|
17
|
+
const dir = path.join(tmpDir, name);
|
|
18
|
+
await fs.mkdir(dir, { recursive: true });
|
|
19
|
+
await fs.writeFile(path.join(dir, "SKILL.md"), content, "utf-8");
|
|
20
|
+
for (const [filePath, fileContent] of Object.entries(extraFiles ?? {})) {
|
|
21
|
+
const full = path.join(dir, filePath);
|
|
22
|
+
await fs.mkdir(path.dirname(full), { recursive: true });
|
|
23
|
+
await fs.writeFile(full, fileContent, "utf-8");
|
|
24
|
+
}
|
|
25
|
+
return dir;
|
|
26
|
+
}
|
|
27
|
+
// ── discoverSkills ──────────────────────────────────────────────────
|
|
28
|
+
describe("discoverSkills", () => {
|
|
29
|
+
it("discovers a single skill", async () => {
|
|
30
|
+
await writeSkill("my-skill", `---
|
|
31
|
+
name: my-skill
|
|
32
|
+
description: A test skill
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
# Instructions
|
|
36
|
+
|
|
37
|
+
Do the thing.`);
|
|
38
|
+
const skills = await discoverSkills({ paths: [tmpDir] });
|
|
39
|
+
expect(skills).toHaveLength(1);
|
|
40
|
+
expect(skills[0].name).toBe("my-skill");
|
|
41
|
+
expect(skills[0].description).toBe("A test skill");
|
|
42
|
+
expect(skills[0].content).toBe("# Instructions\n\nDo the thing.");
|
|
43
|
+
expect(skills[0].location).toBe(path.join(tmpDir, "my-skill", "SKILL.md"));
|
|
44
|
+
});
|
|
45
|
+
it("discovers multiple skills", async () => {
|
|
46
|
+
await writeSkill("skill-a", `---
|
|
47
|
+
name: skill-a
|
|
48
|
+
description: First skill
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
Content A`);
|
|
52
|
+
await writeSkill("skill-b", `---
|
|
53
|
+
name: skill-b
|
|
54
|
+
description: Second skill
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
Content B`);
|
|
58
|
+
const skills = await discoverSkills({ paths: [tmpDir] });
|
|
59
|
+
expect(skills).toHaveLength(2);
|
|
60
|
+
const names = skills.map((s) => s.name).sort();
|
|
61
|
+
expect(names).toEqual(["skill-a", "skill-b"]);
|
|
62
|
+
});
|
|
63
|
+
it("discovers nested skills", async () => {
|
|
64
|
+
const nestedDir = path.join(tmpDir, "deep", "nested", "my-skill");
|
|
65
|
+
await fs.mkdir(nestedDir, { recursive: true });
|
|
66
|
+
await fs.writeFile(path.join(nestedDir, "SKILL.md"), `---
|
|
67
|
+
name: nested-skill
|
|
68
|
+
description: Found deep
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
Nested content`, "utf-8");
|
|
72
|
+
const skills = await discoverSkills({ paths: [tmpDir] });
|
|
73
|
+
expect(skills).toHaveLength(1);
|
|
74
|
+
expect(skills[0].name).toBe("nested-skill");
|
|
75
|
+
});
|
|
76
|
+
it("later paths overwrite earlier ones on name collision", async () => {
|
|
77
|
+
const dir1 = path.join(tmpDir, "first");
|
|
78
|
+
const dir2 = path.join(tmpDir, "second");
|
|
79
|
+
await fs.mkdir(path.join(dir1, "my-skill"), { recursive: true });
|
|
80
|
+
await fs.writeFile(path.join(dir1, "my-skill", "SKILL.md"), `---
|
|
81
|
+
name: my-skill
|
|
82
|
+
description: From first
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
First version`, "utf-8");
|
|
86
|
+
await fs.mkdir(path.join(dir2, "my-skill"), { recursive: true });
|
|
87
|
+
await fs.writeFile(path.join(dir2, "my-skill", "SKILL.md"), `---
|
|
88
|
+
name: my-skill
|
|
89
|
+
description: From second
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
Second version`, "utf-8");
|
|
93
|
+
const skills = await discoverSkills({ paths: [dir1, dir2] });
|
|
94
|
+
expect(skills).toHaveLength(1);
|
|
95
|
+
expect(skills[0].description).toBe("From second");
|
|
96
|
+
expect(skills[0].content).toBe("Second version");
|
|
97
|
+
});
|
|
98
|
+
it("returns empty array for empty paths", async () => {
|
|
99
|
+
const skills = await discoverSkills({ paths: [] });
|
|
100
|
+
expect(skills).toEqual([]);
|
|
101
|
+
});
|
|
102
|
+
it("returns empty array when paths is undefined", async () => {
|
|
103
|
+
const skills = await discoverSkills({});
|
|
104
|
+
expect(skills).toEqual([]);
|
|
105
|
+
});
|
|
106
|
+
it("silently skips non-existent directories", async () => {
|
|
107
|
+
const skills = await discoverSkills({
|
|
108
|
+
paths: [path.join(tmpDir, "does-not-exist")],
|
|
109
|
+
});
|
|
110
|
+
expect(skills).toEqual([]);
|
|
111
|
+
});
|
|
112
|
+
it("skips SKILL.md with missing frontmatter", async () => {
|
|
113
|
+
await writeSkill("bad", "# No frontmatter here\n\nJust content.");
|
|
114
|
+
const skills = await discoverSkills({ paths: [tmpDir] });
|
|
115
|
+
expect(skills).toEqual([]);
|
|
116
|
+
});
|
|
117
|
+
it("skips SKILL.md with missing name", async () => {
|
|
118
|
+
await writeSkill("no-name", `---
|
|
119
|
+
description: Has description but no name
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
Content`);
|
|
123
|
+
const skills = await discoverSkills({ paths: [tmpDir] });
|
|
124
|
+
expect(skills).toEqual([]);
|
|
125
|
+
});
|
|
126
|
+
it("skips SKILL.md with missing description", async () => {
|
|
127
|
+
await writeSkill("no-desc", `---
|
|
128
|
+
name: no-desc
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
Content`);
|
|
132
|
+
const skills = await discoverSkills({ paths: [tmpDir] });
|
|
133
|
+
expect(skills).toEqual([]);
|
|
134
|
+
});
|
|
135
|
+
it("skips SKILL.md with invalid name format", async () => {
|
|
136
|
+
await writeSkill("bad-name", `---
|
|
137
|
+
name: UPPER_CASE
|
|
138
|
+
description: Invalid name
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
Content`);
|
|
142
|
+
const skills = await discoverSkills({ paths: [tmpDir] });
|
|
143
|
+
expect(skills).toEqual([]);
|
|
144
|
+
});
|
|
145
|
+
it("rejects names starting with a hyphen", async () => {
|
|
146
|
+
await writeSkill("hyphen", `---
|
|
147
|
+
name: -leading
|
|
148
|
+
description: Bad
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
Content`);
|
|
152
|
+
const skills = await discoverSkills({ paths: [tmpDir] });
|
|
153
|
+
expect(skills).toEqual([]);
|
|
154
|
+
});
|
|
155
|
+
it("rejects names with consecutive hyphens", async () => {
|
|
156
|
+
await writeSkill("double", `---
|
|
157
|
+
name: foo--bar
|
|
158
|
+
description: Bad
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
Content`);
|
|
162
|
+
const skills = await discoverSkills({ paths: [tmpDir] });
|
|
163
|
+
expect(skills).toEqual([]);
|
|
164
|
+
});
|
|
165
|
+
it("preserves colons in description values", async () => {
|
|
166
|
+
await writeSkill("colon", `---
|
|
167
|
+
name: colon-test
|
|
168
|
+
description: Build AI agents: stateful workflows
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
Content`);
|
|
172
|
+
const skills = await discoverSkills({ paths: [tmpDir] });
|
|
173
|
+
expect(skills).toHaveLength(1);
|
|
174
|
+
expect(skills[0].description).toBe("Build AI agents: stateful workflows");
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
// ── scanSkillFiles ──────────────────────────────────────────────────
|
|
178
|
+
describe("scanSkillFiles", () => {
|
|
179
|
+
it("returns auxiliary files excluding SKILL.md", async () => {
|
|
180
|
+
const dir = await writeSkill("with-files", `---
|
|
181
|
+
name: with-files
|
|
182
|
+
description: Has extra files
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
Content`, {
|
|
186
|
+
"references/api.md": "# API docs",
|
|
187
|
+
"scripts/setup.sh": "#!/bin/bash\necho hi",
|
|
188
|
+
});
|
|
189
|
+
const files = await scanSkillFiles(dir);
|
|
190
|
+
expect(files).toHaveLength(2);
|
|
191
|
+
const basenames = files.map((f) => path.relative(dir, f)).sort();
|
|
192
|
+
expect(basenames).toEqual(["references/api.md", "scripts/setup.sh"]);
|
|
193
|
+
});
|
|
194
|
+
it("excludes SKILL.md from results", async () => {
|
|
195
|
+
const dir = await writeSkill("only-skill", `---
|
|
196
|
+
name: only-skill
|
|
197
|
+
description: Just the skill file
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
Content`);
|
|
201
|
+
const files = await scanSkillFiles(dir);
|
|
202
|
+
expect(files).toEqual([]);
|
|
203
|
+
});
|
|
204
|
+
it("respects the limit parameter", async () => {
|
|
205
|
+
const extras = {};
|
|
206
|
+
for (let i = 0; i < 15; i++) {
|
|
207
|
+
extras[`file-${String(i).padStart(2, "0")}.txt`] = `content ${i}`;
|
|
208
|
+
}
|
|
209
|
+
const dir = await writeSkill("many-files", `---
|
|
210
|
+
name: many-files
|
|
211
|
+
description: Lots of files
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
Content`, extras);
|
|
215
|
+
const files = await scanSkillFiles(dir, 5);
|
|
216
|
+
expect(files).toHaveLength(5);
|
|
217
|
+
});
|
|
218
|
+
it("returns empty array for non-existent directory", async () => {
|
|
219
|
+
const files = await scanSkillFiles(path.join(tmpDir, "nope"));
|
|
220
|
+
expect(files).toEqual([]);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
// ── createSkillTool ─────────────────────────────────────────────────
|
|
224
|
+
describe("createSkillTool", () => {
|
|
225
|
+
const baseSkill = {
|
|
226
|
+
name: "test-skill",
|
|
227
|
+
description: "A test skill",
|
|
228
|
+
location: "/fake/test-skill/SKILL.md",
|
|
229
|
+
content: "# Test\n\nDo the thing.",
|
|
230
|
+
};
|
|
231
|
+
it("creates a tool with available skills in description", () => {
|
|
232
|
+
const skillTool = createSkillTool([baseSkill]);
|
|
233
|
+
expect(skillTool.description).toContain("test-skill");
|
|
234
|
+
expect(skillTool.description).toContain("A test skill");
|
|
235
|
+
expect(skillTool.description).toContain("<available_skills>");
|
|
236
|
+
});
|
|
237
|
+
it("lists multiple skills in description", () => {
|
|
238
|
+
const skills = [
|
|
239
|
+
baseSkill,
|
|
240
|
+
{ name: "other-skill", description: "Another one", location: "/fake/other/SKILL.md", content: "Other" },
|
|
241
|
+
];
|
|
242
|
+
const skillTool = createSkillTool(skills);
|
|
243
|
+
expect(skillTool.description).toContain("test-skill");
|
|
244
|
+
expect(skillTool.description).toContain("other-skill");
|
|
245
|
+
});
|
|
246
|
+
it("executes and returns skill content", async () => {
|
|
247
|
+
const dir = await writeSkill("real-skill", `---
|
|
248
|
+
name: real-skill
|
|
249
|
+
description: Real
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
# Instructions
|
|
253
|
+
|
|
254
|
+
Follow these steps.`, { "ref.md": "# Reference" });
|
|
255
|
+
const skill = {
|
|
256
|
+
name: "real-skill",
|
|
257
|
+
description: "Real",
|
|
258
|
+
location: path.join(dir, "SKILL.md"),
|
|
259
|
+
content: "# Instructions\n\nFollow these steps.",
|
|
260
|
+
};
|
|
261
|
+
const skillTool = createSkillTool([skill]);
|
|
262
|
+
const result = await skillTool.execute({ name: "real-skill" }, { toolCallId: "test", messages: [], abortSignal: undefined });
|
|
263
|
+
expect(result).toContain("<skill_content name=\"real-skill\">");
|
|
264
|
+
expect(result).toContain("# Skill: real-skill");
|
|
265
|
+
expect(result).toContain("Follow these steps.");
|
|
266
|
+
expect(result).toContain(`Base directory for this skill: ${dir}`);
|
|
267
|
+
expect(result).toContain("<skill_files>");
|
|
268
|
+
expect(result).toContain("ref.md</file>");
|
|
269
|
+
expect(result).toContain("</skill_content>");
|
|
270
|
+
});
|
|
271
|
+
it("omits skill_files section when no auxiliary files exist", async () => {
|
|
272
|
+
const dir = await writeSkill("bare-skill", `---
|
|
273
|
+
name: bare-skill
|
|
274
|
+
description: Bare
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
Content only`);
|
|
278
|
+
const skill = {
|
|
279
|
+
name: "bare-skill",
|
|
280
|
+
description: "Bare",
|
|
281
|
+
location: path.join(dir, "SKILL.md"),
|
|
282
|
+
content: "Content only",
|
|
283
|
+
};
|
|
284
|
+
const skillTool = createSkillTool([skill]);
|
|
285
|
+
const result = await skillTool.execute({ name: "bare-skill" }, { toolCallId: "test", messages: [], abortSignal: undefined });
|
|
286
|
+
expect(result).not.toContain("<skill_files>");
|
|
287
|
+
});
|
|
288
|
+
it("throws for unknown skill name", async () => {
|
|
289
|
+
const skillTool = createSkillTool([baseSkill]);
|
|
290
|
+
await expect(skillTool.execute({ name: "nope" }, { toolCallId: "test", messages: [], abortSignal: undefined })).rejects.toThrow('Skill "nope" not found');
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
//# sourceMappingURL=skills.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.test.js","sourceRoot":"","sources":["../../src/__tests__/skills.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,5 +6,6 @@ export { createSkillTool } from "./tools/skill.js";
|
|
|
6
6
|
export { findInstructions, loadInstructions } from "./instructions.js";
|
|
7
7
|
export { type OHDataTypes, type OHMetadata, type OHUIMessage, } from "./types/ui-message.js";
|
|
8
8
|
export { type OHDataPart, type OHSubagentPart, type OHCompactionPart, type OHRetryPart, type OHSessionLifecyclePart, isOHDataPart, isSubagentEvent, isCompactionEvent, isRetryEvent, isSessionLifecycleEvent, formatSSE, formatTextDelta, formatReasoningDelta, formatDataPart, formatToolStart, formatToolResult, formatToolError, formatStepStart, formatStepFinish, formatFinishMessage, formatDone, } from "./types/stream-parts.js";
|
|
9
|
+
export { extractUserInput } from "./messages.js";
|
|
9
10
|
export { sessionEventsToUIStream } from "./ui-stream.js";
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,EACL,eAAe,EACf,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,eAAe,GACrB,MAAM,YAAY,CAAC;AAIpB,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,YAAY,GAClB,MAAM,UAAU,CAAC;AAIlB,OAAO,EACL,OAAO,EACP,yBAAyB,EACzB,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,mBAAmB,GACzB,MAAM,cAAc,CAAC;AAItB,OAAO,EACL,cAAc,EACd,cAAc,EACd,KAAK,SAAS,EACd,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAIvE,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,WAAW,GACjB,MAAM,uBAAuB,CAAC;AAI/B,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,sBAAsB,EAG3B,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,uBAAuB,EAGvB,SAAS,EACT,eAAe,EACf,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,UAAU,GACX,MAAM,yBAAyB,CAAC;AAIjC,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,EACL,eAAe,EACf,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,eAAe,GACrB,MAAM,YAAY,CAAC;AAIpB,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,YAAY,GAClB,MAAM,UAAU,CAAC;AAIlB,OAAO,EACL,OAAO,EACP,yBAAyB,EACzB,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,mBAAmB,GACzB,MAAM,cAAc,CAAC;AAItB,OAAO,EACL,cAAc,EACd,cAAc,EACd,KAAK,SAAS,EACd,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAIvE,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,WAAW,GACjB,MAAM,uBAAuB,CAAC;AAI/B,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,sBAAsB,EAG3B,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,uBAAuB,EAGvB,SAAS,EACT,eAAe,EACf,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,UAAU,GACX,MAAM,yBAAyB,CAAC;AAIjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAIjD,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,8 @@ export {
|
|
|
15
15
|
isOHDataPart, isSubagentEvent, isCompactionEvent, isRetryEvent, isSessionLifecycleEvent,
|
|
16
16
|
// SSE formatters
|
|
17
17
|
formatSSE, formatTextDelta, formatReasoningDelta, formatDataPart, formatToolStart, formatToolResult, formatToolError, formatStepStart, formatStepFinish, formatFinishMessage, formatDone, } from "./types/stream-parts.js";
|
|
18
|
+
// ── Message utilities ────────────────────────────────────────────────
|
|
19
|
+
export { extractUserInput } from "./messages.js";
|
|
18
20
|
// ── UI Stream ───────────────────────────────────────────────────────
|
|
19
21
|
export { sessionEventsToUIStream } from "./ui-stream.js";
|
|
20
22
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wEAAwE;AAExE,OAAO,EACL,KAAK,EACL,eAAe,GAMhB,MAAM,YAAY,CAAC;AAEpB,uEAAuE;AAEvE,OAAO,EACL,iBAAiB,EACjB,eAAe,GAMhB,MAAM,UAAU,CAAC;AAElB,uEAAuE;AAEvE,OAAO,EACL,OAAO,EACP,yBAAyB,GAY1B,MAAM,cAAc,CAAC;AAEtB,uEAAuE;AAEvE,OAAO,EACL,cAAc,EACd,cAAc,GAGf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,uEAAuE;AAEvE,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAUvE,uEAAuE;AAEvE,OAAO;AAQL,cAAc;AACd,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,uBAAuB;AAEvB,iBAAiB;AACjB,SAAS,EACT,eAAe,EACf,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,UAAU,GACX,MAAM,yBAAyB,CAAC;AAEjC,uEAAuE;AAEvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,wEAAwE;AAExE,OAAO,EACL,KAAK,EACL,eAAe,GAMhB,MAAM,YAAY,CAAC;AAEpB,uEAAuE;AAEvE,OAAO,EACL,iBAAiB,EACjB,eAAe,GAMhB,MAAM,UAAU,CAAC;AAElB,uEAAuE;AAEvE,OAAO,EACL,OAAO,EACP,yBAAyB,GAY1B,MAAM,cAAc,CAAC;AAEtB,uEAAuE;AAEvE,OAAO,EACL,cAAc,EACd,cAAc,GAGf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,uEAAuE;AAEvE,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAUvE,uEAAuE;AAEvE,OAAO;AAQL,cAAc;AACd,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,uBAAuB;AAEvB,iBAAiB;AACjB,SAAS,EACT,eAAe,EACf,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,mBAAmB,EACnB,UAAU,GACX,MAAM,yBAAyB,CAAC;AAEjC,wEAAwE;AAExE,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,uEAAuE;AAEvE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { UIMessage, ModelMessage } from "ai";
|
|
2
|
+
/**
|
|
3
|
+
* Extract user input from an AI SDK 5 UIMessage array, ready to pass
|
|
4
|
+
* to `session.send()` or `session.toResponse()`.
|
|
5
|
+
*
|
|
6
|
+
* - Text-only → returns `string` (fast path, preserves existing behavior)
|
|
7
|
+
* - Has files → returns `ModelMessage[]` with properly decoded file data
|
|
8
|
+
*/
|
|
9
|
+
export declare function extractUserInput(messages: UIMessage[]): Promise<string | ModelMessage[]>;
|
|
10
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAElD;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,SAAS,EAAE,GACpB,OAAO,CAAC,MAAM,GAAG,YAAY,EAAE,CAAC,CA2ClC"}
|
package/dist/messages.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract user input from an AI SDK 5 UIMessage array, ready to pass
|
|
3
|
+
* to `session.send()` or `session.toResponse()`.
|
|
4
|
+
*
|
|
5
|
+
* - Text-only → returns `string` (fast path, preserves existing behavior)
|
|
6
|
+
* - Has files → returns `ModelMessage[]` with properly decoded file data
|
|
7
|
+
*/
|
|
8
|
+
export async function extractUserInput(messages) {
|
|
9
|
+
if (messages.length === 0) {
|
|
10
|
+
throw new Error("extractUserInput: messages array is empty");
|
|
11
|
+
}
|
|
12
|
+
const lastMessage = messages[messages.length - 1];
|
|
13
|
+
if (lastMessage.role !== "user") {
|
|
14
|
+
throw new Error(`extractUserInput: last message has role "${lastMessage.role}", expected "user"`);
|
|
15
|
+
}
|
|
16
|
+
const hasFiles = lastMessage.parts.some((p) => p.type === "file");
|
|
17
|
+
if (!hasFiles) {
|
|
18
|
+
// Text-only fast path
|
|
19
|
+
return lastMessage.parts
|
|
20
|
+
.filter((p) => p.type === "text")
|
|
21
|
+
.map((p) => p.text)
|
|
22
|
+
.join("");
|
|
23
|
+
}
|
|
24
|
+
// Has files — build ModelMessage manually so data URLs are decoded to
|
|
25
|
+
// raw base64, which is what FilePart.data expects. convertToModelMessages
|
|
26
|
+
// passes data URLs through as-is, causing providers to attempt an HTTP
|
|
27
|
+
// download of the data: scheme which fails.
|
|
28
|
+
const content = [];
|
|
29
|
+
for (const part of lastMessage.parts) {
|
|
30
|
+
if (part.type === "text") {
|
|
31
|
+
content.push({ type: "text", text: part.text });
|
|
32
|
+
}
|
|
33
|
+
else if (part.type === "file") {
|
|
34
|
+
const filePart = part;
|
|
35
|
+
content.push({
|
|
36
|
+
type: "file",
|
|
37
|
+
data: stripDataUrlPrefix(filePart.url),
|
|
38
|
+
mediaType: filePart.mediaType,
|
|
39
|
+
...(filePart.filename ? { filename: filePart.filename } : {}),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return [{ role: "user", content }];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Strip the `data:<mediaType>;base64,` prefix from a data URL,
|
|
47
|
+
* returning raw base64. Non-data URLs are returned as-is.
|
|
48
|
+
*/
|
|
49
|
+
function stripDataUrlPrefix(url) {
|
|
50
|
+
const match = url.match(/^data:[^;]+;base64,(.*)$/);
|
|
51
|
+
return match ? match[1] : url;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,QAAqB;IAErB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,CAAC;IAED,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAClD,IAAI,WAAW,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,4CAA4C,WAAW,CAAC,IAAI,oBAAoB,CACjF,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IAElE,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,sBAAsB;QACtB,OAAO,WAAW,CAAC,KAAK;aACrB,MAAM,CAAC,CAAC,CAAC,EAA4C,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;aAC1E,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aAClB,IAAI,CAAC,EAAE,CAAC,CAAC;IACd,CAAC;IAED,sEAAsE;IACtE,0EAA0E;IAC1E,uEAAuE;IACvE,4CAA4C;IAC5C,MAAM,OAAO,GAAiH,EAAE,CAAC;IAEjI,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,KAAK,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACzB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAClD,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAChC,MAAM,QAAQ,GAAG,IAA2E,CAAC;YAC7F,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,kBAAkB,CAAC,QAAQ,CAAC,GAAG,CAAC;gBACtC,SAAS,EAAE,QAAQ,CAAC,SAAS;gBAC7B,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC9D,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAmB,CAAC;AACvD,CAAC;AAED;;;GAGG;AACH,SAAS,kBAAkB,CAAC,GAAW;IACrC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;IACpD,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AAChC,CAAC"}
|