@ai-ecoverse/slicc-agent 3.25.2 → 3.27.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/gelatiere/index.ts"],
4
- "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { type Agents, live } from '../agents.ts';\nimport type { Licks } from '../licks/licks.ts';\nimport type { Roles } from '../roles/roles.ts';\nimport type { Scoops } from '../scoops/service.ts';\nimport { skillFromText } from '../skills/skills.ts';\nimport { fold, open, readStore, STORE, type Suggestion, validate, writeStore } from './store.ts';\n\nexport type Answer = { code: number; out: string };\n\nexport const ROLE = 'gelatiere';\nexport const SPRINKLE = 'suggestions';\nexport const PROCEDURE = 'packages/vfs-root/gelatiere/GELATIERE.md';\nexport const SCHEDULE = '0 3 * * *';\n\nexport interface GelatiereAttach {\n agents: Agents;\n licks: Licks;\n env: ExecutionEnv;\n home: string;\n scoops: Scoops;\n roles: (context: Context) => Promise<Roles>;\n assets?: (path: string) => Promise<string>;\n now?: () => number;\n}\n\nexport interface GelatiereRuntime {\n command(argv: readonly string[], caller: string | null, context: Context): Promise<Answer>;\n intercept(action: string, data: unknown, context: Context): Promise<boolean>;\n}\n\nexport const USAGE = `usage:\n gelatiere init start the gelatiere scoop and its nightly crontab line\n gelatiere run ask the gelatiere for a pass now\n gelatiere suggest <file> fold a JSON array of suggestions into the store\n gelatiere deliver lick each cone with the suggestions that are new for it\n gelatiere install <id> install a suggested skill with upskill into ~/.pi/agent/skills\n gelatiere list [--all] [--json]\n gelatiere dismiss <id> mark a suggestion as not wanted\n gelatiere status\n\nThe store is ${STORE}; the suggestions sprinkle shows it.\n`;\n\nfunction line(handle: string): string {\n return `${SCHEDULE} ${ROLE} scoop:${handle} Nightly pass: read ~/.pi/agent/GELATIERE.md and follow it.`;\n}\n\nconst JOB = new RegExp(`^((?:\\\\S+\\\\s+){4}\\\\S+|@\\\\w+)\\\\s+${ROLE}(?:\\\\s|$)`);\n\nexport function scheduleOf(entry: string): string | undefined {\n return JOB.exec(entry.trim())?.[1]?.replace(/\\s+/g, ' ');\n}\n\nfunction handleOf(agents: Agents): string | undefined {\n const found = Object.entries(agents.state().scoops).find(\n ([, record]) => record.role === ROLE && live(record)\n );\n return found?.[1].folder;\n}\n\nfunction row(item: Suggestion): string {\n const state = item.takenAt ? 'taken' : item.dismissedAt ? 'dismissed' : 'open';\n return `${item.id}\\t${item.kind}\\t${state}\\t${item.title}`;\n}\n\ntype G = {\n serial: <T>(work: () => Promise<T>) => Promise<T>;\n options: GelatiereAttach;\n now: () => number;\n crontab: string;\n procedure: string;\n load: (context: Context) => Promise<Suggestion[]>;\n mark: (id: string, field: 'dismissedAt' | 'takenAt', context: Context) => Promise<boolean>;\n};\n\nconst fail = (out: string): Answer => ({ code: 1, out: `gelatiere: ${out}\\n` });\n\nasync function schedule(g: G, handle: string, context: Context): Promise<void> {\n const { env, home } = g.options;\n const read = await env.readTextFile(g.crontab, context);\n const kept = (read.ok ? read.value : '')\n .split('\\n')\n .filter((entry) => entry.trim() && !scheduleOf(entry));\n await env.createDir(`${home}/.slicc`, { recursive: true }, context);\n await env.writeFile(g.crontab, `${[...kept, line(handle)].join('\\n')}\\n`, context);\n}\n\nasync function seed(g: G, context: Context): Promise<void> {\n const { env, assets } = g.options;\n const existing = await env.exists(g.procedure, context);\n if ((existing.ok && existing.value) || !assets) return;\n const text = await assets(PROCEDURE).catch(() => undefined);\n if (text !== undefined) await env.writeFile(g.procedure, text, context);\n}\n\nasync function init(g: G, context: Context): Promise<Answer> {\n const { options } = g;\n await seed(g, context);\n let handle = handleOf(options.agents);\n if (!handle) {\n const roles = await options.roles(context);\n const role = roles.roles.find((entry) => entry.name === ROLE);\n if (!role) return fail('the gelatiere role is missing');\n const spawned = await options.scoops.spawn(\n {\n cone: 'cone',\n kind: 'async',\n name: ROLE,\n role,\n prompts: [],\n target: null,\n fromAgent: false,\n request: `gelatiere-init-${g.now()}`,\n limits: roles.limits,\n },\n context\n );\n if (spawned.code !== 0) return { code: 1, out: `gelatiere: ${spawned.out}` };\n handle = spawned.out.trim();\n }\n await schedule(g, handle, context);\n return {\n code: 0,\n out: `the gelatiere is scoop ${handle}; it runs at ${SCHEDULE} (crontab) and with gelatiere run, following ${g.procedure}\\n`,\n };\n}\n\nasync function run(g: G, context: Context): Promise<Answer> {\n const handle = handleOf(g.options.agents);\n if (!handle) return fail('there is no gelatiere yet; run gelatiere init');\n await g.options.licks.deliver(\n {\n channel: 'sprinkle',\n source: 'sprinkle:gelatiere',\n title: 'Gelatiere',\n text: 'run',\n body: `Run a pass now: read ${g.procedure} and follow it.`,\n target: `scoop:${handle}`,\n },\n context\n );\n return { code: 0, out: `asked scoop ${handle} for a pass\\n` };\n}\n\nasync function suggest(g: G, path: string | undefined, context: Context): Promise<Answer> {\n if (!path) return { code: 2, out: USAGE };\n const read = await g.options.env.readTextFile(path, context);\n if (!read.ok) return fail(`can't read ${path}`);\n let parsed: unknown;\n try {\n parsed = JSON.parse(read.value);\n } catch (error) {\n return fail(`${path} is not JSON (${(error as Error).message})`);\n }\n const folded = fold(await g.load(context), parsed, g.now());\n await writeStore(g.options.env, folded.store, context);\n const rejected = folded.rejected.map((reason) => `rejected ${reason}\\n`).join('');\n return {\n code: 0,\n out: `added ${folded.added}, updated ${folded.updated}, rejected ${folded.rejected.length}\\n${rejected}`,\n };\n}\n\nasync function deliver(g: G, context: Context): Promise<Answer> {\n const store = await g.load(context);\n const fresh = open(store).filter((item) => !item.deliveredAt);\n const cones = Object.keys(g.options.agents.state().cones);\n const byCone = new Map<string, Suggestion[]>();\n for (const item of fresh) {\n const named = (item.cones ?? []).filter((cone) => cones.includes(cone));\n for (const cone of named.length ? named : [g.options.agents.activeCone()])\n byCone.set(cone, [...(byCone.get(cone) ?? []), item]);\n }\n for (const [cone, items] of byCone) {\n const body = {\n added: items.length,\n open: open(store).length,\n suggestions: items.map(({ id, kind, title, body: text }) => ({\n id,\n kind,\n title,\n body: text,\n })),\n path: STORE,\n };\n await g.options.licks.deliver(\n {\n channel: 'sprinkle',\n source: `sprinkle:${SPRINKLE}`,\n title: 'Suggestions',\n text: 'gelatiere-suggestions',\n body: JSON.stringify(body, null, 2),\n target: `cone:${cone}`,\n },\n context\n );\n }\n for (const item of fresh) item.deliveredAt = g.now();\n if (fresh.length) await writeStore(g.options.env, store, context);\n return {\n code: 0,\n out: `delivered ${fresh.length} to ${[...byCone.keys()].join(', ') || 'nobody'}\\n`,\n };\n}\n\nexport const INSTALLER = 'https://raw.githubusercontent.com/ai-ecoverse/gh-upskill/main/install.sh';\n\nexport function upskillScript(repo: string, skill: string): string {\n return [\n 'bin=\"${PNPM_HOME:-$HOME/.local/share/pnpm}/bin\"',\n `{ command -v upskill >/dev/null 2>&1 || curl -fsSL ${INSTALLER} | bash -s -- --bin-dir \"$bin\"; }`,\n `upskill '${repo}' --skill '${skill}' --dest-path \"$HOME/.pi/agent/skills\"`,\n ].join(' && ');\n}\n\nasync function installed(env: ExecutionEnv, home: string, skill: string, context: Context) {\n const root = `${home}/.pi/agent/skills`;\n const listed = await env.listDir(root, context);\n for (const info of listed.ok ? listed.value : []) {\n const path = `${root}/${info.name}/SKILL.md`;\n const read = await env.readTextFile(path, context);\n if (read.ok && skillFromText(read.value, path, 'user').skills[0]?.name === skill) return path;\n }\n return undefined;\n}\n\nasync function install(g: G, id: string, context: Context): Promise<Answer> {\n const { env, home } = g.options;\n const item = (await g.load(context)).find((entry) => entry.id === id && entry.kind === 'skill');\n const checked = item ? validate(item) : `there is no skill suggestion ${id}`;\n if (typeof checked === 'string') return fail(checked);\n const { skill, repo } = checked as Required<Pick<Suggestion, 'skill' | 'repo'>>;\n const before = await installed(env, home, skill, context);\n if (before) return fail(`${skill} is already installed in ${before}`);\n const lines: string[] = [];\n const ran = await env.exec(\n upskillScript(repo, skill),\n { cwd: home, onOutput: (text) => void lines.push(text) },\n context\n );\n const output = lines.join('').trim().split('\\n').slice(-8).join('\\n');\n const path = await installed(env, home, skill, context);\n if (!ran.ok || ran.value.exitCode !== 0 || !path)\n return fail(\n `upskill couldn't install ${skill} from github.com/${repo}${output ? `:\\n${output}` : ''}`\n );\n await g.mark(id, 'takenAt', context);\n return {\n code: 0,\n out: `installed ${skill} in ${path}; it is /skill:${skill} once the skills reload\\n`,\n };\n}\n\nasync function list(g: G, argv: readonly string[], context: Context): Promise<Answer> {\n const store = await g.load(context);\n const shown = argv.includes('--all') ? store : open(store);\n if (argv.includes('--json')) return { code: 0, out: `${JSON.stringify(shown, null, 2)}\\n` };\n return { code: 0, out: shown.length ? `${shown.map(row).join('\\n')}\\n` : 'no suggestions\\n' };\n}\n\nasync function status(g: G, context: Context): Promise<Answer> {\n const { env, agents } = g.options;\n const store = await readStore(env, context);\n const handle = handleOf(agents);\n const read = await env.readTextFile(g.crontab, context);\n const scheduled = (read.ok ? read.value : '').split('\\n').map(scheduleOf).find(Boolean);\n const counts = store\n ? `${open(store).length} open, ${store.filter((item) => item.takenAt).length} taken, ${store.filter((item) => item.dismissedAt).length} dismissed`\n : 'none yet';\n const lines = [\n `scoop: ${handle ?? 'none (gelatiere init)'}`,\n `schedule: ${scheduled ?? 'none'}`,\n `store: ${counts}`,\n ];\n return { code: 0, out: `${lines.join('\\n')}\\n` };\n}\n\nasync function dismiss(g: G, id: string, context: Context): Promise<Answer> {\n return (await g.mark(id, 'dismissedAt', context))\n ? { code: 0, out: `dismissed ${id}\\n` }\n : fail(`there is no suggestion ${id}`);\n}\n\nconst VERBS: Record<string, (g: G, argv: readonly string[], context: Context) => Promise<Answer>> =\n {\n init: (g, _argv, context) => init(g, context),\n run: (g, _argv, context) => run(g, context),\n suggest: (g, argv, context) => g.serial(() => suggest(g, argv[1], context)),\n deliver: (g, _argv, context) => g.serial(() => deliver(g, context)),\n install: (g, argv, context) => install(g, argv[1] ?? '', context),\n list: (g, argv, context) => list(g, argv, context),\n status: (g, _argv, context) => status(g, context),\n dismiss: (g, argv, context) => dismiss(g, argv[1] ?? '', context),\n };\n\nexport function attachGelatiere(options: GelatiereAttach): GelatiereRuntime {\n const { env, home } = options;\n const now = options.now ?? Date.now;\n const load = async (context: Context) => (await readStore(env, context)) ?? [];\n let chain: Promise<unknown> = Promise.resolve();\n const serial = <T>(work: () => Promise<T>): Promise<T> => {\n const next = chain.catch(() => undefined).then(work);\n chain = next;\n return next;\n };\n const g: G = {\n serial,\n options,\n now,\n crontab: `${home}/.slicc/crontab`,\n procedure: `${home}/.pi/agent/GELATIERE.md`,\n load,\n mark: (id, field, context) =>\n serial(async () => {\n const store = await load(context);\n const item = store.find((entry) => entry.id === id);\n if (!item) return false;\n item[field] ??= now();\n await writeStore(env, store, context);\n return true;\n }),\n };\n return {\n command(argv, _caller, context) {\n const verb = argv[0] ?? 'help';\n const handler = VERBS[verb];\n if (handler) return handler(g, argv, context);\n return Promise.resolve({ code: verb === 'help' || verb === '--help' ? 0 : 2, out: USAGE });\n },\n async intercept(action, data, context) {\n const id = String((data as { id?: unknown } | null)?.id ?? '');\n if (action === 'gelatiere-dismiss') {\n await g.mark(id, 'dismissedAt', context);\n return true;\n }\n if (action === 'gelatiere-install' || action === 'gelatiere-try')\n await g.mark(id, 'takenAt', context);\n return false;\n },\n };\n}\n\nexport function lateGelatiere() {\n let runtime: GelatiereRuntime | undefined;\n return {\n async intercept(\n id: string,\n payload: { action: string; data: unknown },\n context: Context\n ): Promise<boolean> {\n return id === SPRINKLE && runtime !== undefined\n ? runtime.intercept(payload.action, payload.data, context)\n : false;\n },\n command(argv: readonly string[], caller: string | null, context: Context): Promise<Answer> {\n return runtime\n ? runtime.command(argv, caller, context)\n : Promise.resolve({ code: 1, out: 'gelatiere: not ready yet\\n' });\n },\n attach(options: GelatiereAttach): GelatiereRuntime {\n runtime = attachGelatiere(options);\n return runtime;\n },\n };\n}\n"],
5
- "mappings": "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",
6
- "names": []
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { type Agents, live } from '../agents.ts';\nimport type { Licks } from '../licks/licks.ts';\nimport type { Roles } from '../roles/roles.ts';\nimport type { Scoops } from '../scoops/service.ts';\nimport { skillFromText } from '../skills/skills.ts';\nimport {\n CATALOG_BASE,\n CATALOG_DIR,\n type Fetcher,\n merge,\n PROFILE,\n type Profile,\n parseSkills,\n parseUseCases,\n profileOf,\n type Sheet,\n score,\n sheet,\n slugifyCompany,\n} from './catalog.ts';\nimport { fold, open, readStore, STORE, type Suggestion, validate, writeStore } from './store.ts';\n\nexport type Answer = { code: number; out: string };\n\nexport const ROLE = 'gelatiere';\nexport const SPRINKLE = 'suggestions';\nexport const PROCEDURE = 'packages/vfs-root/gelatiere/GELATIERE.md';\nexport const SCHEDULE = '0 3 * * *';\n\nexport interface GelatiereAttach {\n agents: Agents;\n licks: Licks;\n env: ExecutionEnv;\n home: string;\n scoops: Scoops;\n roles: (context: Context) => Promise<Roles>;\n assets?: (path: string) => Promise<string>;\n now?: () => number;\n fetch?: Fetcher;\n catalog?: string;\n skills?: () => readonly { name: string }[];\n}\n\nexport interface GelatiereRuntime {\n command(argv: readonly string[], caller: string | null, context: Context): Promise<Answer>;\n intercept(action: string, data: unknown, context: Context): Promise<boolean>;\n}\n\nexport const USAGE = `usage:\n gelatiere init start the gelatiere scoop and its nightly crontab line\n gelatiere run ask the gelatiere for a pass now\n gelatiere suggest <file> fold a JSON array of suggestions into the store\n gelatiere deliver lick each cone with the suggestions that are new for it\n gelatiere catalog [--refresh] [--all] [--json]\n rank the www.sliccy.com skill and use-case catalog for this user\n gelatiere install <id> install a suggested skill with upskill into ~/.pi/agent/skills\n gelatiere list [--all] [--json]\n gelatiere dismiss <id> mark a suggestion as not wanted\n gelatiere status\n\nThe store is ${STORE}; the suggestions sprinkle shows it.\n`;\n\nfunction line(handle: string): string {\n return `${SCHEDULE} ${ROLE} scoop:${handle} Nightly pass: read ~/.pi/agent/GELATIERE.md and follow it.`;\n}\n\nconst JOB = new RegExp(`^((?:\\\\S+\\\\s+){4}\\\\S+|@\\\\w+)\\\\s+${ROLE}(?:\\\\s|$)`);\n\nexport function scheduleOf(entry: string): string | undefined {\n return JOB.exec(entry.trim())?.[1]?.replace(/\\s+/g, ' ');\n}\n\nfunction handleOf(agents: Agents): string | undefined {\n const found = Object.entries(agents.state().scoops).find(\n ([, record]) => record.role === ROLE && live(record)\n );\n return found?.[1].folder;\n}\n\nfunction row(item: Suggestion): string {\n const state = item.takenAt ? 'taken' : item.dismissedAt ? 'dismissed' : 'open';\n return `${item.id}\\t${item.kind}\\t${state}\\t${item.title}`;\n}\n\ntype G = {\n serial: <T>(work: () => Promise<T>) => Promise<T>;\n options: GelatiereAttach;\n now: () => number;\n crontab: string;\n procedure: string;\n load: (context: Context) => Promise<Suggestion[]>;\n mark: (id: string, field: 'dismissedAt' | 'takenAt', context: Context) => Promise<boolean>;\n};\n\nconst fail = (out: string): Answer => ({ code: 1, out: `gelatiere: ${out}\\n` });\n\nasync function schedule(g: G, handle: string, context: Context): Promise<void> {\n const { env, home } = g.options;\n const read = await env.readTextFile(g.crontab, context);\n const kept = (read.ok ? read.value : '')\n .split('\\n')\n .filter((entry) => entry.trim() && !scheduleOf(entry));\n await env.createDir(`${home}/.slicc`, { recursive: true }, context);\n await env.writeFile(g.crontab, `${[...kept, line(handle)].join('\\n')}\\n`, context);\n}\n\nasync function seed(g: G, context: Context): Promise<void> {\n const { env, assets } = g.options;\n const existing = await env.exists(g.procedure, context);\n if ((existing.ok && existing.value) || !assets) return;\n const text = await assets(PROCEDURE).catch(() => undefined);\n if (text !== undefined) await env.writeFile(g.procedure, text, context);\n}\n\nasync function init(g: G, context: Context): Promise<Answer> {\n const { options } = g;\n await seed(g, context);\n let handle = handleOf(options.agents);\n if (!handle) {\n const roles = await options.roles(context);\n const role = roles.roles.find((entry) => entry.name === ROLE);\n if (!role) return fail('the gelatiere role is missing');\n const spawned = await options.scoops.spawn(\n {\n cone: 'cone',\n kind: 'async',\n name: ROLE,\n role,\n prompts: [],\n target: null,\n fromAgent: false,\n request: `gelatiere-init-${g.now()}`,\n limits: roles.limits,\n },\n context\n );\n if (spawned.code !== 0) return { code: 1, out: `gelatiere: ${spawned.out}` };\n handle = spawned.out.trim();\n }\n await schedule(g, handle, context);\n return {\n code: 0,\n out: `the gelatiere is scoop ${handle}; it runs at ${SCHEDULE} (crontab) and with gelatiere run, following ${g.procedure}\\n`,\n };\n}\n\nasync function run(g: G, context: Context): Promise<Answer> {\n const handle = handleOf(g.options.agents);\n if (!handle) return fail('there is no gelatiere yet; run gelatiere init');\n await g.options.licks.deliver(\n {\n channel: 'sprinkle',\n source: 'sprinkle:gelatiere',\n title: 'Gelatiere',\n text: 'run',\n body: `Run a pass now: read ${g.procedure} and follow it.`,\n target: `scoop:${handle}`,\n },\n context\n );\n return { code: 0, out: `asked scoop ${handle} for a pass\\n` };\n}\n\nasync function suggest(g: G, path: string | undefined, context: Context): Promise<Answer> {\n if (!path) return { code: 2, out: USAGE };\n const read = await g.options.env.readTextFile(path, context);\n if (!read.ok) return fail(`can't read ${path}`);\n let parsed: unknown;\n try {\n parsed = JSON.parse(read.value);\n } catch (error) {\n return fail(`${path} is not JSON (${(error as Error).message})`);\n }\n const folded = fold(await g.load(context), parsed, g.now());\n await writeStore(g.options.env, folded.store, context);\n const rejected = folded.rejected.map((reason) => `rejected ${reason}\\n`).join('');\n return {\n code: 0,\n out: `added ${folded.added}, updated ${folded.updated}, rejected ${folded.rejected.length}\\n${rejected}`,\n };\n}\n\nasync function deliver(g: G, context: Context): Promise<Answer> {\n const store = await g.load(context);\n const fresh = open(store).filter((item) => !item.deliveredAt);\n const cones = Object.keys(g.options.agents.state().cones);\n const byCone = new Map<string, Suggestion[]>();\n for (const item of fresh) {\n const named = (item.cones ?? []).filter((cone) => cones.includes(cone));\n for (const cone of named.length ? named : [g.options.agents.activeCone()])\n byCone.set(cone, [...(byCone.get(cone) ?? []), item]);\n }\n for (const [cone, items] of byCone) {\n const body = {\n added: items.length,\n open: open(store).length,\n suggestions: items.map(({ id, kind, title, body: text }) => ({\n id,\n kind,\n title,\n body: text,\n })),\n path: STORE,\n };\n await g.options.licks.deliver(\n {\n channel: 'sprinkle',\n source: `sprinkle:${SPRINKLE}`,\n title: 'Suggestions',\n text: 'gelatiere-suggestions',\n body: JSON.stringify(body, null, 2),\n target: `cone:${cone}`,\n },\n context\n );\n }\n for (const item of fresh) item.deliveredAt = g.now();\n if (fresh.length) await writeStore(g.options.env, store, context);\n return {\n code: 0,\n out: `delivered ${fresh.length} to ${[...byCone.keys()].join(', ') || 'nobody'}\\n`,\n };\n}\n\nexport const INSTALLER = 'https://raw.githubusercontent.com/ai-ecoverse/gh-upskill/main/install.sh';\n\nexport function upskillScript(\n repo: string,\n skill: string | undefined,\n where: { path?: string; all?: boolean } = {}\n): string {\n const from = where.path ? ` --path '${where.path}'` : '';\n const which = where.all ? ' --all' : ` --skill '${skill}'`;\n return [\n 'bin=\"${PNPM_HOME:-$HOME/.local/share/pnpm}/bin\"',\n `{ command -v upskill >/dev/null 2>&1 || curl -fsSL ${INSTALLER} | bash -s -- --bin-dir \"$bin\"; }`,\n `upskill '${repo}'${from}${which} --dest-path \"$HOME/.pi/agent/skills\"`,\n ].join(' && ');\n}\n\nasync function skillsIn(\n env: ExecutionEnv,\n home: string,\n context: Context\n): Promise<Map<string, string>> {\n const root = `${home}/.pi/agent/skills`;\n const found = new Map<string, string>();\n const listed = await env.listDir(root, context);\n for (const info of listed.ok ? listed.value : []) {\n const path = `${root}/${info.name}/SKILL.md`;\n const read = await env.readTextFile(path, context);\n const name = read.ok ? skillFromText(read.value, path, 'user').skills[0]?.name : undefined;\n if (name) found.set(name, path);\n }\n return found;\n}\n\nasync function install(g: G, id: string, context: Context): Promise<Answer> {\n const { env, home } = g.options;\n const item = (await g.load(context)).find((entry) => entry.id === id && entry.kind === 'skill');\n const checked = item ? validate(item) : `there is no skill suggestion ${id}`;\n if (typeof checked === 'string') return fail(checked);\n const { skill, repo, path, all } = checked as Suggestion & { repo: string };\n const what = all ? `the skills${path ? ` in ${path}` : ''}` : (skill as string);\n const before = await skillsIn(env, home, context);\n const existing = skill ? before.get(skill) : undefined;\n if (!all && existing) return fail(`${skill} is already installed in ${existing}`);\n const lines: string[] = [];\n const ran = await env.exec(\n upskillScript(repo, skill, { ...(path ? { path } : {}), ...(all ? { all } : {}) }),\n { cwd: home, onOutput: (text) => void lines.push(text) },\n context\n );\n const output = lines.join('').trim().split('\\n').slice(-8).join('\\n');\n const after = await skillsIn(env, home, context);\n const added = all\n ? [...after].filter(([name, at]) => before.get(name) !== at)\n : [...after].filter(([name]) => name === skill);\n if (!ran.ok || ran.value.exitCode !== 0 || !added.length)\n return fail(\n `upskill couldn't install ${what} from github.com/${repo}${output ? `:\\n${output}` : ''}`\n );\n await g.mark(id, 'takenAt', context);\n const names = added.map(([name]) => name).sort();\n return {\n code: 0,\n out: all\n ? `installed ${names.join(', ')} in ${home}/.pi/agent/skills; ${names.map((name) => `/skill:${name}`).join(', ')} once the skills reload\\n`\n : `installed ${skill} in ${added[0]?.[1]}; it is /skill:${skill} once the skills reload\\n`,\n };\n}\n\ninterface Candidate {\n id: string;\n kind: 'skill' | 'use-case';\n title: string;\n body: string;\n evidence: string;\n score: number;\n skill?: string;\n repo?: string;\n path?: string;\n all?: boolean;\n prompt?: string;\n skills?: string[];\n}\n\nasync function profileFor(g: G, context: Context): Promise<Profile | undefined> {\n const read = await g.options.env.readTextFile(`${g.options.home}/${PROFILE}`, context);\n if (!read.ok) return undefined;\n try {\n return profileOf(JSON.parse(read.value));\n } catch {\n return undefined;\n }\n}\n\nfunction sourceLine(label: string, found: Sheet): string {\n const at = found.fetchedAt ? `, fetched ${new Date(found.fetchedAt).toISOString()}` : '';\n return `${label}: ${found.state} (${found.url}${at})`;\n}\n\nasync function catalog(g: G, argv: readonly string[], context: Context): Promise<Answer> {\n const { env, home } = g.options;\n const base = g.options.catalog ?? CATALOG_BASE;\n const fetcher: Fetcher = g.options.fetch ?? ((url, init) => globalThis.fetch(url, init));\n const options = { env, fetcher, now: g.now(), refresh: argv.includes('--refresh') };\n const profile = await profileFor(g, context);\n const slug = slugifyCompany(profile?.company);\n const company = slug && !['catalog', 'use-cases'].includes(slug) ? slug : null;\n const [global, own, cases] = await Promise.all([\n sheet(options, `${base}catalog.json`, `${CATALOG_DIR}/catalog.json`, context),\n company\n ? sheet(options, `${base}${company}.json`, `${CATALOG_DIR}/company-${company}.json`, context)\n : undefined,\n sheet(options, `${base}use-cases.json`, `${CATALOG_DIR}/use-cases.json`, context),\n ]);\n const store = await g.load(context);\n const closed = new Set(store.filter((item) => item.dismissedAt || item.takenAt).map((i) => i.id));\n const have = new Set([\n ...(await skillsIn(env, home, context)).keys(),\n ...(g.options.skills?.() ?? []).map((entry) => entry.name),\n ]);\n const evidence = (reasons: string[]) =>\n `www.sliccy.com catalog: ${reasons.length ? reasons.join(', ') : 'featured'}`;\n const skills: Candidate[] = merge(parseSkills(global.rows), parseSkills(own?.rows ?? []))\n .filter((entry) => !entry.skill || !have.has(entry.skill))\n .map((entry) => {\n const ranked = score(entry, profile);\n return {\n id: `catalog-${entry.name}`,\n kind: 'skill',\n title: entry.title,\n body: entry.description || entry.title,\n evidence: evidence(ranked.reasons),\n score: ranked.score,\n ...(entry.skill ? { skill: entry.skill } : {}),\n repo: entry.repo,\n ...(entry.path ? { path: entry.path } : {}),\n ...(entry.all ? { all: true } : {}),\n };\n });\n const uses: Candidate[] = parseUseCases(cases.rows).map((entry) => {\n const ranked = score(entry, profile);\n return {\n id: `catalog-use-${entry.name}`,\n kind: 'use-case',\n title: entry.title,\n body: entry.description || entry.title,\n evidence: evidence(ranked.reasons),\n score: ranked.score,\n prompt: entry.prompt,\n ...(entry.skills.length ? { skills: entry.skills } : {}),\n };\n });\n const all = argv.includes('--all');\n const ranked = [...skills, ...uses]\n .filter((item) => !closed.has(item.id) && (all || item.score > 0))\n .sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));\n const sources = [\n sourceLine('skills', global),\n ...(own ? [sourceLine(`company ${company}`, own)] : []),\n sourceLine('use cases', cases),\n ];\n const who = profile\n ? `profile: ${home}/${PROFILE} (${[profile.role, profile.purpose].filter(Boolean).join(', ') || 'empty'})`\n : `profile: none (${home}/${PROFILE}); ranked by boost only`;\n if (argv.includes('--json'))\n return {\n code: 0,\n out: `${JSON.stringify({ sources, profile: Boolean(profile), candidates: ranked }, null, 2)}\\n`,\n };\n const rows = ranked.map(\n (item) => `${item.score}\\t${item.id}\\t${item.kind}\\t${item.title}\\t${item.evidence}`\n );\n return {\n code: 0,\n out: `${[...sources, who, ...(rows.length ? rows : ['no catalog entries match'])].join('\\n')}\\n`,\n };\n}\n\nasync function list(g: G, argv: readonly string[], context: Context): Promise<Answer> {\n const store = await g.load(context);\n const shown = argv.includes('--all') ? store : open(store);\n if (argv.includes('--json')) return { code: 0, out: `${JSON.stringify(shown, null, 2)}\\n` };\n return { code: 0, out: shown.length ? `${shown.map(row).join('\\n')}\\n` : 'no suggestions\\n' };\n}\n\nasync function status(g: G, context: Context): Promise<Answer> {\n const { env, agents } = g.options;\n const store = await readStore(env, context);\n const handle = handleOf(agents);\n const read = await env.readTextFile(g.crontab, context);\n const scheduled = (read.ok ? read.value : '').split('\\n').map(scheduleOf).find(Boolean);\n const counts = store\n ? `${open(store).length} open, ${store.filter((item) => item.takenAt).length} taken, ${store.filter((item) => item.dismissedAt).length} dismissed`\n : 'none yet';\n const lines = [\n `scoop: ${handle ?? 'none (gelatiere init)'}`,\n `schedule: ${scheduled ?? 'none'}`,\n `store: ${counts}`,\n ];\n return { code: 0, out: `${lines.join('\\n')}\\n` };\n}\n\nasync function dismiss(g: G, id: string, context: Context): Promise<Answer> {\n return (await g.mark(id, 'dismissedAt', context))\n ? { code: 0, out: `dismissed ${id}\\n` }\n : fail(`there is no suggestion ${id}`);\n}\n\nconst VERBS: Record<string, (g: G, argv: readonly string[], context: Context) => Promise<Answer>> =\n {\n init: (g, _argv, context) => init(g, context),\n run: (g, _argv, context) => run(g, context),\n suggest: (g, argv, context) => g.serial(() => suggest(g, argv[1], context)),\n deliver: (g, _argv, context) => g.serial(() => deliver(g, context)),\n install: (g, argv, context) => install(g, argv[1] ?? '', context),\n catalog: (g, argv, context) => catalog(g, argv, context),\n list: (g, argv, context) => list(g, argv, context),\n status: (g, _argv, context) => status(g, context),\n dismiss: (g, argv, context) => dismiss(g, argv[1] ?? '', context),\n };\n\nexport function attachGelatiere(options: GelatiereAttach): GelatiereRuntime {\n const { env, home } = options;\n const now = options.now ?? Date.now;\n const load = async (context: Context) => (await readStore(env, context)) ?? [];\n let chain: Promise<unknown> = Promise.resolve();\n const serial = <T>(work: () => Promise<T>): Promise<T> => {\n const next = chain.catch(() => undefined).then(work);\n chain = next;\n return next;\n };\n const g: G = {\n serial,\n options,\n now,\n crontab: `${home}/.slicc/crontab`,\n procedure: `${home}/.pi/agent/GELATIERE.md`,\n load,\n mark: (id, field, context) =>\n serial(async () => {\n const store = await load(context);\n const item = store.find((entry) => entry.id === id);\n if (!item) return false;\n item[field] ??= now();\n await writeStore(env, store, context);\n return true;\n }),\n };\n return {\n command(argv, _caller, context) {\n const verb = argv[0] ?? 'help';\n const handler = VERBS[verb];\n if (handler) return handler(g, argv, context);\n return Promise.resolve({ code: verb === 'help' || verb === '--help' ? 0 : 2, out: USAGE });\n },\n async intercept(action, data, context) {\n const id = String((data as { id?: unknown } | null)?.id ?? '');\n if (action === 'gelatiere-dismiss') {\n await g.mark(id, 'dismissedAt', context);\n return true;\n }\n if (action === 'gelatiere-install' || action === 'gelatiere-try')\n await g.mark(id, 'takenAt', context);\n return false;\n },\n };\n}\n\nexport function lateGelatiere() {\n let runtime: GelatiereRuntime | undefined;\n return {\n async intercept(\n id: string,\n payload: { action: string; data: unknown },\n context: Context\n ): Promise<boolean> {\n return id === SPRINKLE && runtime !== undefined\n ? runtime.intercept(payload.action, payload.data, context)\n : false;\n },\n command(argv: readonly string[], caller: string | null, context: Context): Promise<Answer> {\n return runtime\n ? runtime.command(argv, caller, context)\n : Promise.resolve({ code: 1, out: 'gelatiere: not ready yet\\n' });\n },\n attach(options: GelatiereAttach): GelatiereRuntime {\n runtime = attachGelatiere(options);\n return runtime;\n },\n };\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["init", "ranked"]
7
7
  }
