@ai-ecoverse/slicc-agent 3.3.0 → 3.5.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 +37 -1
- package/dist/control.js +44 -1
- package/dist/control.js.map +2 -2
- package/dist/host.js +5 -1
- package/dist/host.js.map +2 -2
- package/dist/kernel/activity.js +39 -0
- package/dist/kernel/activity.js.map +7 -0
- package/dist/kernel/env.js +16 -8
- package/dist/kernel/env.js.map +2 -2
- package/dist/licks/boot.js +66 -0
- package/dist/licks/boot.js.map +7 -0
- package/dist/licks/config.js +126 -0
- package/dist/licks/config.js.map +7 -0
- package/dist/licks/cron.js +120 -0
- package/dist/licks/cron.js.map +7 -0
- package/dist/licks/extension.js +123 -0
- package/dist/licks/extension.js.map +7 -0
- package/dist/licks/fswatch.js +156 -0
- package/dist/licks/fswatch.js.map +7 -0
- package/dist/licks/glob.js +47 -0
- package/dist/licks/glob.js.map +7 -0
- package/dist/licks/index.js +36 -0
- package/dist/licks/index.js.map +7 -0
- package/dist/licks/lick.js +107 -0
- package/dist/licks/lick.js.map +7 -0
- package/dist/licks/licks.js +194 -0
- package/dist/licks/licks.js.map +7 -0
- package/dist/licks/schedules.js +50 -0
- package/dist/licks/schedules.js.map +7 -0
- package/dist/licks/sources.js +139 -0
- package/dist/licks/sources.js.map +7 -0
- package/dist/licks/state.js +56 -0
- package/dist/licks/state.js.map +7 -0
- package/dist/main.js +23 -4
- package/dist/main.js.map +2 -2
- package/dist/services.js.map +2 -2
- package/dist/spectrum/agent-port.js +6 -1
- package/dist/spectrum/agent-port.js.map +2 -2
- package/dist/spectrum/messages.js +29 -5
- package/dist/spectrum/messages.js.map +2 -2
- package/dist/system.js +16 -0
- package/dist/system.js.map +2 -2
- package/dist/types/control.d.ts +6 -1
- package/dist/types/host.d.ts +2 -0
- package/dist/types/kernel/activity.d.ts +10 -0
- package/dist/types/kernel/env.d.ts +2 -0
- package/dist/types/licks/boot.d.ts +9 -0
- package/dist/types/licks/config.d.ts +35 -0
- package/dist/types/licks/cron.d.ts +16 -0
- package/dist/types/licks/extension.d.ts +22 -0
- package/dist/types/licks/fswatch.d.ts +20 -0
- package/dist/types/licks/glob.d.ts +1 -0
- package/dist/types/licks/index.d.ts +24 -0
- package/dist/types/licks/lick.d.ts +23 -0
- package/dist/types/licks/licks.d.ts +58 -0
- package/dist/types/licks/schedules.d.ts +25 -0
- package/dist/types/licks/sources.d.ts +38 -0
- package/dist/types/licks/state.d.ts +14 -0
- package/dist/types/services.d.ts +18 -0
- package/dist/types/spectrum/agent-port.d.ts +1 -1
- package/dist/types/system.d.ts +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { bootLicks } from "./boot.js";
|
|
2
|
+
import { parseCrontab, parseWebhook } from "./config.js";
|
|
3
|
+
import { configFiles, Watches } from "./fswatch.js";
|
|
4
|
+
import { reconcileCron } from "./schedules.js";
|
|
5
|
+
const SLICC_DIR = ".slicc";
|
|
6
|
+
function hash(text) {
|
|
7
|
+
let value = 2166136261;
|
|
8
|
+
for (let at = 0; at < text.length; at++) value = Math.imul(value ^ text.charCodeAt(at), 16777619);
|
|
9
|
+
return (value >>> 0).toString(36);
|
|
10
|
+
}
|
|
11
|
+
function shortPath(path, home) {
|
|
12
|
+
return path === home || path.startsWith(`${home}/`) ? `~${path.slice(home.length)}` : path;
|
|
13
|
+
}
|
|
14
|
+
function configLick(error, home) {
|
|
15
|
+
const file = shortPath(error.file, home);
|
|
16
|
+
const channel = error.file.includes("/watches/") ? "fswatch" : error.file.includes("/webhooks/") ? "webhook" : "cron";
|
|
17
|
+
const text = `${error.line ? `line ${error.line}: ` : ""}${error.error}`;
|
|
18
|
+
return {
|
|
19
|
+
channel,
|
|
20
|
+
source: `config:${file}`,
|
|
21
|
+
title: `Invalid ${file}`,
|
|
22
|
+
text,
|
|
23
|
+
body: "Ignored until the file is fixed.\nThe formats are in the slicc-agent README.",
|
|
24
|
+
target: "cone",
|
|
25
|
+
severity: "error",
|
|
26
|
+
eventId: hash(`${file}
|
|
27
|
+
${text}`)
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function webhookLick(name, entry, delivery) {
|
|
31
|
+
const payload = typeof delivery.body === "string" ? delivery.body : JSON.stringify(delivery.body, null, 2);
|
|
32
|
+
const headers = Object.entries(delivery.headers ?? {}).map(([key, value]) => `${key}: ${value}`).join("\n");
|
|
33
|
+
return {
|
|
34
|
+
channel: "webhook",
|
|
35
|
+
source: name,
|
|
36
|
+
title: name,
|
|
37
|
+
text: entry.message || `Webhook ${name} received a request`,
|
|
38
|
+
body: [headers, payload].filter(Boolean).join("\n\n"),
|
|
39
|
+
target: entry.target,
|
|
40
|
+
coalesce: false,
|
|
41
|
+
...delivery.id ? { eventId: delivery.id } : {}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async function webhooksIn(files, dir, into, context) {
|
|
45
|
+
const errors = [];
|
|
46
|
+
into.clear();
|
|
47
|
+
for (const name of await files.list(dir, context)) {
|
|
48
|
+
const file = `${dir}/${name}`;
|
|
49
|
+
const json = await files.read(file, context);
|
|
50
|
+
if (json === void 0) continue;
|
|
51
|
+
try {
|
|
52
|
+
const entry = parseWebhook(file, json);
|
|
53
|
+
into.set(entry.name, entry);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
errors.push({ file, error: error.message });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return errors;
|
|
59
|
+
}
|
|
60
|
+
function createLickSources(options) {
|
|
61
|
+
const { harness, env, licks, home } = options;
|
|
62
|
+
const dir = `${home}/${SLICC_DIR}`;
|
|
63
|
+
const now = options.now ?? Date.now;
|
|
64
|
+
const files = configFiles(env);
|
|
65
|
+
const deliver = (event, context) => licks.deliver(event, context);
|
|
66
|
+
const watches = new Watches(env, deliver, home, options.activity);
|
|
67
|
+
const webhooks = /* @__PURE__ */ new Map();
|
|
68
|
+
let control;
|
|
69
|
+
let reconcileTimer;
|
|
70
|
+
let flushTimer;
|
|
71
|
+
let closed = false;
|
|
72
|
+
let lock = Promise.resolve();
|
|
73
|
+
const serial = (operation) => {
|
|
74
|
+
const run = lock.then(operation, operation);
|
|
75
|
+
lock = run.catch(() => void 0);
|
|
76
|
+
return run;
|
|
77
|
+
};
|
|
78
|
+
async function reconcileNow(context) {
|
|
79
|
+
if (closed) return;
|
|
80
|
+
const crontabFile = `${dir}/crontab`;
|
|
81
|
+
const crontab = parseCrontab(crontabFile, await files.read(crontabFile, context) ?? "");
|
|
82
|
+
await reconcileCron({ ...options, now }, crontab.entries, context);
|
|
83
|
+
const errors = [
|
|
84
|
+
...crontab.errors,
|
|
85
|
+
...await watches.reconcile(`${dir}/watches`, context),
|
|
86
|
+
...await webhooksIn(files, `${dir}/webhooks`, webhooks, context)
|
|
87
|
+
];
|
|
88
|
+
for (const error of errors) await deliver(configLick(error, home), context);
|
|
89
|
+
}
|
|
90
|
+
const reconcile = (context) => serial(() => reconcileNow(context));
|
|
91
|
+
function scheduleReconcile(context) {
|
|
92
|
+
clearTimeout(reconcileTimer);
|
|
93
|
+
reconcileTimer = setTimeout(() => {
|
|
94
|
+
void reconcile(context).catch(() => void 0);
|
|
95
|
+
}, 300);
|
|
96
|
+
}
|
|
97
|
+
async function flushIfWaiting(context) {
|
|
98
|
+
if (await licks.pending(context)) await licks.flush(context);
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
async start(context) {
|
|
102
|
+
await licks.flush(context);
|
|
103
|
+
await reconcile(context);
|
|
104
|
+
const result = await env.watch(
|
|
105
|
+
[{ path: dir, recursive: true }],
|
|
106
|
+
() => scheduleReconcile(context),
|
|
107
|
+
context
|
|
108
|
+
);
|
|
109
|
+
if (result.ok) control = result.value;
|
|
110
|
+
flushTimer = setInterval(() => {
|
|
111
|
+
void flushIfWaiting(context).catch(() => void 0);
|
|
112
|
+
}, options.flushEvery ?? 2e3);
|
|
113
|
+
},
|
|
114
|
+
reconcile,
|
|
115
|
+
boot: (facts, context) => bootLicks(harness, options.cone(), deliver, facts, context),
|
|
116
|
+
async webhook(name, delivery, context) {
|
|
117
|
+
const entry = webhooks.get(name);
|
|
118
|
+
if (!entry) return false;
|
|
119
|
+
await deliver(webhookLick(name, entry, delivery), context);
|
|
120
|
+
return true;
|
|
121
|
+
},
|
|
122
|
+
async close(context) {
|
|
123
|
+
closed = true;
|
|
124
|
+
clearTimeout(reconcileTimer);
|
|
125
|
+
clearInterval(flushTimer);
|
|
126
|
+
await control?.close(context);
|
|
127
|
+
await watches.close(context);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export {
|
|
132
|
+
SLICC_DIR,
|
|
133
|
+
configLick,
|
|
134
|
+
createLickSources,
|
|
135
|
+
hash,
|
|
136
|
+
shortPath,
|
|
137
|
+
webhookLick
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=sources.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/licks/sources.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { Conversation, Harness } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv, FileWatcher } from '@earendil-works/pi-durable/env';\nimport type { Activity } from '../kernel/activity.ts';\nimport { type BootFacts, bootLicks } from './boot.ts';\nimport { type ConfigError, parseCrontab, parseWebhook, type WebhookEntry } from './config.ts';\nimport type { cronTask } from './extension.ts';\nimport { type ConfigFiles, configFiles, Watches } from './fswatch.ts';\nimport type { LickChannel } from './lick.ts';\nimport type { LickEvent, Licks } from './licks.ts';\nimport { reconcileCron } from './schedules.ts';\n\nexport const SLICC_DIR = '.slicc';\n\nexport interface LickSourcesOptions {\n harness: Harness;\n root: Conversation;\n cone: () => Conversation;\n env: ExecutionEnv;\n home: string;\n licks: Licks;\n cron: ReturnType<typeof cronTask>;\n now?: () => number;\n flushEvery?: number;\n activity?: Activity;\n}\n\nexport interface WebhookDelivery {\n id?: string;\n headers?: Record<string, string>;\n body: unknown;\n}\n\nexport interface LickSources {\n start(context: Context): Promise<void>;\n reconcile(context: Context): Promise<void>;\n boot(facts: BootFacts, context: Context): Promise<void>;\n webhook(name: string, delivery: WebhookDelivery, context: Context): Promise<boolean>;\n close(context: Context): Promise<void>;\n}\n\nexport function hash(text: string): string {\n let value = 2166136261;\n for (let at = 0; at < text.length; at++) value = Math.imul(value ^ text.charCodeAt(at), 16777619);\n return (value >>> 0).toString(36);\n}\n\nexport function shortPath(path: string, home: string): string {\n return path === home || path.startsWith(`${home}/`) ? `~${path.slice(home.length)}` : path;\n}\n\nexport function configLick(error: ConfigError, home: string): LickEvent {\n const file = shortPath(error.file, home);\n const channel: LickChannel = error.file.includes('/watches/')\n ? 'fswatch'\n : error.file.includes('/webhooks/')\n ? 'webhook'\n : 'cron';\n const text = `${error.line ? `line ${error.line}: ` : ''}${error.error}`;\n return {\n channel,\n source: `config:${file}`,\n title: `Invalid ${file}`,\n text,\n body: 'Ignored until the file is fixed.\\nThe formats are in the slicc-agent README.',\n target: 'cone',\n severity: 'error',\n eventId: hash(`${file}\\n${text}`),\n };\n}\n\nexport function webhookLick(\n name: string,\n entry: WebhookEntry,\n delivery: WebhookDelivery\n): LickEvent {\n const payload =\n typeof delivery.body === 'string' ? delivery.body : JSON.stringify(delivery.body, null, 2);\n const headers = Object.entries(delivery.headers ?? {})\n .map(([key, value]) => `${key}: ${value}`)\n .join('\\n');\n return {\n channel: 'webhook',\n source: name,\n title: name,\n text: entry.message || `Webhook ${name} received a request`,\n body: [headers, payload].filter(Boolean).join('\\n\\n'),\n target: entry.target,\n coalesce: false,\n ...(delivery.id ? { eventId: delivery.id } : {}),\n };\n}\n\nasync function webhooksIn(\n files: ConfigFiles,\n dir: string,\n into: Map<string, WebhookEntry>,\n context: Context\n): Promise<ConfigError[]> {\n const errors: ConfigError[] = [];\n into.clear();\n for (const name of await files.list(dir, context)) {\n const file = `${dir}/${name}`;\n const json = await files.read(file, context);\n if (json === undefined) continue;\n try {\n const entry = parseWebhook(file, json);\n into.set(entry.name, entry);\n } catch (error) {\n errors.push({ file, error: (error as Error).message });\n }\n }\n return errors;\n}\n\nexport function createLickSources(options: LickSourcesOptions): LickSources {\n const { harness, env, licks, home } = options;\n const dir = `${home}/${SLICC_DIR}`;\n const now = options.now ?? Date.now;\n const files = configFiles(env);\n const deliver = (event: LickEvent, context: Context) => licks.deliver(event, context);\n const watches = new Watches(env, deliver, home, options.activity);\n const webhooks = new Map<string, WebhookEntry>();\n let control: FileWatcher | undefined;\n let reconcileTimer: ReturnType<typeof setTimeout> | undefined;\n let flushTimer: ReturnType<typeof setInterval> | undefined;\n let closed = false;\n let lock: Promise<unknown> = Promise.resolve();\n const serial = <T>(operation: () => Promise<T>): Promise<T> => {\n const run = lock.then(operation, operation);\n lock = run.catch(() => undefined);\n return run;\n };\n\n async function reconcileNow(context: Context): Promise<void> {\n if (closed) return;\n const crontabFile = `${dir}/crontab`;\n const crontab = parseCrontab(crontabFile, (await files.read(crontabFile, context)) ?? '');\n await reconcileCron({ ...options, now }, crontab.entries, context);\n const errors = [\n ...crontab.errors,\n ...(await watches.reconcile(`${dir}/watches`, context)),\n ...(await webhooksIn(files, `${dir}/webhooks`, webhooks, context)),\n ];\n for (const error of errors) await deliver(configLick(error, home), context);\n }\n\n const reconcile = (context: Context) => serial(() => reconcileNow(context));\n\n function scheduleReconcile(context: Context): void {\n clearTimeout(reconcileTimer);\n reconcileTimer = setTimeout(() => {\n void reconcile(context).catch(() => undefined);\n }, 300);\n }\n\n async function flushIfWaiting(context: Context): Promise<void> {\n if (await licks.pending(context)) await licks.flush(context);\n }\n\n return {\n async start(context) {\n await licks.flush(context);\n await reconcile(context);\n const result = await env.watch(\n [{ path: dir, recursive: true }],\n () => scheduleReconcile(context),\n context\n );\n if (result.ok) control = result.value;\n flushTimer = setInterval(() => {\n void flushIfWaiting(context).catch(() => undefined);\n }, options.flushEvery ?? 2000);\n },\n reconcile,\n boot: (facts, context) => bootLicks(harness, options.cone(), deliver, facts, context),\n async webhook(name, delivery, context) {\n const entry = webhooks.get(name);\n if (!entry) return false;\n await deliver(webhookLick(name, entry, delivery), context);\n return true;\n },\n async close(context) {\n closed = true;\n clearTimeout(reconcileTimer);\n clearInterval(flushTimer);\n await control?.close(context);\n await watches.close(context);\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { parseLick } from "./lick.js";
|
|
2
|
+
const LICK_TOOLS = {
|
|
3
|
+
lick_confirm: "confirmed",
|
|
4
|
+
lick_dismiss: "dismissed"
|
|
5
|
+
};
|
|
6
|
+
const LICK_STATE_KIND = "slicc.lick-state";
|
|
7
|
+
function userText(entry) {
|
|
8
|
+
if (entry.kind !== "pi.user") return void 0;
|
|
9
|
+
const content = entry.model?.[0]?.content;
|
|
10
|
+
if (typeof content === "string") return content;
|
|
11
|
+
if (!Array.isArray(content)) return void 0;
|
|
12
|
+
return content.map((block) => block.type === "text" ? block.text ?? "" : "").join("");
|
|
13
|
+
}
|
|
14
|
+
function entryLick(entry) {
|
|
15
|
+
const text = userText(entry);
|
|
16
|
+
return text === void 0 ? void 0 : parseLick(text);
|
|
17
|
+
}
|
|
18
|
+
function stateEntry(entry, decisions) {
|
|
19
|
+
const data = entry.data;
|
|
20
|
+
if (typeof data?.lick !== "string") return;
|
|
21
|
+
if (data.state === "confirmed" || data.state === "dismissed")
|
|
22
|
+
decisions.set(data.lick, data.state);
|
|
23
|
+
}
|
|
24
|
+
function toolCalls(message, calls) {
|
|
25
|
+
if (!Array.isArray(message?.content)) return;
|
|
26
|
+
for (const block of message.content) {
|
|
27
|
+
const decision = LICK_TOOLS[block.name ?? ""];
|
|
28
|
+
const lick = block.arguments?.lick_id;
|
|
29
|
+
if (block.type === "toolCall" && decision && typeof lick === "string" && block.id)
|
|
30
|
+
calls.set(block.id, { lick, decision });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function toolResult(message, calls, decisions) {
|
|
34
|
+
if (!message || message.isError) return;
|
|
35
|
+
const call = calls.get(message.toolCallId ?? "");
|
|
36
|
+
if (call) decisions.set(call.lick, call.decision);
|
|
37
|
+
}
|
|
38
|
+
function lickDecisions(entries) {
|
|
39
|
+
const decisions = /* @__PURE__ */ new Map();
|
|
40
|
+
const calls = /* @__PURE__ */ new Map();
|
|
41
|
+
for (const entry of entries) {
|
|
42
|
+
const message = entry.model?.[0];
|
|
43
|
+
if (entry.kind === LICK_STATE_KIND) stateEntry(entry, decisions);
|
|
44
|
+
else if (entry.kind === "pi.assistant") toolCalls(message, calls);
|
|
45
|
+
else if (entry.kind === "pi.tool-result") toolResult(message, calls, decisions);
|
|
46
|
+
}
|
|
47
|
+
return decisions;
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
LICK_STATE_KIND,
|
|
51
|
+
LICK_TOOLS,
|
|
52
|
+
entryLick,
|
|
53
|
+
lickDecisions,
|
|
54
|
+
userText
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/licks/state.ts"],
|
|
4
|
+
"sourcesContent": ["import { type Lick, parseLick } from './lick.ts';\n\nexport type LickDecision = 'confirmed' | 'dismissed';\n\nexport const LICK_TOOLS: Record<string, LickDecision> = {\n lick_confirm: 'confirmed',\n lick_dismiss: 'dismissed',\n};\n\nexport const LICK_STATE_KIND = 'slicc.lick-state';\n\ninterface Block {\n type?: string;\n text?: string;\n id?: string;\n name?: string;\n arguments?: Record<string, unknown>;\n}\n\ninterface ModelMessage {\n role?: string;\n content?: string | Block[];\n toolCallId?: string;\n isError?: boolean;\n}\n\ninterface EntryLike {\n id: number;\n kind: string;\n model?: readonly unknown[];\n data?: unknown;\n}\n\nexport function userText(entry: EntryLike): string | undefined {\n if (entry.kind !== 'pi.user') return undefined;\n const content = (entry.model?.[0] as ModelMessage | undefined)?.content;\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return undefined;\n return content.map((block) => (block.type === 'text' ? (block.text ?? '') : '')).join('');\n}\n\nexport function entryLick(entry: EntryLike): Lick | undefined {\n const text = userText(entry);\n return text === undefined ? undefined : parseLick(text);\n}\n\ntype Calls = Map<string, { lick: string; decision: LickDecision }>;\n\nfunction stateEntry(entry: EntryLike, decisions: Map<string, LickDecision>): void {\n const data = entry.data as { lick?: unknown; state?: unknown } | undefined;\n if (typeof data?.lick !== 'string') return;\n if (data.state === 'confirmed' || data.state === 'dismissed')\n decisions.set(data.lick, data.state);\n}\n\nfunction toolCalls(message: ModelMessage | undefined, calls: Calls): void {\n if (!Array.isArray(message?.content)) return;\n for (const block of message.content) {\n const decision = LICK_TOOLS[block.name ?? ''];\n const lick = block.arguments?.lick_id;\n if (block.type === 'toolCall' && decision && typeof lick === 'string' && block.id)\n calls.set(block.id, { lick, decision });\n }\n}\n\nfunction toolResult(\n message: ModelMessage | undefined,\n calls: Calls,\n decisions: Map<string, LickDecision>\n): void {\n if (!message || message.isError) return;\n const call = calls.get(message.toolCallId ?? '');\n if (call) decisions.set(call.lick, call.decision);\n}\n\nexport function lickDecisions(entries: readonly EntryLike[]): Map<string, LickDecision> {\n const decisions = new Map<string, LickDecision>();\n const calls: Calls = new Map();\n for (const entry of entries) {\n const message = entry.model?.[0] as ModelMessage | undefined;\n if (entry.kind === LICK_STATE_KIND) stateEntry(entry, decisions);\n else if (entry.kind === 'pi.assistant') toolCalls(message, calls);\n else if (entry.kind === 'pi.tool-result') toolResult(message, calls, decisions);\n }\n return decisions;\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAoB,iBAAiB;AAI9B,MAAM,aAA2C;AAAA,EACtD,cAAc;AAAA,EACd,cAAc;AAChB;AAEO,MAAM,kBAAkB;AAwBxB,SAAS,SAAS,OAAsC;AAC7D,MAAI,MAAM,SAAS,UAAW,QAAO;AACrC,QAAM,UAAW,MAAM,QAAQ,CAAC,GAAgC;AAChE,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO;AACpC,SAAO,QAAQ,IAAI,CAAC,UAAW,MAAM,SAAS,SAAU,MAAM,QAAQ,KAAM,EAAG,EAAE,KAAK,EAAE;AAC1F;AAEO,SAAS,UAAU,OAAoC;AAC5D,QAAM,OAAO,SAAS,KAAK;AAC3B,SAAO,SAAS,SAAY,SAAY,UAAU,IAAI;AACxD;AAIA,SAAS,WAAW,OAAkB,WAA4C;AAChF,QAAM,OAAO,MAAM;AACnB,MAAI,OAAO,MAAM,SAAS,SAAU;AACpC,MAAI,KAAK,UAAU,eAAe,KAAK,UAAU;AAC/C,cAAU,IAAI,KAAK,MAAM,KAAK,KAAK;AACvC;AAEA,SAAS,UAAU,SAAmC,OAAoB;AACxE,MAAI,CAAC,MAAM,QAAQ,SAAS,OAAO,EAAG;AACtC,aAAW,SAAS,QAAQ,SAAS;AACnC,UAAM,WAAW,WAAW,MAAM,QAAQ,EAAE;AAC5C,UAAM,OAAO,MAAM,WAAW;AAC9B,QAAI,MAAM,SAAS,cAAc,YAAY,OAAO,SAAS,YAAY,MAAM;AAC7E,YAAM,IAAI,MAAM,IAAI,EAAE,MAAM,SAAS,CAAC;AAAA,EAC1C;AACF;AAEA,SAAS,WACP,SACA,OACA,WACM;AACN,MAAI,CAAC,WAAW,QAAQ,QAAS;AACjC,QAAM,OAAO,MAAM,IAAI,QAAQ,cAAc,EAAE;AAC/C,MAAI,KAAM,WAAU,IAAI,KAAK,MAAM,KAAK,QAAQ;AAClD;AAEO,SAAS,cAAc,SAA0D;AACtF,QAAM,YAAY,oBAAI,IAA0B;AAChD,QAAM,QAAe,oBAAI,IAAI;AAC7B,aAAW,SAAS,SAAS;AAC3B,UAAM,UAAU,MAAM,QAAQ,CAAC;AAC/B,QAAI,MAAM,SAAS,gBAAiB,YAAW,OAAO,SAAS;AAAA,aACtD,MAAM,SAAS,eAAgB,WAAU,SAAS,KAAK;AAAA,aACvD,MAAM,SAAS,iBAAkB,YAAW,SAAS,OAAO,SAAS;AAAA,EAChF;AACA,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/main.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { attachKernel } from "@ai-ecoverse/slicc-kernel";
|
|
2
|
+
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
|
|
2
3
|
import { createRegistry } from "@earendil-works/pi-durable";
|
|
3
4
|
import { CodingTools } from "@earendil-works/pi-durable/tools";
|
|
4
5
|
import { openAgent } from "./agent.js";
|
|
5
6
|
import { EncryptedCredentialStore } from "./credentials.js";
|
|
6
7
|
import { hostAgent } from "./host.js";
|
|
7
|
-
import {
|
|
8
|
+
import { createActivity } from "./kernel/activity.js";
|
|
9
|
+
import { HOME, kernelEnvironment } from "./kernel/env.js";
|
|
10
|
+
import { setupLicks } from "./licks/index.js";
|
|
8
11
|
import { transportFetch } from "./net.js";
|
|
9
12
|
import { sliccPrompt } from "./prompt.js";
|
|
10
13
|
import {
|
|
@@ -14,7 +17,7 @@ import {
|
|
|
14
17
|
sliccProviders
|
|
15
18
|
} from "./settings.js";
|
|
16
19
|
import { openOpfsSqliteStorage } from "./sqlite.js";
|
|
17
|
-
import { agentVersion, commandsOnPath, transportName } from "./system.js";
|
|
20
|
+
import { agentVersion, commandsOnPath, kernelBoot, transportName } from "./system.js";
|
|
18
21
|
import { kernelPort, serveConnections } from "./worker.js";
|
|
19
22
|
async function start(scope, port, options) {
|
|
20
23
|
const attach = options.attach ?? attachKernel;
|
|
@@ -32,14 +35,30 @@ async function start(scope, port, options) {
|
|
|
32
35
|
const registry = createRegistry();
|
|
33
36
|
registry.install(CodingTools);
|
|
34
37
|
registry.install(sliccPrompt(facts));
|
|
38
|
+
const licks = setupLicks(registry);
|
|
39
|
+
const activity = createActivity();
|
|
40
|
+
const environment = kernelEnvironment(client, { activity });
|
|
35
41
|
const agent = await openAgent({
|
|
36
42
|
models,
|
|
37
43
|
model: options.model ?? DEFAULT_MODEL,
|
|
38
44
|
storage: await (options.storage ?? (() => openOpfsSqliteStorage()))(),
|
|
39
45
|
registry,
|
|
40
|
-
env:
|
|
46
|
+
env: environment
|
|
47
|
+
});
|
|
48
|
+
const sources = licks.attach(agent, {
|
|
49
|
+
env: kernelEnvironment(client)({ cwd: HOME }),
|
|
50
|
+
home: HOME,
|
|
51
|
+
activity
|
|
52
|
+
});
|
|
53
|
+
await sources.start(BACKGROUND_CONTEXT);
|
|
54
|
+
await sources.boot(
|
|
55
|
+
{ version: facts.version, boot: await kernelBoot(client) },
|
|
56
|
+
BACKGROUND_CONTEXT
|
|
57
|
+
);
|
|
58
|
+
return hostAgent(agent, {
|
|
59
|
+
settings: await createAgentSettings(models, credentials, providers),
|
|
60
|
+
licks: { licks: licks.licks, sources }
|
|
41
61
|
});
|
|
42
|
-
return hostAgent(agent, { settings: await createAgentSettings(models, credentials, providers) });
|
|
43
62
|
}
|
|
44
63
|
function runAgentWorker(scope, options = {}) {
|
|
45
64
|
const host = start(scope, kernelPort(scope), options);
|
package/dist/main.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/main.ts"],
|
|
4
|
-
"sourcesContent": ["import { attachKernel } from '@ai-ecoverse/slicc-kernel';\nimport type { CredentialStore, Provider } from '@earendil-works/pi-ai';\nimport { createRegistry, type ModelRef, type Storage } from '@earendil-works/pi-durable';\nimport { CodingTools } from '@earendil-works/pi-durable/tools';\nimport { openAgent } from './agent.ts';\nimport { EncryptedCredentialStore } from './credentials.ts';\nimport { type AgentHost, hostAgent } from './host.ts';\nimport type { KernelClient } from './kernel/client.ts';\nimport { kernelEnvironment } from './kernel/env.ts';\nimport { type Transport, transportFetch } from './net.ts';\nimport { sliccPrompt } from './prompt.ts';\nimport {\n createAgentSettings,\n createSliccModels,\n DEFAULT_MODEL,\n sliccProviders,\n} from './settings.ts';\nimport { openOpfsSqliteStorage } from './sqlite.ts';\nimport { agentVersion, commandsOnPath, transportName } from './system.ts';\nimport { kernelPort, serveConnections, type WorkerScope } from './worker.ts';\n\nexport interface AgentWorkerScope extends WorkerScope {\n fetch: typeof fetch;\n location: { origin: string };\n}\n\nexport type KernelAttach = (port: MessagePort) => Promise<KernelClient & { transport: Transport }>;\n\nexport interface AgentWorkerOptions {\n model?: ModelRef;\n providers?: Provider[];\n attach?: KernelAttach;\n credentials?: () => Promise<CredentialStore>;\n storage?: () => Promise<Storage>;\n}\n\nasync function start(\n scope: AgentWorkerScope,\n port: Promise<MessagePort>,\n options: AgentWorkerOptions\n): Promise<AgentHost> {\n const attach: KernelAttach = options.attach ?? attachKernel;\n const client = await attach(await port);\n const native = scope.fetch.bind(scope);\n scope.fetch = transportFetch(client.transport, scope.location.origin, native);\n const facts = {\n version: await agentVersion((url) => native(url)),\n commands: await commandsOnPath(client),\n transport: transportName(client.transport),\n };\n const credentials = await (options.credentials ?? (() => EncryptedCredentialStore.open()))();\n const providers = options.providers ?? sliccProviders();\n const models = createSliccModels(credentials, providers);\n const registry = createRegistry();\n registry.install(CodingTools);\n registry.install(sliccPrompt(facts));\n const agent = await openAgent({\n models,\n model: options.model ?? DEFAULT_MODEL,\n storage: await (options.storage ?? (() => openOpfsSqliteStorage()))(),\n registry,\n env: kernelEnvironment(client),\n });\n return hostAgent(agent, {
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;
|
|
4
|
+
"sourcesContent": ["import { attachKernel } from '@ai-ecoverse/slicc-kernel';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { CredentialStore, Provider } from '@earendil-works/pi-ai';\nimport { createRegistry, type ModelRef, type Storage } from '@earendil-works/pi-durable';\nimport { CodingTools } from '@earendil-works/pi-durable/tools';\nimport { openAgent } from './agent.ts';\nimport { EncryptedCredentialStore } from './credentials.ts';\nimport { type AgentHost, hostAgent } from './host.ts';\nimport { createActivity } from './kernel/activity.ts';\nimport type { KernelClient } from './kernel/client.ts';\nimport { HOME, kernelEnvironment } from './kernel/env.ts';\nimport { setupLicks } from './licks/index.ts';\nimport { type Transport, transportFetch } from './net.ts';\nimport { sliccPrompt } from './prompt.ts';\nimport {\n createAgentSettings,\n createSliccModels,\n DEFAULT_MODEL,\n sliccProviders,\n} from './settings.ts';\nimport { openOpfsSqliteStorage } from './sqlite.ts';\nimport { agentVersion, commandsOnPath, kernelBoot, transportName } from './system.ts';\nimport { kernelPort, serveConnections, type WorkerScope } from './worker.ts';\n\nexport interface AgentWorkerScope extends WorkerScope {\n fetch: typeof fetch;\n location: { origin: string };\n}\n\nexport type KernelAttach = (port: MessagePort) => Promise<KernelClient & { transport: Transport }>;\n\nexport interface AgentWorkerOptions {\n model?: ModelRef;\n providers?: Provider[];\n attach?: KernelAttach;\n credentials?: () => Promise<CredentialStore>;\n storage?: () => Promise<Storage>;\n}\n\nasync function start(\n scope: AgentWorkerScope,\n port: Promise<MessagePort>,\n options: AgentWorkerOptions\n): Promise<AgentHost> {\n const attach: KernelAttach = options.attach ?? attachKernel;\n const client = await attach(await port);\n const native = scope.fetch.bind(scope);\n scope.fetch = transportFetch(client.transport, scope.location.origin, native);\n const facts = {\n version: await agentVersion((url) => native(url)),\n commands: await commandsOnPath(client),\n transport: transportName(client.transport),\n };\n const credentials = await (options.credentials ?? (() => EncryptedCredentialStore.open()))();\n const providers = options.providers ?? sliccProviders();\n const models = createSliccModels(credentials, providers);\n const registry = createRegistry();\n registry.install(CodingTools);\n registry.install(sliccPrompt(facts));\n const licks = setupLicks(registry);\n const activity = createActivity();\n const environment = kernelEnvironment(client, { activity });\n const agent = await openAgent({\n models,\n model: options.model ?? DEFAULT_MODEL,\n storage: await (options.storage ?? (() => openOpfsSqliteStorage()))(),\n registry,\n env: environment,\n });\n const sources = licks.attach(agent, {\n env: kernelEnvironment(client)({ cwd: HOME }),\n home: HOME,\n activity,\n });\n await sources.start(BACKGROUND_CONTEXT);\n await sources.boot(\n { version: facts.version, boot: await kernelBoot(client) },\n BACKGROUND_CONTEXT\n );\n return hostAgent(agent, {\n settings: await createAgentSettings(models, credentials, providers),\n licks: { licks: licks.licks, sources },\n });\n}\n\nexport function runAgentWorker(\n scope: AgentWorkerScope,\n options: AgentWorkerOptions = {}\n): Promise<AgentHost> {\n const host = start(scope, kernelPort(scope), options);\n host.catch(() => undefined);\n serveConnections(scope, host);\n return host;\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AAEnC,SAAS,sBAAmD;AAC5D,SAAS,mBAAmB;AAC5B,SAAS,iBAAiB;AAC1B,SAAS,gCAAgC;AACzC,SAAyB,iBAAiB;AAC1C,SAAS,sBAAsB;AAE/B,SAAS,MAAM,yBAAyB;AACxC,SAAS,kBAAkB;AAC3B,SAAyB,sBAAsB;AAC/C,SAAS,mBAAmB;AAC5B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,6BAA6B;AACtC,SAAS,cAAc,gBAAgB,YAAY,qBAAqB;AACxE,SAAS,YAAY,wBAA0C;AAiB/D,eAAe,MACb,OACA,MACA,SACoB;AACpB,QAAM,SAAuB,QAAQ,UAAU;AAC/C,QAAM,SAAS,MAAM,OAAO,MAAM,IAAI;AACtC,QAAM,SAAS,MAAM,MAAM,KAAK,KAAK;AACrC,QAAM,QAAQ,eAAe,OAAO,WAAW,MAAM,SAAS,QAAQ,MAAM;AAC5E,QAAM,QAAQ;AAAA,IACZ,SAAS,MAAM,aAAa,CAAC,QAAQ,OAAO,GAAG,CAAC;AAAA,IAChD,UAAU,MAAM,eAAe,MAAM;AAAA,IACrC,WAAW,cAAc,OAAO,SAAS;AAAA,EAC3C;AACA,QAAM,cAAc,OAAO,QAAQ,gBAAgB,MAAM,yBAAyB,KAAK,IAAI;AAC3F,QAAM,YAAY,QAAQ,aAAa,eAAe;AACtD,QAAM,SAAS,kBAAkB,aAAa,SAAS;AACvD,QAAM,WAAW,eAAe;AAChC,WAAS,QAAQ,WAAW;AAC5B,WAAS,QAAQ,YAAY,KAAK,CAAC;AACnC,QAAM,QAAQ,WAAW,QAAQ;AACjC,QAAM,WAAW,eAAe;AAChC,QAAM,cAAc,kBAAkB,QAAQ,EAAE,SAAS,CAAC;AAC1D,QAAM,QAAQ,MAAM,UAAU;AAAA,IAC5B;AAAA,IACA,OAAO,QAAQ,SAAS;AAAA,IACxB,SAAS,OAAO,QAAQ,YAAY,MAAM,sBAAsB,IAAI;AAAA,IACpE;AAAA,IACA,KAAK;AAAA,EACP,CAAC;AACD,QAAM,UAAU,MAAM,OAAO,OAAO;AAAA,IAClC,KAAK,kBAAkB,MAAM,EAAE,EAAE,KAAK,KAAK,CAAC;AAAA,IAC5C,MAAM;AAAA,IACN;AAAA,EACF,CAAC;AACD,QAAM,QAAQ,MAAM,kBAAkB;AACtC,QAAM,QAAQ;AAAA,IACZ,EAAE,SAAS,MAAM,SAAS,MAAM,MAAM,WAAW,MAAM,EAAE;AAAA,IACzD;AAAA,EACF;AACA,SAAO,UAAU,OAAO;AAAA,IACtB,UAAU,MAAM,oBAAoB,QAAQ,aAAa,SAAS;AAAA,IAClE,OAAO,EAAE,OAAO,MAAM,OAAO,QAAQ;AAAA,EACvC,CAAC;AACH;AAEO,SAAS,eACd,OACA,UAA8B,CAAC,GACX;AACpB,QAAM,OAAO,MAAM,OAAO,WAAW,KAAK,GAAG,OAAO;AACpD,OAAK,MAAM,MAAM,MAAS;AAC1B,mBAAiB,OAAO,IAAI;AAC5B,SAAO;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/services.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/services.ts"],
|
|
4
|
-
"sourcesContent": ["import { type Context, defineService, type ReplicatedState } from '@earendil-works/chord';\nimport type { ConversationView } from '@earendil-works/pi-durable';\n\nexport type SendMode = 'steer' | 'followUp' | 'reject';\n\nexport interface SendRequest {\n text: string;\n whenBusy: SendMode;\n requestId: string | null;\n}\n\nexport interface OperationError {\n code: string;\n message: string;\n}\n\nexport type SendResponse =\n | { accepted: true; submissionId: string; error: null }\n | { accepted: false; submissionId: null; error: OperationError };\n\nexport type PromptResult =\n | { status: 'done'; text: string; reason: null }\n | { status: 'unanswered'; text: null; reason: string };\n\nexport interface AgentControl {\n send(request: SendRequest, context: Context): Promise<SendResponse>;\n wait(submissionId: string, context: Context): Promise<PromptResult>;\n withdraw(\n submissionId: string,\n context: Context\n ): Promise<{ outcome: 'withdrawn' | 'already_placed' | 'not_found' }>;\n abort(context: Context): Promise<void>;\n compact(instructions: string | null, context: Context): Promise<SendResponse>;\n reset(handoff: string | null, context: Context): Promise<void>;\n configure(change: AgentSettingsChange, context: Context): Promise<void>;\n rewind(messageId: string | null, context: Context): Promise<Rewound>;\n}\n\nexport type Rewound =\n | { done: true; text: string; reason: null }\n | { done: false; text: null; reason: 'busy' | 'no-turn' };\n\nexport interface AgentSettingsChange {\n model: { provider: string; modelId: string } | null;\n thinkingLevel: 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | null;\n}\n\nexport const AgentControl = defineService<AgentControl>('slicc.agent.control');\n\nexport type Delivered = 'run' | 'steer' | 'follow-up';\n\nexport interface AgentTranscript {\n readonly state: ReplicatedState<ConversationView>;\n readonly deliveries: ReplicatedState<Record<string, Delivered>>;\n}\n\nexport const AgentTranscript = defineService<AgentTranscript>('slicc.agent.transcript');\n\nexport interface AgentSessions {\n attach(sessionId: string, context: Context): Promise<void>;\n detach(context: Context): Promise<void>;\n}\n\nexport const AgentSessions = defineService<AgentSessions>('slicc.agent.sessions');\n\nexport const AGENT_SESSION = 'agent';\n\nexport interface ModelChoice {\n id: string;\n label: string;\n provider: string;\n kind: 'chat' | 'classifier';\n reasoning: boolean;\n contextWindow: number;\n}\n\nexport interface AccountState {\n id: string;\n provider: string;\n identity: string;\n status: 'connected' | 'disconnected';\n auth: 'api-key' | 'oauth';\n needs: 'cors-free-transport' | null;\n}\n\nexport interface BudgetState {\n provider: string;\n percent: number;\n window: 'weekly';\n resets: string;\n}\n\nexport interface SignIn {\n clientId: string;\n scopes: string;\n imsEnvironment: string;\n}\n\nexport interface SettingsState {\n models: ModelChoice[];\n accounts: AccountState[];\n budget?: BudgetState | null;\n}\n\nexport interface AgentSettings {\n readonly state: ReplicatedState<SettingsState>;\n connect(\n providerId: string,\n secret: string,\n region: string | null,\n context: Context\n ): Promise<void>;\n disconnect(providerId: string, context: Context): Promise<void>;\n signIn(providerId: string, context: Context): Promise<SignIn | null>;\n}\n\nexport const AgentSettings = defineService<AgentSettings>('slicc.agent.settings');\n"],
|
|
5
|
-
"mappings": "AAAA,SAAuB,qBAA2C;
|
|
4
|
+
"sourcesContent": ["import { type Context, defineService, type ReplicatedState } from '@earendil-works/chord';\nimport type { ConversationView } from '@earendil-works/pi-durable';\n\nexport type SendMode = 'steer' | 'followUp' | 'reject';\n\nexport interface SendRequest {\n text: string;\n whenBusy: SendMode;\n requestId: string | null;\n}\n\nexport interface OperationError {\n code: string;\n message: string;\n}\n\nexport type SendResponse =\n | { accepted: true; submissionId: string; error: null }\n | { accepted: false; submissionId: null; error: OperationError };\n\nexport type PromptResult =\n | { status: 'done'; text: string; reason: null }\n | { status: 'unanswered'; text: null; reason: string };\n\nexport interface AgentControl {\n send(request: SendRequest, context: Context): Promise<SendResponse>;\n wait(submissionId: string, context: Context): Promise<PromptResult>;\n withdraw(\n submissionId: string,\n context: Context\n ): Promise<{ outcome: 'withdrawn' | 'already_placed' | 'not_found' }>;\n abort(context: Context): Promise<void>;\n compact(instructions: string | null, context: Context): Promise<SendResponse>;\n reset(handoff: string | null, context: Context): Promise<void>;\n configure(change: AgentSettingsChange, context: Context): Promise<void>;\n rewind(messageId: string | null, context: Context): Promise<Rewound>;\n resolveLick(\n lickId: string,\n state: 'confirmed' | 'dismissed',\n context: Context\n ): Promise<LickResolved>;\n webhook(\n name: string,\n delivery: WebhookRequest,\n context: Context\n ): Promise<{ delivered: boolean }>;\n}\n\nexport type LickResolved =\n | { done: true; text: string; error: null }\n | { done: false; text: null; error: string };\n\nexport interface WebhookRequest {\n id: string | null;\n headers: Record<string, string>;\n body: string;\n}\n\nexport type Rewound =\n | { done: true; text: string; reason: null }\n | { done: false; text: null; reason: 'busy' | 'no-turn' };\n\nexport interface AgentSettingsChange {\n model: { provider: string; modelId: string } | null;\n thinkingLevel: 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | null;\n}\n\nexport const AgentControl = defineService<AgentControl>('slicc.agent.control');\n\nexport type Delivered = 'run' | 'steer' | 'follow-up';\n\nexport interface AgentTranscript {\n readonly state: ReplicatedState<ConversationView>;\n readonly deliveries: ReplicatedState<Record<string, Delivered>>;\n}\n\nexport const AgentTranscript = defineService<AgentTranscript>('slicc.agent.transcript');\n\nexport interface AgentSessions {\n attach(sessionId: string, context: Context): Promise<void>;\n detach(context: Context): Promise<void>;\n}\n\nexport const AgentSessions = defineService<AgentSessions>('slicc.agent.sessions');\n\nexport const AGENT_SESSION = 'agent';\n\nexport interface ModelChoice {\n id: string;\n label: string;\n provider: string;\n kind: 'chat' | 'classifier';\n reasoning: boolean;\n contextWindow: number;\n}\n\nexport interface AccountState {\n id: string;\n provider: string;\n identity: string;\n status: 'connected' | 'disconnected';\n auth: 'api-key' | 'oauth';\n needs: 'cors-free-transport' | null;\n}\n\nexport interface BudgetState {\n provider: string;\n percent: number;\n window: 'weekly';\n resets: string;\n}\n\nexport interface SignIn {\n clientId: string;\n scopes: string;\n imsEnvironment: string;\n}\n\nexport interface SettingsState {\n models: ModelChoice[];\n accounts: AccountState[];\n budget?: BudgetState | null;\n}\n\nexport interface AgentSettings {\n readonly state: ReplicatedState<SettingsState>;\n connect(\n providerId: string,\n secret: string,\n region: string | null,\n context: Context\n ): Promise<void>;\n disconnect(providerId: string, context: Context): Promise<void>;\n signIn(providerId: string, context: Context): Promise<SignIn | null>;\n}\n\nexport const AgentSettings = defineService<AgentSettings>('slicc.agent.settings');\n"],
|
|
5
|
+
"mappings": "AAAA,SAAuB,qBAA2C;AAmE3D,MAAM,eAAe,cAA4B,qBAAqB;AAStE,MAAM,kBAAkB,cAA+B,wBAAwB;AAO/E,MAAM,gBAAgB,cAA6B,sBAAsB;AAEzE,MAAM,gBAAgB;AAmDtB,MAAM,gBAAgB,cAA6B,sBAAsB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
|
|
2
|
+
import { entryLick } from "../licks/state.js";
|
|
2
3
|
import { Emitter } from "./emitter.js";
|
|
3
4
|
import { isBusy, queued, toMessages } from "./messages.js";
|
|
4
5
|
const CONE = "cone";
|
|
@@ -115,7 +116,11 @@ class AgentAdapter extends Emitter {
|
|
|
115
116
|
}
|
|
116
117
|
answer() {
|
|
117
118
|
}
|
|
118
|
-
resolveLick() {
|
|
119
|
+
resolveLick(_agentId, messageId, state) {
|
|
120
|
+
const message = this.#messages.find((candidate) => candidate.id === messageId);
|
|
121
|
+
const entry = this.#view()?.entries.find((candidate) => `e${candidate.id}` === messageId);
|
|
122
|
+
const lick = message?.role === "lick" && entry ? entryLick(entry) : void 0;
|
|
123
|
+
if (lick) void this.#connection.control.resolveLick(lick.id, state, BACKGROUND_CONTEXT);
|
|
119
124
|
}
|
|
120
125
|
compact() {
|
|
121
126
|
void this.#connection.control.compact(null, BACKGROUND_CONTEXT);
|
|
@@ -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 type { AgentSettingsChange } 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 #messages: Message[] = [];\n\n constructor(connection: AgentConnection) {\n super();\n this.#connection = connection;\n this.#ready = new Promise((resolve) => {\n connection.transcript.subscribe((view) => {\n this.#update(view);\n resolve();\n });\n connection.deliveries.subscribe(() => {\n const view = this.#view();\n if (view) this.#update(view);\n });\n });\n }\n\n #view(): ConversationView | undefined {\n return this.#connection.transcript.value;\n }\n\n #update(view: ConversationView): void {\n const before = this.#messages;\n this.#messages = toMessages(view, this.#connection.deliveries.value);\n this.emit('agents', this.list());\n if (before.length !== this.#messages.length) {\n this.emit('messages', CONE);\n return;\n }\n const changed = this.#messages.filter(\n (message, index) => JSON.stringify(message) !== JSON.stringify(before[index])\n );\n if (changed.length === 1)\n this.emit('message', { agentId: CONE, message: changed[0] as Message });\n else if (changed.length > 1) this.emit('messages', CONE);\n }\n\n ready(): Promise<void> {\n return this.#ready;\n }\n\n list(): readonly Agent[] {\n const doc = this.#view()?.docs['pi.agent'] as AgentDoc | undefined;\n return [\n {\n id: CONE,\n name: 'sliccy',\n kind: 'cone',\n parentId: null,\n status: status(this.#view(), this.#messages),\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 return CONE;\n }\n\n select(): void {}\n\n messages(): readonly Message[] {\n return this.#messages;\n }\n\n async send(_agentId: string, input: string | Outgoing): Promise<void> {\n const outgoing = typeof input === 'string' ? { text: input } : input;\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 },\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(): void {\n void this.#connection.control.abort(BACKGROUND_CONTEXT);\n }\n\n busy(): boolean {\n return isBusy(this.#view());\n }\n\n queue(): readonly UserMessage[] {\n return queued(this.#view());\n }\n\n unqueue(_agentId: string, messageId: string): void {\n void this.#connection.control.withdraw(messageId.replace(/^q/, ''), BACKGROUND_CONTEXT);\n }\n\n suggestion(): string | null {\n return null;\n }\n\n answer(): void {}\n\n resolveLick(): void {}\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(change: Partial<AgentSettingsChange>): void {\n void this.#connection.control.configure(\n { model: change.model ?? null, thinkingLevel: change.thinkingLevel ?? null },\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({ model: { provider: model.slice(0, at), modelId: model.slice(at + 1) } });\n }\n\n setThinking(_agentId: string, level: Thinking): void {\n this.#configure({ thinkingLevel: level });\n }\n\n async older(): Promise<readonly Message[]> {\n return [];\n }\n\n commands(): readonly SlashCommand[] {\n return [];\n }\n\n createScoop(): Agent {\n throw new Error('Scoops come with a later slicc-agent');\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;
|
|
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 } 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 #messages: Message[] = [];\n\n constructor(connection: AgentConnection) {\n super();\n this.#connection = connection;\n this.#ready = new Promise((resolve) => {\n connection.transcript.subscribe((view) => {\n this.#update(view);\n resolve();\n });\n connection.deliveries.subscribe(() => {\n const view = this.#view();\n if (view) this.#update(view);\n });\n });\n }\n\n #view(): ConversationView | undefined {\n return this.#connection.transcript.value;\n }\n\n #update(view: ConversationView): void {\n const before = this.#messages;\n this.#messages = toMessages(view, this.#connection.deliveries.value);\n this.emit('agents', this.list());\n if (before.length !== this.#messages.length) {\n this.emit('messages', CONE);\n return;\n }\n const changed = this.#messages.filter(\n (message, index) => JSON.stringify(message) !== JSON.stringify(before[index])\n );\n if (changed.length === 1)\n this.emit('message', { agentId: CONE, message: changed[0] as Message });\n else if (changed.length > 1) this.emit('messages', CONE);\n }\n\n ready(): Promise<void> {\n return this.#ready;\n }\n\n list(): readonly Agent[] {\n const doc = this.#view()?.docs['pi.agent'] as AgentDoc | undefined;\n return [\n {\n id: CONE,\n name: 'sliccy',\n kind: 'cone',\n parentId: null,\n status: status(this.#view(), this.#messages),\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 return CONE;\n }\n\n select(): void {}\n\n messages(): readonly Message[] {\n return this.#messages;\n }\n\n async send(_agentId: string, input: string | Outgoing): Promise<void> {\n const outgoing = typeof input === 'string' ? { text: input } : input;\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 },\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(): void {\n void this.#connection.control.abort(BACKGROUND_CONTEXT);\n }\n\n busy(): boolean {\n return isBusy(this.#view());\n }\n\n queue(): readonly UserMessage[] {\n return queued(this.#view());\n }\n\n unqueue(_agentId: string, messageId: string): void {\n void this.#connection.control.withdraw(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.find((candidate) => candidate.id === messageId);\n const entry = this.#view()?.entries.find((candidate) => `e${candidate.id}` === messageId);\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(change: Partial<AgentSettingsChange>): void {\n void this.#connection.control.configure(\n { model: change.model ?? null, thinkingLevel: change.thinkingLevel ?? null },\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({ model: { provider: model.slice(0, at), modelId: model.slice(at + 1) } });\n }\n\n setThinking(_agentId: string, level: Thinking): void {\n this.#configure({ thinkingLevel: level });\n }\n\n async older(): Promise<readonly Message[]> {\n return [];\n }\n\n commands(): readonly SlashCommand[] {\n return [];\n }\n\n createScoop(): Agent {\n throw new Error('Scoops come with a later slicc-agent');\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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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { parseLick } from "../licks/lick.js";
|
|
2
|
+
import { entryLick, LICK_TOOLS, lickDecisions } from "../licks/state.js";
|
|
1
3
|
function textOf(content) {
|
|
2
4
|
if (typeof content === "string") return content;
|
|
3
5
|
return (content ?? []).map((block) => block.type === "text" ? block.text ?? "" : "").join("");
|
|
@@ -153,6 +155,26 @@ function user(id, message, delivered) {
|
|
|
153
155
|
...delivered ? { delivered } : {}
|
|
154
156
|
};
|
|
155
157
|
}
|
|
158
|
+
function lickMessage(id, entry, decisions) {
|
|
159
|
+
const lick = entryLick(entry);
|
|
160
|
+
if (!lick) return void 0;
|
|
161
|
+
const state = lick.actions?.length ? decisions.get(lick.id) ?? "pending" : void 0;
|
|
162
|
+
return {
|
|
163
|
+
id,
|
|
164
|
+
role: "lick",
|
|
165
|
+
channel: lick.channel,
|
|
166
|
+
title: lick.title,
|
|
167
|
+
text: lick.text,
|
|
168
|
+
createdAt: lick.at,
|
|
169
|
+
...lick.body ? { body: lick.body } : {},
|
|
170
|
+
...lick.count > 1 ? { count: lick.count } : {},
|
|
171
|
+
...state ? { state } : {}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function withoutLickTools(message) {
|
|
175
|
+
const parts = message.parts.filter((item) => item.type !== "tool" || !LICK_TOOLS[item.tool.name]);
|
|
176
|
+
return parts.length === message.parts.length ? message : { ...message, parts };
|
|
177
|
+
}
|
|
156
178
|
function system(id, entry, text) {
|
|
157
179
|
const reason = entry.data?.reason;
|
|
158
180
|
return {
|
|
@@ -191,14 +213,15 @@ const rewound = (id) => ({
|
|
|
191
213
|
text: "Its prompt is back in the composer.",
|
|
192
214
|
createdAt: 0
|
|
193
215
|
});
|
|
194
|
-
function entryMessage(entry, out, calls, delivered) {
|
|
216
|
+
function entryMessage(entry, out, calls, delivered, decisions) {
|
|
195
217
|
const id = `e${entry.id}`;
|
|
196
218
|
const model = entry.model?.[0];
|
|
197
219
|
if (entry.kind === "slicc.rewound") out.push(rewound(id));
|
|
198
|
-
else if (entry.kind === "pi.user" && model)
|
|
220
|
+
else if (entry.kind === "pi.user" && model)
|
|
221
|
+
out.push(lickMessage(id, entry, decisions) ?? user(id, model, delivered));
|
|
199
222
|
else if (entry.kind === "pi.assistant" && model) {
|
|
200
223
|
const message = assistant(id, model);
|
|
201
|
-
out.push(message);
|
|
224
|
+
out.push(withoutLickTools(message));
|
|
202
225
|
for (const [key, value] of tools([message])) calls.set(key, value);
|
|
203
226
|
} else if (entry.kind === "pi.tool-result" && model) settle(calls, model);
|
|
204
227
|
else if (entry.kind === "pi.compaction") out.push(system(id, entry, textOf(model?.content)));
|
|
@@ -244,10 +267,11 @@ function toMessages(view, deliveries = {}) {
|
|
|
244
267
|
const out = [];
|
|
245
268
|
if (!view) return out;
|
|
246
269
|
const calls = /* @__PURE__ */ new Map();
|
|
270
|
+
const decisions = lickDecisions(view.entries);
|
|
247
271
|
let previous;
|
|
248
272
|
for (const entry of view.entries) {
|
|
249
273
|
const steered = previous?.kind === "pi.tool-result" ? "steer" : void 0;
|
|
250
|
-
entryMessage(entry, out, calls, deliveries[String(entry.id)] ?? steered);
|
|
274
|
+
entryMessage(entry, out, calls, deliveries[String(entry.id)] ?? steered, decisions);
|
|
251
275
|
previous = entry;
|
|
252
276
|
}
|
|
253
277
|
live(view, out, calls);
|
|
@@ -258,7 +282,7 @@ function isBusy(view) {
|
|
|
258
282
|
}
|
|
259
283
|
function queued(view) {
|
|
260
284
|
const items = view?.docs["pi.inbox"]?.items ?? [];
|
|
261
|
-
return items.filter((item) => item.mode !== "write").map((item) => ({
|
|
285
|
+
return items.filter((item) => item.mode !== "write" && !parseLick(textOf(item.content))).map((item) => ({
|
|
262
286
|
id: `q${item.id}`,
|
|
263
287
|
role: "user",
|
|
264
288
|
text: textOf(item.content),
|
|
@@ -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 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 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 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\nconst rewound = (id: string): SystemMessage => ({\n id,\n role: 'system',\n kind: 'notice',\n title: 'Rewound 1 turn',\n text: 'Its prompt is back in the composer.',\n createdAt: 0,\n});\n\nfunction entryMessage(\n entry: EntryRecord,\n out: Message[],\n calls: Map<string, ToolCall>,\n delivered: Delivered | undefined\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));\n else if (entry.kind === 'pi.user' && model) out.push(user(id, model, delivered));\n else if (entry.kind === 'pi.assistant' && model) {\n const message = assistant(id, model);\n out.push(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 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);\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')\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": "
|
|
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 };\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\nconst rewound = (id: string): SystemMessage => ({\n id,\n role: 'system',\n kind: 'notice',\n title: 'Rewound 1 turn',\n text: 'Its prompt is back in the composer.',\n createdAt: 0,\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));\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": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|