@ai-ecoverse/slicc-agent 3.43.1 → 3.44.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 +12 -2
- package/dist/agent.js +1 -0
- package/dist/agent.js.map +2 -2
- package/dist/client.js +3 -1
- package/dist/client.js.map +2 -2
- package/dist/codemode/index.js +2 -1
- package/dist/codemode/index.js.map +2 -2
- package/dist/control.js +13 -3
- package/dist/control.js.map +2 -2
- package/dist/freezer/index.js +5 -4
- package/dist/freezer/index.js.map +2 -2
- package/dist/host.js +13 -2
- package/dist/host.js.map +2 -2
- package/dist/kernel/env.js +55 -4
- package/dist/kernel/env.js.map +2 -2
- package/dist/kernel/exec.js +84 -16
- package/dist/kernel/exec.js.map +3 -3
- package/dist/licks/licks.js +17 -1
- package/dist/licks/licks.js.map +2 -2
- package/dist/main.js +29 -10
- package/dist/main.js.map +2 -2
- package/dist/mcp/content.js +3 -1
- package/dist/mcp/content.js.map +2 -2
- package/dist/mcp/tools.js +2 -1
- package/dist/mcp/tools.js.map +2 -2
- package/dist/memory/prompt.js +3 -2
- package/dist/memory/prompt.js.map +2 -2
- package/dist/memory/store.js +5 -3
- package/dist/memory/store.js.map +2 -2
- package/dist/remote/host.js +21 -1
- package/dist/remote/host.js.map +2 -2
- package/dist/secrets/extension.js +150 -0
- package/dist/secrets/extension.js.map +7 -0
- package/dist/secrets/names.js +72 -0
- package/dist/secrets/names.js.map +7 -0
- package/dist/secrets/scrub.js +36 -0
- package/dist/secrets/scrub.js.map +7 -0
- package/dist/secrets/vault.js +161 -0
- package/dist/secrets/vault.js.map +7 -0
- package/dist/services.js.map +2 -2
- package/dist/skills/index.js +8 -1
- package/dist/skills/index.js.map +2 -2
- package/dist/spectrum/agent-port.js +6 -2
- package/dist/spectrum/agent-port.js.map +2 -2
- package/dist/types/agent.d.ts +1 -0
- package/dist/types/client.d.ts +2 -0
- package/dist/types/control.d.ts +2 -1
- package/dist/types/host.d.ts +9 -1
- package/dist/types/kernel/env.d.ts +6 -0
- package/dist/types/kernel/exec.d.ts +8 -1
- package/dist/types/licks/licks.d.ts +1 -0
- package/dist/types/mcp/content.d.ts +1 -1
- package/dist/types/remote/host.d.ts +5 -1
- package/dist/types/secrets/extension.d.ts +31 -0
- package/dist/types/secrets/names.d.ts +24 -0
- package/dist/types/secrets/scrub.d.ts +21 -0
- package/dist/types/secrets/vault.d.ts +30 -0
- package/dist/types/services.d.ts +3 -1
- package/dist/types/skills/index.d.ts +1 -0
- package/dist/types/spectrum/agent-port.d.ts +2 -0
- package/package.json +3 -3
package/dist/kernel/exec.js
CHANGED
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
import { descendants, REFUSED } from "./groups.js";
|
|
8
8
|
import { resolve } from "./paths.js";
|
|
9
9
|
const SHELL = ["bash", "-c"];
|
|
10
|
+
const HELD_LIMIT = 32 * 1024 * 1024;
|
|
11
|
+
const HELD = "output held: waiting for the vault";
|
|
10
12
|
class Spill {
|
|
11
13
|
#chunks = [];
|
|
12
14
|
#limit;
|
|
@@ -39,17 +41,45 @@ class Spill {
|
|
|
39
41
|
return path;
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
|
-
function sink(stream, spill, options, context) {
|
|
44
|
+
function sink(stream, spill, options, context, scrub) {
|
|
43
45
|
const decoder = new StreamDecoder();
|
|
46
|
+
const emit = (bytes) => {
|
|
47
|
+
spill.add(bytes);
|
|
48
|
+
const text = decoder.decode(bytes);
|
|
49
|
+
if (text) options?.onOutput?.(text, context, { stream });
|
|
50
|
+
};
|
|
51
|
+
const flush = () => {
|
|
52
|
+
const text = decoder.decode();
|
|
53
|
+
if (text) options?.onOutput?.(text, context, { stream });
|
|
54
|
+
};
|
|
55
|
+
if (!scrub)
|
|
56
|
+
return {
|
|
57
|
+
push: emit,
|
|
58
|
+
end: async () => flush(),
|
|
59
|
+
drop: flush
|
|
60
|
+
};
|
|
61
|
+
let chain = Promise.resolve();
|
|
62
|
+
let dropped = false;
|
|
63
|
+
const live = (bytes) => {
|
|
64
|
+
if (!dropped) emit(bytes);
|
|
65
|
+
};
|
|
44
66
|
return {
|
|
45
67
|
push(bytes) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
68
|
+
scrub.held(bytes.length);
|
|
69
|
+
chain = chain.then(() => scrub.stream.push(bytes)).then((out) => {
|
|
70
|
+
scrub.held(-bytes.length);
|
|
71
|
+
live(out);
|
|
72
|
+
});
|
|
49
73
|
},
|
|
50
|
-
end() {
|
|
51
|
-
|
|
52
|
-
|
|
74
|
+
async end() {
|
|
75
|
+
await chain;
|
|
76
|
+
live(await scrub.stream.end());
|
|
77
|
+
if (!dropped) flush();
|
|
78
|
+
},
|
|
79
|
+
drop() {
|
|
80
|
+
dropped = true;
|
|
81
|
+
chain.catch(() => void 0);
|
|
82
|
+
scrub.stream.drop();
|
|
53
83
|
}
|
|
54
84
|
};
|
|
55
85
|
}
|
|
@@ -72,12 +102,30 @@ function guard(process, options, context, kill) {
|
|
|
72
102
|
get ending() {
|
|
73
103
|
return ending;
|
|
74
104
|
},
|
|
105
|
+
stop,
|
|
75
106
|
release() {
|
|
76
107
|
clearTimeout(timer);
|
|
77
108
|
context.abortSignal?.removeEventListener("abort", onAbort);
|
|
78
109
|
}
|
|
79
110
|
};
|
|
80
111
|
}
|
|
112
|
+
async function drained(sinks, context) {
|
|
113
|
+
const signal = context.abortSignal;
|
|
114
|
+
if (signal?.aborted) return "aborted";
|
|
115
|
+
let off = () => void 0;
|
|
116
|
+
const aborted = new Promise((resolve2) => {
|
|
117
|
+
const onAbort = () => resolve2("aborted");
|
|
118
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
119
|
+
off = () => signal?.removeEventListener("abort", onAbort);
|
|
120
|
+
});
|
|
121
|
+
const ends = Promise.all(sinks.map((each) => each.end())).then(() => void 0);
|
|
122
|
+
ends.catch(() => void 0);
|
|
123
|
+
try {
|
|
124
|
+
return await Promise.race([ends, aborted]);
|
|
125
|
+
} finally {
|
|
126
|
+
off();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
81
129
|
async function start(client, argv, options, join) {
|
|
82
130
|
if (join?.pgid !== void 0) {
|
|
83
131
|
try {
|
|
@@ -90,14 +138,27 @@ async function start(client, argv, options, join) {
|
|
|
90
138
|
}
|
|
91
139
|
return { process: await client.spawn(argv, options), joined: false };
|
|
92
140
|
}
|
|
93
|
-
async function execute(client, cwd, command, options, context, spawned, join) {
|
|
94
|
-
const
|
|
95
|
-
if (
|
|
141
|
+
async function execute(client, cwd, command, options, context, spawned, join, hooks = {}) {
|
|
142
|
+
const given = typeof command === "string" ? [...SHELL, command] : [...command];
|
|
143
|
+
if (given.length === 0) return err(new ExecutionError("spawn_error", "No program to run"));
|
|
144
|
+
const argv = hooks.spawn ? [...hooks.spawn(given)] : given;
|
|
96
145
|
if (context.abortSignal?.aborted)
|
|
97
146
|
return err(new ExecutionError("aborted", "The command was aborted"));
|
|
98
147
|
const spill = new Spill(options?.spill);
|
|
99
|
-
|
|
100
|
-
|
|
148
|
+
let held = 0;
|
|
149
|
+
let overflowed = false;
|
|
150
|
+
let over = () => {
|
|
151
|
+
overflowed = true;
|
|
152
|
+
};
|
|
153
|
+
const scrubbed = () => hooks.scrub && {
|
|
154
|
+
stream: hooks.scrub(),
|
|
155
|
+
held(bytes) {
|
|
156
|
+
held += bytes;
|
|
157
|
+
if (held > HELD_LIMIT) over();
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
const out = sink("stdout", spill, options, context, scrubbed());
|
|
161
|
+
const errors = sink("stderr", spill, options, context, scrubbed());
|
|
101
162
|
let started;
|
|
102
163
|
try {
|
|
103
164
|
started = await start(
|
|
@@ -123,13 +184,18 @@ async function execute(client, cwd, command, options, context, spawned, join) {
|
|
|
123
184
|
context,
|
|
124
185
|
() => join ? void sweep(client, process) : process.signal("SIGKILL")
|
|
125
186
|
);
|
|
187
|
+
over = () => watch.stop("held");
|
|
188
|
+
if (overflowed) over();
|
|
126
189
|
const exitCode = await process.exited.finally(() => watch.release());
|
|
127
|
-
|
|
128
|
-
|
|
190
|
+
const ended = watch.ending ?? await drained([out, errors], context);
|
|
191
|
+
if (ended) {
|
|
192
|
+
out.drop();
|
|
193
|
+
errors.drop();
|
|
194
|
+
}
|
|
129
195
|
const spillPath = await spill.write(client);
|
|
130
|
-
const ending = watch.ending;
|
|
196
|
+
const ending = watch.ending ?? ended;
|
|
131
197
|
if (ending) {
|
|
132
|
-
const error = new ExecutionError(
|
|
198
|
+
const error = ending === "held" ? new ExecutionError("unknown", HELD) : new ExecutionError(
|
|
133
199
|
ending,
|
|
134
200
|
`The command was ${ending === "timeout" ? "timed out" : "aborted"}`
|
|
135
201
|
);
|
|
@@ -139,6 +205,8 @@ async function execute(client, cwd, command, options, context, spawned, join) {
|
|
|
139
205
|
return ok(spillPath ? { exitCode, spillPath } : { exitCode });
|
|
140
206
|
}
|
|
141
207
|
export {
|
|
208
|
+
HELD,
|
|
209
|
+
HELD_LIMIT,
|
|
142
210
|
SHELL,
|
|
143
211
|
execute,
|
|
144
212
|
sweep
|
package/dist/kernel/exec.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/kernel/exec.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n ExecutionError,\n err,\n ok,\n type Result,\n type ShellExecOptions,\n type ShellExecResult,\n StreamDecoder,\n} from '@earendil-works/pi-durable/env';\nimport type { KernelClient, KernelProcess, SpawnOptions } from './client.ts';\nimport { descendants, REFUSED } from './groups.ts';\nimport { resolve } from './paths.ts';\n\nexport const SHELL = ['bash', '-c'] as const;\n\nclass Spill {\n readonly #chunks: Uint8Array[] = [];\n readonly #limit: { afterBytes: number; afterLines: number } | undefined;\n #bytes = 0;\n #lines = 0;\n\n constructor(limit: ShellExecOptions['spill']) {\n this.#limit = limit;\n }\n\n add(chunk: Uint8Array): void {\n if (!this.#limit) return;\n this.#chunks.push(chunk);\n this.#bytes += chunk.length;\n for (const byte of chunk) if (byte === 10) this.#lines++;\n }\n\n get over(): boolean {\n const limit = this.#limit;\n return (\n limit !== undefined && (this.#bytes > limit.afterBytes || this.#lines > limit.afterLines)\n );\n }\n\n async write(client: KernelClient): Promise<string | undefined> {\n if (!this.over) return undefined;\n const path = `/tmp/slicc-agent-output-${crypto.randomUUID()}.log`;\n const all = new Uint8Array(this.#bytes);\n let offset = 0;\n for (const chunk of this.#chunks) {\n all.set(chunk, offset);\n offset += chunk.length;\n }\n await client.fs.mkdir('/tmp');\n await client.fs.writeFile(path, all);\n return path;\n }\n}\n\nfunction sink(\n stream: 'stdout' | 'stderr',\n spill: Spill,\n options: ShellExecOptions | undefined,\n context: Context\n) {\n const decoder = new StreamDecoder();\n
|
|
5
|
-
"mappings": "AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EAIA;AAAA,OACK;
|
|
6
|
-
"names": []
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n ExecutionError,\n err,\n ok,\n type Result,\n type ShellExecOptions,\n type ShellExecResult,\n StreamDecoder,\n} from '@earendil-works/pi-durable/env';\nimport type { ScrubStream } from '../secrets/scrub.ts';\nimport type { KernelClient, KernelProcess, SpawnOptions } from './client.ts';\nimport { descendants, REFUSED } from './groups.ts';\nimport { resolve } from './paths.ts';\n\nexport const SHELL = ['bash', '-c'] as const;\nexport const HELD_LIMIT = 32 * 1024 * 1024;\nexport const HELD = 'output held: waiting for the vault';\n\nexport interface ExecHooks {\n scrub?: () => ScrubStream;\n spawn?: (argv: readonly string[]) => readonly string[];\n}\n\nclass Spill {\n readonly #chunks: Uint8Array[] = [];\n readonly #limit: { afterBytes: number; afterLines: number } | undefined;\n #bytes = 0;\n #lines = 0;\n\n constructor(limit: ShellExecOptions['spill']) {\n this.#limit = limit;\n }\n\n add(chunk: Uint8Array): void {\n if (!this.#limit) return;\n this.#chunks.push(chunk);\n this.#bytes += chunk.length;\n for (const byte of chunk) if (byte === 10) this.#lines++;\n }\n\n get over(): boolean {\n const limit = this.#limit;\n return (\n limit !== undefined && (this.#bytes > limit.afterBytes || this.#lines > limit.afterLines)\n );\n }\n\n async write(client: KernelClient): Promise<string | undefined> {\n if (!this.over) return undefined;\n const path = `/tmp/slicc-agent-output-${crypto.randomUUID()}.log`;\n const all = new Uint8Array(this.#bytes);\n let offset = 0;\n for (const chunk of this.#chunks) {\n all.set(chunk, offset);\n offset += chunk.length;\n }\n await client.fs.mkdir('/tmp');\n await client.fs.writeFile(path, all);\n return path;\n }\n}\n\ninterface Sink {\n push(bytes: Uint8Array): void;\n end(): Promise<void>;\n drop(): void;\n}\n\nfunction sink(\n stream: 'stdout' | 'stderr',\n spill: Spill,\n options: ShellExecOptions | undefined,\n context: Context,\n scrub?: { stream: ScrubStream; held(bytes: number): void }\n): Sink {\n const decoder = new StreamDecoder();\n const emit = (bytes: Uint8Array) => {\n spill.add(bytes);\n const text = decoder.decode(bytes);\n if (text) options?.onOutput?.(text, context, { stream });\n };\n const flush = () => {\n const text = decoder.decode();\n if (text) options?.onOutput?.(text, context, { stream });\n };\n if (!scrub)\n return {\n push: emit,\n end: async () => flush(),\n drop: flush,\n };\n let chain: Promise<void> = Promise.resolve();\n let dropped = false;\n const live = (bytes: Uint8Array) => {\n if (!dropped) emit(bytes);\n };\n return {\n push(bytes) {\n scrub.held(bytes.length);\n chain = chain\n .then(() => scrub.stream.push(bytes))\n .then((out) => {\n scrub.held(-bytes.length);\n live(out);\n });\n },\n async end() {\n await chain;\n live(await scrub.stream.end());\n if (!dropped) flush();\n },\n drop() {\n dropped = true;\n chain.catch(() => undefined);\n scrub.stream.drop();\n },\n };\n}\n\ntype Ending = 'timeout' | 'aborted' | 'held' | undefined;\n\nexport async function sweep(\n client: Pick<KernelClient, 'kill' | 'ps'>,\n process: KernelProcess\n): Promise<void> {\n const table = (await client.ps?.().catch(() => undefined)) ?? [];\n const pids = [process.pid, ...descendants(table, process.pid)];\n await Promise.all(pids.map((pid) => client.kill?.(pid, 'SIGKILL').catch(() => undefined)));\n}\n\nfunction guard(\n process: KernelProcess,\n options: ShellExecOptions | undefined,\n context: Context,\n kill: () => void\n) {\n let ending: Ending;\n const stop = (why: Ending) => {\n ending ??= why;\n kill();\n };\n const timer =\n options?.timeout === undefined\n ? undefined\n : setTimeout(() => stop('timeout'), options.timeout * 1000);\n const onAbort = () => stop('aborted');\n context.abortSignal?.addEventListener('abort', onAbort, { once: true });\n if (context.abortSignal?.aborted) onAbort();\n return {\n get ending() {\n return ending;\n },\n stop,\n release() {\n clearTimeout(timer);\n context.abortSignal?.removeEventListener('abort', onAbort);\n },\n };\n}\n\nasync function drained(sinks: readonly Sink[], context: Context): Promise<Ending> {\n const signal = context.abortSignal;\n if (signal?.aborted) return 'aborted';\n let off = () => undefined as void;\n const aborted = new Promise<Ending>((resolve) => {\n const onAbort = () => resolve('aborted');\n signal?.addEventListener('abort', onAbort, { once: true });\n off = () => signal?.removeEventListener('abort', onAbort);\n });\n const ends = Promise.all(sinks.map((each) => each.end())).then(() => undefined);\n ends.catch(() => undefined);\n try {\n return await Promise.race([ends, aborted]);\n } finally {\n off();\n }\n}\n\nexport interface Join {\n pgid?: number;\n refused(code: string): void;\n}\n\nasync function start(\n client: KernelClient,\n argv: readonly string[],\n options: SpawnOptions,\n join: Join | undefined\n): Promise<{ process: KernelProcess; joined: boolean }> {\n if (join?.pgid !== undefined) {\n try {\n return { process: await client.spawn(argv, { ...options, pgid: join.pgid }), joined: true };\n } catch (error) {\n const code = String((error as { code?: unknown }).code ?? '');\n if (!REFUSED.has(code)) throw error;\n join.refused(code);\n }\n }\n return { process: await client.spawn(argv, options), joined: false };\n}\n\nexport async function execute(\n client: KernelClient,\n cwd: string,\n command: string | readonly string[],\n options: ShellExecOptions | undefined,\n context: Context,\n spawned?: (process: KernelProcess) => void,\n join?: Join,\n hooks: ExecHooks = {}\n): Promise<Result<ShellExecResult, ExecutionError>> {\n const given = typeof command === 'string' ? [...SHELL, command] : [...command];\n if (given.length === 0) return err(new ExecutionError('spawn_error', 'No program to run'));\n const argv = hooks.spawn ? [...hooks.spawn(given)] : given;\n if (context.abortSignal?.aborted)\n return err(new ExecutionError('aborted', 'The command was aborted'));\n const spill = new Spill(options?.spill);\n let held = 0;\n let overflowed = false;\n let over = () => {\n overflowed = true;\n };\n const scrubbed = () =>\n hooks.scrub && {\n stream: hooks.scrub(),\n held(bytes: number) {\n held += bytes;\n if (held > HELD_LIMIT) over();\n },\n };\n const out = sink('stdout', spill, options, context, scrubbed());\n const errors = sink('stderr', spill, options, context, scrubbed());\n let started: { process: KernelProcess; joined: boolean };\n try {\n started = await start(\n client,\n argv,\n {\n cwd: resolve(cwd, options?.cwd ?? '.'),\n ...(options?.env ? { env: options.env } : {}),\n onStdout: (bytes) => out.push(bytes),\n onStderr: (bytes) => errors.push(bytes),\n },\n join\n );\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return err(new ExecutionError('spawn_error', message));\n }\n const { process } = started;\n spawned?.(process);\n const watch = guard(process, options, context, () =>\n join ? void sweep(client, process) : process.signal('SIGKILL')\n );\n over = () => watch.stop('held');\n if (overflowed) over();\n const exitCode = await process.exited.finally(() => watch.release());\n const ended = watch.ending ?? (await drained([out, errors], context));\n if (ended) {\n out.drop();\n errors.drop();\n }\n const spillPath = await spill.write(client);\n const ending = watch.ending ?? ended;\n if (ending) {\n const error =\n ending === 'held'\n ? new ExecutionError('unknown', HELD)\n : new ExecutionError(\n ending,\n `The command was ${ending === 'timeout' ? 'timed out' : 'aborted'}`\n );\n if (spillPath) error.spillPath = spillPath;\n return err(error);\n }\n return ok(spillPath ? { exitCode, spillPath } : { exitCode });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["resolve"]
|
|
7
7
|
}
|
package/dist/licks/licks.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
defineDoc
|
|
3
3
|
} from "@earendil-works/pi-durable";
|
|
4
|
+
import { scrubTexts } from "../secrets/scrub.js";
|
|
4
5
|
import {
|
|
5
6
|
formatLick,
|
|
6
7
|
lickId
|
|
@@ -83,6 +84,19 @@ function keyOf(event) {
|
|
|
83
84
|
const base = `${event.target}|${event.channel}|${event.source}`;
|
|
84
85
|
return event.coalesce === false ? `${base}|${event.eventId ?? lickId()}` : base;
|
|
85
86
|
}
|
|
87
|
+
async function scrubbedLick(event) {
|
|
88
|
+
const fields = ["title", "text", "body", "triage"].filter(
|
|
89
|
+
(key) => event[key] !== void 0
|
|
90
|
+
);
|
|
91
|
+
const items = event.items ?? [];
|
|
92
|
+
const scrubbed = await scrubTexts([...fields.map((key) => event[key]), ...items]);
|
|
93
|
+
const out = { ...event };
|
|
94
|
+
fields.forEach((key, at) => {
|
|
95
|
+
out[key] = scrubbed[at];
|
|
96
|
+
});
|
|
97
|
+
if (event.items) out.items = scrubbed.slice(fields.length);
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
86
100
|
function createLicks(host) {
|
|
87
101
|
const handlers = /* @__PURE__ */ new Map();
|
|
88
102
|
let lock = Promise.resolve();
|
|
@@ -173,7 +187,8 @@ function createLicks(host) {
|
|
|
173
187
|
triage(next) {
|
|
174
188
|
judge = next;
|
|
175
189
|
},
|
|
176
|
-
async deliver(
|
|
190
|
+
async deliver(raw, context) {
|
|
191
|
+
const incoming = await scrubbedLick(raw);
|
|
177
192
|
const blocked = check?.(incoming.target);
|
|
178
193
|
const event = blocked ? frozenNotice(incoming, blocked) : incoming;
|
|
179
194
|
if (judge && event.triage && await seenBefore(event, context)) return;
|
|
@@ -246,6 +261,7 @@ export {
|
|
|
246
261
|
createLicks,
|
|
247
262
|
frozenNotice,
|
|
248
263
|
render,
|
|
264
|
+
scrubbedLick,
|
|
249
265
|
strongest
|
|
250
266
|
};
|
|
251
267
|
//# sourceMappingURL=licks.js.map
|
package/dist/licks/licks.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/licks/licks.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type Conversation,\n defineDoc,\n type Harness,\n type SubmissionId,\n} from '@earendil-works/pi-durable';\nimport {\n formatLick,\n type Lick,\n type LickAction,\n type LickChannel,\n type LickSeverity,\n type LickTarget,\n lickId,\n} from './lick.ts';\nimport { entryLick, LICK_STATE_KIND, type LickDecision, lickDecisions } from './state.ts';\nimport { skipEntry, skips, type Triage, type Verdict } from './triage.ts';\n\nexport interface LickEvent {\n channel: LickChannel;\n source: string;\n title: string;\n text: string;\n body?: string;\n items?: string[];\n count?: number;\n target: LickTarget;\n coalesce?: boolean;\n severity?: LickSeverity;\n eventId?: string;\n at?: number;\n triage?: string;\n}\n\ntype Pending = {\n lick: Lick;\n items: string[];\n body: string | null;\n};\n\ntype Queue = {\n submission: number | null;\n pending: Pending | null;\n};\n\ntype OutboxState = {\n queues: Record<string, Queue>;\n events: Record<string, string[]>;\n};\n\nexport const LicksOutbox = defineDoc<OutboxState>({\n kind: 'slicc.licks',\n version: 1,\n scope: 'session',\n initial: () => ({ queues: {}, events: {} }),\n});\n\nexport interface LickHandler {\n confirm?(lick: Lick, reason: string | undefined, context: Context): Promise<string | undefined>;\n dismiss?(lick: Lick, reason: string | undefined, context: Context): Promise<string | undefined>;\n}\n\nexport interface LickFound {\n lick: Lick;\n decision: LickDecision | undefined;\n}\n\nexport interface Licks {\n deliver(event: LickEvent, context: Context): Promise<void>;\n flush(context: Context): Promise<void>;\n find(conversation: Conversation, id: string, context: Context): Promise<LickFound | undefined>;\n decide(\n conversation: Conversation,\n id: string,\n action: LickAction,\n reason: string | undefined,\n context: Context\n ): Promise<string>;\n handle(channel: LickChannel, handler: LickHandler): void;\n pending(context: Context): Promise<number>;\n forget(target: LickTarget, channel: LickChannel, source: string, context: Context): Promise<void>;\n guard(check: (target: LickTarget) => Blocked | undefined): void;\n triage(judge: Triage): void;\n}\n\nexport type Blocked = { id: string; name: string; at: number };\n\nexport function frozenNotice(event: LickEvent, blocked: Blocked): LickEvent {\n return {\n channel: event.channel,\n source: `frozen:${event.source}`,\n title: `${blocked.name} is frozen`,\n text: `${event.title} was skipped`,\n body: `${event.target} belongs to ${blocked.name}, which is frozen (${blocked.id}). Thaw it in the Freezer to get its licks again.`,\n target: 'cone',\n severity: 'warn',\n eventId: `${blocked.id}|${event.target}|${event.channel}|${event.source}|${blocked.at}`,\n };\n}\n\nexport interface LicksHost {\n harness: Harness;\n resolve(target: LickTarget, channel: LickChannel, context: Context): Promise<Conversation>;\n}\n\nexport const MAX_ITEMS = 50;\nexport const MAX_EVENTS = 64;\n\nexport function render(pending: Pending): Lick {\n const shown = pending.items.slice(0, MAX_ITEMS);\n const more = pending.items.length - shown.length;\n const lines = [\n ...(pending.body ? [pending.body] : []),\n ...shown,\n ...(more > 0 ? [`\u2026 and ${more} more`] : []),\n ];\n const { body: _, ...lick } = pending.lick;\n return lines.length ? { ...lick, body: lines.join('\\n') } : lick;\n}\n\nconst RANK: Record<LickSeverity, number> = { warn: 1, error: 2 };\n\nexport function strongest(\n a: LickSeverity | undefined,\n b: LickSeverity | undefined\n): LickSeverity | undefined {\n if (!a) return b;\n if (!b) return a;\n return RANK[b] > RANK[a] ? b : a;\n}\n\nfunction merge(pending: Pending, event: LickEvent, now: number): Pending {\n const items = [...pending.items];\n for (const item of event.items ?? []) if (!items.includes(item)) items.push(item);\n const { severity: _, ...lick } = pending.lick;\n const severity = strongest(pending.lick.severity, event.severity);\n return {\n lick: {\n ...lick,\n title: event.title,\n text: event.text,\n count: pending.lick.count + (event.count ?? 1),\n at: event.at ?? now,\n ...(severity ? { severity } : {}),\n },\n items,\n body: event.body ?? pending.body,\n };\n}\n\nfunction fresh(event: LickEvent, now: number, listed: LickAction[] | undefined): Pending {\n return {\n lick: {\n id: lickId(),\n channel: event.channel,\n source: event.source,\n title: event.title,\n text: event.text,\n count: event.count ?? 1,\n at: now,\n ...(listed ? { actions: listed } : {}),\n ...(event.severity ? { severity: event.severity } : {}),\n },\n items: [...new Set(event.items ?? [])],\n body: event.body ?? null,\n };\n}\n\nfunction keyOf(event: LickEvent): string {\n const base = `${event.target}|${event.channel}|${event.source}`;\n return event.coalesce === false ? `${base}|${event.eventId ?? lickId()}` : base;\n}\n\nexport function createLicks(host: Promise<LicksHost>): Licks {\n const handlers = new Map<LickChannel, LickHandler>();\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 const actions = (channel: LickChannel): LickAction[] | undefined => {\n const handler = handlers.get(channel);\n const list: LickAction[] = [];\n if (handler?.confirm) list.push('confirm');\n if (handler?.dismiss) list.push('dismiss');\n return list.length ? list : undefined;\n };\n\n async function previous(\n harness: Harness,\n id: number | null,\n context: Context\n ): Promise<'queued' | 'withdrawn' | 'gone'> {\n if (id === null) return 'gone';\n const submission = await harness.submission(id as SubmissionId, context);\n const record = await submission?.status(context);\n if (record?.status === 'queued') return 'queued';\n return record?.status === 'unanswered' && record.reason === 'aborted' ? 'withdrawn' : 'gone';\n }\n\n async function settled(key: string, context: Context): Promise<Queue | undefined> {\n const { harness } = await host;\n const queue = (await harness.snapshot(LicksOutbox, context))?.queues[key];\n if (!queue) return undefined;\n const before = await previous(harness, queue.submission, context);\n if (before === 'queued') return undefined;\n if (queue.pending && before === 'gone') return queue;\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n if (doc.queues[key]?.submission === queue.submission) delete doc.queues[key];\n }, context);\n return undefined;\n }\n\n async function flushKey(key: string, context: Context): Promise<void> {\n const { harness, resolve } = await host;\n const queue = await settled(key, context);\n if (!queue?.pending) return;\n const lick = render(queue.pending);\n const target = key.split('|')[0] as LickTarget;\n const conversation = await resolve(target, queue.pending.lick.channel, context);\n const submission = await conversation.submit(\n {\n type: 'input',\n content: formatLick(lick),\n whenBusy: 'steer',\n requestId: `lick:${lick.id}`,\n },\n context\n );\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n doc.queues[key] = { submission: submission.id, pending: null };\n }, context);\n }\n\n async function flushAll(context: Context): Promise<void> {\n const { harness } = await host;\n const keys = Object.keys((await harness.snapshot(LicksOutbox, context))?.queues ?? {});\n for (const key of keys) await flushKey(key, context);\n }\n\n async function find(\n conversation: Conversation,\n id: string,\n context: Context\n ): Promise<LickFound | undefined> {\n const { entries } = await conversation.context(context);\n const lick = entries.map(entryLick).find((candidate) => candidate?.id === id);\n if (!lick) return undefined;\n return { lick, decision: lickDecisions(entries).get(id) };\n }\n\n async function judged(event: LickEvent, context: Context): Promise<Verdict | undefined> {\n if (!judge || !event.triage) return undefined;\n return judge(event, context).catch(() => undefined);\n }\n\n async function seenBefore(event: LickEvent, context: Context): Promise<boolean> {\n if (event.eventId === undefined) return false;\n const { harness } = await host;\n const events = (await harness.snapshot(LicksOutbox, context))?.events ?? {};\n return (events[`${event.channel}|${event.source}`] ?? []).includes(event.eventId);\n }\n\n async function record(event: LickEvent, verdict: Verdict, now: number, context: Context) {\n const conversation = await (await host).resolve(event.target, event.channel, context);\n await conversation.submit(skipEntry(event, verdict, now), context);\n }\n\n let check: ((target: LickTarget) => Blocked | undefined) | undefined;\n let judge: Triage | undefined;\n return {\n guard(next) {\n check = next;\n },\n triage(next) {\n judge = next;\n },\n async deliver(incoming, context) {\n const blocked = check?.(incoming.target);\n const event = blocked ? frozenNotice(incoming, blocked) : incoming;\n if (judge && event.triage && (await seenBefore(event, context))) return;\n const verdict = await judged(event, context);\n const skipped = skips(verdict);\n return serial(async () => {\n const { harness } = await host;\n const key = keyOf(event);\n await settled(key, context);\n const stream = `${event.channel}|${event.source}`;\n const now = event.at ?? Date.now();\n if (skipped) {\n if (await seenBefore(event, context)) return;\n await record(event, verdict, now, context);\n }\n const accepted = await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n const seen = doc.events[stream] ?? [];\n if (event.eventId !== undefined) {\n if (seen.includes(event.eventId)) return false;\n doc.events[stream] = [...seen, event.eventId].slice(-MAX_EVENTS);\n }\n if (skipped) return false;\n const queue = doc.queues[key] ?? { submission: null, pending: null };\n queue.pending = queue.pending\n ? merge(queue.pending, event, now)\n : fresh(event, now, actions(event.channel));\n doc.queues[key] = queue;\n return true;\n }, context);\n if (accepted) await flushKey(key, context);\n });\n },\n flush: (context) => serial(() => flushAll(context)),\n find,\n async decide(conversation, id, action, reason, context) {\n const found = await find(conversation, id, context);\n if (!found) throw new Error(`There is no lick ${id} in this conversation.`);\n if (found.decision) throw new Error(`Lick ${id} is already ${found.decision}.`);\n if (!found.lick.actions?.includes(action))\n throw new Error(`Lick ${id} (${found.lick.channel}) has no ${action} action.`);\n const run = handlers.get(found.lick.channel)?.[action];\n if (!run) throw new Error(`Lick ${id} (${found.lick.channel}) has no ${action} action.`);\n return (await run(found.lick, reason, context)) ?? `Lick ${id} ${action}ed.`;\n },\n handle(channel, handler) {\n handlers.set(channel, handler);\n },\n forget(target, channel, source, context) {\n return serial(async () => {\n const { harness } = await host;\n const key = `${target}|${channel}|${source}`;\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n const queue = doc.queues[key];\n if (queue?.pending) queue.pending = null;\n }, context);\n });\n },\n async pending(context) {\n const { harness } = await host;\n const queues = (await harness.snapshot(LicksOutbox, context))?.queues ?? {};\n return Object.values(queues).filter((queue) => queue.submission !== null || queue.pending)\n .length;\n },\n };\n}\n\nexport { LICK_STATE_KIND };\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type Conversation,\n defineDoc,\n type Harness,\n type SubmissionId,\n} from '@earendil-works/pi-durable';\nimport { scrubTexts } from '../secrets/scrub.ts';\nimport {\n formatLick,\n type Lick,\n type LickAction,\n type LickChannel,\n type LickSeverity,\n type LickTarget,\n lickId,\n} from './lick.ts';\nimport { entryLick, LICK_STATE_KIND, type LickDecision, lickDecisions } from './state.ts';\nimport { skipEntry, skips, type Triage, type Verdict } from './triage.ts';\n\nexport interface LickEvent {\n channel: LickChannel;\n source: string;\n title: string;\n text: string;\n body?: string;\n items?: string[];\n count?: number;\n target: LickTarget;\n coalesce?: boolean;\n severity?: LickSeverity;\n eventId?: string;\n at?: number;\n triage?: string;\n}\n\ntype Pending = {\n lick: Lick;\n items: string[];\n body: string | null;\n};\n\ntype Queue = {\n submission: number | null;\n pending: Pending | null;\n};\n\ntype OutboxState = {\n queues: Record<string, Queue>;\n events: Record<string, string[]>;\n};\n\nexport const LicksOutbox = defineDoc<OutboxState>({\n kind: 'slicc.licks',\n version: 1,\n scope: 'session',\n initial: () => ({ queues: {}, events: {} }),\n});\n\nexport interface LickHandler {\n confirm?(lick: Lick, reason: string | undefined, context: Context): Promise<string | undefined>;\n dismiss?(lick: Lick, reason: string | undefined, context: Context): Promise<string | undefined>;\n}\n\nexport interface LickFound {\n lick: Lick;\n decision: LickDecision | undefined;\n}\n\nexport interface Licks {\n deliver(event: LickEvent, context: Context): Promise<void>;\n flush(context: Context): Promise<void>;\n find(conversation: Conversation, id: string, context: Context): Promise<LickFound | undefined>;\n decide(\n conversation: Conversation,\n id: string,\n action: LickAction,\n reason: string | undefined,\n context: Context\n ): Promise<string>;\n handle(channel: LickChannel, handler: LickHandler): void;\n pending(context: Context): Promise<number>;\n forget(target: LickTarget, channel: LickChannel, source: string, context: Context): Promise<void>;\n guard(check: (target: LickTarget) => Blocked | undefined): void;\n triage(judge: Triage): void;\n}\n\nexport type Blocked = { id: string; name: string; at: number };\n\nexport function frozenNotice(event: LickEvent, blocked: Blocked): LickEvent {\n return {\n channel: event.channel,\n source: `frozen:${event.source}`,\n title: `${blocked.name} is frozen`,\n text: `${event.title} was skipped`,\n body: `${event.target} belongs to ${blocked.name}, which is frozen (${blocked.id}). Thaw it in the Freezer to get its licks again.`,\n target: 'cone',\n severity: 'warn',\n eventId: `${blocked.id}|${event.target}|${event.channel}|${event.source}|${blocked.at}`,\n };\n}\n\nexport interface LicksHost {\n harness: Harness;\n resolve(target: LickTarget, channel: LickChannel, context: Context): Promise<Conversation>;\n}\n\nexport const MAX_ITEMS = 50;\nexport const MAX_EVENTS = 64;\n\nexport function render(pending: Pending): Lick {\n const shown = pending.items.slice(0, MAX_ITEMS);\n const more = pending.items.length - shown.length;\n const lines = [\n ...(pending.body ? [pending.body] : []),\n ...shown,\n ...(more > 0 ? [`\u2026 and ${more} more`] : []),\n ];\n const { body: _, ...lick } = pending.lick;\n return lines.length ? { ...lick, body: lines.join('\\n') } : lick;\n}\n\nconst RANK: Record<LickSeverity, number> = { warn: 1, error: 2 };\n\nexport function strongest(\n a: LickSeverity | undefined,\n b: LickSeverity | undefined\n): LickSeverity | undefined {\n if (!a) return b;\n if (!b) return a;\n return RANK[b] > RANK[a] ? b : a;\n}\n\nfunction merge(pending: Pending, event: LickEvent, now: number): Pending {\n const items = [...pending.items];\n for (const item of event.items ?? []) if (!items.includes(item)) items.push(item);\n const { severity: _, ...lick } = pending.lick;\n const severity = strongest(pending.lick.severity, event.severity);\n return {\n lick: {\n ...lick,\n title: event.title,\n text: event.text,\n count: pending.lick.count + (event.count ?? 1),\n at: event.at ?? now,\n ...(severity ? { severity } : {}),\n },\n items,\n body: event.body ?? pending.body,\n };\n}\n\nfunction fresh(event: LickEvent, now: number, listed: LickAction[] | undefined): Pending {\n return {\n lick: {\n id: lickId(),\n channel: event.channel,\n source: event.source,\n title: event.title,\n text: event.text,\n count: event.count ?? 1,\n at: now,\n ...(listed ? { actions: listed } : {}),\n ...(event.severity ? { severity: event.severity } : {}),\n },\n items: [...new Set(event.items ?? [])],\n body: event.body ?? null,\n };\n}\n\nfunction keyOf(event: LickEvent): string {\n const base = `${event.target}|${event.channel}|${event.source}`;\n return event.coalesce === false ? `${base}|${event.eventId ?? lickId()}` : base;\n}\n\nexport async function scrubbedLick(event: LickEvent): Promise<LickEvent> {\n const fields = (['title', 'text', 'body', 'triage'] as const).filter(\n (key) => event[key] !== undefined\n );\n const items = event.items ?? [];\n const scrubbed = await scrubTexts([...fields.map((key) => event[key] as string), ...items]);\n const out: LickEvent = { ...event };\n fields.forEach((key, at) => {\n out[key] = scrubbed[at] as string;\n });\n if (event.items) out.items = scrubbed.slice(fields.length);\n return out;\n}\n\nexport function createLicks(host: Promise<LicksHost>): Licks {\n const handlers = new Map<LickChannel, LickHandler>();\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 const actions = (channel: LickChannel): LickAction[] | undefined => {\n const handler = handlers.get(channel);\n const list: LickAction[] = [];\n if (handler?.confirm) list.push('confirm');\n if (handler?.dismiss) list.push('dismiss');\n return list.length ? list : undefined;\n };\n\n async function previous(\n harness: Harness,\n id: number | null,\n context: Context\n ): Promise<'queued' | 'withdrawn' | 'gone'> {\n if (id === null) return 'gone';\n const submission = await harness.submission(id as SubmissionId, context);\n const record = await submission?.status(context);\n if (record?.status === 'queued') return 'queued';\n return record?.status === 'unanswered' && record.reason === 'aborted' ? 'withdrawn' : 'gone';\n }\n\n async function settled(key: string, context: Context): Promise<Queue | undefined> {\n const { harness } = await host;\n const queue = (await harness.snapshot(LicksOutbox, context))?.queues[key];\n if (!queue) return undefined;\n const before = await previous(harness, queue.submission, context);\n if (before === 'queued') return undefined;\n if (queue.pending && before === 'gone') return queue;\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n if (doc.queues[key]?.submission === queue.submission) delete doc.queues[key];\n }, context);\n return undefined;\n }\n\n async function flushKey(key: string, context: Context): Promise<void> {\n const { harness, resolve } = await host;\n const queue = await settled(key, context);\n if (!queue?.pending) return;\n const lick = render(queue.pending);\n const target = key.split('|')[0] as LickTarget;\n const conversation = await resolve(target, queue.pending.lick.channel, context);\n const submission = await conversation.submit(\n {\n type: 'input',\n content: formatLick(lick),\n whenBusy: 'steer',\n requestId: `lick:${lick.id}`,\n },\n context\n );\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n doc.queues[key] = { submission: submission.id, pending: null };\n }, context);\n }\n\n async function flushAll(context: Context): Promise<void> {\n const { harness } = await host;\n const keys = Object.keys((await harness.snapshot(LicksOutbox, context))?.queues ?? {});\n for (const key of keys) await flushKey(key, context);\n }\n\n async function find(\n conversation: Conversation,\n id: string,\n context: Context\n ): Promise<LickFound | undefined> {\n const { entries } = await conversation.context(context);\n const lick = entries.map(entryLick).find((candidate) => candidate?.id === id);\n if (!lick) return undefined;\n return { lick, decision: lickDecisions(entries).get(id) };\n }\n\n async function judged(event: LickEvent, context: Context): Promise<Verdict | undefined> {\n if (!judge || !event.triage) return undefined;\n return judge(event, context).catch(() => undefined);\n }\n\n async function seenBefore(event: LickEvent, context: Context): Promise<boolean> {\n if (event.eventId === undefined) return false;\n const { harness } = await host;\n const events = (await harness.snapshot(LicksOutbox, context))?.events ?? {};\n return (events[`${event.channel}|${event.source}`] ?? []).includes(event.eventId);\n }\n\n async function record(event: LickEvent, verdict: Verdict, now: number, context: Context) {\n const conversation = await (await host).resolve(event.target, event.channel, context);\n await conversation.submit(skipEntry(event, verdict, now), context);\n }\n\n let check: ((target: LickTarget) => Blocked | undefined) | undefined;\n let judge: Triage | undefined;\n return {\n guard(next) {\n check = next;\n },\n triage(next) {\n judge = next;\n },\n async deliver(raw, context) {\n const incoming = await scrubbedLick(raw);\n const blocked = check?.(incoming.target);\n const event = blocked ? frozenNotice(incoming, blocked) : incoming;\n if (judge && event.triage && (await seenBefore(event, context))) return;\n const verdict = await judged(event, context);\n const skipped = skips(verdict);\n return serial(async () => {\n const { harness } = await host;\n const key = keyOf(event);\n await settled(key, context);\n const stream = `${event.channel}|${event.source}`;\n const now = event.at ?? Date.now();\n if (skipped) {\n if (await seenBefore(event, context)) return;\n await record(event, verdict, now, context);\n }\n const accepted = await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n const seen = doc.events[stream] ?? [];\n if (event.eventId !== undefined) {\n if (seen.includes(event.eventId)) return false;\n doc.events[stream] = [...seen, event.eventId].slice(-MAX_EVENTS);\n }\n if (skipped) return false;\n const queue = doc.queues[key] ?? { submission: null, pending: null };\n queue.pending = queue.pending\n ? merge(queue.pending, event, now)\n : fresh(event, now, actions(event.channel));\n doc.queues[key] = queue;\n return true;\n }, context);\n if (accepted) await flushKey(key, context);\n });\n },\n flush: (context) => serial(() => flushAll(context)),\n find,\n async decide(conversation, id, action, reason, context) {\n const found = await find(conversation, id, context);\n if (!found) throw new Error(`There is no lick ${id} in this conversation.`);\n if (found.decision) throw new Error(`Lick ${id} is already ${found.decision}.`);\n if (!found.lick.actions?.includes(action))\n throw new Error(`Lick ${id} (${found.lick.channel}) has no ${action} action.`);\n const run = handlers.get(found.lick.channel)?.[action];\n if (!run) throw new Error(`Lick ${id} (${found.lick.channel}) has no ${action} action.`);\n return (await run(found.lick, reason, context)) ?? `Lick ${id} ${action}ed.`;\n },\n handle(channel, handler) {\n handlers.set(channel, handler);\n },\n forget(target, channel, source, context) {\n return serial(async () => {\n const { harness } = await host;\n const key = `${target}|${channel}|${source}`;\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n const queue = doc.queues[key];\n if (queue?.pending) queue.pending = null;\n }, context);\n });\n },\n async pending(context) {\n const { harness } = await host;\n const queues = (await harness.snapshot(LicksOutbox, context))?.queues ?? {};\n return Object.values(queues).filter((queue) => queue.submission !== null || queue.pending)\n .length;\n },\n };\n}\n\nexport { LICK_STATE_KIND };\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["record"]
|
|
7
7
|
}
|
package/dist/main.js
CHANGED
|
@@ -34,6 +34,10 @@ import { sliccPrompt } from "./prompt.js";
|
|
|
34
34
|
import { createPages } from "./remote/pages.js";
|
|
35
35
|
import { identity } from "./scoops/identity.js";
|
|
36
36
|
import { packageAssets, setupScoops } from "./scoops/index.js";
|
|
37
|
+
import { repin, secretsExtension, shellGrants } from "./secrets/extension.js";
|
|
38
|
+
import { secretNames } from "./secrets/names.js";
|
|
39
|
+
import { scrubWith } from "./secrets/scrub.js";
|
|
40
|
+
import { listenVault, Vault } from "./secrets/vault.js";
|
|
37
41
|
import {
|
|
38
42
|
createAgentSettings,
|
|
39
43
|
createSliccModels,
|
|
@@ -82,10 +86,25 @@ async function codemodeTools(client, options, native, extra) {
|
|
|
82
86
|
];
|
|
83
87
|
}
|
|
84
88
|
const MCP_CALLBACK = "/oauth/mcp-callback";
|
|
85
|
-
async function
|
|
89
|
+
async function openRepinned(options, base) {
|
|
90
|
+
const storage = await (options.storage ?? (() => openOpfsSqliteStorage()))();
|
|
91
|
+
return openAgent({
|
|
92
|
+
...base,
|
|
93
|
+
storage,
|
|
94
|
+
beforeResume: (harness, using) => repin(harness, storage, using)
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
async function secretsFor(home, vault) {
|
|
98
|
+
scrubWith(vault);
|
|
99
|
+
const names = secretNames(home, vault);
|
|
100
|
+
await names.refresh(BACKGROUND_CONTEXT);
|
|
101
|
+
return names;
|
|
102
|
+
}
|
|
103
|
+
async function start(scope, ports, options, vault) {
|
|
86
104
|
const { client, users } = await attached(ports, options);
|
|
87
105
|
const home = new SliccKernelEnv(client, { cwd: HOME });
|
|
88
106
|
await privateStore(home);
|
|
107
|
+
const names = await secretsFor(home, vault);
|
|
89
108
|
const native = scope.fetch.bind(scope);
|
|
90
109
|
scope.fetch = transportFetch(client.transport, scope.location.origin, native);
|
|
91
110
|
const facts = {
|
|
@@ -117,6 +136,7 @@ async function start(scope, ports, options) {
|
|
|
117
136
|
licks.licks.triage(lickTriage(models));
|
|
118
137
|
const scoops = setupScoops(registry, licks.licks, [...coding, ...codemode, ...memory.tools]);
|
|
119
138
|
const skills = setupSkills(registry, licks.licks);
|
|
139
|
+
registry.install(secretsExtension({ names, vault }));
|
|
120
140
|
const activity = createActivity();
|
|
121
141
|
const groups = processGroups(client);
|
|
122
142
|
const model = options.model ?? DEFAULT_MODEL;
|
|
@@ -124,15 +144,11 @@ async function start(scope, ports, options) {
|
|
|
124
144
|
const environment = kernelEnvironment(client, {
|
|
125
145
|
activity,
|
|
126
146
|
groups,
|
|
127
|
-
identify: identity(() => opened?.agents, model)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
models,
|
|
131
|
-
model,
|
|
132
|
-
storage: await (options.storage ?? (() => openOpfsSqliteStorage()))(),
|
|
133
|
-
registry,
|
|
134
|
-
env: environment
|
|
147
|
+
identify: identity(() => opened?.agents, model),
|
|
148
|
+
scrub: vault,
|
|
149
|
+
spawn: shellGrants(names)
|
|
135
150
|
});
|
|
151
|
+
const agent = await openRepinned(options, { models, model, registry, env: environment });
|
|
136
152
|
opened = agent;
|
|
137
153
|
const sources = licks.attach(agent, { env: home, home: HOME, activity });
|
|
138
154
|
await sources.start(BACKGROUND_CONTEXT);
|
|
@@ -240,6 +256,7 @@ async function start(scope, ports, options) {
|
|
|
240
256
|
pages,
|
|
241
257
|
mcp: mcpRuntime,
|
|
242
258
|
...users ? { users } : {},
|
|
259
|
+
secrets: { vault: vault.state, list: () => names.state.value, close: names.close },
|
|
243
260
|
monitor: {
|
|
244
261
|
kernel: client,
|
|
245
262
|
groups,
|
|
@@ -255,7 +272,9 @@ function alive(client) {
|
|
|
255
272
|
};
|
|
256
273
|
}
|
|
257
274
|
function runAgentWorker(scope, options = {}) {
|
|
258
|
-
const
|
|
275
|
+
const vault = new Vault();
|
|
276
|
+
listenVault(scope, vault);
|
|
277
|
+
const host = start(scope, kernelPorts(scope), options, vault);
|
|
259
278
|
host.catch(() => void 0);
|
|
260
279
|
serveConnections(scope, host);
|
|
261
280
|
return host;
|
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 { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { CredentialStore, Provider } from '@earendil-works/pi-ai';\nimport {\n createRegistry,\n type ModelRef,\n type Storage,\n type ToolRegistration,\n} from '@earendil-works/pi-durable';\nimport { CodingTools } from '@earendil-works/pi-durable/tools';\nimport { type Agent, openAgent } from './agent.ts';\nimport { browserGuard } from './browser/guard.ts';\nimport { attachChanges, CHANGES_MS } from './changes/index.ts';\nimport {\n codemodeExtension,\n codemodeTool,\n disabledBySettings,\n locateWasm,\n sandboxFactory,\n} from './codemode/index.ts';\nimport { EncryptedCredentialStore } from './credentials.ts';\nimport { attachFreezer } from './freezer/index.ts';\nimport { settingsText, titler } from './freezer/title.ts';\nimport { lateGelatiere } from './gelatiere/index.ts';\nimport { type AgentHost, hostAgent } from './host.ts';\nimport { imageReads } from './images/index.ts';\nimport { createActivity } from './kernel/activity.ts';\nimport type { KernelClient } from './kernel/client.ts';\nimport { HOME, kernelEnvironment, SliccKernelEnv } from './kernel/env.ts';\nimport { processGroups } from './kernel/groups.ts';\nimport { adminOver, type UserClient, type UserClients, userClients } from './kernel/users.ts';\nimport { setupLicks } from './licks/index.ts';\nimport { lickTriage } from './licks/triage.ts';\nimport { setupMcp } from './mcp/index.ts';\nimport { setupMemory } from './memory/index.ts';\nimport { modelsCommand } from './models/index.ts';\nimport { type Transport, transportFetch } from './net.ts';\nimport { sliccPrompt } from './prompt.ts';\nimport { createPages } from './remote/pages.ts';\nimport { identity } from './scoops/identity.ts';\nimport { type Assets, packageAssets, type ScoopsRuntime, setupScoops } from './scoops/index.ts';\nimport {\n createAgentSettings,\n createSliccModels,\n DEFAULT_MODEL,\n sliccProviders,\n} from './settings.ts';\nimport { RELOAD_MS, setupSkills } from './skills/index.ts';\nimport { setupSprinkles } from './sprinkles/index.ts';\nimport { openOpfsSqliteStorage } from './sqlite.ts';\nimport {\n agentVersion,\n browserConnected,\n commandsOnPath,\n kernelBoot,\n transportName,\n} from './system.ts';\nimport { type KernelPorts, kernelPorts, 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 assets?: Assets;\n codemodeWasm?: () => Promise<WebAssembly.Module | undefined>;\n codemodeWorker?: string | URL;\n catalog?: string;\n users?: boolean;\n mcpRedirect?: string;\n}\n\nexport function usersOf(\n admin: MessagePort | undefined,\n wanted: boolean | undefined,\n attach: KernelAttach\n): UserClients | undefined {\n if (!admin) return undefined;\n if (wanted === false) {\n admin.close();\n return undefined;\n }\n return userClients(adminOver(admin), attach as (port: MessagePort) => Promise<UserClient>);\n}\n\nexport const STORE_DIR = '/.slicc';\n\nasync function attached(ports: Promise<KernelPorts>, options: AgentWorkerOptions) {\n const attach: KernelAttach = options.attach ?? attachKernel;\n const { kernel, admin } = await ports;\n const client = await attach(kernel);\n return { client, users: usersOf(admin, options.users, attach) };\n}\n\nfunction privateStore(env: SliccKernelEnv): Promise<unknown> {\n return env.exec(`mkdir -p ${STORE_DIR} && chmod 700 ${STORE_DIR}`, undefined, BACKGROUND_CONTEXT);\n}\n\nasync function codemodeTools(\n client: KernelClient,\n options: AgentWorkerOptions,\n native: typeof fetch,\n extra: Parameters<typeof codemodeTool>[0]['extra']\n) {\n const settings = await client.fs\n .readFile(`${HOME}/.pi/agent/settings.json`)\n .catch(() => undefined);\n if (disabledBySettings(settings === undefined ? undefined : new TextDecoder().decode(settings)))\n return [];\n const sandbox = sandboxFactory(\n options.codemodeWasm ?? (() => locateWasm(native, new URL(import.meta.url))),\n options.codemodeWorker\n );\n return [\n codemodeTool({ declared: CodingTools.tools as readonly ToolRegistration[], sandbox, extra }),\n ];\n}\n\nexport const MCP_CALLBACK = '/oauth/mcp-callback';\n\nasync function start(\n scope: AgentWorkerScope,\n ports: Promise<KernelPorts>,\n options: AgentWorkerOptions\n): Promise<AgentHost> {\n const { client, users } = await attached(ports, options);\n const home = new SliccKernelEnv(client, { cwd: HOME });\n await privateStore(home);\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 browser: await browserConnected(client),\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 registry.install(\n imageReads(\n (CodingTools.tools as readonly ToolRegistration[]).find(\n (tool) => tool.name === 'read'\n ) as ToolRegistration\n )\n );\n const memory = setupMemory(registry);\n const coding = CodingTools.tools as readonly ToolRegistration[];\n registry.install(browserGuard([scope.location.origin], coding));\n const mcp = setupMcp(registry);\n const codemode = await codemodeTools(client, options, native, mcp.codemode);\n registry.install(codemodeExtension(codemode));\n const licks = setupLicks(registry);\n licks.licks.triage(lickTriage(models));\n const scoops = setupScoops(registry, licks.licks, [...coding, ...codemode, ...memory.tools]);\n const skills = setupSkills(registry, licks.licks);\n const activity = createActivity();\n const groups = processGroups(client);\n const model = options.model ?? DEFAULT_MODEL;\n let opened: Agent | undefined;\n const environment = kernelEnvironment(client, {\n activity,\n groups,\n identify: identity(() => opened?.agents, model),\n });\n const agent = await openAgent({\n models,\n model,\n storage: await (options.storage ?? (() => openOpfsSqliteStorage()))(),\n registry,\n env: environment,\n });\n opened = agent;\n const sources = licks.attach(agent, { env: home, home: HOME, activity });\n await sources.start(BACKGROUND_CONTEXT);\n await sources.boot(\n { version: facts.version, boot: await kernelBoot(client) },\n BACKGROUND_CONTEXT\n );\n const skillsRuntime = await skills.attach(\n { env: home, home: HOME, assets: options.assets ?? packageAssets(), reloadMs: RELOAD_MS },\n BACKGROUND_CONTEXT\n );\n const mcpRuntime = await mcp.attach(\n {\n env: home,\n home: HOME,\n credentials,\n licks: licks.licks,\n fetch: scope.fetch,\n version: facts.version,\n reloadMs: RELOAD_MS,\n codemode: codemode.length > 0,\n cors: client.transport.traits?.crossOrigin === 'cors',\n redirect: options.mcpRedirect ?? new URL(MCP_CALLBACK, scope.location.origin).href,\n },\n BACKGROUND_CONTEXT\n );\n const gelatiere = lateGelatiere();\n const sprinkles = await setupSprinkles().attach(\n {\n intercept: gelatiere.intercept,\n harness: agent.harness,\n agents: agent.agents,\n licks: licks.licks,\n env: home,\n assets: options.assets ?? packageAssets(),\n reloadMs: RELOAD_MS,\n },\n BACKGROUND_CONTEXT\n );\n let roles: ScoopsRuntime | undefined;\n const memoryRuntime = await memory.attach(\n {\n harness: agent.harness,\n agents: agent.agents,\n env: home,\n home: HOME,\n reloadMs: RELOAD_MS,\n roles: async (using) => roles?.roles(using),\n },\n BACKGROUND_CONTEXT\n );\n const freezer = attachFreezer({\n harness: agent.harness,\n agents: agent.agents,\n env: home,\n extract: memory.extract,\n title: titler(models, settingsText(home, HOME)),\n });\n licks.licks.guard(freezer.frozenTarget);\n const settings = await createAgentSettings(models, credentials, providers, { mcp: mcpRuntime });\n const pages = createPages({ claim: sprinkles.claim });\n const runtime = await scoops.attach(\n {\n harness: agent.harness,\n agents: agent.agents,\n groups,\n env: home,\n home: HOME,\n alive: alive(client),\n reads: skillsRuntime.dirs,\n memory: memoryRuntime.command,\n gelatiere: gelatiere.command,\n freezer: freezer.command,\n models: modelsCommand({ agents: agent.agents, settings, catalog: models }),\n panel: pages.command,\n mcp: mcp.direct,\n ...(options.assets ? { assets: options.assets } : {}),\n },\n BACKGROUND_CONTEXT\n );\n roles = runtime;\n gelatiere.attach({\n agents: agent.agents,\n licks: licks.licks,\n env: home,\n home: HOME,\n scoops: scoops.scoops,\n roles: runtime.roles,\n assets: options.assets ?? packageAssets(),\n fetch: scope.fetch,\n skills: skillsRuntime.skills,\n ...(options.catalog ? { catalog: options.catalog } : {}),\n });\n await memoryRuntime.reload(BACKGROUND_CONTEXT).catch(() => undefined);\n const changes = attachChanges({ agents: agent.agents, env: home, reloadMs: CHANGES_MS });\n return hostAgent(agent, {\n settings,\n licks: { licks: licks.licks, sources },\n scoops: { scoops: scoops.scoops, runtime },\n skills: skillsRuntime,\n sprinkles,\n memory: memoryRuntime,\n freezer,\n changes,\n pages,\n mcp: mcpRuntime,\n ...(users ? { users } : {}),\n monitor: {\n kernel: client,\n groups,\n ...(users ? { users: () => users.users.list() } : {}),\n secrets: () => mcpRuntime.secrets(),\n },\n });\n}\n\nexport function alive(client: KernelClient) {\n return async (pid: number): Promise<boolean> => {\n if (!client.ps) return true;\n return (await client.ps()).some((process) => process.pid === pid);\n };\n}\n\nexport function runAgentWorker(\n scope: AgentWorkerScope,\n options: AgentWorkerOptions = {}\n): Promise<AgentHost> {\n const host = start(scope, kernelPorts(scope), options);\n host.catch(() => undefined);\n serveConnections(scope, host);\n return host;\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AAEnC;AAAA,EACE;AAAA,OAIK;AACP,SAAS,mBAAmB;AAC5B,
|
|
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 {\n createRegistry,\n type ModelRef,\n type Storage,\n type ToolRegistration,\n} from '@earendil-works/pi-durable';\nimport { CodingTools } from '@earendil-works/pi-durable/tools';\nimport { type Agent, type AgentOptions, openAgent } from './agent.ts';\nimport { browserGuard } from './browser/guard.ts';\nimport { attachChanges, CHANGES_MS } from './changes/index.ts';\nimport {\n codemodeExtension,\n codemodeTool,\n disabledBySettings,\n locateWasm,\n sandboxFactory,\n} from './codemode/index.ts';\nimport { EncryptedCredentialStore } from './credentials.ts';\nimport { attachFreezer } from './freezer/index.ts';\nimport { settingsText, titler } from './freezer/title.ts';\nimport { lateGelatiere } from './gelatiere/index.ts';\nimport { type AgentHost, hostAgent } from './host.ts';\nimport { imageReads } from './images/index.ts';\nimport { createActivity } from './kernel/activity.ts';\nimport type { KernelClient } from './kernel/client.ts';\nimport { HOME, kernelEnvironment, SliccKernelEnv } from './kernel/env.ts';\nimport { processGroups } from './kernel/groups.ts';\nimport { adminOver, type UserClient, type UserClients, userClients } from './kernel/users.ts';\nimport { setupLicks } from './licks/index.ts';\nimport { lickTriage } from './licks/triage.ts';\nimport { setupMcp } from './mcp/index.ts';\nimport { setupMemory } from './memory/index.ts';\nimport { modelsCommand } from './models/index.ts';\nimport { type Transport, transportFetch } from './net.ts';\nimport { sliccPrompt } from './prompt.ts';\nimport { createPages } from './remote/pages.ts';\nimport { identity } from './scoops/identity.ts';\nimport { type Assets, packageAssets, type ScoopsRuntime, setupScoops } from './scoops/index.ts';\nimport { repin, secretsExtension, shellGrants } from './secrets/extension.ts';\nimport { secretNames } from './secrets/names.ts';\nimport { scrubWith } from './secrets/scrub.ts';\nimport { listenVault, Vault } from './secrets/vault.ts';\nimport {\n createAgentSettings,\n createSliccModels,\n DEFAULT_MODEL,\n sliccProviders,\n} from './settings.ts';\nimport { RELOAD_MS, setupSkills } from './skills/index.ts';\nimport { setupSprinkles } from './sprinkles/index.ts';\nimport { openOpfsSqliteStorage } from './sqlite.ts';\nimport {\n agentVersion,\n browserConnected,\n commandsOnPath,\n kernelBoot,\n transportName,\n} from './system.ts';\nimport { type KernelPorts, kernelPorts, 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 assets?: Assets;\n codemodeWasm?: () => Promise<WebAssembly.Module | undefined>;\n codemodeWorker?: string | URL;\n catalog?: string;\n users?: boolean;\n mcpRedirect?: string;\n}\n\nexport function usersOf(\n admin: MessagePort | undefined,\n wanted: boolean | undefined,\n attach: KernelAttach\n): UserClients | undefined {\n if (!admin) return undefined;\n if (wanted === false) {\n admin.close();\n return undefined;\n }\n return userClients(adminOver(admin), attach as (port: MessagePort) => Promise<UserClient>);\n}\n\nexport const STORE_DIR = '/.slicc';\n\nasync function attached(ports: Promise<KernelPorts>, options: AgentWorkerOptions) {\n const attach: KernelAttach = options.attach ?? attachKernel;\n const { kernel, admin } = await ports;\n const client = await attach(kernel);\n return { client, users: usersOf(admin, options.users, attach) };\n}\n\nfunction privateStore(env: SliccKernelEnv): Promise<unknown> {\n return env.exec(`mkdir -p ${STORE_DIR} && chmod 700 ${STORE_DIR}`, undefined, BACKGROUND_CONTEXT);\n}\n\nasync function codemodeTools(\n client: KernelClient,\n options: AgentWorkerOptions,\n native: typeof fetch,\n extra: Parameters<typeof codemodeTool>[0]['extra']\n) {\n const settings = await client.fs\n .readFile(`${HOME}/.pi/agent/settings.json`)\n .catch(() => undefined);\n if (disabledBySettings(settings === undefined ? undefined : new TextDecoder().decode(settings)))\n return [];\n const sandbox = sandboxFactory(\n options.codemodeWasm ?? (() => locateWasm(native, new URL(import.meta.url))),\n options.codemodeWorker\n );\n return [\n codemodeTool({ declared: CodingTools.tools as readonly ToolRegistration[], sandbox, extra }),\n ];\n}\n\nexport const MCP_CALLBACK = '/oauth/mcp-callback';\n\nasync function openRepinned(\n options: AgentWorkerOptions,\n base: Omit<AgentOptions, 'storage' | 'beforeResume'>\n): Promise<Agent> {\n const storage = await (options.storage ?? (() => openOpfsSqliteStorage()))();\n return openAgent({\n ...base,\n storage,\n beforeResume: (harness, using) => repin(harness, storage, using),\n });\n}\n\nasync function secretsFor(home: SliccKernelEnv, vault: Vault) {\n scrubWith(vault);\n const names = secretNames(home, vault);\n await names.refresh(BACKGROUND_CONTEXT);\n return names;\n}\n\nasync function start(\n scope: AgentWorkerScope,\n ports: Promise<KernelPorts>,\n options: AgentWorkerOptions,\n vault: Vault\n): Promise<AgentHost> {\n const { client, users } = await attached(ports, options);\n const home = new SliccKernelEnv(client, { cwd: HOME });\n await privateStore(home);\n const names = await secretsFor(home, vault);\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 browser: await browserConnected(client),\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 registry.install(\n imageReads(\n (CodingTools.tools as readonly ToolRegistration[]).find(\n (tool) => tool.name === 'read'\n ) as ToolRegistration\n )\n );\n const memory = setupMemory(registry);\n const coding = CodingTools.tools as readonly ToolRegistration[];\n registry.install(browserGuard([scope.location.origin], coding));\n const mcp = setupMcp(registry);\n const codemode = await codemodeTools(client, options, native, mcp.codemode);\n registry.install(codemodeExtension(codemode));\n const licks = setupLicks(registry);\n licks.licks.triage(lickTriage(models));\n const scoops = setupScoops(registry, licks.licks, [...coding, ...codemode, ...memory.tools]);\n const skills = setupSkills(registry, licks.licks);\n registry.install(secretsExtension({ names, vault }));\n const activity = createActivity();\n const groups = processGroups(client);\n const model = options.model ?? DEFAULT_MODEL;\n let opened: Agent | undefined;\n const environment = kernelEnvironment(client, {\n activity,\n groups,\n identify: identity(() => opened?.agents, model),\n scrub: vault,\n spawn: shellGrants(names),\n });\n const agent = await openRepinned(options, { models, model, registry, env: environment });\n opened = agent;\n const sources = licks.attach(agent, { env: home, home: HOME, activity });\n await sources.start(BACKGROUND_CONTEXT);\n await sources.boot(\n { version: facts.version, boot: await kernelBoot(client) },\n BACKGROUND_CONTEXT\n );\n const skillsRuntime = await skills.attach(\n { env: home, home: HOME, assets: options.assets ?? packageAssets(), reloadMs: RELOAD_MS },\n BACKGROUND_CONTEXT\n );\n const mcpRuntime = await mcp.attach(\n {\n env: home,\n home: HOME,\n credentials,\n licks: licks.licks,\n fetch: scope.fetch,\n version: facts.version,\n reloadMs: RELOAD_MS,\n codemode: codemode.length > 0,\n cors: client.transport.traits?.crossOrigin === 'cors',\n redirect: options.mcpRedirect ?? new URL(MCP_CALLBACK, scope.location.origin).href,\n },\n BACKGROUND_CONTEXT\n );\n const gelatiere = lateGelatiere();\n const sprinkles = await setupSprinkles().attach(\n {\n intercept: gelatiere.intercept,\n harness: agent.harness,\n agents: agent.agents,\n licks: licks.licks,\n env: home,\n assets: options.assets ?? packageAssets(),\n reloadMs: RELOAD_MS,\n },\n BACKGROUND_CONTEXT\n );\n let roles: ScoopsRuntime | undefined;\n const memoryRuntime = await memory.attach(\n {\n harness: agent.harness,\n agents: agent.agents,\n env: home,\n home: HOME,\n reloadMs: RELOAD_MS,\n roles: async (using) => roles?.roles(using),\n },\n BACKGROUND_CONTEXT\n );\n const freezer = attachFreezer({\n harness: agent.harness,\n agents: agent.agents,\n env: home,\n extract: memory.extract,\n title: titler(models, settingsText(home, HOME)),\n });\n licks.licks.guard(freezer.frozenTarget);\n const settings = await createAgentSettings(models, credentials, providers, { mcp: mcpRuntime });\n const pages = createPages({ claim: sprinkles.claim });\n const runtime = await scoops.attach(\n {\n harness: agent.harness,\n agents: agent.agents,\n groups,\n env: home,\n home: HOME,\n alive: alive(client),\n reads: skillsRuntime.dirs,\n memory: memoryRuntime.command,\n gelatiere: gelatiere.command,\n freezer: freezer.command,\n models: modelsCommand({ agents: agent.agents, settings, catalog: models }),\n panel: pages.command,\n mcp: mcp.direct,\n ...(options.assets ? { assets: options.assets } : {}),\n },\n BACKGROUND_CONTEXT\n );\n roles = runtime;\n gelatiere.attach({\n agents: agent.agents,\n licks: licks.licks,\n env: home,\n home: HOME,\n scoops: scoops.scoops,\n roles: runtime.roles,\n assets: options.assets ?? packageAssets(),\n fetch: scope.fetch,\n skills: skillsRuntime.skills,\n ...(options.catalog ? { catalog: options.catalog } : {}),\n });\n await memoryRuntime.reload(BACKGROUND_CONTEXT).catch(() => undefined);\n const changes = attachChanges({ agents: agent.agents, env: home, reloadMs: CHANGES_MS });\n return hostAgent(agent, {\n settings,\n licks: { licks: licks.licks, sources },\n scoops: { scoops: scoops.scoops, runtime },\n skills: skillsRuntime,\n sprinkles,\n memory: memoryRuntime,\n freezer,\n changes,\n pages,\n mcp: mcpRuntime,\n ...(users ? { users } : {}),\n secrets: { vault: vault.state, list: () => names.state.value, close: names.close },\n monitor: {\n kernel: client,\n groups,\n ...(users ? { users: () => users.users.list() } : {}),\n secrets: () => mcpRuntime.secrets(),\n },\n });\n}\n\nexport function alive(client: KernelClient) {\n return async (pid: number): Promise<boolean> => {\n if (!client.ps) return true;\n return (await client.ps()).some((process) => process.pid === pid);\n };\n}\n\nexport function runAgentWorker(\n scope: AgentWorkerScope,\n options: AgentWorkerOptions = {}\n): Promise<AgentHost> {\n const vault = new Vault();\n listenVault(scope, vault);\n const host = start(scope, kernelPorts(scope), options, vault);\n host.catch(() => undefined);\n serveConnections(scope, host);\n return host;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/mcp/content.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { toLlmContent } from "@earendil-works/pi-mcp";
|
|
2
|
+
import { scrubItems } from "../secrets/scrub.js";
|
|
2
3
|
const MCP_OUTPUT_MAX_BYTES = 20 * 1024;
|
|
3
4
|
const READ_MCP_RESOURCE = "read_mcp_resource";
|
|
4
5
|
function formatSize(bytes) {
|
|
@@ -30,7 +31,8 @@ function truncateMiddle(content, maxBytes) {
|
|
|
30
31
|
function textOf(content) {
|
|
31
32
|
return content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
32
33
|
}
|
|
33
|
-
async function limitContent(
|
|
34
|
+
async function limitContent(raw, save) {
|
|
35
|
+
const content = await scrubItems(raw);
|
|
34
36
|
const combined = textOf(content);
|
|
35
37
|
const cut = truncateMiddle(combined, MCP_OUTPUT_MAX_BYTES);
|
|
36
38
|
if (!cut.truncated) return { content };
|
package/dist/mcp/content.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/mcp/content.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { ImageContent, TextContent } from '@earendil-works/pi-ai';\nimport { type CallToolResult, type ContentBlock, toLlmContent } from '@earendil-works/pi-mcp';\n\nexport const MCP_OUTPUT_MAX_BYTES = 20 * 1024;\nexport const READ_MCP_RESOURCE = 'read_mcp_resource';\n\nexport type Content = TextContent | ImageContent;\n\nexport type Saver = (data: string | Uint8Array, extension: string) => Promise<string>;\n\nexport interface Truncation {\n content: string;\n truncated: boolean;\n totalBytes: number;\n totalLines: number;\n}\n\nexport function formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n}\n\nexport function truncateMiddle(content: string, maxBytes: number): Truncation {\n const bytes = new TextEncoder().encode(content);\n const totalLines = content === '' ? 0 : content.replace(/\\n$/, '').split('\\n').length;\n if (bytes.length <= maxBytes)\n return { content, truncated: false, totalBytes: bytes.length, totalLines };\n const boundary = (index: number) =>\n index >= bytes.length || ((bytes[index] as number) & 0xc0) !== 0x80;\n let headEnd = Math.floor(maxBytes / 2);\n while (headEnd > 0 && !boundary(headEnd)) headEnd--;\n let tailStart = bytes.length - (maxBytes - Math.floor(maxBytes / 2));\n while (tailStart < bytes.length && !boundary(tailStart)) tailStart++;\n const decoder = new TextDecoder();\n const head = decoder.decode(bytes.subarray(0, headEnd));\n const tail = decoder.decode(bytes.subarray(tailStart));\n const removed = Array.from(decoder.decode(bytes.subarray(headEnd, tailStart))).length;\n return {\n content: `${head}\u2026${removed} chars truncated\u2026${tail}`,\n truncated: true,\n totalBytes: bytes.length,\n totalLines,\n };\n}\n\nfunction textOf(content: readonly Content[]): string {\n return content\n .filter((block): block is TextContent => block.type === 'text')\n .map((block) => block.text)\n .join('\\n');\n}\n\nexport async function limitContent(\n
|
|
5
|
-
"mappings": "AAEA,SAAiD,oBAAoB;
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { ImageContent, TextContent } from '@earendil-works/pi-ai';\nimport { type CallToolResult, type ContentBlock, toLlmContent } from '@earendil-works/pi-mcp';\nimport { scrubItems } from '../secrets/scrub.ts';\n\nexport const MCP_OUTPUT_MAX_BYTES = 20 * 1024;\nexport const READ_MCP_RESOURCE = 'read_mcp_resource';\n\nexport type Content = TextContent | ImageContent;\n\nexport type Saver = (data: string | Uint8Array, extension: string) => Promise<string>;\n\nexport interface Truncation {\n content: string;\n truncated: boolean;\n totalBytes: number;\n totalLines: number;\n}\n\nexport function formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n}\n\nexport function truncateMiddle(content: string, maxBytes: number): Truncation {\n const bytes = new TextEncoder().encode(content);\n const totalLines = content === '' ? 0 : content.replace(/\\n$/, '').split('\\n').length;\n if (bytes.length <= maxBytes)\n return { content, truncated: false, totalBytes: bytes.length, totalLines };\n const boundary = (index: number) =>\n index >= bytes.length || ((bytes[index] as number) & 0xc0) !== 0x80;\n let headEnd = Math.floor(maxBytes / 2);\n while (headEnd > 0 && !boundary(headEnd)) headEnd--;\n let tailStart = bytes.length - (maxBytes - Math.floor(maxBytes / 2));\n while (tailStart < bytes.length && !boundary(tailStart)) tailStart++;\n const decoder = new TextDecoder();\n const head = decoder.decode(bytes.subarray(0, headEnd));\n const tail = decoder.decode(bytes.subarray(tailStart));\n const removed = Array.from(decoder.decode(bytes.subarray(headEnd, tailStart))).length;\n return {\n content: `${head}\u2026${removed} chars truncated\u2026${tail}`,\n truncated: true,\n totalBytes: bytes.length,\n totalLines,\n };\n}\n\nfunction textOf(content: readonly Content[]): string {\n return content\n .filter((block): block is TextContent => block.type === 'text')\n .map((block) => block.text)\n .join('\\n');\n}\n\nexport async function limitContent(\n raw: Content[],\n save: Saver\n): Promise<{ content: Content[]; fullOutputPath?: string }> {\n const content = await scrubItems(raw);\n const combined = textOf(content);\n const cut = truncateMiddle(combined, MCP_OUTPUT_MAX_BYTES);\n if (!cut.truncated) return { content };\n let fullOutputPath: string | undefined;\n let where: string;\n try {\n fullOutputPath = await save(combined, '.txt');\n where = `[Full output: ${fullOutputPath} (read it with offset/limit)]`;\n } catch (error) {\n where = `[Could not save the full output: ${error instanceof Error ? error.message : String(error)}]`;\n }\n const tokens = Math.ceil(cut.totalBytes / 4);\n const text = `Warning: truncated output (original token count: ${tokens})\\nTotal output lines: ${cut.totalLines}\\n\\n${cut.content}\\n\\n${where}`;\n return {\n content: [{ type: 'text', text }, ...content.filter((block) => block.type === 'image')],\n ...(fullOutputPath ? { fullOutputPath } : {}),\n };\n}\n\nfunction extensionOf(uri: string): string {\n const path = URL.canParse(uri) ? new URL(uri).pathname : uri;\n return /\\.[A-Za-z0-9]{1,8}$/.exec(path)?.[0] ?? '.bin';\n}\n\nfunction isTextMime(mimeType: string | undefined): boolean {\n if (!mimeType) return false;\n const type = (mimeType.split(';', 1)[0] as string).trim().toLowerCase();\n return (\n type.startsWith('text/') ||\n type === 'application/json' ||\n type.endsWith('+json') ||\n type.endsWith('+xml')\n );\n}\n\nfunction decode(base64: string): Uint8Array {\n const binary = atob(base64);\n const out = new Uint8Array(binary.length);\n for (let at = 0; at < binary.length; at++) out[at] = binary.charCodeAt(at);\n return out;\n}\n\nasync function blockContent(\n server: string,\n block: ContentBlock,\n save: Saver,\n readable: boolean\n): Promise<Content[]> {\n if (block.type === 'resource_link') {\n const details = [\n block.mimeType,\n block.size === undefined ? undefined : formatSize(block.size),\n ].filter(Boolean);\n const read = readable ? `. Read it with ${READ_MCP_RESOURCE} (server \"${server}\")` : '';\n const description = block.description ? `: ${block.description}` : '';\n return [\n {\n type: 'text',\n text: `[Resource ${block.uri} \"${block.title ?? block.name}\"${details.length ? ` (${details.join(', ')})` : ''}${description}${read}]`,\n },\n ];\n }\n if (\n block.type === 'resource' &&\n 'blob' in block.resource &&\n !block.resource.mimeType?.startsWith('image/')\n ) {\n const { uri, mimeType, blob } = block.resource;\n const data = decode(blob);\n if (isTextMime(mimeType)) return [{ type: 'text', text: new TextDecoder().decode(data) }];\n const kind = `${mimeType ?? 'unknown type'}, ${formatSize(data.length)}`;\n try {\n const path = await save(data, extensionOf(uri));\n return [{ type: 'text', text: `[Binary resource ${uri} (${kind}) saved to ${path}]` }];\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n return [\n { type: 'text', text: `[Binary resource ${uri} (${kind}) could not be saved: ${reason}]` },\n ];\n }\n }\n return toLlmContent({ content: [block] }) as Content[];\n}\n\nexport async function modelContent(\n server: string,\n blocks: readonly ContentBlock[],\n save: Saver,\n readable = false\n): Promise<Content[]> {\n const parts = await Promise.all(\n blocks.map((block) => blockContent(server, block, save, readable))\n );\n return parts.flat();\n}\n\nexport async function convertResult(\n server: string,\n tool: string,\n result: CallToolResult,\n save: Saver,\n readable = false\n): Promise<{ content: Content[]; isError: boolean; fullOutputPath?: string }> {\n const converted =\n result.content.length > 0\n ? await modelContent(server, result.content, save, readable)\n : (toLlmContent(result) as Content[]);\n if (result.isError && textOf(converted) === '')\n converted.push({ type: 'text', text: `MCP tool ${server}/${tool} returned an error` });\n const limited = await limitContent(converted, save);\n return { ...limited, isError: result.isError === true };\n}\n\nexport function scriptResult(result: CallToolResult): Record<string, unknown> {\n const { _meta: _ignored, ...rest } = result as CallToolResult & { _meta?: unknown };\n return rest;\n}\n\nexport function spiller(\n write: (path: string, data: string | Uint8Array, context: Context) => Promise<boolean>,\n id: () => string,\n context: Context\n): Saver {\n return async (data, extension) => {\n const path = `/tmp/slicc-mcp-${id().replace(/[^\\w-]/g, '_')}${extension}`;\n if (!(await write(path, data, context))) throw new Error(`could not write ${path}`);\n return path;\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|