@@ -9,6 +9,7 @@ const ID = /^[a-z0-9][a-z0-9-]{0,63}$/;
9
9
  const CONE = /^[a-z0-9][a-z0-9._-]{0,63}$/;
10
10
  const REPO = /^[A-Za-z0-9][A-Za-z0-9-]{0,38}\/[A-Za-z0-9._-]{1,100}$/;
11
11
  const SKILL = /^[A-Za-z0-9][A-Za-z0-9 ._-]{0,79}$/;
12
+ const PATH = /^(?!.*\.\.)[A-Za-z0-9._/-]{1,200}$/;
12
13
  function text(value, limit) {
13
14
  if (typeof value !== "string") return void 0;
14
15
  const clean = redactSecrets(value.trim()).text;
@@ -21,11 +22,15 @@ function repoOf(source) {
21
22
  );
22
23
  return found ? `${found[1]}/${found[2]}` : void 0;
23
24
  }
24
- function origin(skill, repo) {
25
+ function origin(skill, repo, where) {
26
+ if (where.all)
27
+ return `all skills${where.path ? ` in ${where.path}` : ""} from github.com/${repo}`;
25
28
  return `${skill} from github.com/${repo}`;
26
29
  }
27
- function kindProblem(kind, skill, repo, prompt) {
28
- if (kind === "skill" && (!skill || !SKILL.test(skill)))
30
+ function kindProblem(kind, skill, repo, prompt, where = {}) {
31
+ if (kind === "skill" && where.path !== void 0 && !PATH.test(where.path))
32
+ return "a skill path must be a plain folder in the repo";
33
+ if (kind === "skill" && !where.all && (!skill || !SKILL.test(skill)))
29
34
  return "a skill needs its name (letters, digits, spaces, . _ -)";
30
35
  if (kind === "skill" && (!repo || !REPO.test(repo)))
31
36
  return "a skill needs its repo, a GitHub owner/repo";
@@ -52,9 +57,16 @@ function validate(candidate) {
52
57
  const cones = Array.isArray(item.cones) ? item.cones.filter((cone) => typeof cone === "string") : [];
53
58
  const evidence = text(item.evidence, 300);
54
59
  const prompt = text(item.prompt, 1e3);
55
- const problem = kindProblem(item.kind, skill, repo, prompt) ?? shapeProblem(url, cones);
60
+ const path = typeof item.path === "string" && item.path.trim() ? item.path.trim() : void 0;
61
+ const where = { ...path ? { path } : {}, ...item.all === true ? { all: true } : {} };
62
+ const problem = kindProblem(item.kind, skill, repo, prompt, where) ?? shapeProblem(url, cones);
56
63
  if (problem) return `${id}: ${problem}`;
57
- const installs = item.kind === "skill" ? { skill, repo, install: origin(skill, repo) } : {};
64
+ const installs = item.kind === "skill" ? {
65
+ ...where.all ? {} : { skill },
66
+ repo,
67
+ ...where,
68
+ install: origin(skill, repo, where)
69
+ } : {};
58
70
  return {
59
71
  id,
60
72
  kind: item.kind,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/gelatiere/store.ts"],
4
- "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { redactSecrets } from '../memory/format.ts';\nimport { SUGGESTIONS_DIR, SUGGESTIONS_STORE } from '../sprinkles/sprinkles.ts';\n\nexport const STORE_DIR = SUGGESTIONS_DIR;\nexport const STORE = SUGGESTIONS_STORE;\nexport const KINDS = ['skill', 'use-case', 'tip', 'skill-idea', 'issue'] as const;\nexport const MAX_OPEN = 50;\nconst PROMPTED = new Set<Kind>(['use-case', 'skill-idea', 'issue']);\n\nexport type Kind = (typeof KINDS)[number];\n\nexport interface Suggestion {\n id: string;\n kind: Kind;\n title: string;\n body: string;\n evidence?: string;\n skill?: string;\n repo?: string;\n install?: string;\n prompt?: string;\n url?: string;\n cones: string[];\n createdAt: number;\n deliveredAt?: number;\n dismissedAt?: number;\n takenAt?: number;\n}\n\nconst ID = /^[a-z0-9][a-z0-9-]{0,63}$/;\nconst CONE = /^[a-z0-9][a-z0-9._-]{0,63}$/;\nconst REPO = /^[A-Za-z0-9][A-Za-z0-9-]{0,38}\\/[A-Za-z0-9._-]{1,100}$/;\nconst SKILL = /^[A-Za-z0-9][A-Za-z0-9 ._-]{0,79}$/;\n\nfunction text(value: unknown, limit: number): string | undefined {\n if (typeof value !== 'string') return undefined;\n const clean = redactSecrets(value.trim()).text;\n return clean ? clean.slice(0, limit) : undefined;\n}\n\nexport function repoOf(source: unknown): string | undefined {\n if (typeof source !== 'string') return undefined;\n const found = /^https:\\/\\/raw\\.githubusercontent\\.com\\/([^/\\s]+)\\/([^/\\s]+)\\//.exec(\n source.trim()\n );\n return found ? `${found[1]}/${found[2]}` : undefined;\n}\n\nfunction origin(skill: string | undefined, repo: string | undefined): string {\n return `${skill} from github.com/${repo}`;\n}\n\nfunction kindProblem(\n kind: Kind,\n skill: string | undefined,\n repo: string | undefined,\n prompt: string | undefined\n): string | undefined {\n if (kind === 'skill' && (!skill || !SKILL.test(skill)))\n return 'a skill needs its name (letters, digits, spaces, . _ -)';\n if (kind === 'skill' && (!repo || !REPO.test(repo)))\n return 'a skill needs its repo, a GitHub owner/repo';\n if (PROMPTED.has(kind) && !prompt) return `a ${kind} needs its prompt`;\n return undefined;\n}\n\nfunction shapeProblem(url: string | undefined, cones: string[]): string | undefined {\n if (url !== undefined && !/^https:\\/\\/[^\\s\"'<>]+$/.test(url)) return 'url must be https';\n if (cones.some((cone) => !CONE.test(cone))) return 'cones must be cone ids';\n return undefined;\n}\n\nexport function validate(candidate: unknown): Suggestion | string {\n if (!candidate || typeof candidate !== 'object') return 'not an object';\n const item = candidate as Record<string, unknown>;\n const id = typeof item.id === 'string' ? item.id : '';\n if (!ID.test(id)) return `id \"${String(item.id)}\" must be 1\u201364 of a-z, 0-9 and -`;\n if (!KINDS.includes(item.kind as Kind)) return `${id}: kind must be one of ${KINDS.join(', ')}`;\n const title = text(item.title, 120);\n const body = text(item.body, 600);\n if (!title || !body) return `${id}: needs a title and a body`;\n const skill = typeof item.skill === 'string' ? item.skill.trim() : undefined;\n const repo = typeof item.repo === 'string' ? item.repo.trim() : repoOf(item.source);\n const url = typeof item.url === 'string' ? item.url.trim() : undefined;\n const cones = Array.isArray(item.cones)\n ? item.cones.filter((cone): cone is string => typeof cone === 'string')\n : [];\n const evidence = text(item.evidence, 300);\n const prompt = text(item.prompt, 1000);\n const problem = kindProblem(item.kind as Kind, skill, repo, prompt) ?? shapeProblem(url, cones);\n if (problem) return `${id}: ${problem}`;\n const installs = item.kind === 'skill' ? { skill, repo, install: origin(skill, repo) } : {};\n return {\n id,\n kind: item.kind as Kind,\n title,\n body,\n ...(evidence ? { evidence } : {}),\n ...installs,\n ...(prompt ? { prompt } : {}),\n ...(url ? { url } : {}),\n cones,\n createdAt: 0,\n };\n}\n\nexport function fold(\n store: readonly Suggestion[],\n candidates: unknown,\n now: number\n): { store: Suggestion[]; added: number; updated: number; rejected: string[] } {\n const next = store.map((item) => ({ ...item }));\n const rejected: string[] = [];\n let added = 0;\n let updated = 0;\n if (!Array.isArray(candidates))\n return { store: next, added, updated, rejected: ['not a JSON array'] };\n for (const candidate of candidates) {\n const checked = validate(candidate);\n if (typeof checked === 'string') {\n rejected.push(checked);\n continue;\n }\n const at = next.findIndex((item) => item.id === checked.id);\n const old = next[at];\n if (old) {\n if (old.dismissedAt || old.takenAt) continue;\n next[at] = { ...checked, createdAt: old.createdAt };\n updated++;\n } else if (open(next).length >= MAX_OPEN)\n rejected.push(`${checked.id}: ${MAX_OPEN} suggestions are already open`);\n else {\n next.push({ ...checked, createdAt: now });\n added++;\n }\n }\n return { store: next, added, updated, rejected };\n}\n\nexport function open(store: readonly Suggestion[]): Suggestion[] {\n return store.filter((item) => !item.dismissedAt && !item.takenAt);\n}\n\nexport async function readStore(\n env: Pick<ExecutionEnv, 'readTextFile'>,\n context: Context\n): Promise<Suggestion[] | null> {\n const read = await env.readTextFile(STORE, context);\n if (!read.ok) return null;\n try {\n const parsed = JSON.parse(read.value) as unknown;\n return Array.isArray(parsed)\n ? parsed.filter(\n (item): item is Suggestion =>\n Boolean(item) && typeof item === 'object' && typeof (item as Suggestion).id === 'string'\n )\n : [];\n } catch {\n return [];\n }\n}\n\nexport async function writeStore(\n env: Pick<ExecutionEnv, 'writeFile' | 'createDir'>,\n store: readonly Suggestion[],\n context: Context\n): Promise<void> {\n await env.createDir(STORE_DIR, { recursive: true }, context);\n const written = await env.writeFile(STORE, `${JSON.stringify(store, null, 2)}\\n`, context);\n if (!written.ok) throw new Error(written.error.message);\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { redactSecrets } from '../memory/format.ts';\nimport { SUGGESTIONS_DIR, SUGGESTIONS_STORE } from '../sprinkles/sprinkles.ts';\n\nexport const STORE_DIR = SUGGESTIONS_DIR;\nexport const STORE = SUGGESTIONS_STORE;\nexport const KINDS = ['skill', 'use-case', 'tip', 'skill-idea', 'issue'] as const;\nexport const MAX_OPEN = 50;\nconst PROMPTED = new Set<Kind>(['use-case', 'skill-idea', 'issue']);\n\nexport type Kind = (typeof KINDS)[number];\n\nexport interface Suggestion {\n id: string;\n kind: Kind;\n title: string;\n body: string;\n evidence?: string;\n skill?: string;\n repo?: string;\n path?: string;\n all?: boolean;\n install?: string;\n prompt?: string;\n url?: string;\n cones: string[];\n createdAt: number;\n deliveredAt?: number;\n dismissedAt?: number;\n takenAt?: number;\n}\n\nconst ID = /^[a-z0-9][a-z0-9-]{0,63}$/;\nconst CONE = /^[a-z0-9][a-z0-9._-]{0,63}$/;\nconst REPO = /^[A-Za-z0-9][A-Za-z0-9-]{0,38}\\/[A-Za-z0-9._-]{1,100}$/;\nconst SKILL = /^[A-Za-z0-9][A-Za-z0-9 ._-]{0,79}$/;\nconst PATH = /^(?!.*\\.\\.)[A-Za-z0-9._/-]{1,200}$/;\n\nfunction text(value: unknown, limit: number): string | undefined {\n if (typeof value !== 'string') return undefined;\n const clean = redactSecrets(value.trim()).text;\n return clean ? clean.slice(0, limit) : undefined;\n}\n\nexport function repoOf(source: unknown): string | undefined {\n if (typeof source !== 'string') return undefined;\n const found = /^https:\\/\\/raw\\.githubusercontent\\.com\\/([^/\\s]+)\\/([^/\\s]+)\\//.exec(\n source.trim()\n );\n return found ? `${found[1]}/${found[2]}` : undefined;\n}\n\nfunction origin(skill: string | undefined, repo: string | undefined, where: Where): string {\n if (where.all)\n return `all skills${where.path ? ` in ${where.path}` : ''} from github.com/${repo}`;\n return `${skill} from github.com/${repo}`;\n}\n\ntype Where = { path?: string; all?: boolean };\n\nfunction kindProblem(\n kind: Kind,\n skill: string | undefined,\n repo: string | undefined,\n prompt: string | undefined,\n where: Where = {}\n): string | undefined {\n if (kind === 'skill' && where.path !== undefined && !PATH.test(where.path))\n return 'a skill path must be a plain folder in the repo';\n if (kind === 'skill' && !where.all && (!skill || !SKILL.test(skill)))\n return 'a skill needs its name (letters, digits, spaces, . _ -)';\n if (kind === 'skill' && (!repo || !REPO.test(repo)))\n return 'a skill needs its repo, a GitHub owner/repo';\n if (PROMPTED.has(kind) && !prompt) return `a ${kind} needs its prompt`;\n return undefined;\n}\n\nfunction shapeProblem(url: string | undefined, cones: string[]): string | undefined {\n if (url !== undefined && !/^https:\\/\\/[^\\s\"'<>]+$/.test(url)) return 'url must be https';\n if (cones.some((cone) => !CONE.test(cone))) return 'cones must be cone ids';\n return undefined;\n}\n\nexport function validate(candidate: unknown): Suggestion | string {\n if (!candidate || typeof candidate !== 'object') return 'not an object';\n const item = candidate as Record<string, unknown>;\n const id = typeof item.id === 'string' ? item.id : '';\n if (!ID.test(id)) return `id \"${String(item.id)}\" must be 1\u201364 of a-z, 0-9 and -`;\n if (!KINDS.includes(item.kind as Kind)) return `${id}: kind must be one of ${KINDS.join(', ')}`;\n const title = text(item.title, 120);\n const body = text(item.body, 600);\n if (!title || !body) return `${id}: needs a title and a body`;\n const skill = typeof item.skill === 'string' ? item.skill.trim() : undefined;\n const repo = typeof item.repo === 'string' ? item.repo.trim() : repoOf(item.source);\n const url = typeof item.url === 'string' ? item.url.trim() : undefined;\n const cones = Array.isArray(item.cones)\n ? item.cones.filter((cone): cone is string => typeof cone === 'string')\n : [];\n const evidence = text(item.evidence, 300);\n const prompt = text(item.prompt, 1000);\n const path = typeof item.path === 'string' && item.path.trim() ? item.path.trim() : undefined;\n const where: Where = { ...(path ? { path } : {}), ...(item.all === true ? { all: true } : {}) };\n const problem =\n kindProblem(item.kind as Kind, skill, repo, prompt, where) ?? shapeProblem(url, cones);\n if (problem) return `${id}: ${problem}`;\n const installs =\n item.kind === 'skill'\n ? {\n ...(where.all ? {} : { skill }),\n repo,\n ...where,\n install: origin(skill, repo, where),\n }\n : {};\n return {\n id,\n kind: item.kind as Kind,\n title,\n body,\n ...(evidence ? { evidence } : {}),\n ...installs,\n ...(prompt ? { prompt } : {}),\n ...(url ? { url } : {}),\n cones,\n createdAt: 0,\n };\n}\n\nexport function fold(\n store: readonly Suggestion[],\n candidates: unknown,\n now: number\n): { store: Suggestion[]; added: number; updated: number; rejected: string[] } {\n const next = store.map((item) => ({ ...item }));\n const rejected: string[] = [];\n let added = 0;\n let updated = 0;\n if (!Array.isArray(candidates))\n return { store: next, added, updated, rejected: ['not a JSON array'] };\n for (const candidate of candidates) {\n const checked = validate(candidate);\n if (typeof checked === 'string') {\n rejected.push(checked);\n continue;\n }\n const at = next.findIndex((item) => item.id === checked.id);\n const old = next[at];\n if (old) {\n if (old.dismissedAt || old.takenAt) continue;\n next[at] = { ...checked, createdAt: old.createdAt };\n updated++;\n } else if (open(next).length >= MAX_OPEN)\n rejected.push(`${checked.id}: ${MAX_OPEN} suggestions are already open`);\n else {\n next.push({ ...checked, createdAt: now });\n added++;\n }\n }\n return { store: next, added, updated, rejected };\n}\n\nexport function open(store: readonly Suggestion[]): Suggestion[] {\n return store.filter((item) => !item.dismissedAt && !item.takenAt);\n}\n\nexport async function readStore(\n env: Pick<ExecutionEnv, 'readTextFile'>,\n context: Context\n): Promise<Suggestion[] | null> {\n const read = await env.readTextFile(STORE, context);\n if (!read.ok) return null;\n try {\n const parsed = JSON.parse(read.value) as unknown;\n return Array.isArray(parsed)\n ? parsed.filter(\n (item): item is Suggestion =>\n Boolean(item) && typeof item === 'object' && typeof (item as Suggestion).id === 'string'\n )\n : [];\n } catch {\n return [];\n }\n}\n\nexport async function writeStore(\n env: Pick<ExecutionEnv, 'writeFile' | 'createDir'>,\n store: readonly Suggestion[],\n context: Context\n): Promise<void> {\n await env.createDir(STORE_DIR, { recursive: true }, context);\n const written = await env.writeFile(STORE, `${JSON.stringify(store, null, 2)}\\n`, context);\n if (!written.ok) throw new Error(written.error.message);\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/main.js CHANGED
@@ -173,7 +173,10 @@ async function start(scope, port, options) {
173
173
  home: HOME,
174
174
  scoops: scoops.scoops,
175
175
  roles: runtime.roles,
176
- assets: options.assets ?? packageAssets()
176
+ assets: options.assets ?? packageAssets(),
177
+ fetch: scope.fetch,
178
+ skills: skillsRuntime.skills,
179
+ ...options.catalog ? { catalog: options.catalog } : {}
177
180
  });
178
181
  await memoryRuntime.reload(BACKGROUND_CONTEXT).catch(() => void 0);
179
182
  const changes = attachChanges({ agents: agent.agents, env: home, reloadMs: CHANGES_MS });
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 { 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 { setupLicks } from './licks/index.ts';\nimport { setupMemory } from './memory/index.ts';\nimport { type Transport, transportFetch } from './net.ts';\nimport { sliccPrompt } from './prompt.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 { kernelPort, serveConnections, type WorkerScope } from './worker.ts';\n\nexport interface AgentWorkerScope extends WorkerScope {\n fetch: typeof fetch;\n location: { origin: string };\n}\n\nexport type KernelAttach = (port: MessagePort) => Promise<KernelClient & { transport: Transport }>;\n\nexport interface AgentWorkerOptions {\n model?: ModelRef;\n providers?: Provider[];\n attach?: KernelAttach;\n credentials?: () => Promise<CredentialStore>;\n storage?: () => Promise<Storage>;\n assets?: Assets;\n codemodeWasm?: () => Promise<WebAssembly.Module | undefined>;\n codemodeWorker?: string | URL;\n}\n\nasync function start(\n scope: AgentWorkerScope,\n port: Promise<MessagePort>,\n options: AgentWorkerOptions\n): Promise<AgentHost> {\n const attach: KernelAttach = options.attach ?? attachKernel;\n const client = await attach(await port);\n const native = scope.fetch.bind(scope);\n scope.fetch = transportFetch(client.transport, scope.location.origin, native);\n const facts = {\n version: await agentVersion((url) => native(url)),\n commands: await commandsOnPath(client),\n transport: transportName(client.transport),\n 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 settings = await client.fs\n .readFile(`${HOME}/.pi/agent/settings.json`)\n .catch(() => undefined);\n const coding = CodingTools.tools as readonly ToolRegistration[];\n const codemode = disabledBySettings(\n settings === undefined ? undefined : new TextDecoder().decode(settings)\n )\n ? []\n : [\n codemodeTool({\n declared: coding,\n sandbox: sandboxFactory(\n options.codemodeWasm ?? (() => locateWasm(native, new URL(import.meta.url))),\n options.codemodeWorker\n ),\n }),\n ];\n registry.install(codemodeExtension(codemode));\n const licks = setupLicks(registry);\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 home = new SliccKernelEnv(client, { cwd: HOME });\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 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 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 sprinkle: sprinkles.command,\n memory: memoryRuntime.command,\n gelatiere: gelatiere.command,\n freezer: freezer.command,\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 });\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: await createAgentSettings(models, credentials, providers),\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 });\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, kernelPort(scope), options);\n host.catch(() => undefined);\n serveConnections(scope, host);\n return host;\n}\n"],
5
- "mappings": "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",
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 { 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 { setupLicks } from './licks/index.ts';\nimport { setupMemory } from './memory/index.ts';\nimport { type Transport, transportFetch } from './net.ts';\nimport { sliccPrompt } from './prompt.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 { kernelPort, serveConnections, type WorkerScope } from './worker.ts';\n\nexport interface AgentWorkerScope extends WorkerScope {\n fetch: typeof fetch;\n location: { origin: string };\n}\n\nexport type KernelAttach = (port: MessagePort) => Promise<KernelClient & { transport: Transport }>;\n\nexport interface AgentWorkerOptions {\n model?: ModelRef;\n providers?: Provider[];\n attach?: KernelAttach;\n credentials?: () => Promise<CredentialStore>;\n storage?: () => Promise<Storage>;\n assets?: Assets;\n codemodeWasm?: () => Promise<WebAssembly.Module | undefined>;\n codemodeWorker?: string | URL;\n catalog?: string;\n}\n\nasync function start(\n scope: AgentWorkerScope,\n port: Promise<MessagePort>,\n options: AgentWorkerOptions\n): Promise<AgentHost> {\n const attach: KernelAttach = options.attach ?? attachKernel;\n const client = await attach(await port);\n const native = scope.fetch.bind(scope);\n scope.fetch = transportFetch(client.transport, scope.location.origin, native);\n const facts = {\n version: await agentVersion((url) => native(url)),\n commands: await commandsOnPath(client),\n transport: transportName(client.transport),\n 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 settings = await client.fs\n .readFile(`${HOME}/.pi/agent/settings.json`)\n .catch(() => undefined);\n const coding = CodingTools.tools as readonly ToolRegistration[];\n const codemode = disabledBySettings(\n settings === undefined ? undefined : new TextDecoder().decode(settings)\n )\n ? []\n : [\n codemodeTool({\n declared: coding,\n sandbox: sandboxFactory(\n options.codemodeWasm ?? (() => locateWasm(native, new URL(import.meta.url))),\n options.codemodeWorker\n ),\n }),\n ];\n registry.install(codemodeExtension(codemode));\n const licks = setupLicks(registry);\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 home = new SliccKernelEnv(client, { cwd: HOME });\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 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 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 sprinkle: sprinkles.command,\n memory: memoryRuntime.command,\n gelatiere: gelatiere.command,\n freezer: freezer.command,\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: await createAgentSettings(models, credentials, providers),\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 });\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, kernelPort(scope), options);\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/owner.js CHANGED
@@ -2,7 +2,7 @@ import { connectAgent } from "./client.js";
2
2
  import { holdLock } from "./opfs-pool.js";
3
3
  const AGENT_LOCK = "slicc-agent";
4
4
  async function startAgent(options) {
5
- const release = await holdLock(options.locks ?? navigator.locks, AGENT_LOCK);
5
+ const release = options.held ? () => void 0 : await holdLock(options.locks ?? navigator.locks, AGENT_LOCK);
6
6
  const channel = options.channel ?? (() => new MessageChannel());
7
7
  const spawn = async () => {
8
8
  const created = options.worker();
@@ -24,14 +24,18 @@ async function startAgent(options) {
24
24
  release();
25
25
  throw error;
26
26
  }
27
+ const open = async () => {
28
+ const ready = await worker;
29
+ if (!ready) throw new Error("the agent was released");
30
+ const { port1, port2 } = channel();
31
+ ready.postMessage({ connect: port2 }, [port2]);
32
+ return port1;
33
+ };
27
34
  return {
28
35
  async connect() {
29
- const ready = await worker;
30
- if (!ready) throw new Error("the agent was released");
31
- const { port1, port2 } = channel();
32
- ready.postMessage({ connect: port2 }, [port2]);
33
- return connectAgent(port1);
36
+ return connectAgent(await open());
34
37
  },
38
+ port: open,
35
39
  restart() {
36
40
  worker = worker.catch(() => void 0).then((old) => {
37
41
  old?.terminate();
package/dist/owner.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/owner.ts"],
4
- "sourcesContent": ["import { type AgentConnection, connectAgent } from './client.ts';\nimport { holdLock } from './opfs-pool.ts';\nimport type { PortEndpoint } from './wire.ts';\n\nexport const AGENT_LOCK = 'slicc-agent';\n\nexport interface AgentWorker {\n postMessage(message: unknown, transfer: Transferable[]): void;\n terminate(): void;\n}\n\nexport interface AgentOwner {\n connect(): Promise<AgentConnection>;\n restart(): Promise<void>;\n release(): Promise<void>;\n}\n\nexport interface KernelSource {\n connect(): Promise<Transferable>;\n}\n\nexport interface OwnerOptions {\n worker: () => AgentWorker;\n kernel?: KernelSource;\n locks?: LockManager;\n channel?: () => { port1: PortEndpoint; port2: Transferable };\n}\n\nexport async function startAgent(options: OwnerOptions): Promise<AgentOwner> {\n const release = await holdLock(options.locks ?? navigator.locks, AGENT_LOCK);\n const channel = options.channel ?? (() => new MessageChannel());\n const spawn = async (): Promise<AgentWorker> => {\n const created = options.worker();\n try {\n if (options.kernel) {\n const port = await options.kernel.connect();\n created.postMessage({ kernel: port }, [port]);\n }\n } catch (error) {\n created.terminate();\n throw error;\n }\n return created;\n };\n let worker: Promise<AgentWorker | undefined> = Promise.resolve().then(spawn);\n try {\n await worker;\n } catch (error) {\n release();\n throw error;\n }\n return {\n async connect() {\n const ready = await worker;\n if (!ready) throw new Error('the agent was released');\n const { port1, port2 } = channel();\n ready.postMessage({ connect: port2 }, [port2]);\n return connectAgent(port1);\n },\n restart() {\n worker = worker\n .catch(() => undefined)\n .then((old) => {\n old?.terminate();\n return spawn();\n });\n return worker.then(() => undefined);\n },\n async release() {\n const previous = worker;\n worker = Promise.resolve(undefined);\n try {\n (await previous.catch(() => undefined))?.terminate();\n } finally {\n release();\n }\n },\n };\n}\n"],
5
- "mappings": "AAAA,SAA+B,oBAAoB;AACnD,SAAS,gBAAgB;AAGlB,MAAM,aAAa;AAwB1B,eAAsB,WAAW,SAA4C;AAC3E,QAAM,UAAU,MAAM,SAAS,QAAQ,SAAS,UAAU,OAAO,UAAU;AAC3E,QAAM,UAAU,QAAQ,YAAY,MAAM,IAAI,eAAe;AAC7D,QAAM,QAAQ,YAAkC;AAC9C,UAAM,UAAU,QAAQ,OAAO;AAC/B,QAAI;AACF,UAAI,QAAQ,QAAQ;AAClB,cAAM,OAAO,MAAM,QAAQ,OAAO,QAAQ;AAC1C,gBAAQ,YAAY,EAAE,QAAQ,KAAK,GAAG,CAAC,IAAI,CAAC;AAAA,MAC9C;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,UAAU;AAClB,YAAM;AAAA,IACR;AACA,WAAO;AAAA,EACT;AACA,MAAI,SAA2C,QAAQ,QAAQ,EAAE,KAAK,KAAK;AAC3E,MAAI;AACF,UAAM;AAAA,EACR,SAAS,OAAO;AACd,YAAQ;AACR,UAAM;AAAA,EACR;AACA,SAAO;AAAA,IACL,MAAM,UAAU;AACd,YAAM,QAAQ,MAAM;AACpB,UAAI,CAAC,MAAO,OAAM,IAAI,MAAM,wBAAwB;AACpD,YAAM,EAAE,OAAO,MAAM,IAAI,QAAQ;AACjC,YAAM,YAAY,EAAE,SAAS,MAAM,GAAG,CAAC,KAAK,CAAC;AAC7C,aAAO,aAAa,KAAK;AAAA,IAC3B;AAAA,IACA,UAAU;AACR,eAAS,OACN,MAAM,MAAM,MAAS,EACrB,KAAK,CAAC,QAAQ;AACb,aAAK,UAAU;AACf,eAAO,MAAM;AAAA,MACf,CAAC;AACH,aAAO,OAAO,KAAK,MAAM,MAAS;AAAA,IACpC;AAAA,IACA,MAAM,UAAU;AACd,YAAM,WAAW;AACjB,eAAS,QAAQ,QAAQ,MAAS;AAClC,UAAI;AACF,SAAC,MAAM,SAAS,MAAM,MAAM,MAAS,IAAI,UAAU;AAAA,MACrD,UAAE;AACA,gBAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { type AgentConnection, connectAgent } from './client.ts';\nimport { holdLock } from './opfs-pool.ts';\nimport type { PortEndpoint } from './wire.ts';\n\nexport const AGENT_LOCK = 'slicc-agent';\n\nexport interface AgentWorker {\n postMessage(message: unknown, transfer: Transferable[]): void;\n terminate(): void;\n}\n\nexport interface AgentOwner {\n connect(): Promise<AgentConnection>;\n port(): Promise<Transferable>;\n restart(): Promise<void>;\n release(): Promise<void>;\n}\n\nexport interface KernelSource {\n connect(): Promise<Transferable>;\n}\n\nexport interface OwnerOptions {\n worker: () => AgentWorker;\n kernel?: KernelSource;\n locks?: LockManager;\n held?: boolean;\n channel?: () => { port1: PortEndpoint & Transferable; port2: Transferable };\n}\n\nexport async function startAgent(options: OwnerOptions): Promise<AgentOwner> {\n const release = options.held\n ? () => undefined\n : await holdLock(options.locks ?? navigator.locks, AGENT_LOCK);\n const channel = options.channel ?? (() => new MessageChannel());\n const spawn = async (): Promise<AgentWorker> => {\n const created = options.worker();\n try {\n if (options.kernel) {\n const port = await options.kernel.connect();\n created.postMessage({ kernel: port }, [port]);\n }\n } catch (error) {\n created.terminate();\n throw error;\n }\n return created;\n };\n let worker: Promise<AgentWorker | undefined> = Promise.resolve().then(spawn);\n try {\n await worker;\n } catch (error) {\n release();\n throw error;\n }\n const open = async (): Promise<PortEndpoint & Transferable> => {\n const ready = await worker;\n if (!ready) throw new Error('the agent was released');\n const { port1, port2 } = channel();\n ready.postMessage({ connect: port2 }, [port2]);\n return port1;\n };\n return {\n async connect() {\n return connectAgent(await open());\n },\n port: open,\n restart() {\n worker = worker\n .catch(() => undefined)\n .then((old) => {\n old?.terminate();\n return spawn();\n });\n return worker.then(() => undefined);\n },\n async release() {\n const previous = worker;\n worker = Promise.resolve(undefined);\n try {\n (await previous.catch(() => undefined))?.terminate();\n } finally {\n release();\n }\n },\n };\n}\n"],
5
+ "mappings": "AAAA,SAA+B,oBAAoB;AACnD,SAAS,gBAAgB;AAGlB,MAAM,aAAa;AA0B1B,eAAsB,WAAW,SAA4C;AAC3E,QAAM,UAAU,QAAQ,OACpB,MAAM,SACN,MAAM,SAAS,QAAQ,SAAS,UAAU,OAAO,UAAU;AAC/D,QAAM,UAAU,QAAQ,YAAY,MAAM,IAAI,eAAe;AAC7D,QAAM,QAAQ,YAAkC;AAC9C,UAAM,UAAU,QAAQ,OAAO;AAC/B,QAAI;AACF,UAAI,QAAQ,QAAQ;AAClB,cAAM,OAAO,MAAM,QAAQ,OAAO,QAAQ;AAC1C,gBAAQ,YAAY,EAAE,QAAQ,KAAK,GAAG,CAAC,IAAI,CAAC;AAAA,MAC9C;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,UAAU;AAClB,YAAM;AAAA,IACR;AACA,WAAO;AAAA,EACT;AACA,MAAI,SAA2C,QAAQ,QAAQ,EAAE,KAAK,KAAK;AAC3E,MAAI;AACF,UAAM;AAAA,EACR,SAAS,OAAO;AACd,YAAQ;AACR,UAAM;AAAA,EACR;AACA,QAAM,OAAO,YAAkD;AAC7D,UAAM,QAAQ,MAAM;AACpB,QAAI,CAAC,MAAO,OAAM,IAAI,MAAM,wBAAwB;AACpD,UAAM,EAAE,OAAO,MAAM,IAAI,QAAQ;AACjC,UAAM,YAAY,EAAE,SAAS,MAAM,GAAG,CAAC,KAAK,CAAC;AAC7C,WAAO;AAAA,EACT;AACA,SAAO;AAAA,IACL,MAAM,UAAU;AACd,aAAO,aAAa,MAAM,KAAK,CAAC;AAAA,IAClC;AAAA,IACA,MAAM;AAAA,IACN,UAAU;AACR,eAAS,OACN,MAAM,MAAM,MAAS,EACrB,KAAK,CAAC,QAAQ;AACb,aAAK,UAAU;AACf,eAAO,MAAM;AAAA,MACf,CAAC;AACH,aAAO,OAAO,KAAK,MAAM,MAAS;AAAA,IACpC;AAAA,IACA,MAAM,UAAU;AACd,YAAM,WAAW;AACjB,eAAS,QAAQ,QAAQ,MAAS;AAClC,UAAI;AACF,SAAC,MAAM,SAAS,MAAM,MAAM,MAAS,IAAI,UAAU;AAAA,MACrD,UAAE;AACA,gBAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -14,6 +14,7 @@ import {
14
14
  SUGGESTIONS_STORE,
15
15
  sprinkleFiles,
16
16
  WELCOME,
17
+ WELCOME_PROFILE,
17
18
  WELCOMED
18
19
  } from "./sprinkles.js";
19
20
  const USAGE = `usage:
@@ -164,9 +165,17 @@ function setupSprinkles() {
164
165
  const sprinkle = find(id);
165
166
  if (!sprinkle) return { delivered: false };
166
167
  if (await options.intercept?.(id, payload, using)) return { delivered: true };
167
- if (id === WELCOME && payload.action === "onboarding-complete")
168
+ if (id === WELCOME && payload.action === "onboarding-complete") {
168
169
  await env.writeFile(WELCOMED, `${(/* @__PURE__ */ new Date()).toISOString()}
169
170
  `, using);
171
+ if (payload.data && typeof payload.data === "object" && !Array.isArray(payload.data))
172
+ await env.writeFile(
173
+ WELCOME_PROFILE,
174
+ `${JSON.stringify(payload.data, null, 2)}
175
+ `,
176
+ using
177
+ );
178
+ }
170
179
  await licks.deliver(
171
180
  {
172
181
  channel: "sprinkle",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/sprinkles/index.ts"],
4
- "sourcesContent": ["import {\n type Context,\n type JsonValue,\n type ReplicatedState,\n replicatedState,\n} from '@earendil-works/chord';\nimport type { Harness } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv, FileWatcher } from '@earendil-works/pi-durable/env';\nimport { type Agents, live } from '../agents.ts';\nimport type { LickTarget } from '../licks/lick.ts';\nimport type { Licks } from '../licks/licks.ts';\nimport {\n describe,\n homePath,\n NO_EXEC,\n SPRINKLE_KIND,\n SPRINKLES_DIR,\n type Sprinkle,\n type SprinkleMethod,\n SprinklesDoc,\n SUGGESTIONS,\n SUGGESTIONS_DIR,\n SUGGESTIONS_STORE,\n sprinkleFiles,\n WELCOME,\n WELCOMED,\n} from './sprinkles.ts';\n\nexport type Answer = { code: number; out: string };\n\nexport interface SprinklePayload {\n action: string;\n data: JsonValue | null;\n target: string | null;\n}\n\nexport interface SprinklesRuntime {\n readonly sprinkles: ReplicatedState<Sprinkle[]>;\n send(id: string, payload: SprinklePayload, context: Context): Promise<{ delivered: boolean }>;\n call(id: string, method: string, args: readonly unknown[], context: Context): Promise<JsonValue>;\n command(argv: readonly string[], caller: string | null, context: Context): Promise<Answer>;\n reload(context: Context): Promise<void>;\n close(context: Context): Promise<void>;\n}\n\nexport interface SprinklesAttach {\n harness: Harness;\n agents: Agents;\n licks: Licks;\n env: ExecutionEnv;\n assets?: (path: string) => Promise<string>;\n reloadMs: number;\n dir?: string;\n intercept?: (id: string, payload: SprinklePayload, context: Context) => Promise<boolean>;\n}\n\nexport const USAGE = `usage:\n sprinkle list every sprinkle in /home/sprinkles, with its owner\n sprinkle show <name> show a sprinkle in your chat\n sprinkle own <name> [<agent>] send its messages to <agent> (cone, cone-<n> or a scoop's handle)\n\nA sprinkle is an .shtml file in /home/sprinkles, as <name>.shtml or <name>/<name>.shtml.\nEvery one of them has a button in the rail; edit the file and the open panel reloads.\n`;\n\nconst RETIRED: Record<string, string> = {\n open: 'every sprinkle in /home/sprinkles already has a button in the rail; sprinkle show <name> shows one in the chat',\n reload: 'editing the .shtml file reloads the sprinkle',\n close: 'delete the .shtml file to retire a sprinkle; the user closes panels',\n send: 'a sprinkle reads data with slicc.readFile() or keeps it with slicc.setState(); write the file it reads',\n chat: 'show the sprinkle with sprinkle show <name> and wait for its lick',\n};\n\nfunction targetOf(agentId: string): LickTarget {\n return agentId.startsWith('scoop:') ? (agentId as LickTarget) : `cone:${agentId}`;\n}\n\nfunction agentOf(caller: string | null, agents: Agents): string {\n if (caller?.startsWith('cone:')) return caller.slice('cone:'.length);\n return caller ?? agents.activeCone();\n}\n\nfunction named(agents: Agents, value: string): string | undefined {\n const state = agents.state();\n if (value in state.cones) return value;\n const id = value.startsWith('scoop:') ? value : `scoop:${value}`;\n return id in state.scoops ? id : undefined;\n}\n\nexport function ownerOf(agents: Agents, owner: string | undefined): string {\n const state = agents.state();\n if (owner && state.cones[owner]) return owner;\n const scoop = owner ? state.scoops[owner] : undefined;\n return scoop && live(scoop) ? (owner as string) : agents.activeCone();\n}\n\nfunction entry(name: string, html: string, agentId: string): Sprinkle {\n return { id: name, name, ...describe(name, html), agentId, html };\n}\n\nfunction reloader(\n options: SprinklesAttach,\n dir: string,\n builtins: { welcome: string | undefined; suggestions: string | undefined },\n sprinkles: ReplicatedState<Sprinkle[]> & { replace(context: Context, value: Sprinkle[]): void },\n owners: (context: Context) => Promise<Record<string, string>>\n) {\n const { env, agents } = options;\n const { welcome, suggestions } = builtins;\n return async (using: Context) => {\n const owned = await owners(using);\n const stored =\n suggestions === undefined ? undefined : await env.exists(SUGGESTIONS_STORE, using);\n const offered = stored?.ok === true && stored.value;\n const reserved = new Set([WELCOME, ...(offered ? [SUGGESTIONS] : [])]);\n const out: Sprinkle[] = [];\n for (const { name, path } of await sprinkleFiles(env, dir, using)) {\n if (reserved.has(name)) continue;\n const read = await env.readTextFile(path, using);\n if (read.ok) out.push(entry(name, read.value, ownerOf(agents, owned[name])));\n }\n if (offered)\n out.push(entry(SUGGESTIONS, suggestions as string, ownerOf(agents, owned[SUGGESTIONS])));\n if (welcome !== undefined)\n out.push({\n ...entry(WELCOME, welcome, ownerOf(agents, owned[WELCOME] ?? 'cone')),\n inline: true,\n });\n sprinkles.replace(using, out);\n };\n}\n\nexport function setupSprinkles() {\n return {\n async attach(options: SprinklesAttach, context: Context): Promise<SprinklesRuntime> {\n const { harness, agents, licks, env } = options;\n const dir = options.dir ?? SPRINKLES_DIR;\n const sprinkles = replicatedState<Sprinkle[]>([]);\n const builtin = (name: string) =>\n options.assets\n ? options.assets(`packages/vfs-root/sprinkles/${name}.shtml`).catch(() => undefined)\n : Promise.resolve(undefined);\n const welcome = await builtin(WELCOME);\n const suggestions = await builtin(SUGGESTIONS);\n const owners = async (using: Context) =>\n (await harness.snapshot(SprinklesDoc, using))?.owners ?? {};\n const reload = reloader(options, dir, { welcome, suggestions }, sprinkles, owners);\n const inside = async (path: unknown, using: Context) => {\n const asked = homePath(path);\n const real = await env.canonicalPath(asked, using);\n return real.ok ? homePath(real.value) : asked;\n };\n const offCone = agents.onCone(() => reload(context).catch(() => undefined));\n const find = (id: string) => sprinkles.value.find((item) => item.id === id);\n const post = async (id: string, agentId: string, using: Context) => {\n const conversation = await agents.conversation(agentId, using);\n if (!conversation) throw new Error(`there is no agent ${agentId}`);\n await conversation.submit(\n { type: 'write', entry: { kind: SPRINKLE_KIND, data: { sprinkle: id, at: Date.now() } } },\n using\n );\n };\n const setOwner = async (name: string, agentId: string, using: Context) => {\n await harness.commit(async (tx) => {\n (await tx.doc(SprinklesDoc)).owners[name] = agentId;\n }, using);\n await reload(using);\n };\n await reload(context);\n const state = await harness.snapshot(SprinklesDoc, context);\n if (welcome !== undefined && !state?.welcomed) {\n await post(WELCOME, 'cone', context);\n await harness.commit(async (tx) => {\n (await tx.doc(SprinklesDoc)).welcomed = true;\n }, context);\n }\n let timer: ReturnType<typeof setTimeout> | undefined;\n const watched = await env.watch(\n [{ path: dir, recursive: true }, { path: SUGGESTIONS_DIR }],\n () => {\n clearTimeout(timer);\n timer = setTimeout(() => void reload(context).catch(() => undefined), options.reloadMs);\n },\n context\n );\n const watcher: FileWatcher | undefined = watched.ok ? watched.value : undefined;\n const commands: Record<\n string,\n (argv: readonly string[], caller: string | null, using: Context) => Promise<Answer>\n > = {\n async list(_argv, _caller, using) {\n await reload(using);\n const rows = sprinkles.value\n .filter((item) => !item.inline)\n .map((item) => `${item.name}\\t${item.title}\\t${item.agentId}`);\n return { code: 0, out: rows.length ? `${rows.join('\\n')}\\n` : 'no sprinkles\\n' };\n },\n async show(argv, caller, using) {\n await reload(using);\n const name = argv[1] ?? '';\n if (!find(name)) return { code: 1, out: `sprinkle: there is no sprinkle ${name}\\n` };\n const agentId = agentOf(caller, agents);\n await post(name, agentId, using);\n if (name !== SUGGESTIONS && !(await owners(using))[name])\n await setOwner(name, agentId, using);\n return { code: 0, out: `showed ${name} in the chat of ${agentId}\\n` };\n },\n async own(argv, caller, using) {\n const name = argv[1] ?? '';\n if (!find(name)) return { code: 1, out: `sprinkle: there is no sprinkle ${name}\\n` };\n const agentId = argv[2] ? named(agents, argv[2]) : agentOf(caller, agents);\n if (!agentId) return { code: 1, out: `sprinkle: there is no agent ${argv[2]}\\n` };\n await setOwner(name, agentId, using);\n return { code: 0, out: `${name} now talks to ${agentId}\\n` };\n },\n };\n return {\n sprinkles,\n async send(id, payload, using) {\n const sprinkle = find(id);\n if (!sprinkle) return { delivered: false };\n if (await options.intercept?.(id, payload, using)) return { delivered: true };\n if (id === WELCOME && payload.action === 'onboarding-complete')\n await env.writeFile(WELCOMED, `${new Date().toISOString()}\\n`, using);\n await licks.deliver(\n {\n channel: 'sprinkle',\n source: `sprinkle:${id}`,\n title: sprinkle.title,\n text: payload.action || '(no action)',\n ...(payload.data === null ? {} : { body: JSON.stringify(payload.data, null, 2) }),\n target: targetOf(ownerOf(agents, (await owners(using))[id])),\n },\n using\n );\n return { delivered: true };\n },\n async call(id, method, args, using) {\n if (!find(id)) throw new Error(`there is no sprinkle ${id}`);\n const handlers: Record<SprinkleMethod, () => Promise<JsonValue>> = {\n async readFile() {\n const read = await env.readTextFile(await inside(args[0], using), using);\n if (!read.ok) throw new Error(read.error.message);\n return read.value;\n },\n async exists() {\n const found = await env.exists(await inside(args[0], using), using);\n return found.ok && found.value;\n },\n async getState() {\n return (await harness.snapshot(SprinklesDoc, using))?.state[id] ?? null;\n },\n async setState() {\n await harness.commit(async (tx) => {\n (await tx.doc(SprinklesDoc)).state[id] = (args[0] ?? null) as JsonValue;\n }, using);\n return null;\n },\n };\n const handler = handlers[method as SprinkleMethod];\n if (!handler) throw new Error(NO_EXEC);\n return handler();\n },\n async command(argv, caller, using) {\n const verb = argv[0] ?? 'help';\n const run = commands[verb];\n if (run) return run(argv, caller, using);\n if (verb in RETIRED)\n return { code: 2, out: `sprinkle ${verb} isn't in SLICC: ${RETIRED[verb]}\\n` };\n return { code: verb === 'help' || verb === '--help' ? 0 : 2, out: USAGE };\n },\n reload,\n async close(using) {\n offCone();\n clearTimeout(timer);\n await watcher?.close(using);\n },\n };\n },\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n type Context,\n type JsonValue,\n type ReplicatedState,\n replicatedState,\n} from '@earendil-works/chord';\nimport type { Harness } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv, FileWatcher } from '@earendil-works/pi-durable/env';\nimport { type Agents, live } from '../agents.ts';\nimport type { LickTarget } from '../licks/lick.ts';\nimport type { Licks } from '../licks/licks.ts';\nimport {\n describe,\n homePath,\n NO_EXEC,\n SPRINKLE_KIND,\n SPRINKLES_DIR,\n type Sprinkle,\n type SprinkleMethod,\n SprinklesDoc,\n SUGGESTIONS,\n SUGGESTIONS_DIR,\n SUGGESTIONS_STORE,\n sprinkleFiles,\n WELCOME,\n WELCOME_PROFILE,\n WELCOMED,\n} from './sprinkles.ts';\n\nexport type Answer = { code: number; out: string };\n\nexport interface SprinklePayload {\n action: string;\n data: JsonValue | null;\n target: string | null;\n}\n\nexport interface SprinklesRuntime {\n readonly sprinkles: ReplicatedState<Sprinkle[]>;\n send(id: string, payload: SprinklePayload, context: Context): Promise<{ delivered: boolean }>;\n call(id: string, method: string, args: readonly unknown[], context: Context): Promise<JsonValue>;\n command(argv: readonly string[], caller: string | null, context: Context): Promise<Answer>;\n reload(context: Context): Promise<void>;\n close(context: Context): Promise<void>;\n}\n\nexport interface SprinklesAttach {\n harness: Harness;\n agents: Agents;\n licks: Licks;\n env: ExecutionEnv;\n assets?: (path: string) => Promise<string>;\n reloadMs: number;\n dir?: string;\n intercept?: (id: string, payload: SprinklePayload, context: Context) => Promise<boolean>;\n}\n\nexport const USAGE = `usage:\n sprinkle list every sprinkle in /home/sprinkles, with its owner\n sprinkle show <name> show a sprinkle in your chat\n sprinkle own <name> [<agent>] send its messages to <agent> (cone, cone-<n> or a scoop's handle)\n\nA sprinkle is an .shtml file in /home/sprinkles, as <name>.shtml or <name>/<name>.shtml.\nEvery one of them has a button in the rail; edit the file and the open panel reloads.\n`;\n\nconst RETIRED: Record<string, string> = {\n open: 'every sprinkle in /home/sprinkles already has a button in the rail; sprinkle show <name> shows one in the chat',\n reload: 'editing the .shtml file reloads the sprinkle',\n close: 'delete the .shtml file to retire a sprinkle; the user closes panels',\n send: 'a sprinkle reads data with slicc.readFile() or keeps it with slicc.setState(); write the file it reads',\n chat: 'show the sprinkle with sprinkle show <name> and wait for its lick',\n};\n\nfunction targetOf(agentId: string): LickTarget {\n return agentId.startsWith('scoop:') ? (agentId as LickTarget) : `cone:${agentId}`;\n}\n\nfunction agentOf(caller: string | null, agents: Agents): string {\n if (caller?.startsWith('cone:')) return caller.slice('cone:'.length);\n return caller ?? agents.activeCone();\n}\n\nfunction named(agents: Agents, value: string): string | undefined {\n const state = agents.state();\n if (value in state.cones) return value;\n const id = value.startsWith('scoop:') ? value : `scoop:${value}`;\n return id in state.scoops ? id : undefined;\n}\n\nexport function ownerOf(agents: Agents, owner: string | undefined): string {\n const state = agents.state();\n if (owner && state.cones[owner]) return owner;\n const scoop = owner ? state.scoops[owner] : undefined;\n return scoop && live(scoop) ? (owner as string) : agents.activeCone();\n}\n\nfunction entry(name: string, html: string, agentId: string): Sprinkle {\n return { id: name, name, ...describe(name, html), agentId, html };\n}\n\nfunction reloader(\n options: SprinklesAttach,\n dir: string,\n builtins: { welcome: string | undefined; suggestions: string | undefined },\n sprinkles: ReplicatedState<Sprinkle[]> & { replace(context: Context, value: Sprinkle[]): void },\n owners: (context: Context) => Promise<Record<string, string>>\n) {\n const { env, agents } = options;\n const { welcome, suggestions } = builtins;\n return async (using: Context) => {\n const owned = await owners(using);\n const stored =\n suggestions === undefined ? undefined : await env.exists(SUGGESTIONS_STORE, using);\n const offered = stored?.ok === true && stored.value;\n const reserved = new Set([WELCOME, ...(offered ? [SUGGESTIONS] : [])]);\n const out: Sprinkle[] = [];\n for (const { name, path } of await sprinkleFiles(env, dir, using)) {\n if (reserved.has(name)) continue;\n const read = await env.readTextFile(path, using);\n if (read.ok) out.push(entry(name, read.value, ownerOf(agents, owned[name])));\n }\n if (offered)\n out.push(entry(SUGGESTIONS, suggestions as string, ownerOf(agents, owned[SUGGESTIONS])));\n if (welcome !== undefined)\n out.push({\n ...entry(WELCOME, welcome, ownerOf(agents, owned[WELCOME] ?? 'cone')),\n inline: true,\n });\n sprinkles.replace(using, out);\n };\n}\n\nexport function setupSprinkles() {\n return {\n async attach(options: SprinklesAttach, context: Context): Promise<SprinklesRuntime> {\n const { harness, agents, licks, env } = options;\n const dir = options.dir ?? SPRINKLES_DIR;\n const sprinkles = replicatedState<Sprinkle[]>([]);\n const builtin = (name: string) =>\n options.assets\n ? options.assets(`packages/vfs-root/sprinkles/${name}.shtml`).catch(() => undefined)\n : Promise.resolve(undefined);\n const welcome = await builtin(WELCOME);\n const suggestions = await builtin(SUGGESTIONS);\n const owners = async (using: Context) =>\n (await harness.snapshot(SprinklesDoc, using))?.owners ?? {};\n const reload = reloader(options, dir, { welcome, suggestions }, sprinkles, owners);\n const inside = async (path: unknown, using: Context) => {\n const asked = homePath(path);\n const real = await env.canonicalPath(asked, using);\n return real.ok ? homePath(real.value) : asked;\n };\n const offCone = agents.onCone(() => reload(context).catch(() => undefined));\n const find = (id: string) => sprinkles.value.find((item) => item.id === id);\n const post = async (id: string, agentId: string, using: Context) => {\n const conversation = await agents.conversation(agentId, using);\n if (!conversation) throw new Error(`there is no agent ${agentId}`);\n await conversation.submit(\n { type: 'write', entry: { kind: SPRINKLE_KIND, data: { sprinkle: id, at: Date.now() } } },\n using\n );\n };\n const setOwner = async (name: string, agentId: string, using: Context) => {\n await harness.commit(async (tx) => {\n (await tx.doc(SprinklesDoc)).owners[name] = agentId;\n }, using);\n await reload(using);\n };\n await reload(context);\n const state = await harness.snapshot(SprinklesDoc, context);\n if (welcome !== undefined && !state?.welcomed) {\n await post(WELCOME, 'cone', context);\n await harness.commit(async (tx) => {\n (await tx.doc(SprinklesDoc)).welcomed = true;\n }, context);\n }\n let timer: ReturnType<typeof setTimeout> | undefined;\n const watched = await env.watch(\n [{ path: dir, recursive: true }, { path: SUGGESTIONS_DIR }],\n () => {\n clearTimeout(timer);\n timer = setTimeout(() => void reload(context).catch(() => undefined), options.reloadMs);\n },\n context\n );\n const watcher: FileWatcher | undefined = watched.ok ? watched.value : undefined;\n const commands: Record<\n string,\n (argv: readonly string[], caller: string | null, using: Context) => Promise<Answer>\n > = {\n async list(_argv, _caller, using) {\n await reload(using);\n const rows = sprinkles.value\n .filter((item) => !item.inline)\n .map((item) => `${item.name}\\t${item.title}\\t${item.agentId}`);\n return { code: 0, out: rows.length ? `${rows.join('\\n')}\\n` : 'no sprinkles\\n' };\n },\n async show(argv, caller, using) {\n await reload(using);\n const name = argv[1] ?? '';\n if (!find(name)) return { code: 1, out: `sprinkle: there is no sprinkle ${name}\\n` };\n const agentId = agentOf(caller, agents);\n await post(name, agentId, using);\n if (name !== SUGGESTIONS && !(await owners(using))[name])\n await setOwner(name, agentId, using);\n return { code: 0, out: `showed ${name} in the chat of ${agentId}\\n` };\n },\n async own(argv, caller, using) {\n const name = argv[1] ?? '';\n if (!find(name)) return { code: 1, out: `sprinkle: there is no sprinkle ${name}\\n` };\n const agentId = argv[2] ? named(agents, argv[2]) : agentOf(caller, agents);\n if (!agentId) return { code: 1, out: `sprinkle: there is no agent ${argv[2]}\\n` };\n await setOwner(name, agentId, using);\n return { code: 0, out: `${name} now talks to ${agentId}\\n` };\n },\n };\n return {\n sprinkles,\n async send(id, payload, using) {\n const sprinkle = find(id);\n if (!sprinkle) return { delivered: false };\n if (await options.intercept?.(id, payload, using)) return { delivered: true };\n if (id === WELCOME && payload.action === 'onboarding-complete') {\n await env.writeFile(WELCOMED, `${new Date().toISOString()}\\n`, using);\n if (payload.data && typeof payload.data === 'object' && !Array.isArray(payload.data))\n await env.writeFile(\n WELCOME_PROFILE,\n `${JSON.stringify(payload.data, null, 2)}\\n`,\n using\n );\n }\n await licks.deliver(\n {\n channel: 'sprinkle',\n source: `sprinkle:${id}`,\n title: sprinkle.title,\n text: payload.action || '(no action)',\n ...(payload.data === null ? {} : { body: JSON.stringify(payload.data, null, 2) }),\n target: targetOf(ownerOf(agents, (await owners(using))[id])),\n },\n using\n );\n return { delivered: true };\n },\n async call(id, method, args, using) {\n if (!find(id)) throw new Error(`there is no sprinkle ${id}`);\n const handlers: Record<SprinkleMethod, () => Promise<JsonValue>> = {\n async readFile() {\n const read = await env.readTextFile(await inside(args[0], using), using);\n if (!read.ok) throw new Error(read.error.message);\n return read.value;\n },\n async exists() {\n const found = await env.exists(await inside(args[0], using), using);\n return found.ok && found.value;\n },\n async getState() {\n return (await harness.snapshot(SprinklesDoc, using))?.state[id] ?? null;\n },\n async setState() {\n await harness.commit(async (tx) => {\n (await tx.doc(SprinklesDoc)).state[id] = (args[0] ?? null) as JsonValue;\n }, using);\n return null;\n },\n };\n const handler = handlers[method as SprinkleMethod];\n if (!handler) throw new Error(NO_EXEC);\n return handler();\n },\n async command(argv, caller, using) {\n const verb = argv[0] ?? 'help';\n const run = commands[verb];\n if (run) return run(argv, caller, using);\n if (verb in RETIRED)\n return { code: 2, out: `sprinkle ${verb} isn't in SLICC: ${RETIRED[verb]}\\n` };\n return { code: verb === 'help' || verb === '--help' ? 0 : 2, out: USAGE };\n },\n reload,\n async close(using) {\n offCone();\n clearTimeout(timer);\n await watcher?.close(using);\n },\n };\n },\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -7,6 +7,7 @@ const SUGGESTIONS = "suggestions";
7
7
  const SUGGESTIONS_DIR = "/home/.gelatiere";
8
8
  const SUGGESTIONS_STORE = `${SUGGESTIONS_DIR}/suggestions.json`;
9
9
  const WELCOMED = "/home/.welcomed";
10
+ const WELCOME_PROFILE = "/home/.welcome.json";
10
11
  const DEFAULT_ICON = "sparkles";
11
12
  const SprinklesDoc = defineDoc({
12
13
  kind: "slicc.sprinkles",
@@ -64,6 +65,7 @@ export {
64
65
  SprinklesDoc,
65
66
  WELCOME,
66
67
  WELCOMED,
68
+ WELCOME_PROFILE,
67
69
  describe,
68
70
  homePath,
69
71
  sprinkleFiles
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/sprinkles/sprinkles.ts"],
4
- "sourcesContent": ["import type { Context, JsonValue } from '@earendil-works/chord';\nimport { defineDoc } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { normalize } from '../kernel/paths.ts';\n\nexport { SPRINKLE_KIND, type Sprinkle } from './kind.ts';\n\nexport const SPRINKLES_DIR = '/home/sprinkles';\nexport const WELCOME = 'welcome';\nexport const SUGGESTIONS = 'suggestions';\nexport const SUGGESTIONS_DIR = '/home/.gelatiere';\nexport const SUGGESTIONS_STORE = `${SUGGESTIONS_DIR}/suggestions.json`;\nexport const WELCOMED = '/home/.welcomed';\nexport const DEFAULT_ICON = 'sparkles';\n\nexport type SprinkleMethod = 'readFile' | 'exists' | 'getState' | 'setState';\n\nexport const SprinklesDoc = defineDoc<{\n owners: Record<string, string>;\n state: Record<string, JsonValue>;\n welcomed: boolean;\n}>({\n kind: 'slicc.sprinkles',\n version: 1,\n scope: 'session',\n initial: () => ({ owners: {}, state: {}, welcomed: false }),\n});\n\nfunction decode(text: string): string {\n return text\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/&amp;/g, '&');\n}\n\nexport function describe(name: string, html: string): { title: string; icon: string } {\n const title = /<title>([\\s\\S]*?)<\\/title>/i.exec(html)?.[1]?.trim();\n const icon =\n /<link\\b[^>]*\\brel=[\"']icon[\"'][^>]*\\bhref=[\"']([^\"']+)[\"']/i.exec(html)?.[1] ??\n /<link\\b[^>]*\\bhref=[\"']([^\"']+)[\"'][^>]*\\brel=[\"']icon[\"']/i.exec(html)?.[1];\n return { title: title ? decode(title) : name, icon: icon ?? DEFAULT_ICON };\n}\n\nexport const NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/;\n\nexport async function sprinkleFiles(\n env: Pick<ExecutionEnv, 'listDir'>,\n dir: string,\n context: Context\n): Promise<{ name: string; path: string }[]> {\n const listed = await env.listDir(dir, context);\n if (!listed.ok) return [];\n const out = new Map<string, string>();\n for (const entry of [...listed.value].sort((a, b) => a.name.localeCompare(b.name))) {\n if (entry.kind !== 'directory' && entry.name.endsWith('.shtml')) {\n const name = entry.name.slice(0, -'.shtml'.length);\n if (NAME.test(name)) out.set(name, `${dir}/${entry.name}`);\n }\n if (entry.kind === 'directory' && NAME.test(entry.name) && !out.has(entry.name)) {\n const inner = await env.listDir(`${dir}/${entry.name}`, context);\n if (inner.ok && inner.value.some((item) => item.name === `${entry.name}.shtml`))\n out.set(entry.name, `${dir}/${entry.name}/${entry.name}.shtml`);\n }\n }\n return [...out].map(([name, path]) => ({ name, path }));\n}\n\nexport function homePath(path: unknown): string {\n if (typeof path !== 'string' || !path) throw new Error('a sprinkle reads files by absolute path');\n const mapped = path.startsWith('/shared/') ? `/home/${path.slice('/shared/'.length)}` : path;\n const segments = mapped.split('/');\n if (segments.includes('.') || segments.includes('..'))\n throw new Error(`a sprinkle reads paths without . or .. segments, not ${path}`);\n const resolved = normalize(mapped);\n if (resolved !== '/home' && !resolved.startsWith('/home/'))\n throw new Error(`a sprinkle can only read files in /home, not ${path}`);\n return resolved;\n}\n\nexport const NO_EXEC =\n \"sprinkles can't run commands in SLICC; send the agent a message with slicc.lick() and let it run them\";\n"],
5
- "mappings": "AACA,SAAS,iBAAiB;AAE1B,SAAS,iBAAiB;AAE1B,SAAS,qBAAoC;AAEtC,MAAM,gBAAgB;AACtB,MAAM,UAAU;AAChB,MAAM,cAAc;AACpB,MAAM,kBAAkB;AACxB,MAAM,oBAAoB,GAAG,eAAe;AAC5C,MAAM,WAAW;AACjB,MAAM,eAAe;AAIrB,MAAM,eAAe,UAIzB;AAAA,EACD,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS,OAAO,EAAE,QAAQ,CAAC,GAAG,OAAO,CAAC,GAAG,UAAU,MAAM;AAC3D,CAAC;AAED,SAAS,OAAO,MAAsB;AACpC,SAAO,KACJ,QAAQ,SAAS,GAAG,EACpB,QAAQ,SAAS,GAAG,EACpB,QAAQ,WAAW,GAAG,EACtB,QAAQ,UAAU,GAAG,EACrB,QAAQ,UAAU,GAAG;AAC1B;AAEO,SAAS,SAAS,MAAc,MAA+C;AACpF,QAAM,QAAQ,8BAA8B,KAAK,IAAI,IAAI,CAAC,GAAG,KAAK;AAClE,QAAM,OACJ,8DAA8D,KAAK,IAAI,IAAI,CAAC,KAC5E,8DAA8D,KAAK,IAAI,IAAI,CAAC;AAC9E,SAAO,EAAE,OAAO,QAAQ,OAAO,KAAK,IAAI,MAAM,MAAM,QAAQ,aAAa;AAC3E;AAEO,MAAM,OAAO;AAEpB,eAAsB,cACpB,KACA,KACA,SAC2C;AAC3C,QAAM,SAAS,MAAM,IAAI,QAAQ,KAAK,OAAO;AAC7C,MAAI,CAAC,OAAO,GAAI,QAAO,CAAC;AACxB,QAAM,MAAM,oBAAI,IAAoB;AACpC,aAAW,SAAS,CAAC,GAAG,OAAO,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC,GAAG;AAClF,QAAI,MAAM,SAAS,eAAe,MAAM,KAAK,SAAS,QAAQ,GAAG;AAC/D,YAAM,OAAO,MAAM,KAAK,MAAM,GAAG,CAAC,SAAS,MAAM;AACjD,UAAI,KAAK,KAAK,IAAI,EAAG,KAAI,IAAI,MAAM,GAAG,GAAG,IAAI,MAAM,IAAI,EAAE;AAAA,IAC3D;AACA,QAAI,MAAM,SAAS,eAAe,KAAK,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,IAAI,MAAM,IAAI,GAAG;AAC/E,YAAM,QAAQ,MAAM,IAAI,QAAQ,GAAG,GAAG,IAAI,MAAM,IAAI,IAAI,OAAO;AAC/D,UAAI,MAAM,MAAM,MAAM,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,GAAG,MAAM,IAAI,QAAQ;AAC5E,YAAI,IAAI,MAAM,MAAM,GAAG,GAAG,IAAI,MAAM,IAAI,IAAI,MAAM,IAAI,QAAQ;AAAA,IAClE;AAAA,EACF;AACA,SAAO,CAAC,GAAG,GAAG,EAAE,IAAI,CAAC,CAAC,MAAM,IAAI,OAAO,EAAE,MAAM,KAAK,EAAE;AACxD;AAEO,SAAS,SAAS,MAAuB;AAC9C,MAAI,OAAO,SAAS,YAAY,CAAC,KAAM,OAAM,IAAI,MAAM,yCAAyC;AAChG,QAAM,SAAS,KAAK,WAAW,UAAU,IAAI,SAAS,KAAK,MAAM,WAAW,MAAM,CAAC,KAAK;AACxF,QAAM,WAAW,OAAO,MAAM,GAAG;AACjC,MAAI,SAAS,SAAS,GAAG,KAAK,SAAS,SAAS,IAAI;AAClD,UAAM,IAAI,MAAM,wDAAwD,IAAI,EAAE;AAChF,QAAM,WAAW,UAAU,MAAM;AACjC,MAAI,aAAa,WAAW,CAAC,SAAS,WAAW,QAAQ;AACvD,UAAM,IAAI,MAAM,gDAAgD,IAAI,EAAE;AACxE,SAAO;AACT;AAEO,MAAM,UACX;",
4
+ "sourcesContent": ["import type { Context, JsonValue } from '@earendil-works/chord';\nimport { defineDoc } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { normalize } from '../kernel/paths.ts';\n\nexport { SPRINKLE_KIND, type Sprinkle } from './kind.ts';\n\nexport const SPRINKLES_DIR = '/home/sprinkles';\nexport const WELCOME = 'welcome';\nexport const SUGGESTIONS = 'suggestions';\nexport const SUGGESTIONS_DIR = '/home/.gelatiere';\nexport const SUGGESTIONS_STORE = `${SUGGESTIONS_DIR}/suggestions.json`;\nexport const WELCOMED = '/home/.welcomed';\nexport const WELCOME_PROFILE = '/home/.welcome.json';\nexport const DEFAULT_ICON = 'sparkles';\n\nexport type SprinkleMethod = 'readFile' | 'exists' | 'getState' | 'setState';\n\nexport const SprinklesDoc = defineDoc<{\n owners: Record<string, string>;\n state: Record<string, JsonValue>;\n welcomed: boolean;\n}>({\n kind: 'slicc.sprinkles',\n version: 1,\n scope: 'session',\n initial: () => ({ owners: {}, state: {}, welcomed: false }),\n});\n\nfunction decode(text: string): string {\n return text\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n .replace(/&amp;/g, '&');\n}\n\nexport function describe(name: string, html: string): { title: string; icon: string } {\n const title = /<title>([\\s\\S]*?)<\\/title>/i.exec(html)?.[1]?.trim();\n const icon =\n /<link\\b[^>]*\\brel=[\"']icon[\"'][^>]*\\bhref=[\"']([^\"']+)[\"']/i.exec(html)?.[1] ??\n /<link\\b[^>]*\\bhref=[\"']([^\"']+)[\"'][^>]*\\brel=[\"']icon[\"']/i.exec(html)?.[1];\n return { title: title ? decode(title) : name, icon: icon ?? DEFAULT_ICON };\n}\n\nexport const NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/;\n\nexport async function sprinkleFiles(\n env: Pick<ExecutionEnv, 'listDir'>,\n dir: string,\n context: Context\n): Promise<{ name: string; path: string }[]> {\n const listed = await env.listDir(dir, context);\n if (!listed.ok) return [];\n const out = new Map<string, string>();\n for (const entry of [...listed.value].sort((a, b) => a.name.localeCompare(b.name))) {\n if (entry.kind !== 'directory' && entry.name.endsWith('.shtml')) {\n const name = entry.name.slice(0, -'.shtml'.length);\n if (NAME.test(name)) out.set(name, `${dir}/${entry.name}`);\n }\n if (entry.kind === 'directory' && NAME.test(entry.name) && !out.has(entry.name)) {\n const inner = await env.listDir(`${dir}/${entry.name}`, context);\n if (inner.ok && inner.value.some((item) => item.name === `${entry.name}.shtml`))\n out.set(entry.name, `${dir}/${entry.name}/${entry.name}.shtml`);\n }\n }\n return [...out].map(([name, path]) => ({ name, path }));\n}\n\nexport function homePath(path: unknown): string {\n if (typeof path !== 'string' || !path) throw new Error('a sprinkle reads files by absolute path');\n const mapped = path.startsWith('/shared/') ? `/home/${path.slice('/shared/'.length)}` : path;\n const segments = mapped.split('/');\n if (segments.includes('.') || segments.includes('..'))\n throw new Error(`a sprinkle reads paths without . or .. segments, not ${path}`);\n const resolved = normalize(mapped);\n if (resolved !== '/home' && !resolved.startsWith('/home/'))\n throw new Error(`a sprinkle can only read files in /home, not ${path}`);\n return resolved;\n}\n\nexport const NO_EXEC =\n \"sprinkles can't run commands in SLICC; send the agent a message with slicc.lick() and let it run them\";\n"],
5
+ "mappings": "AACA,SAAS,iBAAiB;AAE1B,SAAS,iBAAiB;AAE1B,SAAS,qBAAoC;AAEtC,MAAM,gBAAgB;AACtB,MAAM,UAAU;AAChB,MAAM,cAAc;AACpB,MAAM,kBAAkB;AACxB,MAAM,oBAAoB,GAAG,eAAe;AAC5C,MAAM,WAAW;AACjB,MAAM,kBAAkB;AACxB,MAAM,eAAe;AAIrB,MAAM,eAAe,UAIzB;AAAA,EACD,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS,OAAO,EAAE,QAAQ,CAAC,GAAG,OAAO,CAAC,GAAG,UAAU,MAAM;AAC3D,CAAC;AAED,SAAS,OAAO,MAAsB;AACpC,SAAO,KACJ,QAAQ,SAAS,GAAG,EACpB,QAAQ,SAAS,GAAG,EACpB,QAAQ,WAAW,GAAG,EACtB,QAAQ,UAAU,GAAG,EACrB,QAAQ,UAAU,GAAG;AAC1B;AAEO,SAAS,SAAS,MAAc,MAA+C;AACpF,QAAM,QAAQ,8BAA8B,KAAK,IAAI,IAAI,CAAC,GAAG,KAAK;AAClE,QAAM,OACJ,8DAA8D,KAAK,IAAI,IAAI,CAAC,KAC5E,8DAA8D,KAAK,IAAI,IAAI,CAAC;AAC9E,SAAO,EAAE,OAAO,QAAQ,OAAO,KAAK,IAAI,MAAM,MAAM,QAAQ,aAAa;AAC3E;AAEO,MAAM,OAAO;AAEpB,eAAsB,cACpB,KACA,KACA,SAC2C;AAC3C,QAAM,SAAS,MAAM,IAAI,QAAQ,KAAK,OAAO;AAC7C,MAAI,CAAC,OAAO,GAAI,QAAO,CAAC;AACxB,QAAM,MAAM,oBAAI,IAAoB;AACpC,aAAW,SAAS,CAAC,GAAG,OAAO,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC,GAAG;AAClF,QAAI,MAAM,SAAS,eAAe,MAAM,KAAK,SAAS,QAAQ,GAAG;AAC/D,YAAM,OAAO,MAAM,KAAK,MAAM,GAAG,CAAC,SAAS,MAAM;AACjD,UAAI,KAAK,KAAK,IAAI,EAAG,KAAI,IAAI,MAAM,GAAG,GAAG,IAAI,MAAM,IAAI,EAAE;AAAA,IAC3D;AACA,QAAI,MAAM,SAAS,eAAe,KAAK,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,IAAI,MAAM,IAAI,GAAG;AAC/E,YAAM,QAAQ,MAAM,IAAI,QAAQ,GAAG,GAAG,IAAI,MAAM,IAAI,IAAI,OAAO;AAC/D,UAAI,MAAM,MAAM,MAAM,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,GAAG,MAAM,IAAI,QAAQ;AAC5E,YAAI,IAAI,MAAM,MAAM,GAAG,GAAG,IAAI,MAAM,IAAI,IAAI,MAAM,IAAI,QAAQ;AAAA,IAClE;AAAA,EACF;AACA,SAAO,CAAC,GAAG,GAAG,EAAE,IAAI,CAAC,CAAC,MAAM,IAAI,OAAO,EAAE,MAAM,KAAK,EAAE;AACxD;AAEO,SAAS,SAAS,MAAuB;AAC9C,MAAI,OAAO,SAAS,YAAY,CAAC,KAAM,OAAM,IAAI,MAAM,yCAAyC;AAChG,QAAM,SAAS,KAAK,WAAW,UAAU,IAAI,SAAS,KAAK,MAAM,WAAW,MAAM,CAAC,KAAK;AACxF,QAAM,WAAW,OAAO,MAAM,GAAG;AACjC,MAAI,SAAS,SAAS,GAAG,KAAK,SAAS,SAAS,IAAI;AAClD,UAAM,IAAI,MAAM,wDAAwD,IAAI,EAAE;AAChF,QAAM,WAAW,UAAU,MAAM;AACjC,MAAI,aAAa,WAAW,CAAC,SAAS,WAAW,QAAQ;AACvD,UAAM,IAAI,MAAM,gDAAgD,IAAI,EAAE;AACxE,SAAO;AACT;AAEO,MAAM,UACX;",
6
6
  "names": []
7
7
  }
@@ -20,6 +20,7 @@ export interface AgentConnection {
20
20
  readonly frozen?: ReplicatedState<FrozenCone[]>;
21
21
  readonly changes?: ReplicatedState<ChangesView>;
22
22
  readonly settings: AgentSettings;
23
+ readonly closed: Promise<Error | undefined>;
23
24
  prompt(text: string, whenBusy?: SendMode): Promise<string>;
24
25
  close(): Promise<void>;
25
26
  }