@ai-ecoverse/slicc-agent 7.6.0 → 7.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/kernel/exec.js
CHANGED
|
@@ -9,6 +9,7 @@ import { resolve } from "./paths.js";
|
|
|
9
9
|
const SHELL = ["bash", "-c"];
|
|
10
10
|
const HELD_LIMIT = 32 * 1024 * 1024;
|
|
11
11
|
const HELD = "output held: waiting for the vault";
|
|
12
|
+
const GONE = "the kernel went away mid-run, so the command may have partly run";
|
|
12
13
|
class Spill {
|
|
13
14
|
#chunks = [];
|
|
14
15
|
#limit;
|
|
@@ -198,7 +199,12 @@ async function execute(client, cwd, command, options, context, spawned, join, ho
|
|
|
198
199
|
);
|
|
199
200
|
over = () => watch.stop("held");
|
|
200
201
|
if (overflowed) over();
|
|
201
|
-
const exitCode = await process.exited.finally(() => watch.release());
|
|
202
|
+
const exitCode = await process.exited.finally(() => watch.release()).catch(() => void 0);
|
|
203
|
+
if (exitCode === void 0) {
|
|
204
|
+
out.drop();
|
|
205
|
+
errors.drop();
|
|
206
|
+
return err(new ExecutionError("unknown", GONE));
|
|
207
|
+
}
|
|
202
208
|
const ended = watch.ending ?? await drained([out, errors], context);
|
|
203
209
|
if (ended) {
|
|
204
210
|
out.drop();
|
|
@@ -217,6 +223,7 @@ async function execute(client, cwd, command, options, context, spawned, join, ho
|
|
|
217
223
|
return ok(spillPath ? { exitCode, spillPath } : { exitCode });
|
|
218
224
|
}
|
|
219
225
|
export {
|
|
226
|
+
GONE,
|
|
220
227
|
HELD,
|
|
221
228
|
HELD_LIMIT,
|
|
222
229
|
SHELL,
|
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 { 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 tag?: (context: Context) => Promise<string | undefined>;\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\ntype Started = { process: KernelProcess; tag?: string };\n\nasync function tagged(\n client: KernelClient,\n argv: readonly string[],\n options: SpawnOptions\n): Promise<Started> {\n const { tag, ...untagged } = options;\n if (tag === undefined) return { process: await client.spawn(argv, options) };\n try {\n return { process: await client.spawn(argv, options), tag };\n } catch (error) {\n if ((error as { code?: unknown }).code !== 'ENOSYS') throw error;\n return { process: await client.spawn(argv, untagged) };\n }\n}\n\nasync function start(\n client: KernelClient,\n argv: readonly string[],\n options: SpawnOptions,\n join: Join | undefined\n): Promise<Started & { joined: boolean }> {\n if (join?.pgid !== undefined) {\n try {\n return { ...(await tagged(client, 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 { ...(await tagged(client, 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, tag?: string) => 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: Started & { joined: boolean };\n try {\n const tag = await hooks.tag?.(context);\n started = await start(\n client,\n argv,\n {\n cwd: resolve(cwd, options?.cwd ?? '.'),\n ...(options?.env ? { env: options.env } : {}),\n ...(tag === undefined ? {} : { tag }),\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, started.tag);\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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
|
|
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';\nexport const GONE = 'the kernel went away mid-run, so the command may have partly run';\n\nexport interface ExecHooks {\n scrub?: () => ScrubStream;\n spawn?: (argv: readonly string[]) => readonly string[];\n tag?: (context: Context) => Promise<string | undefined>;\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\ntype Started = { process: KernelProcess; tag?: string };\n\nasync function tagged(\n client: KernelClient,\n argv: readonly string[],\n options: SpawnOptions\n): Promise<Started> {\n const { tag, ...untagged } = options;\n if (tag === undefined) return { process: await client.spawn(argv, options) };\n try {\n return { process: await client.spawn(argv, options), tag };\n } catch (error) {\n if ((error as { code?: unknown }).code !== 'ENOSYS') throw error;\n return { process: await client.spawn(argv, untagged) };\n }\n}\n\nasync function start(\n client: KernelClient,\n argv: readonly string[],\n options: SpawnOptions,\n join: Join | undefined\n): Promise<Started & { joined: boolean }> {\n if (join?.pgid !== undefined) {\n try {\n return { ...(await tagged(client, 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 { ...(await tagged(client, 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, tag?: string) => 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: Started & { joined: boolean };\n try {\n const tag = await hooks.tag?.(context);\n started = await start(\n client,\n argv,\n {\n cwd: resolve(cwd, options?.cwd ?? '.'),\n ...(options?.env ? { env: options.env } : {}),\n ...(tag === undefined ? {} : { tag }),\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, started.tag);\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()).catch(() => undefined);\n if (exitCode === undefined) {\n out.drop();\n errors.drop();\n return err(new ExecutionError('unknown', GONE));\n }\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
6
|
"names": ["resolve"]
|
|
7
7
|
}
|
package/dist/licks/boot.js
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
defineDoc
|
|
3
3
|
} from "@earendil-works/pi-durable";
|
|
4
4
|
import { boundedDeltas } from "../checkpoints.js";
|
|
5
|
+
import { GONE } from "../kernel/exec.js";
|
|
5
6
|
import { shortCommand } from "../kernel/jobs.js";
|
|
6
7
|
import { entryLick } from "./state.js";
|
|
7
8
|
const LicksHostDoc = defineDoc({
|
|
@@ -40,8 +41,11 @@ function ended(entries) {
|
|
|
40
41
|
for (const entry of entries) {
|
|
41
42
|
if (entry.kind !== "pi.tool-result") continue;
|
|
42
43
|
const id = entry.model?.[0]?.toolCallId;
|
|
43
|
-
const
|
|
44
|
-
|
|
44
|
+
const diagnostics = entry.data?.diagnostics ?? [];
|
|
45
|
+
const cut = diagnostics.some(
|
|
46
|
+
(diagnostic) => diagnostic.code === "interrupted" || diagnostic.code === "tool_error" && diagnostic.message === GONE
|
|
47
|
+
);
|
|
48
|
+
if (typeof id === "string" && !cut) done.add(id);
|
|
45
49
|
}
|
|
46
50
|
return done;
|
|
47
51
|
}
|
package/dist/licks/boot.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/licks/boot.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type Conversation,\n defineDoc,\n type EntryRecord,\n type Harness,\n} from '@earendil-works/pi-durable';\nimport { boundedDeltas } from '../checkpoints.ts';\nimport { type Job, type Jobs, shortCommand } from '../kernel/jobs.ts';\nimport type { Deliver } from './fswatch.ts';\nimport type { LickChannel } from './lick.ts';\nimport { entryLick } from './state.ts';\n\ntype HostState = {\n cron: Record<string, { task: number }>;\n agent: string | null;\n boot: string | null;\n mark: number;\n upgrades: number;\n jobs?: Job[];\n};\n\nexport const LicksHostDoc = defineDoc<HostState>({\n kind: 'slicc.licks.host',\n checkpointWhen: boundedDeltas,\n version: 1,\n scope: 'session',\n initial: () => ({ cron: {}, agent: null, boot: null, mark: 0, upgrades: 0 }),\n});\n\nexport interface BootFacts {\n version: string;\n boot: string | null;\n}\n\nconst BOOT_CHANNELS: ReadonlySet<LickChannel> = new Set(['upgrade', 'session-reload']);\n\nexport const LISTED = 5;\n\ninterface Block {\n type?: string;\n id?: string;\n name?: string;\n arguments?: { command?: unknown };\n}\n\ntype Call = { id: string | undefined; command: string; at: number };\n\nfunction bashCalls(entries: readonly EntryRecord[], after: number): Call[] {\n const calls: Call[] = [];\n let reacting = false;\n for (const entry of entries) {\n if (entry.kind === 'pi.user') {\n const lick = entryLick(entry);\n reacting = lick !== undefined && BOOT_CHANNELS.has(lick.channel);\n }\n if (reacting || entry.kind !== 'pi.assistant' || Number(entry.id) <= after) continue;\n const message = entry.model?.[0] as { content?: unknown; timestamp?: unknown } | undefined;\n const at = typeof message?.timestamp === 'number' ? message.timestamp : 0;\n for (const block of Array.isArray(message?.content) ? (message.content as Block[]) : []) {\n const command = block.arguments?.command;\n if (block.type === 'toolCall' && block.name === 'bash' && typeof command === 'string')\n calls.push({ id: block.id, command, at });\n }\n }\n return calls;\n}\n\nexport function bashCommands(entries: readonly EntryRecord[], after: number): string[] {\n return bashCalls(entries, after).map((call) => call.command);\n}\n\nfunction ended(entries: readonly EntryRecord[]): Set<string> {\n const done = new Set<string>();\n for (const entry of entries) {\n if (entry.kind !== 'pi.tool-result') continue;\n const id = (entry.model?.[0] as { toolCallId?: unknown } | undefined)?.toolCallId;\n const
|
|
5
|
-
"mappings": "AACA;AAAA,EAEE;AAAA,OAGK;AACP,SAAS,qBAAqB;AAC9B,SAA8B,oBAAoB;AAGlD,SAAS,iBAAiB;AAWnB,MAAM,eAAe,UAAqB;AAAA,EAC/C,MAAM;AAAA,EACN,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS,OAAO,EAAE,MAAM,CAAC,GAAG,OAAO,MAAM,MAAM,MAAM,MAAM,GAAG,UAAU,EAAE;AAC5E,CAAC;AAOD,MAAM,gBAA0C,oBAAI,IAAI,CAAC,WAAW,gBAAgB,CAAC;AAE9E,MAAM,SAAS;AAWtB,SAAS,UAAU,SAAiC,OAAuB;AACzE,QAAM,QAAgB,CAAC;AACvB,MAAI,WAAW;AACf,aAAW,SAAS,SAAS;AAC3B,QAAI,MAAM,SAAS,WAAW;AAC5B,YAAM,OAAO,UAAU,KAAK;AAC5B,iBAAW,SAAS,UAAa,cAAc,IAAI,KAAK,OAAO;AAAA,IACjE;AACA,QAAI,YAAY,MAAM,SAAS,kBAAkB,OAAO,MAAM,EAAE,KAAK,MAAO;AAC5E,UAAM,UAAU,MAAM,QAAQ,CAAC;AAC/B,UAAM,KAAK,OAAO,SAAS,cAAc,WAAW,QAAQ,YAAY;AACxE,eAAW,SAAS,MAAM,QAAQ,SAAS,OAAO,IAAK,QAAQ,UAAsB,CAAC,GAAG;AACvF,YAAM,UAAU,MAAM,WAAW;AACjC,UAAI,MAAM,SAAS,cAAc,MAAM,SAAS,UAAU,OAAO,YAAY;AAC3E,cAAM,KAAK,EAAE,IAAI,MAAM,IAAI,SAAS,GAAG,CAAC;AAAA,IAC5C;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,aAAa,SAAiC,OAAyB;AACrF,SAAO,UAAU,SAAS,KAAK,EAAE,IAAI,CAAC,SAAS,KAAK,OAAO;AAC7D;AAEA,SAAS,MAAM,SAA8C;AAC3D,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,SAAS,SAAS;AAC3B,QAAI,MAAM,SAAS,iBAAkB;AACrC,UAAM,KAAM,MAAM,QAAQ,CAAC,GAA4C;AACvE,UAAM,
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type Conversation,\n defineDoc,\n type EntryRecord,\n type Harness,\n} from '@earendil-works/pi-durable';\nimport { boundedDeltas } from '../checkpoints.ts';\nimport { GONE } from '../kernel/exec.ts';\nimport { type Job, type Jobs, shortCommand } from '../kernel/jobs.ts';\nimport type { Deliver } from './fswatch.ts';\nimport type { LickChannel } from './lick.ts';\nimport { entryLick } from './state.ts';\n\ntype HostState = {\n cron: Record<string, { task: number }>;\n agent: string | null;\n boot: string | null;\n mark: number;\n upgrades: number;\n jobs?: Job[];\n};\n\nexport const LicksHostDoc = defineDoc<HostState>({\n kind: 'slicc.licks.host',\n checkpointWhen: boundedDeltas,\n version: 1,\n scope: 'session',\n initial: () => ({ cron: {}, agent: null, boot: null, mark: 0, upgrades: 0 }),\n});\n\nexport interface BootFacts {\n version: string;\n boot: string | null;\n}\n\nconst BOOT_CHANNELS: ReadonlySet<LickChannel> = new Set(['upgrade', 'session-reload']);\n\nexport const LISTED = 5;\n\ninterface Block {\n type?: string;\n id?: string;\n name?: string;\n arguments?: { command?: unknown };\n}\n\ntype Call = { id: string | undefined; command: string; at: number };\n\nfunction bashCalls(entries: readonly EntryRecord[], after: number): Call[] {\n const calls: Call[] = [];\n let reacting = false;\n for (const entry of entries) {\n if (entry.kind === 'pi.user') {\n const lick = entryLick(entry);\n reacting = lick !== undefined && BOOT_CHANNELS.has(lick.channel);\n }\n if (reacting || entry.kind !== 'pi.assistant' || Number(entry.id) <= after) continue;\n const message = entry.model?.[0] as { content?: unknown; timestamp?: unknown } | undefined;\n const at = typeof message?.timestamp === 'number' ? message.timestamp : 0;\n for (const block of Array.isArray(message?.content) ? (message.content as Block[]) : []) {\n const command = block.arguments?.command;\n if (block.type === 'toolCall' && block.name === 'bash' && typeof command === 'string')\n calls.push({ id: block.id, command, at });\n }\n }\n return calls;\n}\n\nexport function bashCommands(entries: readonly EntryRecord[], after: number): string[] {\n return bashCalls(entries, after).map((call) => call.command);\n}\n\nfunction ended(entries: readonly EntryRecord[]): Set<string> {\n const done = new Set<string>();\n for (const entry of entries) {\n if (entry.kind !== 'pi.tool-result') continue;\n const id = (entry.model?.[0] as { toolCallId?: unknown } | undefined)?.toolCallId;\n const diagnostics =\n (entry.data as { diagnostics?: { code?: unknown; message?: unknown }[] } | undefined)\n ?.diagnostics ?? [];\n const cut = diagnostics.some(\n (diagnostic) =>\n diagnostic.code === 'interrupted' ||\n (diagnostic.code === 'tool_error' && diagnostic.message === GONE)\n );\n if (typeof id === 'string' && !cut) done.add(id);\n }\n return done;\n}\n\nexport function interruptedCommands(\n entries: readonly EntryRecord[],\n after: number,\n boot: string\n): string[] {\n const done = ended(entries);\n const booted = (Number(boot) + 1) * 1000;\n return bashCalls(entries, after)\n .filter((call) => call.at < booted && !(call.id !== undefined && done.has(call.id)))\n .map((call) => shortCommand(call.command));\n}\n\nfunction upgraded(from: string, to: string, upgrades: number) {\n return {\n channel: 'upgrade',\n source: 'slicc-agent',\n title: `slicc-agent ${from} \u2192 ${to}`,\n text: `The agent was updated from ${from} to ${to}.`,\n body: `Release notes: https://github.com/ai-ecoverse/slicc-agent/releases/tag/v${to}`,\n target: 'cone',\n eventId: `${from}->${to}#${upgrades}`,\n } as const;\n}\n\nexport function listed(all: readonly string[]): string[] {\n const commands = [...new Set(all)];\n const more = commands.length - LISTED;\n return more > 0 ? [...commands.slice(-LISTED), `\u2026and ${more} more`] : commands;\n}\n\nexport function backgroundList(jobs: readonly Job[]): string[] {\n return listed(jobs.map((job) => job.command));\n}\n\nfunction restartBody(interrupted: readonly string[], jobs: readonly Job[]): string {\n const cut = listed(interrupted);\n const background = backgroundList(jobs);\n const parts = [\n ...(cut.length ? [`Commands that were interrupted mid-run:\\n${cut.join('\\n')}`] : []),\n ...(background.length\n ? [`Background commands that are no longer running:\\n${background.join('\\n')}`]\n : []),\n ];\n return parts.length\n ? parts.join('\\n\\n')\n : 'Files are unchanged. Start again whatever you still need running.';\n}\n\nfunction restarted(boot: string, jobs: readonly Job[], interrupted: readonly string[]) {\n return {\n channel: 'session-reload',\n source: 'kernel',\n title: 'The kernel restarted',\n text: 'The page reloaded, so the kernel started fresh: processes from before the reload are gone.',\n body: restartBody(interrupted, jobs),\n target: 'cone',\n eventId: `boot:${boot}`,\n } as const;\n}\n\nexport async function bootLicks(\n harness: Harness,\n cone: Conversation,\n deliver: Deliver,\n facts: BootFacts,\n context: Context,\n jobs?: Jobs,\n listen?: () => void\n): Promise<void> {\n const state = await harness.snapshot(LicksHostDoc, context);\n listen?.();\n const previous = state?.agent ?? null;\n if (previous !== null && previous !== facts.version)\n await deliver(upgraded(previous, facts.version, state?.upgrades ?? 0), context);\n const { entries } = await cone.context(context);\n const before = state?.jobs ?? [];\n if (facts.boot !== null && state?.boot && facts.boot !== state.boot) {\n const commands = bashCommands(entries, state.mark);\n if (commands.length || before.length)\n await deliver(\n restarted(facts.boot, before, interruptedCommands(entries, state.mark, facts.boot)),\n context\n );\n } else if (facts.boot !== null && facts.boot === state?.boot) jobs?.adopt(before);\n const { entries: after } = await cone.context(context);\n const mark = Number(after.at(-1)?.id ?? 0);\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksHostDoc);\n if (doc.agent !== null && doc.agent !== facts.version) doc.upgrades += 1;\n doc.agent = facts.version;\n if (facts.boot !== null && doc.boot !== facts.boot) {\n doc.boot = facts.boot;\n doc.mark = mark;\n }\n doc.jobs = jobs?.list() ?? [];\n }, context);\n}\n\nfunction recordJobs(harness: Harness, jobs: Jobs, context: Context): () => void {\n let chain: Promise<void> = Promise.resolve();\n return jobs.watch(() => {\n chain = chain\n .then(() =>\n harness.commit(async (tx) => {\n (await tx.doc(LicksHostDoc)).jobs = jobs.list();\n }, context)\n )\n .catch(() => undefined);\n return chain;\n });\n}\n\nexport function booting(\n harness: Harness,\n cone: () => Conversation,\n deliver: Deliver,\n jobs: Jobs | undefined\n) {\n let stopped = false;\n let off = () => undefined as void;\n return {\n async boot(facts: BootFacts, context: Context): Promise<void> {\n await bootLicks(harness, cone(), deliver, facts, context, jobs, () => {\n off();\n if (jobs && !stopped) off = recordJobs(harness, jobs, context);\n });\n },\n stop() {\n stopped = true;\n off();\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -5,6 +5,7 @@ import type { KernelClient, KernelProcess } from './client.ts';
|
|
|
5
5
|
export declare const SHELL: readonly ['bash', '-c'];
|
|
6
6
|
export declare const HELD_LIMIT: number;
|
|
7
7
|
export declare const HELD = "output held: waiting for the vault";
|
|
8
|
+
export declare const GONE = "the kernel went away mid-run, so the command may have partly run";
|
|
8
9
|
export interface ExecHooks {
|
|
9
10
|
scrub?: () => ScrubStream;
|
|
10
11
|
spawn?: (argv: readonly string[]) => readonly string[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc-agent",
|
|
3
|
-
"version": "7.6.
|
|
3
|
+
"version": "7.6.1",
|
|
4
4
|
"description": "SLICC's agent core on pi durable: an agent worker, session storage, remoting and pi extensions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"release": "semantic-release"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@ai-ecoverse/slicc-kernel": "1.
|
|
68
|
+
"@ai-ecoverse/slicc-kernel": "1.148.0",
|
|
69
69
|
"@ai-ecoverse/slicc-scrub": "1.0.0",
|
|
70
70
|
"@ai-ecoverse/slicc-shared-web": "2.11.0",
|
|
71
71
|
"@ai-ecoverse/slicc-spectrum": "1.107.1",
|