@ai-ecoverse/slicc-agent 3.3.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +149 -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 +102 -0
- package/dist/licks/lick.js.map +7 -0
- package/dist/licks/licks.js +181 -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 +138 -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 +21 -0
- package/dist/types/licks/licks.d.ts +56 -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 +1 -1
|
@@ -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 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
|
}
|
package/dist/system.js
CHANGED
|
@@ -44,9 +44,25 @@ function systemSection(facts) {
|
|
|
44
44
|
`Not here yet: ${[...missing, "a browser or CDP tool", "GitHub credentials"].join(", ")}.`
|
|
45
45
|
].join("\n");
|
|
46
46
|
}
|
|
47
|
+
async function kernelBoot(client) {
|
|
48
|
+
const decoder = new TextDecoder();
|
|
49
|
+
let out = "";
|
|
50
|
+
try {
|
|
51
|
+
const process = await client.spawn(["cat", "/proc/stat"], {
|
|
52
|
+
onStdout: (bytes) => {
|
|
53
|
+
out += decoder.decode(bytes, { stream: true });
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
await process.exited;
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return /^btime (\d+)$/m.exec(out)?.[1] ?? null;
|
|
61
|
+
}
|
|
47
62
|
export {
|
|
48
63
|
agentVersion,
|
|
49
64
|
commandsOnPath,
|
|
65
|
+
kernelBoot,
|
|
50
66
|
systemSection,
|
|
51
67
|
transportName
|
|
52
68
|
};
|
package/dist/system.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/system.ts"],
|
|
4
|
-
"sourcesContent": ["import type { KernelClient } from './kernel/client.ts';\nimport type { Transport } from './net.ts';\n\nexport interface SystemFacts {\n version: string;\n commands: string[];\n transport: string;\n}\n\nconst LIST_PATH = 'IFS=:; for d in $PATH; do [ -d \"$d\" ] && ls -1 \"$d\"; done';\nconst RUNTIMES = ['node', 'python3'];\n\nexport async function commandsOnPath(client: KernelClient): Promise<string[]> {\n const decoder = new TextDecoder();\n let out = '';\n try {\n const process = await client.spawn(['bash', '-c', LIST_PATH], {\n onStdout: (bytes) => {\n out += decoder.decode(bytes, { stream: true });\n },\n });\n await process.exited;\n } catch {\n return [];\n }\n const names = out.split('\\n').map((name) => name.trim());\n return [...new Set(names.filter(Boolean))].sort();\n}\n\nexport function transportName(transport: Transport | undefined): string {\n const traits = transport?.traits;\n if (traits?.crossOrigin === 'cors') {\n return \"the page's own fetch, so only servers that allow CORS answer\";\n }\n if (traits?.manualRedirects) return 'a local proxy (slicc-node) that fetches without CORS';\n return 'a relay that fetches without CORS';\n}\n\nexport async function agentVersion(\n fetcher: (url: URL) => Promise<Response>,\n url = new URL('../package.json', import.meta.url)\n): Promise<string> {\n try {\n const response = await fetcher(url);\n const manifest = response.ok ? ((await response.json()) as { version?: string }) : {};\n return manifest.version ?? 'unknown';\n } catch {\n return 'unknown';\n }\n}\n\nexport function systemSection(facts: SystemFacts): string {\n const missing = RUNTIMES.filter((name) => !facts.commands.includes(name));\n return [\n `You are SLICC's agent in seven: slicc-agent ${facts.version}, installed in /opt/agent. Your bash runs on the page's shared slicc-kernel, a WebAssembly sandbox in the user's browser, not on a server or the user's machine.`,\n \"Files: / is the browser's private file system (OPFS). Work in /home and /tmp. /os and /opt are the system; change them only when asked.\",\n `Commands on PATH: ${facts.commands.join(' ') || 'none found'}.`,\n 'Install command-line tools with `pnpm add -g <package>`. There is no ipk.',\n `Network: requests go through ${facts.transport}. localhost and 127.0.0.1 are this sandbox's own loopback, not the user's computer.`,\n `Not here yet: ${[...missing, 'a browser or CDP tool', 'GitHub credentials'].join(', ')}.`,\n ].join('\\n');\n}\n"],
|
|
5
|
-
"mappings": "AASA,MAAM,YAAY;AAClB,MAAM,WAAW,CAAC,QAAQ,SAAS;AAEnC,eAAsB,eAAe,QAAyC;AAC5E,QAAM,UAAU,IAAI,YAAY;AAChC,MAAI,MAAM;AACV,MAAI;AACF,UAAM,UAAU,MAAM,OAAO,MAAM,CAAC,QAAQ,MAAM,SAAS,GAAG;AAAA,MAC5D,UAAU,CAAC,UAAU;AACnB,eAAO,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAAA,MAC/C;AAAA,IACF,CAAC;AACD,UAAM,QAAQ;AAAA,EAChB,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,QAAM,QAAQ,IAAI,MAAM,IAAI,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC;AACvD,SAAO,CAAC,GAAG,IAAI,IAAI,MAAM,OAAO,OAAO,CAAC,CAAC,EAAE,KAAK;AAClD;AAEO,SAAS,cAAc,WAA0C;AACtE,QAAM,SAAS,WAAW;AAC1B,MAAI,QAAQ,gBAAgB,QAAQ;AAClC,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,gBAAiB,QAAO;AACpC,SAAO;AACT;AAEA,eAAsB,aACpB,SACA,MAAM,IAAI,IAAI,mBAAmB,YAAY,GAAG,GAC/B;AACjB,MAAI;AACF,UAAM,WAAW,MAAM,QAAQ,GAAG;AAClC,UAAM,WAAW,SAAS,KAAO,MAAM,SAAS,KAAK,IAA8B,CAAC;AACpF,WAAO,SAAS,WAAW;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,cAAc,OAA4B;AACxD,QAAM,UAAU,SAAS,OAAO,CAAC,SAAS,CAAC,MAAM,SAAS,SAAS,IAAI,CAAC;AACxE,SAAO;AAAA,IACL,+CAA+C,MAAM,OAAO;AAAA,IAC5D;AAAA,IACA,qBAAqB,MAAM,SAAS,KAAK,GAAG,KAAK,YAAY;AAAA,IAC7D;AAAA,IACA,gCAAgC,MAAM,SAAS;AAAA,IAC/C,iBAAiB,CAAC,GAAG,SAAS,yBAAyB,oBAAoB,EAAE,KAAK,IAAI,CAAC;AAAA,EACzF,EAAE,KAAK,IAAI;AACb;",
|
|
4
|
+
"sourcesContent": ["import type { KernelClient } from './kernel/client.ts';\nimport type { Transport } from './net.ts';\n\nexport interface SystemFacts {\n version: string;\n commands: string[];\n transport: string;\n}\n\nconst LIST_PATH = 'IFS=:; for d in $PATH; do [ -d \"$d\" ] && ls -1 \"$d\"; done';\nconst RUNTIMES = ['node', 'python3'];\n\nexport async function commandsOnPath(client: KernelClient): Promise<string[]> {\n const decoder = new TextDecoder();\n let out = '';\n try {\n const process = await client.spawn(['bash', '-c', LIST_PATH], {\n onStdout: (bytes) => {\n out += decoder.decode(bytes, { stream: true });\n },\n });\n await process.exited;\n } catch {\n return [];\n }\n const names = out.split('\\n').map((name) => name.trim());\n return [...new Set(names.filter(Boolean))].sort();\n}\n\nexport function transportName(transport: Transport | undefined): string {\n const traits = transport?.traits;\n if (traits?.crossOrigin === 'cors') {\n return \"the page's own fetch, so only servers that allow CORS answer\";\n }\n if (traits?.manualRedirects) return 'a local proxy (slicc-node) that fetches without CORS';\n return 'a relay that fetches without CORS';\n}\n\nexport async function agentVersion(\n fetcher: (url: URL) => Promise<Response>,\n url = new URL('../package.json', import.meta.url)\n): Promise<string> {\n try {\n const response = await fetcher(url);\n const manifest = response.ok ? ((await response.json()) as { version?: string }) : {};\n return manifest.version ?? 'unknown';\n } catch {\n return 'unknown';\n }\n}\n\nexport function systemSection(facts: SystemFacts): string {\n const missing = RUNTIMES.filter((name) => !facts.commands.includes(name));\n return [\n `You are SLICC's agent in seven: slicc-agent ${facts.version}, installed in /opt/agent. Your bash runs on the page's shared slicc-kernel, a WebAssembly sandbox in the user's browser, not on a server or the user's machine.`,\n \"Files: / is the browser's private file system (OPFS). Work in /home and /tmp. /os and /opt are the system; change them only when asked.\",\n `Commands on PATH: ${facts.commands.join(' ') || 'none found'}.`,\n 'Install command-line tools with `pnpm add -g <package>`. There is no ipk.',\n `Network: requests go through ${facts.transport}. localhost and 127.0.0.1 are this sandbox's own loopback, not the user's computer.`,\n `Not here yet: ${[...missing, 'a browser or CDP tool', 'GitHub credentials'].join(', ')}.`,\n ].join('\\n');\n}\n\nexport async function kernelBoot(client: KernelClient): Promise<string | null> {\n const decoder = new TextDecoder();\n let out = '';\n try {\n const process = await client.spawn(['cat', '/proc/stat'], {\n onStdout: (bytes) => {\n out += decoder.decode(bytes, { stream: true });\n },\n });\n await process.exited;\n } catch {\n return null;\n }\n return /^btime (\\d+)$/m.exec(out)?.[1] ?? null;\n}\n"],
|
|
5
|
+
"mappings": "AASA,MAAM,YAAY;AAClB,MAAM,WAAW,CAAC,QAAQ,SAAS;AAEnC,eAAsB,eAAe,QAAyC;AAC5E,QAAM,UAAU,IAAI,YAAY;AAChC,MAAI,MAAM;AACV,MAAI;AACF,UAAM,UAAU,MAAM,OAAO,MAAM,CAAC,QAAQ,MAAM,SAAS,GAAG;AAAA,MAC5D,UAAU,CAAC,UAAU;AACnB,eAAO,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAAA,MAC/C;AAAA,IACF,CAAC;AACD,UAAM,QAAQ;AAAA,EAChB,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,QAAM,QAAQ,IAAI,MAAM,IAAI,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC;AACvD,SAAO,CAAC,GAAG,IAAI,IAAI,MAAM,OAAO,OAAO,CAAC,CAAC,EAAE,KAAK;AAClD;AAEO,SAAS,cAAc,WAA0C;AACtE,QAAM,SAAS,WAAW;AAC1B,MAAI,QAAQ,gBAAgB,QAAQ;AAClC,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,gBAAiB,QAAO;AACpC,SAAO;AACT;AAEA,eAAsB,aACpB,SACA,MAAM,IAAI,IAAI,mBAAmB,YAAY,GAAG,GAC/B;AACjB,MAAI;AACF,UAAM,WAAW,MAAM,QAAQ,GAAG;AAClC,UAAM,WAAW,SAAS,KAAO,MAAM,SAAS,KAAK,IAA8B,CAAC;AACpF,WAAO,SAAS,WAAW;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,cAAc,OAA4B;AACxD,QAAM,UAAU,SAAS,OAAO,CAAC,SAAS,CAAC,MAAM,SAAS,SAAS,IAAI,CAAC;AACxE,SAAO;AAAA,IACL,+CAA+C,MAAM,OAAO;AAAA,IAC5D;AAAA,IACA,qBAAqB,MAAM,SAAS,KAAK,GAAG,KAAK,YAAY;AAAA,IAC7D;AAAA,IACA,gCAAgC,MAAM,SAAS;AAAA,IAC/C,iBAAiB,CAAC,GAAG,SAAS,yBAAyB,oBAAoB,EAAE,KAAK,IAAI,CAAC;AAAA,EACzF,EAAE,KAAK,IAAI;AACb;AAEA,eAAsB,WAAW,QAA8C;AAC7E,QAAM,UAAU,IAAI,YAAY;AAChC,MAAI,MAAM;AACV,MAAI;AACF,UAAM,UAAU,MAAM,OAAO,MAAM,CAAC,OAAO,YAAY,GAAG;AAAA,MACxD,UAAU,CAAC,UAAU;AACnB,eAAO,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAAA,MAC/C;AAAA,IACF,CAAC;AACD,UAAM,QAAQ;AAAA,EAChB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO,iBAAiB,KAAK,GAAG,IAAI,CAAC,KAAK;AAC5C;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/types/control.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { type Conversation, type Harness, type SubmissionId } from '@earendil-works/pi-durable';
|
|
2
2
|
import type { Cones } from './cone.ts';
|
|
3
3
|
import type { Deliveries } from './deliveries.ts';
|
|
4
|
+
import type { LickSources, Licks } from './licks/index.ts';
|
|
4
5
|
import type { AgentControl } from './services.ts';
|
|
6
|
+
export interface HostLicks {
|
|
7
|
+
licks: Licks;
|
|
8
|
+
sources: LickSources;
|
|
9
|
+
}
|
|
5
10
|
export declare function submissionId(value: string): SubmissionId | undefined;
|
|
6
|
-
export declare function createAgentControl(harness: Harness, target: Conversation | Cones, deliveries?: Deliveries): AgentControl;
|
|
11
|
+
export declare function createAgentControl(harness: Harness, target: Conversation | Cones, deliveries?: Deliveries, licks?: HostLicks): AgentControl;
|
package/dist/types/host.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Context } from '@earendil-works/chord';
|
|
2
2
|
import type { Agent } from './agent.ts';
|
|
3
|
+
import { type HostLicks } from './control.ts';
|
|
3
4
|
import { AgentSettings } from './services.ts';
|
|
4
5
|
import type { PortEndpoint } from './wire.ts';
|
|
5
6
|
export interface AgentHost {
|
|
@@ -12,4 +13,5 @@ export declare function hostAgent(agent: Agent, options?: {
|
|
|
12
13
|
serverId?: string;
|
|
13
14
|
context?: Context;
|
|
14
15
|
settings?: AgentSettings;
|
|
16
|
+
licks?: HostLicks;
|
|
15
17
|
}): Promise<AgentHost>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface Activity {
|
|
2
|
+
wrote(path: string): void;
|
|
3
|
+
began(): () => void;
|
|
4
|
+
agentMade(path: string): boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface ActivityOptions {
|
|
7
|
+
windowMs?: number;
|
|
8
|
+
now?: () => number;
|
|
9
|
+
}
|
|
10
|
+
export declare function createActivity(options?: ActivityOptions): Activity;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { Context } from '@earendil-works/chord';
|
|
2
2
|
import { type BinaryReader, type DirReader, type ExecutionEnv, type ExecutionError, FileError, type FileInfo, type FileWatcher, type Result, type ShellExecOptions, type ShellExecResult, type TextLineReader, type WatchChange, type WatchTarget } from '@earendil-works/pi-durable/env';
|
|
3
|
+
import type { Activity } from './activity.ts';
|
|
3
4
|
import type { KernelClient } from './client.ts';
|
|
4
5
|
import { type PollOptions } from './watch.ts';
|
|
5
6
|
export interface KernelEnvOptions {
|
|
6
7
|
cwd: string;
|
|
7
8
|
id?: string;
|
|
8
9
|
watch?: PollOptions;
|
|
10
|
+
activity?: Activity;
|
|
9
11
|
}
|
|
10
12
|
type FileResult<T> = Promise<Result<T, FileError>>;
|
|
11
13
|
export declare class SliccKernelEnv implements ExecutionEnv {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Context } from '@earendil-works/chord';
|
|
2
|
+
import type { Conversation, EntryRecord, Harness } from '@earendil-works/pi-durable';
|
|
3
|
+
import type { Deliver } from './fswatch.ts';
|
|
4
|
+
export interface BootFacts {
|
|
5
|
+
version: string;
|
|
6
|
+
boot: string | null;
|
|
7
|
+
}
|
|
8
|
+
export declare function bashCommands(entries: readonly EntryRecord[], after: number): string[];
|
|
9
|
+
export declare function bootLicks(harness: Harness, cone: Conversation, deliver: Deliver, facts: BootFacts, context: Context): Promise<void>;
|