@ai-ecoverse/slicc-agent 6.18.5 → 6.18.6

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.
@@ -1,11 +1,27 @@
1
1
  import { redacted } from "../monitor/redact.js";
2
2
  const JOBS_MS = 2e3;
3
+ const PS_MS = 2e3;
3
4
  function shortCommand(command) {
4
5
  const lines = command.trim().split("\n");
5
6
  const first = redacted(lines[0]);
6
7
  return lines.length > 1 && !first.endsWith("\u2026") ? `${first} \u2026` : first;
7
8
  }
8
- function backgroundJobs(client, ms = JOBS_MS) {
9
+ const quiet = () => void 0;
10
+ function boundedPs(owner, warn, bound = PS_MS) {
11
+ let timer;
12
+ const stalled = new Promise((resolve) => {
13
+ timer = setTimeout(() => {
14
+ warn(`slicc-agent: the process table read took over ${bound} ms, so it was skipped`);
15
+ resolve(void 0);
16
+ }, bound);
17
+ });
18
+ const read = Promise.resolve().then(() => owner.ps?.()).catch((error) => {
19
+ warn(`slicc-agent: the process table could not be read: ${String(error)}`);
20
+ return void 0;
21
+ });
22
+ return Promise.race([read, stalled]).finally(() => clearTimeout(timer));
23
+ }
24
+ function backgroundJobs(client, ms = JOBS_MS, warn = (message) => console.warn(message), bound = PS_MS) {
9
25
  const jobs = /* @__PURE__ */ new Map();
10
26
  const running = /* @__PURE__ */ new Set();
11
27
  const listeners = /* @__PURE__ */ new Set();
@@ -42,13 +58,13 @@ function backgroundJobs(client, ms = JOBS_MS) {
42
58
  async function prune() {
43
59
  let gone = false;
44
60
  for (const each of new Set([...jobs.values()].map((job) => job.client))) {
45
- const table = await each.ps?.().catch(() => void 0);
61
+ const table = await boundedPs(each, quiet, bound);
46
62
  if (table && sweep(each, table)) gone = true;
47
63
  }
48
64
  if (gone) await changed();
49
65
  }
50
66
  async function survivors(owner, command, process) {
51
- const table = await owner.ps?.().catch(() => void 0);
67
+ const table = await boundedPs(owner, warn, bound);
52
68
  if (!table) return;
53
69
  const found = orphans(table, process.pgid, process.pid);
54
70
  keep(found, process.pgid, shortCommand(command), owner);
@@ -81,7 +97,9 @@ function backgroundJobs(client, ms = JOBS_MS) {
81
97
  }
82
98
  export {
83
99
  JOBS_MS,
100
+ PS_MS,
84
101
  backgroundJobs,
102
+ boundedPs,
85
103
  shortCommand
86
104
  };
87
105
  //# sourceMappingURL=jobs.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/kernel/jobs.ts"],
4
- "sourcesContent": ["import { redacted } from '../monitor/redact.ts';\nimport type { KernelClient, KernelProcess } from './client.ts';\n\nexport type Job = { pid: number; pgid: number; command: string };\n\nexport interface Jobs {\n started(client: KernelClient, command: string, process: KernelProcess): Promise<void>;\n list(): Job[];\n adopt(jobs: readonly Job[]): void;\n watch(listener: () => Promise<void>): () => void;\n}\n\nexport const JOBS_MS = 2000;\n\ntype Ps = Pick<KernelClient, 'ps'>;\n\ntype Row = Awaited<ReturnType<NonNullable<KernelClient['ps']>>>[number];\n\nexport function shortCommand(command: string): string {\n const lines = command.trim().split('\\n');\n const first = redacted(lines[0] as string);\n return lines.length > 1 && !first.endsWith('\u2026') ? `${first} \u2026` : first;\n}\n\nexport function backgroundJobs(client: Ps, ms = JOBS_MS): Jobs {\n const jobs = new Map<number, Job & { client: Ps }>();\n const running = new Set<number>();\n const listeners = new Set<() => Promise<void>>();\n let timer: ReturnType<typeof setInterval> | undefined;\n const list = () => [...jobs.values()].map(({ pid, pgid, command }) => ({ pid, pgid, command }));\n const orphans = (table: readonly Row[], pgid: number, after: number) =>\n table.filter(\n (row) =>\n row.ppid === 1 &&\n row.pgid === pgid &&\n row.pid > after &&\n row.state !== 'Z' &&\n !running.has(row.pid) &&\n !jobs.has(row.pid)\n );\n const keep = (rows: readonly Row[], pgid: number, command: string, owner: Ps) => {\n for (const row of rows) jobs.set(row.pid, { pid: row.pid, pgid, command, client: owner });\n };\n const schedule = () => {\n const wanted = jobs.size > 0 && listeners.size > 0;\n if (wanted && !timer) timer = setInterval(() => void prune(), ms);\n if (!wanted && timer) {\n clearInterval(timer);\n timer = undefined;\n }\n };\n const changed = async () => {\n schedule();\n await Promise.all([...listeners].map((listener) => listener()));\n };\n const sweep = (owner: Ps, table: readonly Row[]) => {\n const alive = new Set(table.filter((row) => row.state !== 'Z').map((row) => row.pid));\n let gone = false;\n for (const [pid, job] of [...jobs])\n if (job.client === owner && !alive.has(pid)) {\n gone = jobs.delete(pid);\n keep(orphans(table, job.pgid, pid), job.pgid, job.command, owner);\n }\n return gone;\n };\n async function prune(): Promise<void> {\n let gone = false;\n for (const each of new Set([...jobs.values()].map((job) => job.client))) {\n const table = await each.ps?.().catch(() => undefined);\n if (table && sweep(each, table)) gone = true;\n }\n if (gone) await changed();\n }\n async function survivors(owner: Ps, command: string, process: KernelProcess): Promise<void> {\n const table = await owner.ps?.().catch(() => undefined);\n if (!table) return;\n const found = orphans(table, process.pgid, process.pid);\n keep(found, process.pgid, shortCommand(command), owner);\n if (sweep(owner, table) || found.length) await changed();\n }\n return {\n async started(owner, command, process) {\n if (!owner.ps) return;\n running.add(process.pid);\n const ended = () => {\n running.delete(process.pid);\n return survivors(owner, command, process);\n };\n await process.exited.then(ended, ended).catch(() => undefined);\n },\n list,\n adopt(adopted) {\n for (const job of adopted) if (!jobs.has(job.pid)) jobs.set(job.pid, { ...job, client });\n schedule();\n },\n watch(listener) {\n listeners.add(listener);\n schedule();\n return () => {\n listeners.delete(listener);\n schedule();\n };\n },\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { redacted } from '../monitor/redact.ts';\nimport type { KernelClient, KernelProcess } from './client.ts';\n\nexport type Job = { pid: number; pgid: number; command: string };\n\nexport interface Jobs {\n started(client: KernelClient, command: string, process: KernelProcess): Promise<void>;\n list(): Job[];\n adopt(jobs: readonly Job[]): void;\n watch(listener: () => Promise<void>): () => void;\n}\n\nexport const JOBS_MS = 2000;\n\nexport const PS_MS = 2000;\n\ntype Ps = Pick<KernelClient, 'ps'>;\n\ntype Row = Awaited<ReturnType<NonNullable<KernelClient['ps']>>>[number];\n\nexport function shortCommand(command: string): string {\n const lines = command.trim().split('\\n');\n const first = redacted(lines[0] as string);\n return lines.length > 1 && !first.endsWith('\u2026') ? `${first} \u2026` : first;\n}\n\ntype Warn = (message: string) => void;\n\nconst quiet: Warn = () => undefined;\n\nexport function boundedPs(\n owner: Ps,\n warn: Warn,\n bound = PS_MS\n): Promise<readonly Row[] | undefined> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const stalled = new Promise<undefined>((resolve) => {\n timer = setTimeout(() => {\n warn(`slicc-agent: the process table read took over ${bound} ms, so it was skipped`);\n resolve(undefined);\n }, bound);\n });\n const read = Promise.resolve()\n .then(() => owner.ps?.())\n .catch((error: unknown) => {\n warn(`slicc-agent: the process table could not be read: ${String(error)}`);\n return undefined;\n });\n return Promise.race([read, stalled]).finally(() => clearTimeout(timer));\n}\n\nexport function backgroundJobs(\n client: Ps,\n ms = JOBS_MS,\n warn: Warn = (message) => console.warn(message),\n bound = PS_MS\n): Jobs {\n const jobs = new Map<number, Job & { client: Ps }>();\n const running = new Set<number>();\n const listeners = new Set<() => Promise<void>>();\n let timer: ReturnType<typeof setInterval> | undefined;\n const list = () => [...jobs.values()].map(({ pid, pgid, command }) => ({ pid, pgid, command }));\n const orphans = (table: readonly Row[], pgid: number, after: number) =>\n table.filter(\n (row) =>\n row.ppid === 1 &&\n row.pgid === pgid &&\n row.pid > after &&\n row.state !== 'Z' &&\n !running.has(row.pid) &&\n !jobs.has(row.pid)\n );\n const keep = (rows: readonly Row[], pgid: number, command: string, owner: Ps) => {\n for (const row of rows) jobs.set(row.pid, { pid: row.pid, pgid, command, client: owner });\n };\n const schedule = () => {\n const wanted = jobs.size > 0 && listeners.size > 0;\n if (wanted && !timer) timer = setInterval(() => void prune(), ms);\n if (!wanted && timer) {\n clearInterval(timer);\n timer = undefined;\n }\n };\n const changed = async () => {\n schedule();\n await Promise.all([...listeners].map((listener) => listener()));\n };\n const sweep = (owner: Ps, table: readonly Row[]) => {\n const alive = new Set(table.filter((row) => row.state !== 'Z').map((row) => row.pid));\n let gone = false;\n for (const [pid, job] of [...jobs])\n if (job.client === owner && !alive.has(pid)) {\n gone = jobs.delete(pid);\n keep(orphans(table, job.pgid, pid), job.pgid, job.command, owner);\n }\n return gone;\n };\n async function prune(): Promise<void> {\n let gone = false;\n for (const each of new Set([...jobs.values()].map((job) => job.client))) {\n const table = await boundedPs(each, quiet, bound);\n if (table && sweep(each, table)) gone = true;\n }\n if (gone) await changed();\n }\n async function survivors(owner: Ps, command: string, process: KernelProcess): Promise<void> {\n const table = await boundedPs(owner, warn, bound);\n if (!table) return;\n const found = orphans(table, process.pgid, process.pid);\n keep(found, process.pgid, shortCommand(command), owner);\n if (sweep(owner, table) || found.length) await changed();\n }\n return {\n async started(owner, command, process) {\n if (!owner.ps) return;\n running.add(process.pid);\n const ended = () => {\n running.delete(process.pid);\n return survivors(owner, command, process);\n };\n await process.exited.then(ended, ended).catch(() => undefined);\n },\n list,\n adopt(adopted) {\n for (const job of adopted) if (!jobs.has(job.pid)) jobs.set(job.pid, { ...job, client });\n schedule();\n },\n watch(listener) {\n listeners.add(listener);\n schedule();\n return () => {\n listeners.delete(listener);\n schedule();\n };\n },\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  defineDoc
3
3
  } from "@earendil-works/pi-durable";
4
+ import { shortCommand } from "../kernel/jobs.js";
4
5
  import { entryLick } from "./state.js";
5
6
  const LicksHostDoc = defineDoc({
6
7
  kind: "slicc.licks.host",
@@ -10,8 +11,8 @@ const LicksHostDoc = defineDoc({
10
11
  });
11
12
  const BOOT_CHANNELS = /* @__PURE__ */ new Set(["upgrade", "session-reload"]);
12
13
  const LISTED = 5;
13
- function bashCommands(entries, after) {
14
- const commands = [];
14
+ function bashCalls(entries, after) {
15
+ const calls = [];
15
16
  let reacting = false;
16
17
  for (const entry of entries) {
17
18
  if (entry.kind === "pi.user") {
@@ -19,14 +20,33 @@ function bashCommands(entries, after) {
19
20
  reacting = lick !== void 0 && BOOT_CHANNELS.has(lick.channel);
20
21
  }
21
22
  if (reacting || entry.kind !== "pi.assistant" || Number(entry.id) <= after) continue;
22
- const content = entry.model?.[0]?.content;
23
- for (const block of Array.isArray(content) ? content : []) {
23
+ const message = entry.model?.[0];
24
+ const at = typeof message?.timestamp === "number" ? message.timestamp : 0;
25
+ for (const block of Array.isArray(message?.content) ? message.content : []) {
24
26
  const command = block.arguments?.command;
25
27
  if (block.type === "toolCall" && block.name === "bash" && typeof command === "string")
26
- commands.push(command);
28
+ calls.push({ id: block.id, command, at });
27
29
  }
28
30
  }
29
- return commands;
31
+ return calls;
32
+ }
33
+ function bashCommands(entries, after) {
34
+ return bashCalls(entries, after).map((call) => call.command);
35
+ }
36
+ function ended(entries) {
37
+ const done = /* @__PURE__ */ new Set();
38
+ for (const entry of entries) {
39
+ if (entry.kind !== "pi.tool-result") continue;
40
+ const id = entry.model?.[0]?.toolCallId;
41
+ const codes = (entry.data?.diagnostics ?? []).map((diagnostic) => diagnostic.code);
42
+ if (typeof id === "string" && !codes.includes("interrupted")) done.add(id);
43
+ }
44
+ return done;
45
+ }
46
+ function interruptedCommands(entries, after, boot) {
47
+ const done = ended(entries);
48
+ const booted = (Number(boot) + 1) * 1e3;
49
+ return bashCalls(entries, after).filter((call) => call.at < booted && !(call.id !== void 0 && done.has(call.id))).map((call) => shortCommand(call.command));
30
50
  }
31
51
  function upgraded(from, to, upgrades) {
32
52
  return {
@@ -39,20 +59,32 @@ function upgraded(from, to, upgrades) {
39
59
  eventId: `${from}->${to}#${upgrades}`
40
60
  };
41
61
  }
42
- function backgroundList(jobs) {
43
- const commands = [...new Set(jobs.map((job) => job.command))];
62
+ function listed(all) {
63
+ const commands = [...new Set(all)];
44
64
  const more = commands.length - LISTED;
45
65
  return more > 0 ? [...commands.slice(-LISTED), `\u2026and ${more} more`] : commands;
46
66
  }
47
- function restarted(boot, jobs) {
67
+ function backgroundList(jobs) {
68
+ return listed(jobs.map((job) => job.command));
69
+ }
70
+ function restartBody(interrupted, jobs) {
71
+ const cut = listed(interrupted);
48
72
  const background = backgroundList(jobs);
73
+ const parts = [
74
+ ...cut.length ? [`Commands that were interrupted mid-run:
75
+ ${cut.join("\n")}`] : [],
76
+ ...background.length ? [`Background commands that are no longer running:
77
+ ${background.join("\n")}`] : []
78
+ ];
79
+ return parts.length ? parts.join("\n\n") : "Files are unchanged. Start again whatever you still need running.";
80
+ }
81
+ function restarted(boot, jobs, interrupted) {
49
82
  return {
50
83
  channel: "session-reload",
51
84
  source: "kernel",
52
85
  title: "The kernel restarted",
53
86
  text: "The page reloaded, so the kernel started fresh: processes from before the reload are gone.",
54
- body: background.length ? `Background commands that are no longer running:
55
- ${background.join("\n")}` : "Files are unchanged. Start again whatever you still need running.",
87
+ body: restartBody(interrupted, jobs),
56
88
  target: "cone",
57
89
  eventId: `boot:${boot}`
58
90
  };
@@ -67,7 +99,11 @@ async function bootLicks(harness, cone, deliver, facts, context, jobs, listen) {
67
99
  const before = state?.jobs ?? [];
68
100
  if (facts.boot !== null && state?.boot && facts.boot !== state.boot) {
69
101
  const commands = bashCommands(entries, state.mark);
70
- if (commands.length || before.length) await deliver(restarted(facts.boot, before), context);
102
+ if (commands.length || before.length)
103
+ await deliver(
104
+ restarted(facts.boot, before, interruptedCommands(entries, state.mark, facts.boot)),
105
+ context
106
+ );
71
107
  } else if (facts.boot !== null && facts.boot === state?.boot) jobs?.adopt(before);
72
108
  const { entries: after } = await cone.context(context);
73
109
  const mark = Number(after.at(-1)?.id ?? 0);
@@ -115,6 +151,8 @@ export {
115
151
  backgroundList,
116
152
  bashCommands,
117
153
  bootLicks,
118
- booting
154
+ booting,
155
+ interruptedCommands,
156
+ listed
119
157
  };
120
158
  //# sourceMappingURL=boot.js.map
@@ -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 type { Job, Jobs } 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 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 name?: string;\n arguments?: { command?: unknown };\n}\n\nexport function bashCommands(entries: readonly EntryRecord[], after: number): string[] {\n const commands: string[] = [];\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 content = (entry.model?.[0] as { content?: unknown } | undefined)?.content;\n for (const block of Array.isArray(content) ? (content as Block[]) : []) {\n const command = block.arguments?.command;\n if (block.type === 'toolCall' && block.name === 'bash' && typeof command === 'string')\n commands.push(command);\n }\n }\n return commands;\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 backgroundList(jobs: readonly Job[]): string[] {\n const commands = [...new Set(jobs.map((job) => job.command))];\n const more = commands.length - LISTED;\n return more > 0 ? [...commands.slice(-LISTED), `\u2026and ${more} more`] : commands;\n}\n\nfunction restarted(boot: string, jobs: readonly Job[]) {\n const background = backgroundList(jobs);\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: background.length\n ? `Background commands that are no longer running:\\n${background.join('\\n')}`\n : 'Files are unchanged. Start again whatever you still need running.',\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) await deliver(restarted(facts.boot, before), context);\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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",
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 { 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 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 codes = (\n (entry.data as { diagnostics?: { code?: unknown }[] } | undefined)?.diagnostics ?? []\n ).map((diagnostic) => diagnostic.code);\n if (typeof id === 'string' && !codes.includes('interrupted')) 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
  }
@@ -11,7 +11,11 @@ export interface Jobs {
11
11
  watch(listener: () => Promise<void>): () => void;
12
12
  }
13
13
  export declare const JOBS_MS = 2000;
14
+ export declare const PS_MS = 2000;
14
15
  type Ps = Pick<KernelClient, 'ps'>;
16
+ type Row = Awaited<ReturnType<NonNullable<KernelClient['ps']>>>[number];
15
17
  export declare function shortCommand(command: string): string;
16
- export declare function backgroundJobs(client: Ps, ms?: number): Jobs;
18
+ type Warn = (message: string) => void;
19
+ export declare function boundedPs(owner: Ps, warn: Warn, bound?: number): Promise<readonly Row[] | undefined>;
20
+ export declare function backgroundJobs(client: Ps, ms?: number, warn?: Warn, bound?: number): Jobs;
17
21
  export {};
@@ -1,6 +1,6 @@
1
1
  import type { Context } from '@earendil-works/chord';
2
2
  import { type Conversation, type EntryRecord, type Harness } from '@earendil-works/pi-durable';
3
- import type { Job, Jobs } from '../kernel/jobs.ts';
3
+ import { type Job, type Jobs } from '../kernel/jobs.ts';
4
4
  import type { Deliver } from './fswatch.ts';
5
5
  type HostState = {
6
6
  cron: Record<string, {
@@ -19,6 +19,8 @@ export interface BootFacts {
19
19
  }
20
20
  export declare const LISTED = 5;
21
21
  export declare function bashCommands(entries: readonly EntryRecord[], after: number): string[];
22
+ export declare function interruptedCommands(entries: readonly EntryRecord[], after: number, boot: string): string[];
23
+ export declare function listed(all: readonly string[]): string[];
22
24
  export declare function backgroundList(jobs: readonly Job[]): string[];
23
25
  export declare function bootLicks(harness: Harness, cone: Conversation, deliver: Deliver, facts: BootFacts, context: Context, jobs?: Jobs, listen?: () => void): Promise<void>;
24
26
  export declare function booting(harness: Harness, cone: () => Conversation, deliver: Deliver, jobs: Jobs | undefined): {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-agent",
3
- "version": "6.18.5",
3
+ "version": "6.18.6",
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,14 +65,14 @@
65
65
  "release": "semantic-release"
66
66
  },
67
67
  "devDependencies": {
68
- "@ai-ecoverse/slicc-kernel": "1.138.1",
68
+ "@ai-ecoverse/slicc-kernel": "1.140.0",
69
69
  "@ai-ecoverse/slicc-scrub": "1.0.0",
70
70
  "@ai-ecoverse/slicc-shared-web": "1.29.1",
71
71
  "@ai-ecoverse/slicc-spectrum": "1.103.3",
72
72
  "@ai-ecoverse/wasi-fnox": "1.39.0-6",
73
73
  "@ai-ecoverse/wasm-bash": "5.3.0-22",
74
74
  "@ai-ecoverse/wasm-coreutils": "9.12.0-12",
75
- "@ai-ecoverse/wasm-git": "2.55.0-17",
75
+ "@ai-ecoverse/wasm-git": "2.55.0-18",
76
76
  "@biomejs/biome": "2.5.15",
77
77
  "@types/node": "24.19.2",
78
78
  "esbuild": "0.28.2",