@ai-ecoverse/slicc-agent 3.7.0 → 3.9.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/README.md +46 -1
- package/dist/client.js +2 -1
- package/dist/client.js.map +2 -2
- package/dist/control.js +3 -2
- package/dist/control.js.map +2 -2
- package/dist/host.js +11 -2
- package/dist/host.js.map +2 -2
- package/dist/main.js +10 -2
- package/dist/main.js.map +2 -2
- package/dist/roles/frontmatter.js +61 -35
- package/dist/roles/frontmatter.js.map +3 -3
- package/dist/roles/roles.js +1 -0
- package/dist/roles/roles.js.map +2 -2
- package/dist/scoops/extension.js +40 -29
- package/dist/scoops/extension.js.map +2 -2
- package/dist/scoops/from.js +5 -0
- package/dist/scoops/from.js.map +7 -0
- package/dist/scoops/index.js +9 -4
- package/dist/scoops/index.js.map +2 -2
- package/dist/scoops/service.js +11 -1
- package/dist/scoops/service.js.map +2 -2
- package/dist/scoops/sync.js +1 -0
- package/dist/scoops/sync.js.map +2 -2
- package/dist/services.js.map +2 -2
- package/dist/skills/index.js +172 -0
- package/dist/skills/index.js.map +7 -0
- package/dist/skills/skills.js +162 -0
- package/dist/skills/skills.js.map +7 -0
- package/dist/skills/templates.js +97 -0
- package/dist/skills/templates.js.map +7 -0
- package/dist/spectrum/agent-port.js +1 -1
- package/dist/spectrum/agent-port.js.map +2 -2
- package/dist/spectrum/messages.js +20 -6
- package/dist/spectrum/messages.js.map +2 -2
- package/dist/types/client.d.ts +2 -1
- package/dist/types/control.d.ts +2 -1
- package/dist/types/host.d.ts +2 -0
- package/dist/types/roles/roles.d.ts +1 -0
- package/dist/types/scoops/extension.d.ts +19 -9
- package/dist/types/scoops/from.d.ts +5 -0
- package/dist/types/scoops/index.d.ts +2 -1
- package/dist/types/scoops/service.d.ts +4 -0
- package/dist/types/services.d.ts +6 -0
- package/dist/types/skills/index.d.ts +45 -0
- package/dist/types/skills/skills.d.ts +32 -0
- package/dist/types/skills/templates.d.ts +18 -0
- package/dist/types/spectrum/messages.d.ts +2 -1
- package/package.json +1 -1
- package/packages/vfs-root/prompts/parallel-review.md +59 -0
- package/packages/vfs-root/prompts/review-loop.md +48 -0
- package/packages/vfs-root/skills/agent/SKILL.md +1 -1
- package/packages/vfs-root/skills/licks/SKILL.md +49 -0
- package/packages/vfs-root/skills/skill-authoring/SKILL.md +76 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { parseFrontmatter } from "../roles/frontmatter.js";
|
|
2
|
+
const MAX_NAME_LENGTH = 64;
|
|
3
|
+
const MAX_DESCRIPTION_LENGTH = 1024;
|
|
4
|
+
function dirname(path) {
|
|
5
|
+
return path.slice(0, path.lastIndexOf("/")) || "/";
|
|
6
|
+
}
|
|
7
|
+
function basename(path) {
|
|
8
|
+
return path.slice(path.lastIndexOf("/") + 1);
|
|
9
|
+
}
|
|
10
|
+
function validateName(name) {
|
|
11
|
+
const errors = [];
|
|
12
|
+
if (name.length > MAX_NAME_LENGTH)
|
|
13
|
+
errors.push(`name exceeds ${MAX_NAME_LENGTH} characters (${name.length})`);
|
|
14
|
+
if (!/^[a-z0-9-]+$/.test(name))
|
|
15
|
+
errors.push("name contains invalid characters (must be lowercase a-z, 0-9, hyphens only)");
|
|
16
|
+
if (name.startsWith("-") || name.endsWith("-"))
|
|
17
|
+
errors.push("name must not start or end with a hyphen");
|
|
18
|
+
if (name.includes("--")) errors.push("name must not contain consecutive hyphens");
|
|
19
|
+
return errors;
|
|
20
|
+
}
|
|
21
|
+
function validateDescription(description) {
|
|
22
|
+
if (typeof description !== "string" || description.trim() === "")
|
|
23
|
+
return ["description is required"];
|
|
24
|
+
if (description.length > MAX_DESCRIPTION_LENGTH)
|
|
25
|
+
return [`description exceeds ${MAX_DESCRIPTION_LENGTH} characters (${description.length})`];
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
function skillFromText(text, filePath, source) {
|
|
29
|
+
const declared = basename(filePath) === "SKILL.md";
|
|
30
|
+
const { fields, problems } = parseFrontmatter(text);
|
|
31
|
+
const diagnostics = [];
|
|
32
|
+
const description = typeof fields.description === "string" ? fields.description.trim() : "";
|
|
33
|
+
if (!declared && !description) return { skills: [], diagnostics };
|
|
34
|
+
for (const problem of problems.filter((item) => !item.includes("nested values")))
|
|
35
|
+
diagnostics.push({ path: filePath, message: problem });
|
|
36
|
+
for (const message of validateDescription(description))
|
|
37
|
+
diagnostics.push({ path: filePath, message });
|
|
38
|
+
const baseDir = dirname(filePath);
|
|
39
|
+
const name = typeof fields.name === "string" && fields.name || basename(baseDir);
|
|
40
|
+
for (const message of validateName(name)) diagnostics.push({ path: filePath, message });
|
|
41
|
+
if (!description) return { skills: [], diagnostics };
|
|
42
|
+
const skill = {
|
|
43
|
+
name,
|
|
44
|
+
description,
|
|
45
|
+
filePath,
|
|
46
|
+
baseDir,
|
|
47
|
+
source,
|
|
48
|
+
disableModelInvocation: fields["disable-model-invocation"] === true
|
|
49
|
+
};
|
|
50
|
+
return { skills: [skill], diagnostics };
|
|
51
|
+
}
|
|
52
|
+
async function loadFile(files, filePath, source, context) {
|
|
53
|
+
const read = await files.readTextFile(filePath, context);
|
|
54
|
+
if (!read.ok)
|
|
55
|
+
return { skills: [], diagnostics: [{ path: filePath, message: read.error.message }] };
|
|
56
|
+
return skillFromText(read.value, filePath, source);
|
|
57
|
+
}
|
|
58
|
+
async function loadSkillsFromDir(files, dir, source, context, root = true, visited = /* @__PURE__ */ new Set()) {
|
|
59
|
+
const out = { skills: [], diagnostics: [] };
|
|
60
|
+
const real = await files.canonicalPath(dir, context);
|
|
61
|
+
const key = real.ok ? real.value : dir;
|
|
62
|
+
if (visited.has(key)) return out;
|
|
63
|
+
visited.add(key);
|
|
64
|
+
const listed = await files.listDir(dir, context);
|
|
65
|
+
if (!listed.ok) return out;
|
|
66
|
+
const merge = (found) => {
|
|
67
|
+
out.skills.push(...found.skills);
|
|
68
|
+
out.diagnostics.push(...found.diagnostics);
|
|
69
|
+
};
|
|
70
|
+
if (listed.value.some((entry) => entry.name === "SKILL.md" && entry.kind !== "directory")) {
|
|
71
|
+
merge(await loadFile(files, `${dir}/SKILL.md`, source, context));
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
const entries = [...listed.value].sort((a, b) => a.name.localeCompare(b.name));
|
|
75
|
+
for (const entry of entries) {
|
|
76
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
77
|
+
const path = `${dir}/${entry.name}`;
|
|
78
|
+
if (entry.kind === "directory" || entry.kind === "symlink")
|
|
79
|
+
merge(await loadSkillsFromDir(files, path, source, context, false, visited));
|
|
80
|
+
if (entry.kind !== "directory" && root && entry.name.endsWith(".md"))
|
|
81
|
+
merge(await loadFile(files, path, source, context));
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
async function loadSkills(files, roots, context) {
|
|
86
|
+
const byName = /* @__PURE__ */ new Map();
|
|
87
|
+
const seen = /* @__PURE__ */ new Set();
|
|
88
|
+
const diagnostics = [];
|
|
89
|
+
const collisions = [];
|
|
90
|
+
for (const root of roots) {
|
|
91
|
+
const found = await loadSkillsFromDir(files, root.dir, root.source, context);
|
|
92
|
+
diagnostics.push(...found.diagnostics);
|
|
93
|
+
for (const skill of found.skills) {
|
|
94
|
+
if (seen.has(skill.filePath)) continue;
|
|
95
|
+
const existing = byName.get(skill.name);
|
|
96
|
+
if (existing)
|
|
97
|
+
collisions.push({
|
|
98
|
+
path: skill.filePath,
|
|
99
|
+
message: `name "${skill.name}" collision; ${existing.filePath} is used`
|
|
100
|
+
});
|
|
101
|
+
else {
|
|
102
|
+
byName.set(skill.name, skill);
|
|
103
|
+
seen.add(skill.filePath);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return { skills: [...byName.values()], diagnostics: [...diagnostics, ...collisions] };
|
|
108
|
+
}
|
|
109
|
+
function escapeXml(text) {
|
|
110
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
111
|
+
}
|
|
112
|
+
function formatSkillsForPrompt(skills) {
|
|
113
|
+
const visible = skills.filter((skill) => !skill.disableModelInvocation);
|
|
114
|
+
if (!visible.length) return "";
|
|
115
|
+
const lines = [
|
|
116
|
+
"The following skills provide specialized instructions for specific tasks.",
|
|
117
|
+
"Use the read tool to load a skill's file when the task matches its description.",
|
|
118
|
+
"When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.",
|
|
119
|
+
"",
|
|
120
|
+
"<available_skills>"
|
|
121
|
+
];
|
|
122
|
+
for (const skill of visible)
|
|
123
|
+
lines.push(
|
|
124
|
+
" <skill>",
|
|
125
|
+
` <name>${escapeXml(skill.name)}</name>`,
|
|
126
|
+
` <description>${escapeXml(skill.description)}</description>`,
|
|
127
|
+
` <location>${escapeXml(skill.filePath)}</location>`,
|
|
128
|
+
" </skill>"
|
|
129
|
+
);
|
|
130
|
+
lines.push("</available_skills>");
|
|
131
|
+
return lines.join("\n");
|
|
132
|
+
}
|
|
133
|
+
async function expandSkillCommand(text, skills, files, context) {
|
|
134
|
+
if (!text.startsWith("/skill:")) return text;
|
|
135
|
+
const space = text.indexOf(" ");
|
|
136
|
+
const name = space === -1 ? text.slice(7) : text.slice(7, space);
|
|
137
|
+
const args = space === -1 ? "" : text.slice(space + 1).trim();
|
|
138
|
+
const skill = skills.find((candidate) => candidate.name === name);
|
|
139
|
+
if (!skill) return text;
|
|
140
|
+
const read = await files.readTextFile(skill.filePath, context);
|
|
141
|
+
if (!read.ok) return text;
|
|
142
|
+
const body = parseFrontmatter(read.value).body.trim();
|
|
143
|
+
const block = `<skill name="${skill.name}" location="${skill.filePath}">
|
|
144
|
+
References are relative to ${skill.baseDir}.
|
|
145
|
+
|
|
146
|
+
${body}
|
|
147
|
+
</skill>`;
|
|
148
|
+
return args ? `${block}
|
|
149
|
+
|
|
150
|
+
${args}` : block;
|
|
151
|
+
}
|
|
152
|
+
export {
|
|
153
|
+
MAX_DESCRIPTION_LENGTH,
|
|
154
|
+
MAX_NAME_LENGTH,
|
|
155
|
+
expandSkillCommand,
|
|
156
|
+
formatSkillsForPrompt,
|
|
157
|
+
loadSkills,
|
|
158
|
+
loadSkillsFromDir,
|
|
159
|
+
skillFromText,
|
|
160
|
+
validateName
|
|
161
|
+
};
|
|
162
|
+
//# sourceMappingURL=skills.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/skills/skills.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { parseFrontmatter } from '../roles/frontmatter.ts';\n\nexport const MAX_NAME_LENGTH = 64;\nexport const MAX_DESCRIPTION_LENGTH = 1024;\n\nexport type SkillFiles = Pick<ExecutionEnv, 'readTextFile' | 'listDir' | 'canonicalPath'>;\n\nexport type SkillSource = 'user' | 'compat' | 'package' | 'builtin';\n\nexport interface Skill {\n name: string;\n description: string;\n filePath: string;\n baseDir: string;\n source: SkillSource;\n disableModelInvocation: boolean;\n}\n\nexport interface Diagnostic {\n path: string;\n message: string;\n}\n\nexport interface Loaded {\n skills: Skill[];\n diagnostics: Diagnostic[];\n}\n\nexport interface SkillRoot {\n dir: string;\n source: SkillSource;\n}\n\nfunction dirname(path: string): string {\n return path.slice(0, path.lastIndexOf('/')) || '/';\n}\n\nfunction basename(path: string): string {\n return path.slice(path.lastIndexOf('/') + 1);\n}\n\nexport function validateName(name: string): string[] {\n const errors: string[] = [];\n if (name.length > MAX_NAME_LENGTH)\n errors.push(`name exceeds ${MAX_NAME_LENGTH} characters (${name.length})`);\n if (!/^[a-z0-9-]+$/.test(name))\n errors.push('name contains invalid characters (must be lowercase a-z, 0-9, hyphens only)');\n if (name.startsWith('-') || name.endsWith('-'))\n errors.push('name must not start or end with a hyphen');\n if (name.includes('--')) errors.push('name must not contain consecutive hyphens');\n return errors;\n}\n\nfunction validateDescription(description: unknown): string[] {\n if (typeof description !== 'string' || description.trim() === '')\n return ['description is required'];\n if (description.length > MAX_DESCRIPTION_LENGTH)\n return [`description exceeds ${MAX_DESCRIPTION_LENGTH} characters (${description.length})`];\n return [];\n}\n\nexport function skillFromText(text: string, filePath: string, source: SkillSource): Loaded {\n const declared = basename(filePath) === 'SKILL.md';\n const { fields, problems } = parseFrontmatter(text);\n const diagnostics: Diagnostic[] = [];\n const description = typeof fields.description === 'string' ? fields.description.trim() : '';\n if (!declared && !description) return { skills: [], diagnostics };\n for (const problem of problems.filter((item) => !item.includes('nested values')))\n diagnostics.push({ path: filePath, message: problem });\n for (const message of validateDescription(description))\n diagnostics.push({ path: filePath, message });\n const baseDir = dirname(filePath);\n const name = (typeof fields.name === 'string' && fields.name) || basename(baseDir);\n for (const message of validateName(name)) diagnostics.push({ path: filePath, message });\n if (!description) return { skills: [], diagnostics };\n const skill: Skill = {\n name,\n description,\n filePath,\n baseDir,\n source,\n disableModelInvocation: fields['disable-model-invocation'] === true,\n };\n return { skills: [skill], diagnostics };\n}\n\nasync function loadFile(\n files: SkillFiles,\n filePath: string,\n source: SkillSource,\n context: Context\n): Promise<Loaded> {\n const read = await files.readTextFile(filePath, context);\n if (!read.ok)\n return { skills: [], diagnostics: [{ path: filePath, message: read.error.message }] };\n return skillFromText(read.value, filePath, source);\n}\n\nexport async function loadSkillsFromDir(\n files: SkillFiles,\n dir: string,\n source: SkillSource,\n context: Context,\n root = true,\n visited = new Set<string>()\n): Promise<Loaded> {\n const out: Loaded = { skills: [], diagnostics: [] };\n const real = await files.canonicalPath(dir, context);\n const key = real.ok ? real.value : dir;\n if (visited.has(key)) return out;\n visited.add(key);\n const listed = await files.listDir(dir, context);\n if (!listed.ok) return out;\n const merge = (found: Loaded) => {\n out.skills.push(...found.skills);\n out.diagnostics.push(...found.diagnostics);\n };\n if (listed.value.some((entry) => entry.name === 'SKILL.md' && entry.kind !== 'directory')) {\n merge(await loadFile(files, `${dir}/SKILL.md`, source, context));\n return out;\n }\n const entries = [...listed.value].sort((a, b) => a.name.localeCompare(b.name));\n for (const entry of entries) {\n if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;\n const path = `${dir}/${entry.name}`;\n if (entry.kind === 'directory' || entry.kind === 'symlink')\n merge(await loadSkillsFromDir(files, path, source, context, false, visited));\n if (entry.kind !== 'directory' && root && entry.name.endsWith('.md'))\n merge(await loadFile(files, path, source, context));\n }\n return out;\n}\n\nexport async function loadSkills(\n files: SkillFiles,\n roots: readonly SkillRoot[],\n context: Context\n): Promise<Loaded> {\n const byName = new Map<string, Skill>();\n const seen = new Set<string>();\n const diagnostics: Diagnostic[] = [];\n const collisions: Diagnostic[] = [];\n for (const root of roots) {\n const found = await loadSkillsFromDir(files, root.dir, root.source, context);\n diagnostics.push(...found.diagnostics);\n for (const skill of found.skills) {\n if (seen.has(skill.filePath)) continue;\n const existing = byName.get(skill.name);\n if (existing)\n collisions.push({\n path: skill.filePath,\n message: `name \"${skill.name}\" collision; ${existing.filePath} is used`,\n });\n else {\n byName.set(skill.name, skill);\n seen.add(skill.filePath);\n }\n }\n }\n return { skills: [...byName.values()], diagnostics: [...diagnostics, ...collisions] };\n}\n\nfunction escapeXml(text: string): string {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nexport function formatSkillsForPrompt(skills: readonly Skill[]): string {\n const visible = skills.filter((skill) => !skill.disableModelInvocation);\n if (!visible.length) return '';\n const lines = [\n 'The following skills provide specialized instructions for specific tasks.',\n \"Use the read tool to load a skill's file when the task matches its description.\",\n 'When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.',\n '',\n '<available_skills>',\n ];\n for (const skill of visible)\n lines.push(\n ' <skill>',\n ` <name>${escapeXml(skill.name)}</name>`,\n ` <description>${escapeXml(skill.description)}</description>`,\n ` <location>${escapeXml(skill.filePath)}</location>`,\n ' </skill>'\n );\n lines.push('</available_skills>');\n return lines.join('\\n');\n}\n\nexport async function expandSkillCommand(\n text: string,\n skills: readonly Skill[],\n files: SkillFiles,\n context: Context\n): Promise<string> {\n if (!text.startsWith('/skill:')) return text;\n const space = text.indexOf(' ');\n const name = space === -1 ? text.slice(7) : text.slice(7, space);\n const args = space === -1 ? '' : text.slice(space + 1).trim();\n const skill = skills.find((candidate) => candidate.name === name);\n if (!skill) return text;\n const read = await files.readTextFile(skill.filePath, context);\n if (!read.ok) return text;\n const body = parseFrontmatter(read.value).body.trim();\n const block = `<skill name=\"${skill.name}\" location=\"${skill.filePath}\">\\nReferences are relative to ${skill.baseDir}.\\n\\n${body}\\n</skill>`;\n return args ? `${block}\\n\\n${args}` : block;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { parseFrontmatter } from "../roles/frontmatter.js";
|
|
2
|
+
function parseCommandArgs(text) {
|
|
3
|
+
const args = [];
|
|
4
|
+
let current = "";
|
|
5
|
+
let quote = null;
|
|
6
|
+
for (const char of text) {
|
|
7
|
+
if (quote) {
|
|
8
|
+
if (char === quote) quote = null;
|
|
9
|
+
else current += char;
|
|
10
|
+
} else if (char === '"' || char === "'") quote = char;
|
|
11
|
+
else if (/\s/.test(char)) {
|
|
12
|
+
if (current) args.push(current);
|
|
13
|
+
current = "";
|
|
14
|
+
} else current += char;
|
|
15
|
+
}
|
|
16
|
+
if (current) args.push(current);
|
|
17
|
+
return args;
|
|
18
|
+
}
|
|
19
|
+
function all(args, target) {
|
|
20
|
+
return target === "@" || target === "ARGUMENTS" ? args.join(" ") : args[Number.parseInt(target, 10) - 1];
|
|
21
|
+
}
|
|
22
|
+
function substituteArgs(content, args) {
|
|
23
|
+
return content.replace(
|
|
24
|
+
/\$\{(\d+|ARGUMENTS|@):-([^}]*)\}|\$\{@:(\d+)(?::(\d+))?\}|\$(ARGUMENTS|@|\d+)/g,
|
|
25
|
+
(_match, fallbackTarget, fallback, sliceStart, sliceLength, simple) => {
|
|
26
|
+
if (fallbackTarget) return all(args, fallbackTarget) || fallback;
|
|
27
|
+
if (sliceStart) {
|
|
28
|
+
const start = Math.max(0, Number.parseInt(sliceStart, 10) - 1);
|
|
29
|
+
const end = sliceLength ? start + Number.parseInt(sliceLength, 10) : void 0;
|
|
30
|
+
return args.slice(start, end).join(" ");
|
|
31
|
+
}
|
|
32
|
+
return all(args, simple) ?? "";
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
function templateFromText(text, filePath) {
|
|
37
|
+
const { fields, body } = parseFrontmatter(text);
|
|
38
|
+
const name = filePath.slice(filePath.lastIndexOf("/") + 1).replace(/\.md$/, "");
|
|
39
|
+
let description = typeof fields.description === "string" ? fields.description : "";
|
|
40
|
+
if (!description) {
|
|
41
|
+
const first = body.split("\n").find((line) => line.trim()) ?? "";
|
|
42
|
+
description = first.length > 60 ? `${first.slice(0, 60)}...` : first;
|
|
43
|
+
}
|
|
44
|
+
const hint = fields["argument-hint"];
|
|
45
|
+
return {
|
|
46
|
+
name,
|
|
47
|
+
description,
|
|
48
|
+
...typeof hint === "string" && hint ? { argumentHint: hint } : {},
|
|
49
|
+
content: body,
|
|
50
|
+
filePath
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
async function loadTemplates(files, sources, context) {
|
|
54
|
+
const out = { templates: [], diagnostics: [] };
|
|
55
|
+
const add = async (path) => {
|
|
56
|
+
if (out.templates.some((template) => template.filePath === path)) return;
|
|
57
|
+
const read = await files.readTextFile(path, context);
|
|
58
|
+
if (!read.ok) out.diagnostics.push({ path, message: read.error.message });
|
|
59
|
+
else {
|
|
60
|
+
const template = templateFromText(read.value, path);
|
|
61
|
+
const existing = out.templates.find((item) => item.name === template.name);
|
|
62
|
+
if (existing)
|
|
63
|
+
out.diagnostics.push({
|
|
64
|
+
path,
|
|
65
|
+
message: `name "${template.name}" collision; ${existing.filePath} is used`
|
|
66
|
+
});
|
|
67
|
+
else out.templates.push(template);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
for (const source of sources) {
|
|
71
|
+
if (source.endsWith(".md") && !source.endsWith("*.md")) {
|
|
72
|
+
await add(source);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const dir = source.replace(/\/\*\.md$/, "");
|
|
76
|
+
const listed = await files.listDir(dir, context);
|
|
77
|
+
if (!listed.ok) continue;
|
|
78
|
+
const names = listed.value.filter((entry) => entry.kind !== "directory" && entry.name.endsWith(".md")).map((entry) => entry.name).sort();
|
|
79
|
+
for (const name of names) await add(`${dir}/${name}`);
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
function expandPromptTemplate(text, templates) {
|
|
84
|
+
const match = /^\/([^\s]+)(?:\s+([\s\S]*))?$/.exec(text);
|
|
85
|
+
if (!match) return text;
|
|
86
|
+
const template = templates.find((item) => item.name === match[1]);
|
|
87
|
+
if (!template) return text;
|
|
88
|
+
return substituteArgs(template.content, parseCommandArgs(match[2] ?? ""));
|
|
89
|
+
}
|
|
90
|
+
export {
|
|
91
|
+
expandPromptTemplate,
|
|
92
|
+
loadTemplates,
|
|
93
|
+
parseCommandArgs,
|
|
94
|
+
substituteArgs,
|
|
95
|
+
templateFromText
|
|
96
|
+
};
|
|
97
|
+
//# sourceMappingURL=templates.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/skills/templates.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport { parseFrontmatter } from '../roles/frontmatter.ts';\nimport type { Diagnostic, SkillFiles } from './skills.ts';\n\nexport interface PromptTemplate {\n name: string;\n description: string;\n argumentHint?: string;\n content: string;\n filePath: string;\n}\n\nexport interface LoadedTemplates {\n templates: PromptTemplate[];\n diagnostics: Diagnostic[];\n}\n\nexport function parseCommandArgs(text: string): string[] {\n const args: string[] = [];\n let current = '';\n let quote: string | null = null;\n for (const char of text) {\n if (quote) {\n if (char === quote) quote = null;\n else current += char;\n } else if (char === '\"' || char === \"'\") quote = char;\n else if (/\\s/.test(char)) {\n if (current) args.push(current);\n current = '';\n } else current += char;\n }\n if (current) args.push(current);\n return args;\n}\n\nfunction all(args: readonly string[], target: string): string | undefined {\n return target === '@' || target === 'ARGUMENTS'\n ? args.join(' ')\n : args[Number.parseInt(target, 10) - 1];\n}\n\nexport function substituteArgs(content: string, args: readonly string[]): string {\n return content.replace(\n /\\$\\{(\\d+|ARGUMENTS|@):-([^}]*)\\}|\\$\\{@:(\\d+)(?::(\\d+))?\\}|\\$(ARGUMENTS|@|\\d+)/g,\n (_match, fallbackTarget, fallback, sliceStart, sliceLength, simple) => {\n if (fallbackTarget) return all(args, fallbackTarget) || fallback;\n if (sliceStart) {\n const start = Math.max(0, Number.parseInt(sliceStart, 10) - 1);\n const end = sliceLength ? start + Number.parseInt(sliceLength, 10) : undefined;\n return args.slice(start, end).join(' ');\n }\n return all(args, simple) ?? '';\n }\n );\n}\n\nexport function templateFromText(text: string, filePath: string): PromptTemplate {\n const { fields, body } = parseFrontmatter(text);\n const name = filePath.slice(filePath.lastIndexOf('/') + 1).replace(/\\.md$/, '');\n let description = typeof fields.description === 'string' ? fields.description : '';\n if (!description) {\n const first = body.split('\\n').find((line) => line.trim()) ?? '';\n description = first.length > 60 ? `${first.slice(0, 60)}...` : first;\n }\n const hint = fields['argument-hint'];\n return {\n name,\n description,\n ...(typeof hint === 'string' && hint ? { argumentHint: hint } : {}),\n content: body,\n filePath,\n };\n}\n\nexport async function loadTemplates(\n files: SkillFiles,\n sources: readonly string[],\n context: Context\n): Promise<LoadedTemplates> {\n const out: LoadedTemplates = { templates: [], diagnostics: [] };\n const add = async (path: string) => {\n if (out.templates.some((template) => template.filePath === path)) return;\n const read = await files.readTextFile(path, context);\n if (!read.ok) out.diagnostics.push({ path, message: read.error.message });\n else {\n const template = templateFromText(read.value, path);\n const existing = out.templates.find((item) => item.name === template.name);\n if (existing)\n out.diagnostics.push({\n path,\n message: `name \"${template.name}\" collision; ${existing.filePath} is used`,\n });\n else out.templates.push(template);\n }\n };\n for (const source of sources) {\n if (source.endsWith('.md') && !source.endsWith('*.md')) {\n await add(source);\n continue;\n }\n const dir = source.replace(/\\/\\*\\.md$/, '');\n const listed = await files.listDir(dir, context);\n if (!listed.ok) continue;\n const names = listed.value\n .filter((entry) => entry.kind !== 'directory' && entry.name.endsWith('.md'))\n .map((entry) => entry.name)\n .sort();\n for (const name of names) await add(`${dir}/${name}`);\n }\n return out;\n}\n\nexport function expandPromptTemplate(text: string, templates: readonly PromptTemplate[]): string {\n const match = /^\\/([^\\s]+)(?:\\s+([\\s\\S]*))?$/.exec(text);\n if (!match) return text;\n const template = templates.find((item) => item.name === match[1]);\n if (!template) return text;\n return substituteArgs(template.content, parseCommandArgs(match[2] ?? ''));\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -188,7 +188,7 @@ class AgentAdapter extends Emitter {
|
|
|
188
188
|
return [];
|
|
189
189
|
}
|
|
190
190
|
commands() {
|
|
191
|
-
return [];
|
|
191
|
+
return (this.#connection.commands?.value ?? []).map((command) => ({ ...command }));
|
|
192
192
|
}
|
|
193
193
|
createScoop(parentId, name) {
|
|
194
194
|
const provisional = `scoop:creating-${crypto.randomUUID()}`;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/spectrum/agent-port.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n Agent,\n AgentEvents,\n AgentPort,\n AgentStatus,\n FrozenCone,\n Message,\n Outgoing,\n SlashCommand,\n Thinking,\n UserMessage,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport type { AgentConnection } from '../client.ts';\nimport { entryLick } from '../licks/state.ts';\nimport type { AgentSettingsChange, AgentSummary } from '../services.ts';\nimport { Emitter } from './emitter.ts';\nimport { isBusy, queued, toMessages } from './messages.ts';\n\nexport const CONE = 'cone';\n\nconst thinking: Record<string, Thinking> = {\n off: 'off',\n minimal: 'low',\n low: 'low',\n medium: 'medium',\n high: 'high',\n xhigh: 'high',\n max: 'high',\n};\n\ninterface AgentDoc {\n model?: { provider: string; modelId: string };\n thinkingLevel?: string;\n}\n\nfunction status(view: ConversationView | undefined, messages: readonly Message[]): AgentStatus {\n if (isBusy(view)) {\n const live = view?.docs['pi.live'] as { tools?: { status: string }[] } | undefined;\n return live?.tools?.some((slot) => slot.status === 'running') ? 'working' : 'thinking';\n }\n const last = messages.at(-1);\n return last?.role === 'assistant' && last.status === 'error' ? 'error' : 'idle';\n}\n\nexport class AgentAdapter extends Emitter<AgentEvents> implements AgentPort {\n readonly #connection: AgentConnection;\n readonly #ready: Promise<void>;\n readonly #messages = new Map<string, Message[]>();\n #active: string | null = null;\n #creating = new Map<string, Promise<string | null>>();\n\n constructor(connection: AgentConnection) {\n super();\n this.#connection = connection;\n this.#ready = new Promise((resolve) => {\n connection.transcript.subscribe(() => {\n this.#refresh();\n resolve();\n });\n connection.deliveries.subscribe(() => this.#refresh());\n connection.views?.subscribe(() => this.#refresh());\n connection.agents?.subscribe(() => this.#refresh());\n });\n }\n\n #summary(): readonly AgentSummary[] {\n const agents = this.#connection.agents?.value?.agents;\n return agents?.length\n ? agents\n : [{ id: CONE, name: 'sliccy', kind: 'cone', parentId: null, role: null }];\n }\n\n #view(agentId: string = this.active()): ConversationView | undefined {\n const views = this.#connection.views?.value;\n const view = views?.[agentId];\n if (view) return view;\n const cone = this.#connection.agents?.value?.active ?? CONE;\n return agentId === cone ? this.#connection.transcript.value : undefined;\n }\n\n #refresh(): void {\n this.emit('agents', this.list());\n for (const summary of this.#summary()) this.#update(summary.id);\n for (const id of [...this.#messages.keys()])\n if (!this.#summary().some((summary) => summary.id === id)) this.#messages.delete(id);\n }\n\n #update(agentId: string): void {\n const view = this.#view(agentId);\n if (!view) return;\n const before = this.#messages.get(agentId) ?? [];\n const after = toMessages(view, this.#connection.deliveries.value);\n this.#messages.set(agentId, after);\n if (before.length !== after.length) {\n this.emit('messages', agentId);\n return;\n }\n const changed = after.filter(\n (message, index) => JSON.stringify(message) !== JSON.stringify(before[index])\n );\n if (changed.length === 1) this.emit('message', { agentId, message: changed[0] as Message });\n else if (changed.length > 1) this.emit('messages', agentId);\n }\n\n ready(): Promise<void> {\n return this.#ready;\n }\n\n list(): readonly Agent[] {\n return this.#summary().map((summary) => {\n const view = this.#view(summary.id);\n const doc = view?.docs['pi.agent'] as AgentDoc | undefined;\n return {\n id: summary.id,\n name: summary.role ? `${summary.name} \u00B7 ${summary.role}` : summary.name,\n kind: summary.kind,\n parentId: summary.parentId,\n status: status(view, this.#messages.get(summary.id) ?? []),\n model: doc?.model ? `${doc.model.provider}/${doc.model.modelId}` : '',\n contextFill: 0,\n unread: 0,\n thinking: thinking[doc?.thinkingLevel ?? 'off'] ?? 'off',\n };\n });\n }\n\n active(): string {\n const known = this.#summary();\n if (this.#active && this.#creating.has(this.#active)) return this.#active;\n if (this.#active && known.some((summary) => summary.id === this.#active)) return this.#active;\n return this.#connection.agents?.value?.active ?? CONE;\n }\n\n select(id: string): void {\n if (this.#creating.has(id)) {\n this.#active = id;\n this.emit('active', id);\n return;\n }\n const found = this.#summary().find((summary) => summary.id === id);\n if (!found) return;\n this.#active = id;\n if (found.kind === 'cone') void this.#connection.control.selectCone(id, BACKGROUND_CONTEXT);\n this.emit('active', id);\n this.emit('messages', id);\n }\n\n messages(agentId: string = this.active()): readonly Message[] {\n return this.#messages.get(agentId) ?? [];\n }\n\n async send(agentId: string, input: string | Outgoing): Promise<void> {\n const outgoing = typeof input === 'string' ? { text: input } : input;\n const target = this.#creating.has(agentId) ? await this.#creating.get(agentId) : agentId;\n if (target === null) throw new Error('The scoop could not be created.');\n const sent = await this.#connection.control.send(\n {\n text: outgoing.text,\n whenBusy: outgoing.mode === 'queue' ? 'followUp' : 'steer',\n requestId: crypto.randomUUID(),\n agentId: target || null,\n },\n BACKGROUND_CONTEXT\n );\n if (!sent.accepted) throw new Error(sent.error.message);\n }\n\n async rewind(_agentId: string, messageId: string): Promise<Outgoing | null> {\n const rewound = await this.#connection.control.rewind(messageId, BACKGROUND_CONTEXT);\n return rewound.done ? { text: rewound.text } : null;\n }\n\n stop(agentId: string = this.active()): void {\n void this.#connection.control.stopAgent(agentId, BACKGROUND_CONTEXT);\n }\n\n busy(agentId: string = this.active()): boolean {\n return isBusy(this.#view(agentId));\n }\n\n queue(agentId: string = this.active()): readonly UserMessage[] {\n return queued(this.#view(agentId));\n }\n\n unqueue(agentId: string, messageId: string): void {\n void this.#connection.control.unqueue(agentId, messageId.replace(/^q/, ''), BACKGROUND_CONTEXT);\n }\n\n suggestion(): string | null {\n return null;\n }\n\n answer(): void {}\n\n resolveLick(agentId: string, messageId: string, state: 'confirmed' | 'dismissed'): void {\n const message = this.messages(agentId).find((candidate) => candidate.id === messageId);\n const entry = this.#view(agentId)?.entries.find(\n (candidate) => `e${candidate.id}` === messageId\n );\n const lick = message?.role === 'lick' && entry ? entryLick(entry) : undefined;\n if (lick) void this.#connection.control.resolveLick(lick.id, state, BACKGROUND_CONTEXT);\n }\n\n compact(): void {\n void this.#connection.control.compact(null, BACKGROUND_CONTEXT);\n }\n\n clear(): void {\n void this.#connection.control.reset(null, BACKGROUND_CONTEXT);\n }\n\n #configure(agentId: string, change: Partial<AgentSettingsChange>): void {\n void this.#connection.control.configure(\n {\n model: change.model ?? null,\n thinkingLevel: change.thinkingLevel ?? null,\n agentId,\n },\n BACKGROUND_CONTEXT\n );\n }\n\n setModel(agentId: string, model: string): void {\n const at = model.indexOf('/');\n if (at <= 0) return;\n this.#configure(agentId, {\n model: { provider: model.slice(0, at), modelId: model.slice(at + 1) },\n });\n }\n\n setThinking(agentId: string, level: Thinking): void {\n this.#configure(agentId, { thinkingLevel: level });\n }\n\n async older(): Promise<readonly Message[]> {\n return [];\n }\n\n commands(): readonly SlashCommand[] {\n return [];\n }\n\n createScoop(parentId: string, name: string): Agent {\n const provisional = `scoop:creating-${crypto.randomUUID()}`;\n const created = this.#connection.control.createScoop(parentId, name, BACKGROUND_CONTEXT).then(\n (result) => result.id,\n () => null\n );\n this.#creating.set(provisional, created);\n void created.then((id) => {\n if (this.#active !== provisional) return;\n this.#active = id ?? parentId;\n this.emit('active', this.#active);\n this.emit('messages', this.#active);\n });\n return {\n id: provisional,\n name,\n kind: 'scoop',\n parentId,\n status: 'idle',\n model: '',\n contextFill: 0,\n unread: 0,\n };\n }\n\n async drop(agentId: string): Promise<void> {\n const parent = this.#summary().find((summary) => summary.id === agentId)?.parentId ?? CONE;\n const dropped = await this.#connection.control.drop(agentId, BACKGROUND_CONTEXT);\n if (dropped.error !== null) throw new Error(dropped.error);\n if (this.#active === agentId) this.select(parent);\n }\n\n async createCone(name: string): Promise<Agent> {\n const created = await this.#connection.control.createCone(name, BACKGROUND_CONTEXT);\n if (created.error !== null) throw new Error(created.error);\n return {\n id: created.id,\n name: name.trim(),\n kind: 'cone',\n parentId: null,\n status: 'idle',\n model: '',\n contextFill: 0,\n unread: 0,\n };\n }\n\n frozen(): readonly FrozenCone[] {\n return [];\n }\n\n freeze(): void {}\n\n thaw(): Agent | null {\n return null;\n }\n\n discard(): void {}\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import type {\n Agent,\n AgentEvents,\n AgentPort,\n AgentStatus,\n FrozenCone,\n Message,\n Outgoing,\n SlashCommand,\n Thinking,\n UserMessage,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport type { AgentConnection } from '../client.ts';\nimport { entryLick } from '../licks/state.ts';\nimport type { AgentSettingsChange, AgentSummary } from '../services.ts';\nimport { Emitter } from './emitter.ts';\nimport { isBusy, queued, toMessages } from './messages.ts';\n\nexport const CONE = 'cone';\n\nconst thinking: Record<string, Thinking> = {\n off: 'off',\n minimal: 'low',\n low: 'low',\n medium: 'medium',\n high: 'high',\n xhigh: 'high',\n max: 'high',\n};\n\ninterface AgentDoc {\n model?: { provider: string; modelId: string };\n thinkingLevel?: string;\n}\n\nfunction status(view: ConversationView | undefined, messages: readonly Message[]): AgentStatus {\n if (isBusy(view)) {\n const live = view?.docs['pi.live'] as { tools?: { status: string }[] } | undefined;\n return live?.tools?.some((slot) => slot.status === 'running') ? 'working' : 'thinking';\n }\n const last = messages.at(-1);\n return last?.role === 'assistant' && last.status === 'error' ? 'error' : 'idle';\n}\n\nexport class AgentAdapter extends Emitter<AgentEvents> implements AgentPort {\n readonly #connection: AgentConnection;\n readonly #ready: Promise<void>;\n readonly #messages = new Map<string, Message[]>();\n #active: string | null = null;\n #creating = new Map<string, Promise<string | null>>();\n\n constructor(connection: AgentConnection) {\n super();\n this.#connection = connection;\n this.#ready = new Promise((resolve) => {\n connection.transcript.subscribe(() => {\n this.#refresh();\n resolve();\n });\n connection.deliveries.subscribe(() => this.#refresh());\n connection.views?.subscribe(() => this.#refresh());\n connection.agents?.subscribe(() => this.#refresh());\n });\n }\n\n #summary(): readonly AgentSummary[] {\n const agents = this.#connection.agents?.value?.agents;\n return agents?.length\n ? agents\n : [{ id: CONE, name: 'sliccy', kind: 'cone', parentId: null, role: null }];\n }\n\n #view(agentId: string = this.active()): ConversationView | undefined {\n const views = this.#connection.views?.value;\n const view = views?.[agentId];\n if (view) return view;\n const cone = this.#connection.agents?.value?.active ?? CONE;\n return agentId === cone ? this.#connection.transcript.value : undefined;\n }\n\n #refresh(): void {\n this.emit('agents', this.list());\n for (const summary of this.#summary()) this.#update(summary.id);\n for (const id of [...this.#messages.keys()])\n if (!this.#summary().some((summary) => summary.id === id)) this.#messages.delete(id);\n }\n\n #update(agentId: string): void {\n const view = this.#view(agentId);\n if (!view) return;\n const before = this.#messages.get(agentId) ?? [];\n const after = toMessages(view, this.#connection.deliveries.value);\n this.#messages.set(agentId, after);\n if (before.length !== after.length) {\n this.emit('messages', agentId);\n return;\n }\n const changed = after.filter(\n (message, index) => JSON.stringify(message) !== JSON.stringify(before[index])\n );\n if (changed.length === 1) this.emit('message', { agentId, message: changed[0] as Message });\n else if (changed.length > 1) this.emit('messages', agentId);\n }\n\n ready(): Promise<void> {\n return this.#ready;\n }\n\n list(): readonly Agent[] {\n return this.#summary().map((summary) => {\n const view = this.#view(summary.id);\n const doc = view?.docs['pi.agent'] as AgentDoc | undefined;\n return {\n id: summary.id,\n name: summary.role ? `${summary.name} \u00B7 ${summary.role}` : summary.name,\n kind: summary.kind,\n parentId: summary.parentId,\n status: status(view, this.#messages.get(summary.id) ?? []),\n model: doc?.model ? `${doc.model.provider}/${doc.model.modelId}` : '',\n contextFill: 0,\n unread: 0,\n thinking: thinking[doc?.thinkingLevel ?? 'off'] ?? 'off',\n };\n });\n }\n\n active(): string {\n const known = this.#summary();\n if (this.#active && this.#creating.has(this.#active)) return this.#active;\n if (this.#active && known.some((summary) => summary.id === this.#active)) return this.#active;\n return this.#connection.agents?.value?.active ?? CONE;\n }\n\n select(id: string): void {\n if (this.#creating.has(id)) {\n this.#active = id;\n this.emit('active', id);\n return;\n }\n const found = this.#summary().find((summary) => summary.id === id);\n if (!found) return;\n this.#active = id;\n if (found.kind === 'cone') void this.#connection.control.selectCone(id, BACKGROUND_CONTEXT);\n this.emit('active', id);\n this.emit('messages', id);\n }\n\n messages(agentId: string = this.active()): readonly Message[] {\n return this.#messages.get(agentId) ?? [];\n }\n\n async send(agentId: string, input: string | Outgoing): Promise<void> {\n const outgoing = typeof input === 'string' ? { text: input } : input;\n const target = this.#creating.has(agentId) ? await this.#creating.get(agentId) : agentId;\n if (target === null) throw new Error('The scoop could not be created.');\n const sent = await this.#connection.control.send(\n {\n text: outgoing.text,\n whenBusy: outgoing.mode === 'queue' ? 'followUp' : 'steer',\n requestId: crypto.randomUUID(),\n agentId: target || null,\n },\n BACKGROUND_CONTEXT\n );\n if (!sent.accepted) throw new Error(sent.error.message);\n }\n\n async rewind(_agentId: string, messageId: string): Promise<Outgoing | null> {\n const rewound = await this.#connection.control.rewind(messageId, BACKGROUND_CONTEXT);\n return rewound.done ? { text: rewound.text } : null;\n }\n\n stop(agentId: string = this.active()): void {\n void this.#connection.control.stopAgent(agentId, BACKGROUND_CONTEXT);\n }\n\n busy(agentId: string = this.active()): boolean {\n return isBusy(this.#view(agentId));\n }\n\n queue(agentId: string = this.active()): readonly UserMessage[] {\n return queued(this.#view(agentId));\n }\n\n unqueue(agentId: string, messageId: string): void {\n void this.#connection.control.unqueue(agentId, messageId.replace(/^q/, ''), BACKGROUND_CONTEXT);\n }\n\n suggestion(): string | null {\n return null;\n }\n\n answer(): void {}\n\n resolveLick(agentId: string, messageId: string, state: 'confirmed' | 'dismissed'): void {\n const message = this.messages(agentId).find((candidate) => candidate.id === messageId);\n const entry = this.#view(agentId)?.entries.find(\n (candidate) => `e${candidate.id}` === messageId\n );\n const lick = message?.role === 'lick' && entry ? entryLick(entry) : undefined;\n if (lick) void this.#connection.control.resolveLick(lick.id, state, BACKGROUND_CONTEXT);\n }\n\n compact(): void {\n void this.#connection.control.compact(null, BACKGROUND_CONTEXT);\n }\n\n clear(): void {\n void this.#connection.control.reset(null, BACKGROUND_CONTEXT);\n }\n\n #configure(agentId: string, change: Partial<AgentSettingsChange>): void {\n void this.#connection.control.configure(\n {\n model: change.model ?? null,\n thinkingLevel: change.thinkingLevel ?? null,\n agentId,\n },\n BACKGROUND_CONTEXT\n );\n }\n\n setModel(agentId: string, model: string): void {\n const at = model.indexOf('/');\n if (at <= 0) return;\n this.#configure(agentId, {\n model: { provider: model.slice(0, at), modelId: model.slice(at + 1) },\n });\n }\n\n setThinking(agentId: string, level: Thinking): void {\n this.#configure(agentId, { thinkingLevel: level });\n }\n\n async older(): Promise<readonly Message[]> {\n return [];\n }\n\n commands(): readonly SlashCommand[] {\n return (this.#connection.commands?.value ?? []).map((command) => ({ ...command }));\n }\n\n createScoop(parentId: string, name: string): Agent {\n const provisional = `scoop:creating-${crypto.randomUUID()}`;\n const created = this.#connection.control.createScoop(parentId, name, BACKGROUND_CONTEXT).then(\n (result) => result.id,\n () => null\n );\n this.#creating.set(provisional, created);\n void created.then((id) => {\n if (this.#active !== provisional) return;\n this.#active = id ?? parentId;\n this.emit('active', this.#active);\n this.emit('messages', this.#active);\n });\n return {\n id: provisional,\n name,\n kind: 'scoop',\n parentId,\n status: 'idle',\n model: '',\n contextFill: 0,\n unread: 0,\n };\n }\n\n async drop(agentId: string): Promise<void> {\n const parent = this.#summary().find((summary) => summary.id === agentId)?.parentId ?? CONE;\n const dropped = await this.#connection.control.drop(agentId, BACKGROUND_CONTEXT);\n if (dropped.error !== null) throw new Error(dropped.error);\n if (this.#active === agentId) this.select(parent);\n }\n\n async createCone(name: string): Promise<Agent> {\n const created = await this.#connection.control.createCone(name, BACKGROUND_CONTEXT);\n if (created.error !== null) throw new Error(created.error);\n return {\n id: created.id,\n name: name.trim(),\n kind: 'cone',\n parentId: null,\n status: 'idle',\n model: '',\n contextFill: 0,\n unread: 0,\n };\n }\n\n frozen(): readonly FrozenCone[] {\n return [];\n }\n\n freeze(): void {}\n\n thaw(): Agent | null {\n return null;\n }\n\n discard(): void {}\n}\n"],
|
|
5
|
+
"mappings": "AAYA,SAAS,0BAA0B;AAGnC,SAAS,iBAAiB;AAE1B,SAAS,eAAe;AACxB,SAAS,QAAQ,QAAQ,kBAAkB;AAEpC,MAAM,OAAO;AAEpB,MAAM,WAAqC;AAAA,EACzC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,OAAO;AAAA,EACP,KAAK;AACP;AAOA,SAAS,OAAO,MAAoC,UAA2C;AAC7F,MAAI,OAAO,IAAI,GAAG;AAChB,UAAM,OAAO,MAAM,KAAK,SAAS;AACjC,WAAO,MAAM,OAAO,KAAK,CAAC,SAAS,KAAK,WAAW,SAAS,IAAI,YAAY;AAAA,EAC9E;AACA,QAAM,OAAO,SAAS,GAAG,EAAE;AAC3B,SAAO,MAAM,SAAS,eAAe,KAAK,WAAW,UAAU,UAAU;AAC3E;AAEO,MAAM,qBAAqB,QAA0C;AAAA,EACjE;AAAA,EACA;AAAA,EACA,YAAY,oBAAI,IAAuB;AAAA,EAChD,UAAyB;AAAA,EACzB,YAAY,oBAAI,IAAoC;AAAA,EAEpD,YAAY,YAA6B;AACvC,UAAM;AACN,SAAK,cAAc;AACnB,SAAK,SAAS,IAAI,QAAQ,CAAC,YAAY;AACrC,iBAAW,WAAW,UAAU,MAAM;AACpC,aAAK,SAAS;AACd,gBAAQ;AAAA,MACV,CAAC;AACD,iBAAW,WAAW,UAAU,MAAM,KAAK,SAAS,CAAC;AACrD,iBAAW,OAAO,UAAU,MAAM,KAAK,SAAS,CAAC;AACjD,iBAAW,QAAQ,UAAU,MAAM,KAAK,SAAS,CAAC;AAAA,IACpD,CAAC;AAAA,EACH;AAAA,EAEA,WAAoC;AAClC,UAAM,SAAS,KAAK,YAAY,QAAQ,OAAO;AAC/C,WAAO,QAAQ,SACX,SACA,CAAC,EAAE,IAAI,MAAM,MAAM,UAAU,MAAM,QAAQ,UAAU,MAAM,MAAM,KAAK,CAAC;AAAA,EAC7E;AAAA,EAEA,MAAM,UAAkB,KAAK,OAAO,GAAiC;AACnE,UAAM,QAAQ,KAAK,YAAY,OAAO;AACtC,UAAM,OAAO,QAAQ,OAAO;AAC5B,QAAI,KAAM,QAAO;AACjB,UAAM,OAAO,KAAK,YAAY,QAAQ,OAAO,UAAU;AACvD,WAAO,YAAY,OAAO,KAAK,YAAY,WAAW,QAAQ;AAAA,EAChE;AAAA,EAEA,WAAiB;AACf,SAAK,KAAK,UAAU,KAAK,KAAK,CAAC;AAC/B,eAAW,WAAW,KAAK,SAAS,EAAG,MAAK,QAAQ,QAAQ,EAAE;AAC9D,eAAW,MAAM,CAAC,GAAG,KAAK,UAAU,KAAK,CAAC;AACxC,UAAI,CAAC,KAAK,SAAS,EAAE,KAAK,CAAC,YAAY,QAAQ,OAAO,EAAE,EAAG,MAAK,UAAU,OAAO,EAAE;AAAA,EACvF;AAAA,EAEA,QAAQ,SAAuB;AAC7B,UAAM,OAAO,KAAK,MAAM,OAAO;AAC/B,QAAI,CAAC,KAAM;AACX,UAAM,SAAS,KAAK,UAAU,IAAI,OAAO,KAAK,CAAC;AAC/C,UAAM,QAAQ,WAAW,MAAM,KAAK,YAAY,WAAW,KAAK;AAChE,SAAK,UAAU,IAAI,SAAS,KAAK;AACjC,QAAI,OAAO,WAAW,MAAM,QAAQ;AAClC,WAAK,KAAK,YAAY,OAAO;AAC7B;AAAA,IACF;AACA,UAAM,UAAU,MAAM;AAAA,MACpB,CAAC,SAAS,UAAU,KAAK,UAAU,OAAO,MAAM,KAAK,UAAU,OAAO,KAAK,CAAC;AAAA,IAC9E;AACA,QAAI,QAAQ,WAAW,EAAG,MAAK,KAAK,WAAW,EAAE,SAAS,SAAS,QAAQ,CAAC,EAAa,CAAC;AAAA,aACjF,QAAQ,SAAS,EAAG,MAAK,KAAK,YAAY,OAAO;AAAA,EAC5D;AAAA,EAEA,QAAuB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,OAAyB;AACvB,WAAO,KAAK,SAAS,EAAE,IAAI,CAAC,YAAY;AACtC,YAAM,OAAO,KAAK,MAAM,QAAQ,EAAE;AAClC,YAAM,MAAM,MAAM,KAAK,UAAU;AACjC,aAAO;AAAA,QACL,IAAI,QAAQ;AAAA,QACZ,MAAM,QAAQ,OAAO,GAAG,QAAQ,IAAI,SAAM,QAAQ,IAAI,KAAK,QAAQ;AAAA,QACnE,MAAM,QAAQ;AAAA,QACd,UAAU,QAAQ;AAAA,QAClB,QAAQ,OAAO,MAAM,KAAK,UAAU,IAAI,QAAQ,EAAE,KAAK,CAAC,CAAC;AAAA,QACzD,OAAO,KAAK,QAAQ,GAAG,IAAI,MAAM,QAAQ,IAAI,IAAI,MAAM,OAAO,KAAK;AAAA,QACnE,aAAa;AAAA,QACb,QAAQ;AAAA,QACR,UAAU,SAAS,KAAK,iBAAiB,KAAK,KAAK;AAAA,MACrD;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,SAAiB;AACf,UAAM,QAAQ,KAAK,SAAS;AAC5B,QAAI,KAAK,WAAW,KAAK,UAAU,IAAI,KAAK,OAAO,EAAG,QAAO,KAAK;AAClE,QAAI,KAAK,WAAW,MAAM,KAAK,CAAC,YAAY,QAAQ,OAAO,KAAK,OAAO,EAAG,QAAO,KAAK;AACtF,WAAO,KAAK,YAAY,QAAQ,OAAO,UAAU;AAAA,EACnD;AAAA,EAEA,OAAO,IAAkB;AACvB,QAAI,KAAK,UAAU,IAAI,EAAE,GAAG;AAC1B,WAAK,UAAU;AACf,WAAK,KAAK,UAAU,EAAE;AACtB;AAAA,IACF;AACA,UAAM,QAAQ,KAAK,SAAS,EAAE,KAAK,CAAC,YAAY,QAAQ,OAAO,EAAE;AACjE,QAAI,CAAC,MAAO;AACZ,SAAK,UAAU;AACf,QAAI,MAAM,SAAS,OAAQ,MAAK,KAAK,YAAY,QAAQ,WAAW,IAAI,kBAAkB;AAC1F,SAAK,KAAK,UAAU,EAAE;AACtB,SAAK,KAAK,YAAY,EAAE;AAAA,EAC1B;AAAA,EAEA,SAAS,UAAkB,KAAK,OAAO,GAAuB;AAC5D,WAAO,KAAK,UAAU,IAAI,OAAO,KAAK,CAAC;AAAA,EACzC;AAAA,EAEA,MAAM,KAAK,SAAiB,OAAyC;AACnE,UAAM,WAAW,OAAO,UAAU,WAAW,EAAE,MAAM,MAAM,IAAI;AAC/D,UAAM,SAAS,KAAK,UAAU,IAAI,OAAO,IAAI,MAAM,KAAK,UAAU,IAAI,OAAO,IAAI;AACjF,QAAI,WAAW,KAAM,OAAM,IAAI,MAAM,iCAAiC;AACtE,UAAM,OAAO,MAAM,KAAK,YAAY,QAAQ;AAAA,MAC1C;AAAA,QACE,MAAM,SAAS;AAAA,QACf,UAAU,SAAS,SAAS,UAAU,aAAa;AAAA,QACnD,WAAW,OAAO,WAAW;AAAA,QAC7B,SAAS,UAAU;AAAA,MACrB;AAAA,MACA;AAAA,IACF;AACA,QAAI,CAAC,KAAK,SAAU,OAAM,IAAI,MAAM,KAAK,MAAM,OAAO;AAAA,EACxD;AAAA,EAEA,MAAM,OAAO,UAAkB,WAA6C;AAC1E,UAAM,UAAU,MAAM,KAAK,YAAY,QAAQ,OAAO,WAAW,kBAAkB;AACnF,WAAO,QAAQ,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI;AAAA,EACjD;AAAA,EAEA,KAAK,UAAkB,KAAK,OAAO,GAAS;AAC1C,SAAK,KAAK,YAAY,QAAQ,UAAU,SAAS,kBAAkB;AAAA,EACrE;AAAA,EAEA,KAAK,UAAkB,KAAK,OAAO,GAAY;AAC7C,WAAO,OAAO,KAAK,MAAM,OAAO,CAAC;AAAA,EACnC;AAAA,EAEA,MAAM,UAAkB,KAAK,OAAO,GAA2B;AAC7D,WAAO,OAAO,KAAK,MAAM,OAAO,CAAC;AAAA,EACnC;AAAA,EAEA,QAAQ,SAAiB,WAAyB;AAChD,SAAK,KAAK,YAAY,QAAQ,QAAQ,SAAS,UAAU,QAAQ,MAAM,EAAE,GAAG,kBAAkB;AAAA,EAChG;AAAA,EAEA,aAA4B;AAC1B,WAAO;AAAA,EACT;AAAA,EAEA,SAAe;AAAA,EAAC;AAAA,EAEhB,YAAY,SAAiB,WAAmB,OAAwC;AACtF,UAAM,UAAU,KAAK,SAAS,OAAO,EAAE,KAAK,CAAC,cAAc,UAAU,OAAO,SAAS;AACrF,UAAM,QAAQ,KAAK,MAAM,OAAO,GAAG,QAAQ;AAAA,MACzC,CAAC,cAAc,IAAI,UAAU,EAAE,OAAO;AAAA,IACxC;AACA,UAAM,OAAO,SAAS,SAAS,UAAU,QAAQ,UAAU,KAAK,IAAI;AACpE,QAAI,KAAM,MAAK,KAAK,YAAY,QAAQ,YAAY,KAAK,IAAI,OAAO,kBAAkB;AAAA,EACxF;AAAA,EAEA,UAAgB;AACd,SAAK,KAAK,YAAY,QAAQ,QAAQ,MAAM,kBAAkB;AAAA,EAChE;AAAA,EAEA,QAAc;AACZ,SAAK,KAAK,YAAY,QAAQ,MAAM,MAAM,kBAAkB;AAAA,EAC9D;AAAA,EAEA,WAAW,SAAiB,QAA4C;AACtE,SAAK,KAAK,YAAY,QAAQ;AAAA,MAC5B;AAAA,QACE,OAAO,OAAO,SAAS;AAAA,QACvB,eAAe,OAAO,iBAAiB;AAAA,QACvC;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,SAAS,SAAiB,OAAqB;AAC7C,UAAM,KAAK,MAAM,QAAQ,GAAG;AAC5B,QAAI,MAAM,EAAG;AACb,SAAK,WAAW,SAAS;AAAA,MACvB,OAAO,EAAE,UAAU,MAAM,MAAM,GAAG,EAAE,GAAG,SAAS,MAAM,MAAM,KAAK,CAAC,EAAE;AAAA,IACtE,CAAC;AAAA,EACH;AAAA,EAEA,YAAY,SAAiB,OAAuB;AAClD,SAAK,WAAW,SAAS,EAAE,eAAe,MAAM,CAAC;AAAA,EACnD;AAAA,EAEA,MAAM,QAAqC;AACzC,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,WAAoC;AAClC,YAAQ,KAAK,YAAY,UAAU,SAAS,CAAC,GAAG,IAAI,CAAC,aAAa,EAAE,GAAG,QAAQ,EAAE;AAAA,EACnF;AAAA,EAEA,YAAY,UAAkB,MAAqB;AACjD,UAAM,cAAc,kBAAkB,OAAO,WAAW,CAAC;AACzD,UAAM,UAAU,KAAK,YAAY,QAAQ,YAAY,UAAU,MAAM,kBAAkB,EAAE;AAAA,MACvF,CAAC,WAAW,OAAO;AAAA,MACnB,MAAM;AAAA,IACR;AACA,SAAK,UAAU,IAAI,aAAa,OAAO;AACvC,SAAK,QAAQ,KAAK,CAAC,OAAO;AACxB,UAAI,KAAK,YAAY,YAAa;AAClC,WAAK,UAAU,MAAM;AACrB,WAAK,KAAK,UAAU,KAAK,OAAO;AAChC,WAAK,KAAK,YAAY,KAAK,OAAO;AAAA,IACpC,CAAC;AACD,WAAO;AAAA,MACL,IAAI;AAAA,MACJ;AAAA,MACA,MAAM;AAAA,MACN;AAAA,MACA,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,IACV;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,SAAgC;AACzC,UAAM,SAAS,KAAK,SAAS,EAAE,KAAK,CAAC,YAAY,QAAQ,OAAO,OAAO,GAAG,YAAY;AACtF,UAAM,UAAU,MAAM,KAAK,YAAY,QAAQ,KAAK,SAAS,kBAAkB;AAC/E,QAAI,QAAQ,UAAU,KAAM,OAAM,IAAI,MAAM,QAAQ,KAAK;AACzD,QAAI,KAAK,YAAY,QAAS,MAAK,OAAO,MAAM;AAAA,EAClD;AAAA,EAEA,MAAM,WAAW,MAA8B;AAC7C,UAAM,UAAU,MAAM,KAAK,YAAY,QAAQ,WAAW,MAAM,kBAAkB;AAClF,QAAI,QAAQ,UAAU,KAAM,OAAM,IAAI,MAAM,QAAQ,KAAK;AACzD,WAAO;AAAA,MACL,IAAI,QAAQ;AAAA,MACZ,MAAM,KAAK,KAAK;AAAA,MAChB,MAAM;AAAA,MACN,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,aAAa;AAAA,MACb,QAAQ;AAAA,IACV;AAAA,EACF;AAAA,EAEA,SAAgC;AAC9B,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,SAAe;AAAA,EAAC;AAAA,EAEhB,OAAqB;AACnB,WAAO;AAAA,EACT;AAAA,EAEA,UAAgB;AAAA,EAAC;AACnB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parseLick } from "../licks/lick.js";
|
|
2
2
|
import { entryLick, LICK_TOOLS, lickDecisions } from "../licks/state.js";
|
|
3
|
+
import { FROM_KIND } from "../scoops/from.js";
|
|
3
4
|
function textOf(content) {
|
|
4
5
|
if (typeof content === "string") return content;
|
|
5
6
|
return (content ?? []).map((block) => block.type === "text" ? block.text ?? "" : "").join("");
|
|
@@ -146,15 +147,25 @@ function assistant(id, message, status) {
|
|
|
146
147
|
...used ? { usage: used } : {}
|
|
147
148
|
};
|
|
148
149
|
}
|
|
149
|
-
function user(id, message, delivered) {
|
|
150
|
+
function user(id, message, delivered, from) {
|
|
150
151
|
return {
|
|
151
152
|
id,
|
|
152
153
|
role: "user",
|
|
153
154
|
text: textOf(message.content),
|
|
154
155
|
createdAt: message.timestamp ?? 0,
|
|
155
|
-
...delivered ? { delivered } : {}
|
|
156
|
+
...delivered ? { delivered } : {},
|
|
157
|
+
...from ? { from } : {}
|
|
156
158
|
};
|
|
157
159
|
}
|
|
160
|
+
function origins(entries) {
|
|
161
|
+
const out = /* @__PURE__ */ new Map();
|
|
162
|
+
for (const entry of entries)
|
|
163
|
+
if (entry.kind === FROM_KIND) {
|
|
164
|
+
const note = entry.data;
|
|
165
|
+
out.set(note.entry, note.from);
|
|
166
|
+
}
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
158
169
|
function lickMessage(id, entry, decisions) {
|
|
159
170
|
const lick = entryLick(entry);
|
|
160
171
|
if (!lick) return void 0;
|
|
@@ -222,12 +233,14 @@ function rewound(id, entry) {
|
|
|
222
233
|
createdAt: 0
|
|
223
234
|
};
|
|
224
235
|
}
|
|
225
|
-
function entryMessage(entry, out, calls, delivered,
|
|
236
|
+
function entryMessage(entry, out, calls, delivered, notes) {
|
|
226
237
|
const id = `e${entry.id}`;
|
|
227
238
|
const model = entry.model?.[0];
|
|
228
239
|
if (entry.kind === "slicc.rewound") out.push(rewound(id, entry));
|
|
229
240
|
else if (entry.kind === "pi.user" && model)
|
|
230
|
-
out.push(
|
|
241
|
+
out.push(
|
|
242
|
+
lickMessage(id, entry, notes.decisions) ?? user(id, model, delivered, notes.origins.get(entry.id))
|
|
243
|
+
);
|
|
231
244
|
else if (entry.kind === "pi.assistant" && model) {
|
|
232
245
|
const message = assistant(id, model);
|
|
233
246
|
out.push(withoutLickTools(message));
|
|
@@ -276,11 +289,11 @@ function toMessages(view, deliveries = {}) {
|
|
|
276
289
|
const out = [];
|
|
277
290
|
if (!view) return out;
|
|
278
291
|
const calls = /* @__PURE__ */ new Map();
|
|
279
|
-
const
|
|
292
|
+
const notes = { decisions: lickDecisions(view.entries), origins: origins(view.entries) };
|
|
280
293
|
let previous;
|
|
281
294
|
for (const entry of view.entries) {
|
|
282
295
|
const steered = previous?.kind === "pi.tool-result" ? "steer" : void 0;
|
|
283
|
-
entryMessage(entry, out, calls, deliveries[String(entry.id)] ?? steered,
|
|
296
|
+
entryMessage(entry, out, calls, deliveries[String(entry.id)] ?? steered, notes);
|
|
284
297
|
previous = entry;
|
|
285
298
|
}
|
|
286
299
|
live(view, out, calls);
|
|
@@ -306,6 +319,7 @@ export {
|
|
|
306
319
|
errorPart,
|
|
307
320
|
fromPatch,
|
|
308
321
|
isBusy,
|
|
322
|
+
origins,
|
|
309
323
|
queued,
|
|
310
324
|
textOf,
|
|
311
325
|
toMessages
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/spectrum/messages.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n AssistantMessage,\n AssistantStatus,\n ErrorAction,\n LickMessage,\n Message,\n MessagePart,\n SystemMessage,\n ToolCall,\n ToolStatus,\n Usage,\n UserMessage,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport type { ConversationView, EntryRecord } from '@earendil-works/pi-durable';\nimport { parseLick } from '../licks/lick.ts';\nimport { entryLick, LICK_TOOLS, type LickDecision, lickDecisions } from '../licks/state.ts';\nimport type { Delivered } from '../services.ts';\n\ntype Json = Record<string, unknown>;\n\ninterface Block {\n type: string;\n text?: string;\n thinking?: string;\n id?: string;\n name?: string;\n arguments?: Json;\n}\n\ninterface ModelMessage {\n role: string;\n content?: string | Block[];\n timestamp?: number;\n stopReason?: string;\n errorMessage?: string;\n provider?: string;\n model?: string;\n usage?: {\n input?: number;\n output?: number;\n cacheRead?: number;\n cacheWrite?: number;\n cost?: { total?: number };\n };\n toolCallId?: string;\n isError?: boolean;\n details?: { patch?: unknown };\n}\n\ninterface Slot {\n callId: string;\n status: string;\n output?: string;\n}\n\ninterface Live {\n run?: unknown;\n generation?: { message?: ModelMessage; retry?: { at: number; error: string } };\n tools?: Slot[];\n compactions?: { taskId: number; reason: string }[];\n}\n\nexport function textOf(content: ModelMessage['content']): string {\n if (typeof content === 'string') return content;\n return (content ?? []).map((block) => (block.type === 'text' ? (block.text ?? '') : '')).join('');\n}\n\nconst paths = (args: Json | undefined): string[] =>\n typeof args?.path === 'string' ? [args.path] : [];\n\nfunction title(args: Json | undefined): string {\n const subject = args?.command ?? args?.path;\n return typeof subject === 'string' ? subject : '';\n}\n\ninterface Replacement {\n oldText?: string;\n newText?: string;\n}\n\nconst GAP = '\u2026';\n\nfunction seconds(value: unknown): string | undefined {\n return typeof value === 'number' ? `timeout ${value}s` : undefined;\n}\n\nfunction lines(args: Json): string | undefined {\n const { offset, limit } = args as { offset?: number; limit?: number };\n if (typeof offset === 'number' && typeof limit === 'number')\n return `lines ${offset}\u2013${offset + limit - 1}`;\n if (typeof offset === 'number') return `from line ${offset}`;\n return typeof limit === 'number' ? `first ${limit} lines` : undefined;\n}\n\nfunction edits(args: Json): ToolCall['diff'] {\n const list = Array.isArray(args.edits) ? (args.edits as Replacement[]) : [];\n if (list.length === 0) return undefined;\n return {\n before: list.map((edit) => edit.oldText ?? '').join(`\\n${GAP}\\n`),\n after: list.map((edit) => edit.newText ?? '').join(`\\n${GAP}\\n`),\n };\n}\n\nexport function fromPatch(patch: string): ToolCall['diff'] {\n const before: string[] = [];\n const after: string[] = [];\n let hunks = 0;\n for (const line of patch.split('\\n')) {\n if (line.startsWith('@@')) {\n if (hunks++ > 0) {\n before.push(GAP);\n after.push(GAP);\n }\n continue;\n }\n if (hunks === 0 || line === '' || line.startsWith('\\\\')) continue;\n const text = line.slice(1);\n if (line.startsWith('-')) before.push(text);\n else if (line.startsWith('+')) after.push(text);\n else {\n before.push(text);\n after.push(text);\n }\n }\n return hunks ? { before: before.join('\\n'), after: after.join('\\n') } : undefined;\n}\n\nfunction shape(name: string, args: Json): Pick<ToolCall, 'input' | 'meta' | 'diff'> {\n const path = typeof args.path === 'string' ? args.path : '';\n const meta = name === 'bash' ? seconds(args.timeout) : name === 'read' ? lines(args) : undefined;\n const base = meta ? { meta } : {};\n if (name === 'bash' && typeof args.command === 'string') return { input: args.command, ...base };\n if (name === 'read' || name === 'write') return { input: path, ...base };\n if (name === 'edit') {\n const diff = edits(args);\n return { input: path, ...(diff ? { diff } : {}) };\n }\n return { input: JSON.stringify(args, null, 2) };\n}\n\nfunction toolCall(block: Block): ToolCall {\n const args = block.arguments ?? {};\n return {\n id: block.id ?? '',\n name: block.name ?? 'tool',\n title: title(block.arguments),\n ...shape(block.name ?? 'tool', args),\n output: '',\n status: 'running',\n paths: paths(block.arguments),\n };\n}\n\nfunction part(block: Block): MessagePart | undefined {\n if (block.type === 'text') return { type: 'text', text: block.text ?? '' };\n if (block.type === 'thinking') return { type: 'thinking', text: block.thinking ?? '' };\n if (block.type === 'toolCall') return { type: 'tool', tool: toolCall(block) };\n return undefined;\n}\n\nfunction usage(message: ModelMessage): Usage | undefined {\n const used = message.usage;\n if (!used) return undefined;\n return {\n input: used.input ?? 0,\n output: used.output ?? 0,\n cost: used.cost?.total ?? 0,\n cacheRead: used.cacheRead ?? 0,\n cacheWrite: used.cacheWrite ?? 0,\n };\n}\n\nconst statuses: Record<string, AssistantStatus> = { error: 'error', aborted: 'stopped' };\n\nconst credentials =\n /security token|unrecognizedclient|not authori[sz]ed|unauthori[sz]ed|forbidden|\\b40[13]\\b|api key|credential|bearer/i;\nconst models = /model/i;\nconst unavailable =\n /access|invalid|not found|identifier|unsupported|not supported|isn['\u2019]t supported|not available|isn['\u2019]t available/i;\n\nconst providerNames: Record<string, string> = { 'amazon-bedrock': 'Bedrock' };\nconst filtered =\n /Provider (?:stopped with|finish_reason): (?:content_filter(?:ed)?|guardrail_intervened|sensitive)|refused to complete the request/i;\nconst missing = /no api key|missing|not configured|no credentials/i;\n\nexport function errorPart(error: string, provider?: string): MessagePart {\n const action = errorAction(error);\n if (action === 'drop-turn') {\n return {\n type: 'error',\n message: \"The model's content filter stopped this reply.\",\n detail: error,\n action,\n };\n }\n if (action === 'settings' && provider === 'adobe') {\n return {\n type: 'error',\n message: 'Your Adobe session has expired.',\n detail: error,\n action: 'login',\n };\n }\n const name = providerNames[provider ?? ''] ?? 'The provider';\n const lead =\n action === 'settings'\n ? missing.test(error)\n ? `${name} needs an API key.`\n : `${name} rejected the API key.`\n : action === 'change-model'\n ? \"This model isn't available with the current account.\"\n : null;\n return lead\n ? { type: 'error', message: lead, detail: error, action }\n : { type: 'error', message: error, action };\n}\n\nexport function errorAction(message: string): ErrorAction {\n if (filtered.test(message)) return 'drop-turn';\n if (credentials.test(message)) return 'settings';\n if (models.test(message) && unavailable.test(message)) return 'change-model';\n return 'retry';\n}\n\nexport function assistant(\n id: string,\n message: ModelMessage,\n status?: AssistantStatus\n): AssistantMessage {\n const blocks = Array.isArray(message.content) ? message.content : [];\n const parts = blocks.map(part).filter((item): item is MessagePart => item !== undefined);\n if (message.errorMessage) parts.push(errorPart(message.errorMessage, message.provider));\n const used = usage(message);\n return {\n id,\n role: 'assistant',\n parts,\n status: status ?? statuses[message.stopReason ?? ''] ?? 'done',\n createdAt: message.timestamp ?? 0,\n ...(message.provider && message.model ? { model: `${message.provider}/${message.model}` } : {}),\n ...(used ? { usage: used } : {}),\n };\n}\n\nfunction user(id: string, message: ModelMessage, delivered: Delivered | undefined): UserMessage {\n return {\n id,\n role: 'user',\n text: textOf(message.content),\n createdAt: message.timestamp ?? 0,\n ...(delivered ? { delivered } : {}),\n };\n}\n\nfunction lickMessage(\n id: string,\n entry: EntryRecord,\n decisions: ReadonlyMap<string, LickDecision>\n): LickMessage | undefined {\n const lick = entryLick(entry);\n if (!lick) return undefined;\n const state = lick.actions?.length ? (decisions.get(lick.id) ?? 'pending') : undefined;\n return {\n id,\n role: 'lick',\n channel: lick.channel,\n title: lick.title,\n text: lick.text,\n createdAt: lick.at,\n ...(lick.body ? { body: lick.body } : {}),\n ...(lick.count > 1 ? { count: lick.count } : {}),\n ...(state ? { state } : {}),\n ...(lick.severity ? { severity: lick.severity } : {}),\n };\n}\n\nfunction withoutLickTools(message: AssistantMessage): AssistantMessage {\n const parts = message.parts.filter((item) => item.type !== 'tool' || !LICK_TOOLS[item.tool.name]);\n return parts.length === message.parts.length ? message : { ...message, parts };\n}\n\nfunction system(id: string, entry: EntryRecord, text: string): SystemMessage {\n const reason = (entry.data as Json | undefined)?.reason as SystemMessage['trigger'] | undefined;\n return {\n id,\n role: 'system',\n kind: 'compaction',\n text,\n createdAt: 0,\n state: 'summarized',\n ...(reason ? { trigger: reason } : {}),\n };\n}\n\nfunction tools(messages: Message[]): Map<string, ToolCall> {\n const found = new Map<string, ToolCall>();\n for (const message of messages) {\n if (message.role !== 'assistant') continue;\n for (const item of message.parts) if (item.type === 'tool') found.set(item.tool.id, item.tool);\n }\n return found;\n}\n\nfunction settle(calls: Map<string, ToolCall>, result: ModelMessage): void {\n const call = calls.get(result.toolCallId ?? '');\n if (!call) return;\n call.output = textOf(result.content);\n call.status = result.isError ? 'error' : 'done';\n const patch = result.details?.patch;\n const diff = typeof patch === 'string' ? fromPatch(patch) : undefined;\n if (diff) call.diff = diff;\n else if (result.isError) delete call.diff;\n}\n\nfunction rewound(id: string, entry: EntryRecord): SystemMessage {\n const data = (entry.data ?? {}) as { stopped?: string[]; restored?: string[] };\n const notes = [\n 'Its prompt is back in the composer.',\n ...(data.stopped?.length\n ? [`Stopped ${data.stopped.map((name) => `scoop ${name}`).join(', ')}.`]\n : []),\n ...(data.restored?.length\n ? [`Restored ${data.restored.map((name) => `scoop ${name}`).join(', ')}.`]\n : []),\n ];\n return {\n id,\n role: 'system',\n kind: 'notice',\n title: 'Rewound 1 turn',\n text: notes.join(' '),\n createdAt: 0,\n };\n}\n\nfunction entryMessage(\n entry: EntryRecord,\n out: Message[],\n calls: Map<string, ToolCall>,\n delivered: Delivered | undefined,\n decisions: ReadonlyMap<string, LickDecision>\n): void {\n const id = `e${entry.id}`;\n const model = entry.model?.[0] as ModelMessage | undefined;\n if (entry.kind === 'slicc.rewound') out.push(rewound(id, entry));\n else if (entry.kind === 'pi.user' && model)\n out.push(lickMessage(id, entry, decisions) ?? user(id, model, delivered));\n else if (entry.kind === 'pi.assistant' && model) {\n const message = assistant(id, model);\n out.push(withoutLickTools(message));\n for (const [key, value] of tools([message])) calls.set(key, value);\n } else if (entry.kind === 'pi.tool-result' && model) settle(calls, model);\n else if (entry.kind === 'pi.compaction') out.push(system(id, entry, textOf(model?.content)));\n}\n\nconst slotStatus: Record<string, ToolStatus> = {\n pending: 'running',\n running: 'running',\n done: 'done',\n};\n\nfunction live(view: ConversationView, out: Message[], calls: Map<string, ToolCall>): void {\n const state = view.docs['pi.live'] as Live | undefined;\n for (const slot of state?.tools ?? []) {\n const call = calls.get(slot.callId);\n if (call && call.status === 'running') {\n call.status = slotStatus[slot.status] ?? 'running';\n call.output = slot.output ?? call.output;\n }\n }\n const partial = state?.generation?.message;\n if (partial) out.push(assistant('live', partial, 'streaming'));\n for (const compaction of state?.compactions ?? []) {\n out.push({\n id: `compaction-${compaction.taskId}`,\n role: 'system',\n kind: 'compaction',\n text: '',\n createdAt: 0,\n state: 'summarizing',\n trigger: compaction.reason as SystemMessage['trigger'],\n });\n }\n const retry = state?.generation?.retry;\n if (retry)\n out.push({\n id: 'retry',\n role: 'system',\n kind: 'notice',\n text: retry.error,\n createdAt: retry.at,\n });\n}\n\nexport function toMessages(\n view: ConversationView | undefined,\n deliveries: Readonly<Record<string, Delivered>> = {}\n): Message[] {\n const out: Message[] = [];\n if (!view) return out;\n const calls = new Map<string, ToolCall>();\n const decisions = lickDecisions(view.entries);\n let previous: EntryRecord | undefined;\n for (const entry of view.entries) {\n const steered = previous?.kind === 'pi.tool-result' ? 'steer' : undefined;\n entryMessage(entry, out, calls, deliveries[String(entry.id)] ?? steered, decisions);\n previous = entry;\n }\n live(view, out, calls);\n return out;\n}\n\nexport function isBusy(view: ConversationView | undefined): boolean {\n return Boolean((view?.docs['pi.live'] as Live | undefined)?.run);\n}\n\nexport function queued(view: ConversationView | undefined): UserMessage[] {\n const items = ((view?.docs['pi.inbox'] as { items?: Json[] } | undefined)?.items ?? []) as {\n id: number;\n mode: string;\n content?: ModelMessage['content'];\n }[];\n return items\n .filter((item) => item.mode !== 'write' && !parseLick(textOf(item.content)))\n .map((item) => ({\n id: `q${item.id}`,\n role: 'user',\n text: textOf(item.content),\n createdAt: 0,\n queued: true,\n mode: item.mode === 'steer' ? 'steer' : 'queue',\n }));\n}\n"],
|
|
5
|
-
"mappings": "AAcA,SAAS,iBAAiB;AAC1B,SAAS,WAAW,YAA+B,qBAAqB;AA+
|
|
4
|
+
"sourcesContent": ["import type {\n AssistantMessage,\n AssistantStatus,\n ErrorAction,\n LickMessage,\n Message,\n MessagePart,\n SystemMessage,\n ToolCall,\n ToolStatus,\n Usage,\n UserMessage,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport type { ConversationView, EntryRecord } from '@earendil-works/pi-durable';\nimport { parseLick } from '../licks/lick.ts';\nimport { entryLick, LICK_TOOLS, type LickDecision, lickDecisions } from '../licks/state.ts';\nimport { FROM_KIND, type From } from '../scoops/from.ts';\nimport type { Delivered } from '../services.ts';\n\ntype Json = Record<string, unknown>;\n\ninterface Block {\n type: string;\n text?: string;\n thinking?: string;\n id?: string;\n name?: string;\n arguments?: Json;\n}\n\ninterface ModelMessage {\n role: string;\n content?: string | Block[];\n timestamp?: number;\n stopReason?: string;\n errorMessage?: string;\n provider?: string;\n model?: string;\n usage?: {\n input?: number;\n output?: number;\n cacheRead?: number;\n cacheWrite?: number;\n cost?: { total?: number };\n };\n toolCallId?: string;\n isError?: boolean;\n details?: { patch?: unknown };\n}\n\ninterface Slot {\n callId: string;\n status: string;\n output?: string;\n}\n\ninterface Live {\n run?: unknown;\n generation?: { message?: ModelMessage; retry?: { at: number; error: string } };\n tools?: Slot[];\n compactions?: { taskId: number; reason: string }[];\n}\n\nexport function textOf(content: ModelMessage['content']): string {\n if (typeof content === 'string') return content;\n return (content ?? []).map((block) => (block.type === 'text' ? (block.text ?? '') : '')).join('');\n}\n\nconst paths = (args: Json | undefined): string[] =>\n typeof args?.path === 'string' ? [args.path] : [];\n\nfunction title(args: Json | undefined): string {\n const subject = args?.command ?? args?.path;\n return typeof subject === 'string' ? subject : '';\n}\n\ninterface Replacement {\n oldText?: string;\n newText?: string;\n}\n\nconst GAP = '\u2026';\n\nfunction seconds(value: unknown): string | undefined {\n return typeof value === 'number' ? `timeout ${value}s` : undefined;\n}\n\nfunction lines(args: Json): string | undefined {\n const { offset, limit } = args as { offset?: number; limit?: number };\n if (typeof offset === 'number' && typeof limit === 'number')\n return `lines ${offset}\u2013${offset + limit - 1}`;\n if (typeof offset === 'number') return `from line ${offset}`;\n return typeof limit === 'number' ? `first ${limit} lines` : undefined;\n}\n\nfunction edits(args: Json): ToolCall['diff'] {\n const list = Array.isArray(args.edits) ? (args.edits as Replacement[]) : [];\n if (list.length === 0) return undefined;\n return {\n before: list.map((edit) => edit.oldText ?? '').join(`\\n${GAP}\\n`),\n after: list.map((edit) => edit.newText ?? '').join(`\\n${GAP}\\n`),\n };\n}\n\nexport function fromPatch(patch: string): ToolCall['diff'] {\n const before: string[] = [];\n const after: string[] = [];\n let hunks = 0;\n for (const line of patch.split('\\n')) {\n if (line.startsWith('@@')) {\n if (hunks++ > 0) {\n before.push(GAP);\n after.push(GAP);\n }\n continue;\n }\n if (hunks === 0 || line === '' || line.startsWith('\\\\')) continue;\n const text = line.slice(1);\n if (line.startsWith('-')) before.push(text);\n else if (line.startsWith('+')) after.push(text);\n else {\n before.push(text);\n after.push(text);\n }\n }\n return hunks ? { before: before.join('\\n'), after: after.join('\\n') } : undefined;\n}\n\nfunction shape(name: string, args: Json): Pick<ToolCall, 'input' | 'meta' | 'diff'> {\n const path = typeof args.path === 'string' ? args.path : '';\n const meta = name === 'bash' ? seconds(args.timeout) : name === 'read' ? lines(args) : undefined;\n const base = meta ? { meta } : {};\n if (name === 'bash' && typeof args.command === 'string') return { input: args.command, ...base };\n if (name === 'read' || name === 'write') return { input: path, ...base };\n if (name === 'edit') {\n const diff = edits(args);\n return { input: path, ...(diff ? { diff } : {}) };\n }\n return { input: JSON.stringify(args, null, 2) };\n}\n\nfunction toolCall(block: Block): ToolCall {\n const args = block.arguments ?? {};\n return {\n id: block.id ?? '',\n name: block.name ?? 'tool',\n title: title(block.arguments),\n ...shape(block.name ?? 'tool', args),\n output: '',\n status: 'running',\n paths: paths(block.arguments),\n };\n}\n\nfunction part(block: Block): MessagePart | undefined {\n if (block.type === 'text') return { type: 'text', text: block.text ?? '' };\n if (block.type === 'thinking') return { type: 'thinking', text: block.thinking ?? '' };\n if (block.type === 'toolCall') return { type: 'tool', tool: toolCall(block) };\n return undefined;\n}\n\nfunction usage(message: ModelMessage): Usage | undefined {\n const used = message.usage;\n if (!used) return undefined;\n return {\n input: used.input ?? 0,\n output: used.output ?? 0,\n cost: used.cost?.total ?? 0,\n cacheRead: used.cacheRead ?? 0,\n cacheWrite: used.cacheWrite ?? 0,\n };\n}\n\nconst statuses: Record<string, AssistantStatus> = { error: 'error', aborted: 'stopped' };\n\nconst credentials =\n /security token|unrecognizedclient|not authori[sz]ed|unauthori[sz]ed|forbidden|\\b40[13]\\b|api key|credential|bearer/i;\nconst models = /model/i;\nconst unavailable =\n /access|invalid|not found|identifier|unsupported|not supported|isn['\u2019]t supported|not available|isn['\u2019]t available/i;\n\nconst providerNames: Record<string, string> = { 'amazon-bedrock': 'Bedrock' };\nconst filtered =\n /Provider (?:stopped with|finish_reason): (?:content_filter(?:ed)?|guardrail_intervened|sensitive)|refused to complete the request/i;\nconst missing = /no api key|missing|not configured|no credentials/i;\n\nexport function errorPart(error: string, provider?: string): MessagePart {\n const action = errorAction(error);\n if (action === 'drop-turn') {\n return {\n type: 'error',\n message: \"The model's content filter stopped this reply.\",\n detail: error,\n action,\n };\n }\n if (action === 'settings' && provider === 'adobe') {\n return {\n type: 'error',\n message: 'Your Adobe session has expired.',\n detail: error,\n action: 'login',\n };\n }\n const name = providerNames[provider ?? ''] ?? 'The provider';\n const lead =\n action === 'settings'\n ? missing.test(error)\n ? `${name} needs an API key.`\n : `${name} rejected the API key.`\n : action === 'change-model'\n ? \"This model isn't available with the current account.\"\n : null;\n return lead\n ? { type: 'error', message: lead, detail: error, action }\n : { type: 'error', message: error, action };\n}\n\nexport function errorAction(message: string): ErrorAction {\n if (filtered.test(message)) return 'drop-turn';\n if (credentials.test(message)) return 'settings';\n if (models.test(message) && unavailable.test(message)) return 'change-model';\n return 'retry';\n}\n\nexport function assistant(\n id: string,\n message: ModelMessage,\n status?: AssistantStatus\n): AssistantMessage {\n const blocks = Array.isArray(message.content) ? message.content : [];\n const parts = blocks.map(part).filter((item): item is MessagePart => item !== undefined);\n if (message.errorMessage) parts.push(errorPart(message.errorMessage, message.provider));\n const used = usage(message);\n return {\n id,\n role: 'assistant',\n parts,\n status: status ?? statuses[message.stopReason ?? ''] ?? 'done',\n createdAt: message.timestamp ?? 0,\n ...(message.provider && message.model ? { model: `${message.provider}/${message.model}` } : {}),\n ...(used ? { usage: used } : {}),\n };\n}\n\nfunction user(\n id: string,\n message: ModelMessage,\n delivered: Delivered | undefined,\n from: string | undefined\n): UserMessage {\n return {\n id,\n role: 'user',\n text: textOf(message.content),\n createdAt: message.timestamp ?? 0,\n ...(delivered ? { delivered } : {}),\n ...(from ? { from } : {}),\n };\n}\n\nexport function origins(entries: readonly EntryRecord[]): Map<number, string> {\n const out = new Map<number, string>();\n for (const entry of entries)\n if (entry.kind === FROM_KIND) {\n const note = entry.data as From;\n out.set(note.entry, note.from);\n }\n return out;\n}\n\nfunction lickMessage(\n id: string,\n entry: EntryRecord,\n decisions: ReadonlyMap<string, LickDecision>\n): LickMessage | undefined {\n const lick = entryLick(entry);\n if (!lick) return undefined;\n const state = lick.actions?.length ? (decisions.get(lick.id) ?? 'pending') : undefined;\n return {\n id,\n role: 'lick',\n channel: lick.channel,\n title: lick.title,\n text: lick.text,\n createdAt: lick.at,\n ...(lick.body ? { body: lick.body } : {}),\n ...(lick.count > 1 ? { count: lick.count } : {}),\n ...(state ? { state } : {}),\n ...(lick.severity ? { severity: lick.severity } : {}),\n };\n}\n\nfunction withoutLickTools(message: AssistantMessage): AssistantMessage {\n const parts = message.parts.filter((item) => item.type !== 'tool' || !LICK_TOOLS[item.tool.name]);\n return parts.length === message.parts.length ? message : { ...message, parts };\n}\n\nfunction system(id: string, entry: EntryRecord, text: string): SystemMessage {\n const reason = (entry.data as Json | undefined)?.reason as SystemMessage['trigger'] | undefined;\n return {\n id,\n role: 'system',\n kind: 'compaction',\n text,\n createdAt: 0,\n state: 'summarized',\n ...(reason ? { trigger: reason } : {}),\n };\n}\n\nfunction tools(messages: Message[]): Map<string, ToolCall> {\n const found = new Map<string, ToolCall>();\n for (const message of messages) {\n if (message.role !== 'assistant') continue;\n for (const item of message.parts) if (item.type === 'tool') found.set(item.tool.id, item.tool);\n }\n return found;\n}\n\nfunction settle(calls: Map<string, ToolCall>, result: ModelMessage): void {\n const call = calls.get(result.toolCallId ?? '');\n if (!call) return;\n call.output = textOf(result.content);\n call.status = result.isError ? 'error' : 'done';\n const patch = result.details?.patch;\n const diff = typeof patch === 'string' ? fromPatch(patch) : undefined;\n if (diff) call.diff = diff;\n else if (result.isError) delete call.diff;\n}\n\nfunction rewound(id: string, entry: EntryRecord): SystemMessage {\n const data = (entry.data ?? {}) as { stopped?: string[]; restored?: string[] };\n const notes = [\n 'Its prompt is back in the composer.',\n ...(data.stopped?.length\n ? [`Stopped ${data.stopped.map((name) => `scoop ${name}`).join(', ')}.`]\n : []),\n ...(data.restored?.length\n ? [`Restored ${data.restored.map((name) => `scoop ${name}`).join(', ')}.`]\n : []),\n ];\n return {\n id,\n role: 'system',\n kind: 'notice',\n title: 'Rewound 1 turn',\n text: notes.join(' '),\n createdAt: 0,\n };\n}\n\nfunction entryMessage(\n entry: EntryRecord,\n out: Message[],\n calls: Map<string, ToolCall>,\n delivered: Delivered | undefined,\n notes: { decisions: ReadonlyMap<string, LickDecision>; origins: ReadonlyMap<number, string> }\n): void {\n const id = `e${entry.id}`;\n const model = entry.model?.[0] as ModelMessage | undefined;\n if (entry.kind === 'slicc.rewound') out.push(rewound(id, entry));\n else if (entry.kind === 'pi.user' && model)\n out.push(\n lickMessage(id, entry, notes.decisions) ??\n user(id, model, delivered, notes.origins.get(entry.id))\n );\n else if (entry.kind === 'pi.assistant' && model) {\n const message = assistant(id, model);\n out.push(withoutLickTools(message));\n for (const [key, value] of tools([message])) calls.set(key, value);\n } else if (entry.kind === 'pi.tool-result' && model) settle(calls, model);\n else if (entry.kind === 'pi.compaction') out.push(system(id, entry, textOf(model?.content)));\n}\n\nconst slotStatus: Record<string, ToolStatus> = {\n pending: 'running',\n running: 'running',\n done: 'done',\n};\n\nfunction live(view: ConversationView, out: Message[], calls: Map<string, ToolCall>): void {\n const state = view.docs['pi.live'] as Live | undefined;\n for (const slot of state?.tools ?? []) {\n const call = calls.get(slot.callId);\n if (call && call.status === 'running') {\n call.status = slotStatus[slot.status] ?? 'running';\n call.output = slot.output ?? call.output;\n }\n }\n const partial = state?.generation?.message;\n if (partial) out.push(assistant('live', partial, 'streaming'));\n for (const compaction of state?.compactions ?? []) {\n out.push({\n id: `compaction-${compaction.taskId}`,\n role: 'system',\n kind: 'compaction',\n text: '',\n createdAt: 0,\n state: 'summarizing',\n trigger: compaction.reason as SystemMessage['trigger'],\n });\n }\n const retry = state?.generation?.retry;\n if (retry)\n out.push({\n id: 'retry',\n role: 'system',\n kind: 'notice',\n text: retry.error,\n createdAt: retry.at,\n });\n}\n\nexport function toMessages(\n view: ConversationView | undefined,\n deliveries: Readonly<Record<string, Delivered>> = {}\n): Message[] {\n const out: Message[] = [];\n if (!view) return out;\n const calls = new Map<string, ToolCall>();\n const notes = { decisions: lickDecisions(view.entries), origins: origins(view.entries) };\n let previous: EntryRecord | undefined;\n for (const entry of view.entries) {\n const steered = previous?.kind === 'pi.tool-result' ? 'steer' : undefined;\n entryMessage(entry, out, calls, deliveries[String(entry.id)] ?? steered, notes);\n previous = entry;\n }\n live(view, out, calls);\n return out;\n}\n\nexport function isBusy(view: ConversationView | undefined): boolean {\n return Boolean((view?.docs['pi.live'] as Live | undefined)?.run);\n}\n\nexport function queued(view: ConversationView | undefined): UserMessage[] {\n const items = ((view?.docs['pi.inbox'] as { items?: Json[] } | undefined)?.items ?? []) as {\n id: number;\n mode: string;\n content?: ModelMessage['content'];\n }[];\n return items\n .filter((item) => item.mode !== 'write' && !parseLick(textOf(item.content)))\n .map((item) => ({\n id: `q${item.id}`,\n role: 'user',\n text: textOf(item.content),\n createdAt: 0,\n queued: true,\n mode: item.mode === 'steer' ? 'steer' : 'queue',\n }));\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/types/client.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Context, type ReplicatedState } from '@earendil-works/chord';
|
|
2
2
|
import type { ConversationView } from '@earendil-works/pi-durable';
|
|
3
|
-
import { AgentControl, AgentSettings, type AgentsSummary, type Delivered, type SendMode } from './services.ts';
|
|
3
|
+
import { AgentControl, AgentSettings, type AgentsSummary, type Command, type Delivered, type SendMode } from './services.ts';
|
|
4
4
|
import { type PortEndpoint } from './wire.ts';
|
|
5
5
|
export interface AgentConnection {
|
|
6
6
|
readonly serverId: string;
|
|
@@ -9,6 +9,7 @@ export interface AgentConnection {
|
|
|
9
9
|
readonly deliveries: ReplicatedState<Record<string, Delivered>>;
|
|
10
10
|
readonly agents?: ReplicatedState<AgentsSummary>;
|
|
11
11
|
readonly views?: ReplicatedState<Record<string, ConversationView>>;
|
|
12
|
+
readonly commands?: ReplicatedState<Command[]>;
|
|
12
13
|
readonly settings: AgentSettings;
|
|
13
14
|
prompt(text: string, whenBusy?: SendMode): Promise<string>;
|
|
14
15
|
close(): Promise<void>;
|