@ai-ecoverse/slicc-agent 5.5.2 → 5.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.js +2 -1
- package/dist/agent.js.map +2 -2
- package/dist/browser/guard.js +3 -18
- package/dist/browser/guard.js.map +2 -2
- package/dist/codemode/index.js +32 -34
- package/dist/codemode/index.js.map +2 -2
- package/dist/control.js +4 -11
- package/dist/control.js.map +2 -2
- package/dist/freezer/index.js +7 -22
- package/dist/freezer/index.js.map +2 -2
- package/dist/main.js +17 -7
- package/dist/main.js.map +2 -2
- package/dist/scoops/extension.js +33 -32
- package/dist/scoops/extension.js.map +2 -2
- package/dist/scoops/index.js +4 -2
- package/dist/scoops/index.js.map +2 -2
- package/dist/scoops/service.js +5 -0
- package/dist/scoops/service.js.map +2 -2
- package/dist/secrets/extension.js +0 -30
- package/dist/secrets/extension.js.map +2 -2
- package/dist/selection.js +74 -0
- package/dist/selection.js.map +7 -0
- package/dist/types/agent.d.ts +1 -0
- package/dist/types/browser/guard.d.ts +2 -2
- package/dist/types/codemode/index.d.ts +1 -1
- package/dist/types/scoops/extension.d.ts +2 -2
- package/dist/types/scoops/index.d.ts +4 -1
- package/dist/types/scoops/service.d.ts +5 -1
- package/dist/types/secrets/extension.d.ts +2 -4
- package/dist/types/selection.d.ts +15 -0
- package/package.json +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/scoops/extension.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n AssistantEntry,\n type ConversationId,\n defineExtension,\n defineTask,\n type Extension,\n type PromptInput,\n type Submission,\n section,\n type ToolRegistration,\n type Tx,\n wrapTool,\n} from '@earendil-works/pi-durable';\nimport { answerText } from '../agent.ts';\nimport { AgentsDoc, live, type ScoopRecord } from '../agents.ts';\nimport { HOME } from '../kernel/env.ts';\nimport { normalize, resolve } from '../kernel/paths.ts';\nimport { type Layout, LEGACY } from '../layout.ts';\nimport type { LickEvent } from '../licks/licks.ts';\nimport { FROM_KIND } from './from.ts';\nimport { type Feed, type ScoopsHost, ScoopWorkDoc } from './service.ts';\n\nexport const PREVIEW = 1000;\n\ntype Lookup = () => Promise<ScoopsHost>;\n\nexport function scoopFacts(\n record: Pick<ScoopRecord, 'name' | 'folder' | 'role'> & { cone?: string },\n layout: Layout = LEGACY\n): string {\n const place = layout.workspace(record.folder);\n return [\n `You are the scoop \"${record.name}\"${record.role ? ` (role: ${record.role})` : ''}, a helper agent working for a cone. Messages come from the cone or from the user at a terminal; your final answer goes back to whoever asked, so end every task with a complete answer.`,\n layout.users\n ? `You run as the user ${layout.scoopRoot(record.folder).slice('/home/'.length)}, and your home is ${place}: work there. Your cone can read your home; you can't read your cone's home or another scoop's. /tmp is shared scratch.`\n : `Work in ${place}. Your read, write and edit tools only reach ${layout.scoopRoot(record.folder)} and /tmp, and read ${HOME}.`,\n layout.users\n ? 'Your bash runs as your user too, so the kernel decides what it may read and change. Stay in your home, and change nothing outside it unless the request says so.'\n : 'Your bash is not confined: it sees the whole file system and every process. Stay in your folder anyway, and change nothing outside it unless the request says so.',\n 'You may run `agent` synchronously for a bounded side task (calls nest at most three deep), but you cannot start asynchronous scoops (`agent --async`, `subagent spawn`).',\n ].join('\\n');\n}\n\nfunction lines(text: string): number {\n return text ? text.split('\\n').length : 0;\n}\n\nexport type ReportResult = { text: string; failed: string | null; path: string | null };\n\nexport function reportLick(\n record: ScoopRecord,\n id: string,\n report: ReportResult,\n route: { target?: string | null; channel?: 'scoop-notify' | 'bash' } = {}\n): LickEvent {\n const status = report.failed ? (report.failed === 'aborted' ? 'stopped' : 'failed') : 'completed';\n const label = `${record.name} (${record.role ?? 'scoop'})`;\n const preview = report.text.length > PREVIEW ? `${report.text.slice(0, PREVIEW)}\u2026` : report.text;\n const where = report.path ? `Output: ${report.path} (${lines(report.text)} lines)` : null;\n const reason = report.failed && report.failed !== 'aborted' ? `Reason: ${report.failed}` : null;\n return {\n channel: route.channel ?? 'scoop-notify',\n source: id,\n title: label,\n text: `[scoop ${record.name} (${record.role ?? 'scoop'}) ${status}]`,\n body: [where, reason, preview].filter(Boolean).join('\\n\\n'),\n target: (route.target ?? `cone:${record.cone}`) as LickEvent['target'],\n ...(report.failed && report.failed !== 'aborted' ? { severity: 'warn' as const } : {}),\n };\n}\n\ntype Noting = {\n commit: (\n write: (\n tx: Tx\n ) => Promise<{ status: 'running'; checkpoint: { phase: 'deliver'; noted: true } }>,\n context: Context\n ) => Promise<unknown>;\n};\n\nexport async function noteWhenPlaced(\n runtime: Noting,\n submission: Pick<Submission, 'status'>,\n conversation: ConversationId,\n note: { from: string | undefined; agent?: boolean; noted: boolean | undefined },\n context: Context\n): Promise<void> {\n const { from } = note;\n while (from && !note.noted && !context.abortSignal?.aborted) {\n const record = await submission.status(context);\n if (record.status !== 'queued') {\n if (!('entry' in record) || record.entry === undefined) return;\n const entry = record.entry;\n await runtime\n .commit(async (tx) => {\n await tx.appendEntry(conversation, {\n kind: FROM_KIND,\n data: { from, entry, ...(note.agent ? { agent: true } : {}) },\n });\n return { status: 'running', checkpoint: { phase: 'deliver', noted: true } };\n }, context)\n .catch(() => undefined);\n return;\n }\n await new Promise((done) => setTimeout(done, NOTE_POLL_MS));\n }\n}\n\nexport const NOTE_POLL_MS = 100;\n\nexport function waitTask(lookup: Lookup) {\n type WaitInput = { scoops: string[]; cone: string; deadline: number };\n\n return defineTask<WaitInput, { phase: 'start' } | { phase: 'watch' }, null>({\n name: 'slicc.scoop-wait',\n version: 1,\n initial: () => ({ phase: 'start' }),\n phases: {\n async start(task, runtime, context) {\n await runtime.commit(async (tx) => {\n (await tx.doc(ScoopWorkDoc)).waits[String(task.id)] = {\n scoops: task.input.scoops,\n done: {},\n cone: task.input.cone,\n };\n return { status: 'running', checkpoint: { phase: 'watch' } };\n }, context);\n },\n async watch(task, runtime, context) {\n const { agents, licks } = await lookup();\n const key = String(task.id);\n for (;;) {\n const work = await runtime.snapshot(ScoopWorkDoc, context);\n const state = work?.waits[key];\n if (!state) break;\n const feeding = new Set(Object.values(work?.feeds ?? {}).map((feed) => feed.scoop));\n const open = state.scoops.filter((id) => !(id in state.done) && feeding.has(id));\n const expired = runtime.now() >= task.input.deadline;\n if (open.length && !expired) {\n await runtime.sleep(Math.min(runtime.now() + 300, task.input.deadline), context);\n continue;\n }\n const names = (id: string) => agents.state().scoops[id]?.name ?? id;\n const body = state.scoops.map((id) =>\n open.includes(id)\n ? `## ${names(id)}\\n(still working when the wait timed out)`\n : `## ${names(id)}\\n${state.done[id] ?? '(nothing was running)'}`\n );\n await licks.deliver(\n {\n channel: 'scoop-wait',\n source: key,\n title: `Waited for ${state.scoops.map(names).join(', ')}`,\n text: open.length\n ? `${state.scoops.length - open.length} of ${state.scoops.length} done; timed out waiting for ${open.map(names).join(', ')}`\n : `All ${state.scoops.length} done`,\n body: body.join('\\n\\n'),\n target: `cone:${state.cone}`,\n coalesce: false,\n eventId: `wait:${key}`,\n },\n context\n );\n break;\n }\n await runtime.commit(async (tx) => {\n delete (await tx.doc(ScoopWorkDoc)).waits[key];\n return { status: 'terminal', outcome: { status: 'completed', result: null } };\n }, context);\n },\n },\n abort: (task, runtime, context) =>\n runtime.commit(async (tx) => {\n delete (await tx.doc(ScoopWorkDoc)).waits[String(task.id)];\n return { status: 'terminal', outcome: { status: 'aborted' } };\n }, context),\n });\n}\n\nexport function scoopTasks(lookup: Lookup) {\n const anchor = defineTask<null, { phase: 'done' }, null>({\n name: 'slicc.scoop-anchor',\n version: 1,\n initial: () => ({ phase: 'done' }),\n phases: {\n done: (_task, runtime, context) =>\n runtime.commit(\n () => ({ status: 'terminal', outcome: { status: 'completed', result: null } }),\n context\n ),\n },\n abort: (_task, runtime, context) =>\n runtime.commit(() => ({ status: 'terminal', outcome: { status: 'aborted' } }), context),\n });\n\n type ReporterInput = {\n scoop: string;\n prompt: string;\n followUp?: boolean;\n request?: string;\n };\n type ReporterState =\n | { phase: 'deliver'; noted?: boolean }\n | { phase: 'report'; text: string; failed: string | null };\n\n const reporter = defineTask<ReporterInput, ReporterState, ReportResult>({\n name: 'slicc.scoop-reporter',\n version: 1,\n initial: () => ({ phase: 'deliver' }),\n phases: {\n async deliver(task, runtime, context) {\n const { agents } = await lookup();\n const record = agents.state().scoops[task.input.scoop];\n const handle =\n record && live(record)\n ? await runtime.conversation(record.conversation as ConversationId, context)\n : undefined;\n if (!handle) {\n await runtime.commit(async (tx) => {\n delete (await tx.doc(ScoopWorkDoc)).feeds[String(task.id)];\n return {\n status: 'terminal',\n outcome: {\n status: 'completed',\n result: { text: '', failed: 'the scoop is gone', path: null },\n },\n };\n }, context);\n return;\n }\n const requestId = task.input.request ? `subagent:${task.input.request}` : `feed:${task.id}`;\n const feed = (await runtime.snapshot(ScoopWorkDoc, context))?.feeds[String(task.id)];\n const submission = await handle.submit(\n {\n type: 'input',\n content: task.input.prompt,\n whenBusy: task.input.followUp ? 'followUp' : 'steer',\n requestId,\n },\n context\n );\n const noting = noteWhenPlaced(\n runtime,\n submission,\n record?.conversation as ConversationId,\n { from: feed?.from, agent: feed?.agent === true, noted: task.state.checkpoint.noted },\n context\n );\n const settled = await submission.wait(context);\n await noting;\n await runtime.commit(async (tx) => {\n if (settled.status !== 'done' || settled.type !== 'input')\n return {\n status: 'running',\n checkpoint: {\n phase: 'report',\n text: '',\n failed: String(settled.reason),\n },\n };\n const answer = await tx.entry(AssistantEntry, settled.answer);\n return {\n status: 'running',\n checkpoint: {\n phase: 'report',\n text: answerText(answer?.model?.[0]?.content),\n failed: null,\n },\n };\n }, context);\n },\n async report(task, runtime, context) {\n const { agents, licks, layout = LEGACY } = await lookup();\n const id = task.input.scoop;\n const record = agents.state().scoops[id];\n const { text, failed } = task.state.checkpoint;\n const path =\n record && text ? `${layout.reports(record.folder, record.cone)}/${task.id}.md` : null;\n if (path)\n await (await runtime.env(context))?.writeFile(\n path,\n text.endsWith('\\n') ? text : `${text}\\n`,\n context\n );\n const work = await runtime.snapshot(ScoopWorkDoc, context);\n const entry = work?.feeds[String(task.id)] as Feed;\n const waiting = (wait: { scoops: string[]; done: Record<string, string> }) =>\n wait.scoops.includes(id) && !(id in wait.done);\n const folded = Object.values(work?.waits ?? {}).some(waiting);\n const route = { target: entry.target, channel: entry.channel };\n if (!folded && record && entry.report && failed !== 'aborted')\n await licks.deliver(\n {\n ...reportLick(record, id, { text, failed, path }, route),\n eventId: `report:${task.id}`,\n },\n context\n );\n await runtime.commit(async (tx) => {\n const work = await tx.doc(ScoopWorkDoc);\n for (const wait of Object.values(work.waits))\n if (waiting(wait))\n wait.done[id] = failed\n ? `stopped: ${failed}`\n : `${path ? `Output: ${path}\\n` : ''}${text || '(no text)'}`;\n delete work.feeds[String(task.id)];\n return {\n status: 'terminal',\n outcome: { status: 'completed', result: { text, failed, path } },\n };\n }, context);\n },\n },\n abort: (task, runtime, context) =>\n runtime.commit(async (tx) => {\n delete (await tx.doc(ScoopWorkDoc)).feeds[String(task.id)];\n return { status: 'terminal', outcome: { status: 'aborted' } };\n }, context),\n });\n\n return { anchor, reporter, wait: waitTask(lookup) };\n}\n\nasync function whose(input: PromptInput, context: Context) {\n const state = await input.read.snapshot(AgentsDoc, context);\n const scoop = Object.values(state?.scoops ?? {}).find(\n (record) => record.conversation === input.conversationId\n );\n return { scoop };\n}\n\nexport function scoopsExtension(\n tasks: ReturnType<typeof scoopTasks>,\n layout: () => Layout = () => LEGACY\n): Extension {\n return defineExtension({\n name: 'slicc-scoops',\n tasks: [tasks.anchor, tasks.reporter, tasks.wait],\n sections: [\n section('scoop', async (input, context) => {\n const { scoop } = await whose(input, context);\n return scoop ? scoopFacts(scoop, layout()) : undefined;\n }),\n ],\n });\n}\n\nconst WRITES = new Set(['write', 'edit']);\n\nexport function allowed(\n record: Pick<ScoopRecord, 'folder'> & { roots?: ScoopRecord['roots'] },\n coneCwd: string,\n path: string,\n writing: boolean,\n reads: readonly string[] = [],\n own = LEGACY.scoopRoot(record.folder)\n): boolean {\n const write = [own, '/tmp', ...(record.roots?.write ?? [])];\n const roots = writing ? write : [...write, coneCwd, ...(record.roots?.read ?? []), ...reads];\n return roots.some((root) => path === root || path.startsWith(`${root === '/' ? '' : root}/`));\n}\n\nfunction refusal(value: string) {\n return { output: [{ type: 'text' as const, text: value }], isError: true };\n}\n\nexport function guardExtension(lookup: Lookup, tools: readonly ToolRegistration[]): Extension {\n const wraps = tools\n .filter((tool) => tool.name === 'read' || WRITES.has(tool.name))\n .map((tool) =>\n wrapTool(tool, (inner) => ({\n ...inner,\n async execute(args, api, context) {\n const { agents, reads, layout = LEGACY, enforced } = await lookup();\n const record = Object.values(agents.state().scoops).find(\n (scoop) => scoop.conversation === api.conversationId\n );\n if (!record || enforced) return inner.execute(args, api, context);\n const own = layout.scoopRoot(record.folder);\n const cone = layout.users ? own : HOME;\n const path = String((args as { path: unknown }).path);\n const asked = normalize(resolve(layout.workspace(record.folder), path));\n const canonical = await api.env?.canonicalPath(asked, context);\n const target = canonical?.ok ? canonical.value : asked;\n const writing = WRITES.has(inner.name);\n const skills = reads?.() ?? [];\n if (\n allowed(record, cone, target, writing, skills, own) ||\n allowed(record, cone, asked, writing, skills, own)\n )\n return inner.execute(args, api, context);\n const reach = layout.users || WRITES.has(inner.name) ? '' : `, and reads ${HOME}`;\n return refusal(\n `${target} is outside this scoop's folders. ${inner.name} reaches ${own} and /tmp${reach}.`\n );\n },\n }))\n );\n return defineExtension({ name: 'slicc-scoop-guard', wraps });\n}\n"],
|
|
5
|
-
"mappings": "AACA;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n AssistantEntry,\n type ConversationId,\n defineExtension,\n defineTask,\n type Extension,\n hook,\n type PromptInput,\n type Submission,\n section,\n ToolTask,\n type Tx,\n} from '@earendil-works/pi-durable';\nimport { answerText } from '../agent.ts';\nimport { AgentsDoc, live, type ScoopRecord } from '../agents.ts';\nimport { HOME } from '../kernel/env.ts';\nimport { normalize, resolve } from '../kernel/paths.ts';\nimport { type Layout, LEGACY } from '../layout.ts';\nimport type { LickEvent } from '../licks/licks.ts';\nimport { FROM_KIND } from './from.ts';\nimport { type Feed, type ScoopsHost, ScoopWorkDoc } from './service.ts';\n\nexport const PREVIEW = 1000;\n\ntype Lookup = () => Promise<ScoopsHost>;\n\nexport function scoopFacts(\n record: Pick<ScoopRecord, 'name' | 'folder' | 'role'> & { cone?: string },\n layout: Layout = LEGACY\n): string {\n const place = layout.workspace(record.folder);\n return [\n `You are the scoop \"${record.name}\"${record.role ? ` (role: ${record.role})` : ''}, a helper agent working for a cone. Messages come from the cone or from the user at a terminal; your final answer goes back to whoever asked, so end every task with a complete answer.`,\n layout.users\n ? `You run as the user ${layout.scoopRoot(record.folder).slice('/home/'.length)}, and your home is ${place}: work there. Your cone can read your home; you can't read your cone's home or another scoop's. /tmp is shared scratch.`\n : `Work in ${place}. Your read, write and edit tools only reach ${layout.scoopRoot(record.folder)} and /tmp, and read ${HOME}.`,\n layout.users\n ? 'Your bash runs as your user too, so the kernel decides what it may read and change. Stay in your home, and change nothing outside it unless the request says so.'\n : 'Your bash is not confined: it sees the whole file system and every process. Stay in your folder anyway, and change nothing outside it unless the request says so.',\n 'You may run `agent` synchronously for a bounded side task (calls nest at most three deep), but you cannot start asynchronous scoops (`agent --async`, `subagent spawn`).',\n ].join('\\n');\n}\n\nfunction lines(text: string): number {\n return text ? text.split('\\n').length : 0;\n}\n\nexport type ReportResult = { text: string; failed: string | null; path: string | null };\n\nexport function reportLick(\n record: ScoopRecord,\n id: string,\n report: ReportResult,\n route: { target?: string | null; channel?: 'scoop-notify' | 'bash' } = {}\n): LickEvent {\n const status = report.failed ? (report.failed === 'aborted' ? 'stopped' : 'failed') : 'completed';\n const label = `${record.name} (${record.role ?? 'scoop'})`;\n const preview = report.text.length > PREVIEW ? `${report.text.slice(0, PREVIEW)}\u2026` : report.text;\n const where = report.path ? `Output: ${report.path} (${lines(report.text)} lines)` : null;\n const reason = report.failed && report.failed !== 'aborted' ? `Reason: ${report.failed}` : null;\n return {\n channel: route.channel ?? 'scoop-notify',\n source: id,\n title: label,\n text: `[scoop ${record.name} (${record.role ?? 'scoop'}) ${status}]`,\n body: [where, reason, preview].filter(Boolean).join('\\n\\n'),\n target: (route.target ?? `cone:${record.cone}`) as LickEvent['target'],\n ...(report.failed && report.failed !== 'aborted' ? { severity: 'warn' as const } : {}),\n };\n}\n\ntype Noting = {\n commit: (\n write: (\n tx: Tx\n ) => Promise<{ status: 'running'; checkpoint: { phase: 'deliver'; noted: true } }>,\n context: Context\n ) => Promise<unknown>;\n};\n\nexport async function noteWhenPlaced(\n runtime: Noting,\n submission: Pick<Submission, 'status'>,\n conversation: ConversationId,\n note: { from: string | undefined; agent?: boolean; noted: boolean | undefined },\n context: Context\n): Promise<void> {\n const { from } = note;\n while (from && !note.noted && !context.abortSignal?.aborted) {\n const record = await submission.status(context);\n if (record.status !== 'queued') {\n if (!('entry' in record) || record.entry === undefined) return;\n const entry = record.entry;\n await runtime\n .commit(async (tx) => {\n await tx.appendEntry(conversation, {\n kind: FROM_KIND,\n data: { from, entry, ...(note.agent ? { agent: true } : {}) },\n });\n return { status: 'running', checkpoint: { phase: 'deliver', noted: true } };\n }, context)\n .catch(() => undefined);\n return;\n }\n await new Promise((done) => setTimeout(done, NOTE_POLL_MS));\n }\n}\n\nexport const NOTE_POLL_MS = 100;\n\nexport function waitTask(lookup: Lookup) {\n type WaitInput = { scoops: string[]; cone: string; deadline: number };\n\n return defineTask<WaitInput, { phase: 'start' } | { phase: 'watch' }, null>({\n name: 'slicc.scoop-wait',\n version: 1,\n initial: () => ({ phase: 'start' }),\n phases: {\n async start(task, runtime, context) {\n await runtime.commit(async (tx) => {\n (await tx.doc(ScoopWorkDoc)).waits[String(task.id)] = {\n scoops: task.input.scoops,\n done: {},\n cone: task.input.cone,\n };\n return { status: 'running', checkpoint: { phase: 'watch' } };\n }, context);\n },\n async watch(task, runtime, context) {\n const { agents, licks } = await lookup();\n const key = String(task.id);\n for (;;) {\n const work = await runtime.snapshot(ScoopWorkDoc, context);\n const state = work?.waits[key];\n if (!state) break;\n const feeding = new Set(Object.values(work?.feeds ?? {}).map((feed) => feed.scoop));\n const open = state.scoops.filter((id) => !(id in state.done) && feeding.has(id));\n const expired = runtime.now() >= task.input.deadline;\n if (open.length && !expired) {\n await runtime.sleep(Math.min(runtime.now() + 300, task.input.deadline), context);\n continue;\n }\n const names = (id: string) => agents.state().scoops[id]?.name ?? id;\n const body = state.scoops.map((id) =>\n open.includes(id)\n ? `## ${names(id)}\\n(still working when the wait timed out)`\n : `## ${names(id)}\\n${state.done[id] ?? '(nothing was running)'}`\n );\n await licks.deliver(\n {\n channel: 'scoop-wait',\n source: key,\n title: `Waited for ${state.scoops.map(names).join(', ')}`,\n text: open.length\n ? `${state.scoops.length - open.length} of ${state.scoops.length} done; timed out waiting for ${open.map(names).join(', ')}`\n : `All ${state.scoops.length} done`,\n body: body.join('\\n\\n'),\n target: `cone:${state.cone}`,\n coalesce: false,\n eventId: `wait:${key}`,\n },\n context\n );\n break;\n }\n await runtime.commit(async (tx) => {\n delete (await tx.doc(ScoopWorkDoc)).waits[key];\n return { status: 'terminal', outcome: { status: 'completed', result: null } };\n }, context);\n },\n },\n abort: (task, runtime, context) =>\n runtime.commit(async (tx) => {\n delete (await tx.doc(ScoopWorkDoc)).waits[String(task.id)];\n return { status: 'terminal', outcome: { status: 'aborted' } };\n }, context),\n });\n}\n\nexport function scoopTasks(lookup: Lookup) {\n const anchor = defineTask<null, { phase: 'done' }, null>({\n name: 'slicc.scoop-anchor',\n version: 1,\n initial: () => ({ phase: 'done' }),\n phases: {\n done: (_task, runtime, context) =>\n runtime.commit(\n () => ({ status: 'terminal', outcome: { status: 'completed', result: null } }),\n context\n ),\n },\n abort: (_task, runtime, context) =>\n runtime.commit(() => ({ status: 'terminal', outcome: { status: 'aborted' } }), context),\n });\n\n type ReporterInput = {\n scoop: string;\n prompt: string;\n followUp?: boolean;\n request?: string;\n };\n type ReporterState =\n | { phase: 'deliver'; noted?: boolean }\n | { phase: 'report'; text: string; failed: string | null };\n\n const reporter = defineTask<ReporterInput, ReporterState, ReportResult>({\n name: 'slicc.scoop-reporter',\n version: 1,\n initial: () => ({ phase: 'deliver' }),\n phases: {\n async deliver(task, runtime, context) {\n const { agents } = await lookup();\n const record = agents.state().scoops[task.input.scoop];\n const handle =\n record && live(record)\n ? await runtime.conversation(record.conversation as ConversationId, context)\n : undefined;\n if (!handle) {\n await runtime.commit(async (tx) => {\n delete (await tx.doc(ScoopWorkDoc)).feeds[String(task.id)];\n return {\n status: 'terminal',\n outcome: {\n status: 'completed',\n result: { text: '', failed: 'the scoop is gone', path: null },\n },\n };\n }, context);\n return;\n }\n const requestId = task.input.request ? `subagent:${task.input.request}` : `feed:${task.id}`;\n const feed = (await runtime.snapshot(ScoopWorkDoc, context))?.feeds[String(task.id)];\n const submission = await handle.submit(\n {\n type: 'input',\n content: task.input.prompt,\n whenBusy: task.input.followUp ? 'followUp' : 'steer',\n requestId,\n },\n context\n );\n const noting = noteWhenPlaced(\n runtime,\n submission,\n record?.conversation as ConversationId,\n { from: feed?.from, agent: feed?.agent === true, noted: task.state.checkpoint.noted },\n context\n );\n const settled = await submission.wait(context);\n await noting;\n await runtime.commit(async (tx) => {\n if (settled.status !== 'done' || settled.type !== 'input')\n return {\n status: 'running',\n checkpoint: {\n phase: 'report',\n text: '',\n failed: String(settled.reason),\n },\n };\n const answer = await tx.entry(AssistantEntry, settled.answer);\n return {\n status: 'running',\n checkpoint: {\n phase: 'report',\n text: answerText(answer?.model?.[0]?.content),\n failed: null,\n },\n };\n }, context);\n },\n async report(task, runtime, context) {\n const { agents, licks, layout = LEGACY } = await lookup();\n const id = task.input.scoop;\n const record = agents.state().scoops[id];\n const { text, failed } = task.state.checkpoint;\n const path =\n record && text ? `${layout.reports(record.folder, record.cone)}/${task.id}.md` : null;\n if (path)\n await (await runtime.env(context))?.writeFile(\n path,\n text.endsWith('\\n') ? text : `${text}\\n`,\n context\n );\n const work = await runtime.snapshot(ScoopWorkDoc, context);\n const entry = work?.feeds[String(task.id)] as Feed;\n const waiting = (wait: { scoops: string[]; done: Record<string, string> }) =>\n wait.scoops.includes(id) && !(id in wait.done);\n const folded = Object.values(work?.waits ?? {}).some(waiting);\n const route = { target: entry.target, channel: entry.channel };\n if (!folded && record && entry.report && failed !== 'aborted')\n await licks.deliver(\n {\n ...reportLick(record, id, { text, failed, path }, route),\n eventId: `report:${task.id}`,\n },\n context\n );\n await runtime.commit(async (tx) => {\n const work = await tx.doc(ScoopWorkDoc);\n for (const wait of Object.values(work.waits))\n if (waiting(wait))\n wait.done[id] = failed\n ? `stopped: ${failed}`\n : `${path ? `Output: ${path}\\n` : ''}${text || '(no text)'}`;\n delete work.feeds[String(task.id)];\n return {\n status: 'terminal',\n outcome: { status: 'completed', result: { text, failed, path } },\n };\n }, context);\n },\n },\n abort: (task, runtime, context) =>\n runtime.commit(async (tx) => {\n delete (await tx.doc(ScoopWorkDoc)).feeds[String(task.id)];\n return { status: 'terminal', outcome: { status: 'aborted' } };\n }, context),\n });\n\n return { anchor, reporter, wait: waitTask(lookup) };\n}\n\nasync function whose(input: PromptInput, context: Context) {\n const state = await input.read.snapshot(AgentsDoc, context);\n const scoop = Object.values(state?.scoops ?? {}).find(\n (record) => record.conversation === input.conversationId\n );\n return { scoop };\n}\n\nexport function scoopsExtension(\n tasks: ReturnType<typeof scoopTasks>,\n layout: () => Layout = () => LEGACY\n): Extension {\n return defineExtension({\n name: 'slicc-scoops',\n tasks: [tasks.anchor, tasks.reporter, tasks.wait],\n sections: [\n section('scoop', async (input, context) => {\n const { scoop } = await whose(input, context);\n return scoop ? scoopFacts(scoop, layout()) : undefined;\n }),\n ],\n });\n}\n\nconst WRITES = new Set(['write', 'edit']);\n\nexport function allowed(\n record: Pick<ScoopRecord, 'folder'> & { roots?: ScoopRecord['roots'] },\n coneCwd: string,\n path: string,\n writing: boolean,\n reads: readonly string[] = [],\n own = LEGACY.scoopRoot(record.folder)\n): boolean {\n const write = [own, '/tmp', ...(record.roots?.write ?? [])];\n const roots = writing ? write : [...write, coneCwd, ...(record.roots?.read ?? []), ...reads];\n return roots.some((root) => path === root || path.startsWith(`${root === '/' ? '' : root}/`));\n}\n\nexport function guardExtension(lookup: Lookup): Extension {\n return defineExtension({\n name: 'slicc-scoop-guard',\n hooks: [\n hook(ToolTask, {\n async beforeTool(call, api, context) {\n if (call.name !== 'read' && !WRITES.has(call.name)) return undefined;\n const { agents, reads, layout = LEGACY, enforced, envOf } = await lookup();\n const record = Object.values(agents.state().scoops).find(\n (scoop) => scoop.conversation === api.conversationId\n );\n if (!record || enforced) return undefined;\n const own = layout.scoopRoot(record.folder);\n const cone = layout.users ? own : HOME;\n const path = String(call.arguments.path);\n const asked = normalize(resolve(layout.workspace(record.folder), path));\n const env = await envOf?.(api.conversationId, context);\n const canonical = await env?.canonicalPath(asked, context);\n const target = canonical?.ok ? canonical.value : asked;\n const writing = WRITES.has(call.name);\n const skills = reads?.() ?? [];\n if (\n allowed(record, cone, target, writing, skills, own) ||\n allowed(record, cone, asked, writing, skills, own)\n )\n return undefined;\n const reach = layout.users || writing ? '' : `, and reads ${HOME}`;\n return {\n block: `${target} is outside this scoop's folders. ${call.name} reaches ${own} and /tmp${reach}.`,\n };\n },\n }),\n ],\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["work"]
|
|
7
7
|
}
|
package/dist/scoops/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
controlPlane,
|
|
14
14
|
PROTOCOL
|
|
15
15
|
} from "./requests.js";
|
|
16
|
-
import { createScoops } from "./service.js";
|
|
16
|
+
import { choosable, createScoops } from "./service.js";
|
|
17
17
|
const BUILTIN_ROLES = ["scout", "worker", "reviewer", "oracle", "delegate", "gelatiere"];
|
|
18
18
|
const PNPM_HOME = "/home/.local/share/pnpm";
|
|
19
19
|
const BIN_DIR = "/usr/local/bin";
|
|
@@ -179,9 +179,10 @@ function setupScoops(registry, licks, fileTools = []) {
|
|
|
179
179
|
let cached = { roles: [], warnings: [], limits: { ...DEFAULT_LIMITS } };
|
|
180
180
|
let layout = LEGACY;
|
|
181
181
|
registry.install(scoopsExtension(tasks, () => layout));
|
|
182
|
-
registry.install(guardExtension(lookup
|
|
182
|
+
registry.install(guardExtension(lookup));
|
|
183
183
|
return {
|
|
184
184
|
scoops,
|
|
185
|
+
choosable: choosable(fileTools),
|
|
185
186
|
async attach(options, context) {
|
|
186
187
|
const { harness, agents, groups, env, home } = options;
|
|
187
188
|
const assets = options.assets ?? packageAssets();
|
|
@@ -196,6 +197,7 @@ function setupScoops(registry, licks, fileTools = []) {
|
|
|
196
197
|
files: env,
|
|
197
198
|
layout,
|
|
198
199
|
...options.enforced ? { enforced: true } : {},
|
|
200
|
+
envOf: options.envOf,
|
|
199
201
|
...options.mcp ? { mcp: options.mcp } : {}
|
|
200
202
|
});
|
|
201
203
|
const builtin = async () => {
|
package/dist/scoops/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/scoops/index.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { Harness, Registry, ToolRegistration } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { type Agents, live } from '../agents.ts';\nimport type { ProcessGroups } from '../kernel/groups.ts';\nimport { type Layout, LEGACY } from '../layout.ts';\nimport type { Licks } from '../licks/licks.ts';\nimport {\n DEFAULT_LIMITS,\n loadRoles,\n PACKAGE_ROOTS,\n packageDirs,\n type Roles,\n} from '../roles/roles.ts';\nimport { type CliOptions, createCli, RUN_DIR } from './cli.ts';\nimport { guardExtension, scoopsExtension, scoopTasks } from './extension.ts';\nimport {\n CONTROL_DIR,\n type ControlPlane,\n controlPlane,\n type PlaneOptions,\n PROTOCOL,\n} from './requests.ts';\nimport { createScoops, type Scoops, type ScoopsHost } from './service.ts';\n\nexport const BUILTIN_ROLES = ['scout', 'worker', 'reviewer', 'oracle', 'delegate', 'gelatiere'];\nexport const PNPM_HOME = '/home/.local/share/pnpm';\nexport const BIN_DIR = '/usr/local/bin';\n\nexport type Assets = (path: string) => Promise<string>;\n\nexport function packageAssets(base: URL = new URL('../../', import.meta.url)): Assets {\n return async (path) => {\n const response = await fetch(new URL(path, base));\n if (!response.ok) throw new Error(`${path}: ${response.status}`);\n return response.text();\n };\n}\n\nexport interface AttachOptions {\n harness: Harness;\n agents: Agents;\n groups: ProcessGroups;\n env: ExecutionEnv;\n home: string;\n assets?: Assets;\n binDir?: string;\n controlDir?: string;\n sweepEvery?: number;\n alive?: (pid: number) => Promise<boolean>;\n reads: () => readonly string[];\n memory?: CliOptions['memory'];\n gelatiere?: CliOptions['gelatiere'];\n freezer?: CliOptions['freezer'];\n models?: CliOptions['models'];\n panel?: CliOptions['panel'];\n mcp?: (names: readonly string[]) => ToolRegistration[];\n layout?: Layout;\n enforced?: boolean;\n spools?: Spools;\n agentDir?: string;\n}\n\nexport interface Spools {\n users(): readonly string[];\n prepare(user: string, dir: string, context: Context): Promise<void>;\n}\n\nexport interface ScoopsRuntime {\n roles(context: Context): Promise<Roles>;\n plane: ControlPlane;\n close(context: Context): Promise<void>;\n}\n\nexport interface ScoopsSetup {\n scoops: Scoops;\n attach(options: AttachOptions, context: Context): Promise<ScoopsRuntime>;\n}\n\nexport const COMMANDS = ['agent', 'subagent', 'memory', 'gelatiere', 'freezer', 'models', 'panel'];\n\nexport const RETIRED_COMMANDS = ['sprinkle'];\n\nexport async function pnpmHomeOf(env: ExecutionEnv, context: Context): Promise<string> {\n let out = '';\n const ran = await env\n .exec(\n 'printf %s \"$PNPM_HOME\"',\n {\n onOutput: (text) => {\n out += text;\n },\n },\n context\n )\n .catch(() => undefined);\n return ran?.ok && ran.value.exitCode === 0 && /^\\/\\S*$/.test(out) ? out : PNPM_HOME;\n}\n\nexport interface Installed {\n done: boolean;\n skipped: string[];\n}\n\nasync function theirs(env: ExecutionEnv, path: string, context: Context): Promise<boolean> {\n const found = await env.fileInfo(path, context);\n if (!found.ok) return found.error.code !== 'not_found';\n if (found.value.kind !== 'file') return true;\n const read = await env.readTextFile(path, context);\n return !read.ok || !read.value.includes(PROTOCOL);\n}\n\nasync function install(\n env: ExecutionEnv,\n script: string,\n binDir: string,\n context: Context\n): Promise<Installed> {\n const skipped: string[] = [];\n if (!(await env.createDir(binDir, { recursive: true }, context)).ok)\n return { done: false, skipped };\n for (const name of COMMANDS) {\n const path = `${binDir}/${name}`;\n if (await theirs(env, path, context)) {\n skipped.push(name);\n continue;\n }\n if (!(await env.writeFile(path, script, context)).ok) return { done: false, skipped };\n const mode = await env.exec(['chmod', '755', path], undefined, context);\n if (!mode.ok || mode.value.exitCode !== 0) return { done: false, skipped };\n }\n for (const name of RETIRED_COMMANDS) {\n const text = await env.readTextFile(`${binDir}/${name}`, context);\n if (text.ok && text.value.includes(PROTOCOL))\n await env.remove(`${binDir}/${name}`, { force: true }, context);\n }\n return { done: true, skipped };\n}\n\nexport async function installCommands(\n env: ExecutionEnv,\n script: string,\n binDir: string,\n context: Context,\n warn: (message: string) => void = (message) => console.warn(message)\n): Promise<Installed> {\n const installed = await install(env, script, binDir, context).catch(\n (): Installed => ({ done: false, skipped: [] })\n );\n if (installed.skipped.length) {\n const paths = installed.skipped.map((name) => `${binDir}/${name}`).join(', ');\n warn(`slicc-agent: left ${paths} alone, because it isn't slicc-agent's command`);\n }\n if (installed.done) await retire(env, binDir, context).catch(() => undefined);\n return installed;\n}\n\nexport async function retire(env: ExecutionEnv, binDir: string, context: Context) {\n const old = `${await pnpmHomeOf(env, context)}/bin`;\n if (old === binDir) return;\n for (const name of [...COMMANDS, ...RETIRED_COMMANDS]) {\n const path = `${old}/${name}`;\n const text = await env.readTextFile(path, context);\n if (text.ok && text.value.includes(PROTOCOL)) await env.remove(path, { force: true }, context);\n }\n}\n\nfunction mirror(agents: Agents, env: ExecutionEnv) {\n return async (context: Context) => {\n for (const [id, record] of Object.entries(agents.state().scoops)) {\n const state = record.gone ? 'gone' : live(record) ? 'live' : 'stopped';\n const metadata = {\n handle: record.folder,\n id,\n name: record.name,\n role: record.role,\n cone: record.cone,\n state,\n };\n await env.writeFile(\n `${RUN_DIR}/${record.folder}/metadata.json`,\n `${JSON.stringify(metadata, null, 2)}\\n`,\n context\n );\n }\n };\n}\n\nfunction planeOf(\n env: ExecutionEnv,\n cli: ReturnType<typeof createCli>,\n options: AttachOptions,\n context: Context\n): ControlPlane {\n const common: PlaneOptions & { dir: string } = {\n ...(options.sweepEvery ? { sweepEvery: options.sweepEvery } : {}),\n mirror: mirror(options.agents, env),\n dir: options.controlDir ?? CONTROL_DIR,\n };\n if (!options.spools) return controlPlane(env, cli, common);\n return spooled(env, cli, options.spools, options.agents, common, context);\n}\n\nfunction spooled(\n env: ExecutionEnv,\n cli: ReturnType<typeof createCli>,\n spools: Spools,\n agents: Agents,\n options: PlaneOptions & { dir: string },\n context: Context\n): ControlPlane {\n const planes = new Map<string, Promise<ControlPlane | undefined>>();\n let off: () => void = () => undefined;\n let chain: Promise<unknown> = Promise.resolve();\n const open = async (user: string): Promise<ControlPlane | undefined> => {\n const dir = `${options.dir}/${user}`;\n try {\n await spools.prepare(user, dir, context);\n const plane = controlPlane(env, cli, { ...options, dir, user });\n await plane.start(context);\n return plane;\n } catch {\n planes.delete(user);\n return undefined;\n }\n };\n const sync = () => {\n const step = async () => {\n const wanted = new Set(spools.users());\n for (const user of wanted)\n if (!planes.has(user)) {\n const opened = open(user);\n planes.set(user, opened);\n await opened;\n }\n for (const [user, plane] of [...planes])\n if (!wanted.has(user)) {\n planes.delete(user);\n await (await plane)?.close(context);\n }\n };\n chain = chain.then(step, step);\n return chain;\n };\n const all = async () =>\n (await Promise.all(planes.values())).filter((plane): plane is ControlPlane => !!plane);\n return {\n async start() {\n off = agents.onChange(() => void sync());\n await sync();\n },\n async sweep(using) {\n for (const plane of await all()) await plane.sweep(using);\n },\n async close(using) {\n off();\n await chain;\n for (const plane of await all()) await plane.close(using);\n planes.clear();\n },\n };\n}\n\nexport function setupScoops(\n registry: Registry,\n licks: Licks,\n fileTools: readonly ToolRegistration[] = []\n): ScoopsSetup {\n let bind: (host: ScoopsHost) => void = () => undefined;\n const host = new Promise<ScoopsHost>((resolve) => {\n bind = resolve;\n });\n const lookup = () => host;\n const tasks = scoopTasks(lookup);\n const scoops = createScoops(host, () => tasks);\n let cached: Roles = { roles: [], warnings: [], limits: { ...DEFAULT_LIMITS } };\n let layout = LEGACY;\n registry.install(scoopsExtension(tasks, () => layout));\n registry.install(guardExtension(lookup, fileTools));\n return {\n scoops,\n async attach(options, context) {\n const { harness, agents, groups, env, home } = options;\n const assets = options.assets ?? packageAssets();\n layout = options.layout ?? LEGACY;\n bind({\n harness,\n agents,\n licks,\n tools: fileTools,\n groups,\n reads: options.reads,\n files: env,\n layout,\n ...(options.enforced ? { enforced: true } : {}),\n ...(options.mcp ? { mcp: options.mcp } : {}),\n });\n const builtin = async () => {\n const out: { path: string; text: string }[] = [];\n for (const name of BUILTIN_ROLES) {\n const path = `packages/vfs-root/agents/${name}.md`;\n out.push({ path: `builtin:${name}`, text: await assets(path) });\n }\n return out;\n };\n const roles = async (using: Context) => {\n cached = await loadRoles(\n {\n files: env,\n builtin,\n dirs: [\n ...(await packageDirs(env, PACKAGE_ROOTS, using)).map((dir) => ({\n source: 'package' as const,\n dir,\n })),\n { source: 'user', dir: `${options.agentDir ?? `${home}/.pi/agent`}/agents` },\n ],\n settingsPath: `${options.agentDir ?? `${home}/.pi/agent`}/settings.json`,\n },\n using\n );\n return cached;\n };\n await roles(context).catch(() => cached);\n const script = await assets('bin/agent').catch(() => undefined);\n if (script) {\n const binDir = options.binDir ?? BIN_DIR;\n await installCommands(env, script, binDir, context);\n }\n const cli = createCli({\n env,\n harness,\n agents,\n scoops,\n roles: () => roles(context),\n ...(options.alive ? { alive: options.alive } : {}),\n ...(options.memory ? { memory: options.memory } : {}),\n ...(options.gelatiere ? { gelatiere: options.gelatiere } : {}),\n ...(options.freezer ? { freezer: options.freezer } : {}),\n ...(options.models ? { models: options.models } : {}),\n layout,\n ...(options.panel ? { panel: options.panel } : {}),\n });\n const plane = planeOf(env, cli, options, context);\n await plane.start(context);\n return { roles, plane, close: (using) => plane.close(using) };\n },\n };\n}\n\nexport type { Rewound, Scoops } from './service.ts';\n"],
|
|
5
|
-
"mappings": "AAGA,SAAsB,YAAY;AAElC,SAAsB,cAAc;AAEpC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { Harness, Registry, ToolRegistration } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { type Agents, live } from '../agents.ts';\nimport type { ProcessGroups } from '../kernel/groups.ts';\nimport { type Layout, LEGACY } from '../layout.ts';\nimport type { Licks } from '../licks/licks.ts';\nimport {\n DEFAULT_LIMITS,\n loadRoles,\n PACKAGE_ROOTS,\n packageDirs,\n type Roles,\n} from '../roles/roles.ts';\nimport type { Choosable } from '../selection.ts';\nimport { type CliOptions, createCli, RUN_DIR } from './cli.ts';\nimport { guardExtension, scoopsExtension, scoopTasks } from './extension.ts';\nimport {\n CONTROL_DIR,\n type ControlPlane,\n controlPlane,\n type PlaneOptions,\n PROTOCOL,\n} from './requests.ts';\nimport { choosable, createScoops, type EnvOf, type Scoops, type ScoopsHost } from './service.ts';\n\nexport const BUILTIN_ROLES = ['scout', 'worker', 'reviewer', 'oracle', 'delegate', 'gelatiere'];\nexport const PNPM_HOME = '/home/.local/share/pnpm';\nexport const BIN_DIR = '/usr/local/bin';\n\nexport type Assets = (path: string) => Promise<string>;\n\nexport function packageAssets(base: URL = new URL('../../', import.meta.url)): Assets {\n return async (path) => {\n const response = await fetch(new URL(path, base));\n if (!response.ok) throw new Error(`${path}: ${response.status}`);\n return response.text();\n };\n}\n\nexport interface AttachOptions {\n harness: Harness;\n agents: Agents;\n groups: ProcessGroups;\n env: ExecutionEnv;\n home: string;\n assets?: Assets;\n binDir?: string;\n controlDir?: string;\n sweepEvery?: number;\n alive?: (pid: number) => Promise<boolean>;\n reads: () => readonly string[];\n memory?: CliOptions['memory'];\n gelatiere?: CliOptions['gelatiere'];\n freezer?: CliOptions['freezer'];\n models?: CliOptions['models'];\n panel?: CliOptions['panel'];\n mcp?: (names: readonly string[]) => ToolRegistration[];\n layout?: Layout;\n enforced?: boolean;\n envOf: EnvOf;\n spools?: Spools;\n agentDir?: string;\n}\n\nexport interface Spools {\n users(): readonly string[];\n prepare(user: string, dir: string, context: Context): Promise<void>;\n}\n\nexport interface ScoopsRuntime {\n roles(context: Context): Promise<Roles>;\n plane: ControlPlane;\n close(context: Context): Promise<void>;\n}\n\nexport interface ScoopsSetup {\n scoops: Scoops;\n choosable: Choosable;\n attach(options: AttachOptions, context: Context): Promise<ScoopsRuntime>;\n}\n\nexport const COMMANDS = ['agent', 'subagent', 'memory', 'gelatiere', 'freezer', 'models', 'panel'];\n\nexport const RETIRED_COMMANDS = ['sprinkle'];\n\nexport async function pnpmHomeOf(env: ExecutionEnv, context: Context): Promise<string> {\n let out = '';\n const ran = await env\n .exec(\n 'printf %s \"$PNPM_HOME\"',\n {\n onOutput: (text) => {\n out += text;\n },\n },\n context\n )\n .catch(() => undefined);\n return ran?.ok && ran.value.exitCode === 0 && /^\\/\\S*$/.test(out) ? out : PNPM_HOME;\n}\n\nexport interface Installed {\n done: boolean;\n skipped: string[];\n}\n\nasync function theirs(env: ExecutionEnv, path: string, context: Context): Promise<boolean> {\n const found = await env.fileInfo(path, context);\n if (!found.ok) return found.error.code !== 'not_found';\n if (found.value.kind !== 'file') return true;\n const read = await env.readTextFile(path, context);\n return !read.ok || !read.value.includes(PROTOCOL);\n}\n\nasync function install(\n env: ExecutionEnv,\n script: string,\n binDir: string,\n context: Context\n): Promise<Installed> {\n const skipped: string[] = [];\n if (!(await env.createDir(binDir, { recursive: true }, context)).ok)\n return { done: false, skipped };\n for (const name of COMMANDS) {\n const path = `${binDir}/${name}`;\n if (await theirs(env, path, context)) {\n skipped.push(name);\n continue;\n }\n if (!(await env.writeFile(path, script, context)).ok) return { done: false, skipped };\n const mode = await env.exec(['chmod', '755', path], undefined, context);\n if (!mode.ok || mode.value.exitCode !== 0) return { done: false, skipped };\n }\n for (const name of RETIRED_COMMANDS) {\n const text = await env.readTextFile(`${binDir}/${name}`, context);\n if (text.ok && text.value.includes(PROTOCOL))\n await env.remove(`${binDir}/${name}`, { force: true }, context);\n }\n return { done: true, skipped };\n}\n\nexport async function installCommands(\n env: ExecutionEnv,\n script: string,\n binDir: string,\n context: Context,\n warn: (message: string) => void = (message) => console.warn(message)\n): Promise<Installed> {\n const installed = await install(env, script, binDir, context).catch(\n (): Installed => ({ done: false, skipped: [] })\n );\n if (installed.skipped.length) {\n const paths = installed.skipped.map((name) => `${binDir}/${name}`).join(', ');\n warn(`slicc-agent: left ${paths} alone, because it isn't slicc-agent's command`);\n }\n if (installed.done) await retire(env, binDir, context).catch(() => undefined);\n return installed;\n}\n\nexport async function retire(env: ExecutionEnv, binDir: string, context: Context) {\n const old = `${await pnpmHomeOf(env, context)}/bin`;\n if (old === binDir) return;\n for (const name of [...COMMANDS, ...RETIRED_COMMANDS]) {\n const path = `${old}/${name}`;\n const text = await env.readTextFile(path, context);\n if (text.ok && text.value.includes(PROTOCOL)) await env.remove(path, { force: true }, context);\n }\n}\n\nfunction mirror(agents: Agents, env: ExecutionEnv) {\n return async (context: Context) => {\n for (const [id, record] of Object.entries(agents.state().scoops)) {\n const state = record.gone ? 'gone' : live(record) ? 'live' : 'stopped';\n const metadata = {\n handle: record.folder,\n id,\n name: record.name,\n role: record.role,\n cone: record.cone,\n state,\n };\n await env.writeFile(\n `${RUN_DIR}/${record.folder}/metadata.json`,\n `${JSON.stringify(metadata, null, 2)}\\n`,\n context\n );\n }\n };\n}\n\nfunction planeOf(\n env: ExecutionEnv,\n cli: ReturnType<typeof createCli>,\n options: AttachOptions,\n context: Context\n): ControlPlane {\n const common: PlaneOptions & { dir: string } = {\n ...(options.sweepEvery ? { sweepEvery: options.sweepEvery } : {}),\n mirror: mirror(options.agents, env),\n dir: options.controlDir ?? CONTROL_DIR,\n };\n if (!options.spools) return controlPlane(env, cli, common);\n return spooled(env, cli, options.spools, options.agents, common, context);\n}\n\nfunction spooled(\n env: ExecutionEnv,\n cli: ReturnType<typeof createCli>,\n spools: Spools,\n agents: Agents,\n options: PlaneOptions & { dir: string },\n context: Context\n): ControlPlane {\n const planes = new Map<string, Promise<ControlPlane | undefined>>();\n let off: () => void = () => undefined;\n let chain: Promise<unknown> = Promise.resolve();\n const open = async (user: string): Promise<ControlPlane | undefined> => {\n const dir = `${options.dir}/${user}`;\n try {\n await spools.prepare(user, dir, context);\n const plane = controlPlane(env, cli, { ...options, dir, user });\n await plane.start(context);\n return plane;\n } catch {\n planes.delete(user);\n return undefined;\n }\n };\n const sync = () => {\n const step = async () => {\n const wanted = new Set(spools.users());\n for (const user of wanted)\n if (!planes.has(user)) {\n const opened = open(user);\n planes.set(user, opened);\n await opened;\n }\n for (const [user, plane] of [...planes])\n if (!wanted.has(user)) {\n planes.delete(user);\n await (await plane)?.close(context);\n }\n };\n chain = chain.then(step, step);\n return chain;\n };\n const all = async () =>\n (await Promise.all(planes.values())).filter((plane): plane is ControlPlane => !!plane);\n return {\n async start() {\n off = agents.onChange(() => void sync());\n await sync();\n },\n async sweep(using) {\n for (const plane of await all()) await plane.sweep(using);\n },\n async close(using) {\n off();\n await chain;\n for (const plane of await all()) await plane.close(using);\n planes.clear();\n },\n };\n}\n\nexport function setupScoops(\n registry: Registry,\n licks: Licks,\n fileTools: readonly ToolRegistration[] = []\n): ScoopsSetup {\n let bind: (host: ScoopsHost) => void = () => undefined;\n const host = new Promise<ScoopsHost>((resolve) => {\n bind = resolve;\n });\n const lookup = () => host;\n const tasks = scoopTasks(lookup);\n const scoops = createScoops(host, () => tasks);\n let cached: Roles = { roles: [], warnings: [], limits: { ...DEFAULT_LIMITS } };\n let layout = LEGACY;\n registry.install(scoopsExtension(tasks, () => layout));\n registry.install(guardExtension(lookup));\n return {\n scoops,\n choosable: choosable(fileTools),\n async attach(options, context) {\n const { harness, agents, groups, env, home } = options;\n const assets = options.assets ?? packageAssets();\n layout = options.layout ?? LEGACY;\n bind({\n harness,\n agents,\n licks,\n tools: fileTools,\n groups,\n reads: options.reads,\n files: env,\n layout,\n ...(options.enforced ? { enforced: true } : {}),\n envOf: options.envOf,\n ...(options.mcp ? { mcp: options.mcp } : {}),\n });\n const builtin = async () => {\n const out: { path: string; text: string }[] = [];\n for (const name of BUILTIN_ROLES) {\n const path = `packages/vfs-root/agents/${name}.md`;\n out.push({ path: `builtin:${name}`, text: await assets(path) });\n }\n return out;\n };\n const roles = async (using: Context) => {\n cached = await loadRoles(\n {\n files: env,\n builtin,\n dirs: [\n ...(await packageDirs(env, PACKAGE_ROOTS, using)).map((dir) => ({\n source: 'package' as const,\n dir,\n })),\n { source: 'user', dir: `${options.agentDir ?? `${home}/.pi/agent`}/agents` },\n ],\n settingsPath: `${options.agentDir ?? `${home}/.pi/agent`}/settings.json`,\n },\n using\n );\n return cached;\n };\n await roles(context).catch(() => cached);\n const script = await assets('bin/agent').catch(() => undefined);\n if (script) {\n const binDir = options.binDir ?? BIN_DIR;\n await installCommands(env, script, binDir, context);\n }\n const cli = createCli({\n env,\n harness,\n agents,\n scoops,\n roles: () => roles(context),\n ...(options.alive ? { alive: options.alive } : {}),\n ...(options.memory ? { memory: options.memory } : {}),\n ...(options.gelatiere ? { gelatiere: options.gelatiere } : {}),\n ...(options.freezer ? { freezer: options.freezer } : {}),\n ...(options.models ? { models: options.models } : {}),\n layout,\n ...(options.panel ? { panel: options.panel } : {}),\n });\n const plane = planeOf(env, cli, options, context);\n await plane.start(context);\n return { roles, plane, close: (using) => plane.close(using) };\n },\n };\n}\n\nexport type { Rewound, Scoops } from './service.ts';\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/scoops/service.js
CHANGED
|
@@ -93,6 +93,10 @@ async function addFeed(core, tx, parent, feed2) {
|
|
|
93
93
|
};
|
|
94
94
|
return Number(task);
|
|
95
95
|
}
|
|
96
|
+
function choosable(tools) {
|
|
97
|
+
const names = new Set(tools.map((tool) => tool.name));
|
|
98
|
+
return (name) => names.has(name) || (mcpEntries([name]) ?? []).length > 0;
|
|
99
|
+
}
|
|
96
100
|
function selection(tools, names, mcp) {
|
|
97
101
|
if (!names || !tools) return void 0;
|
|
98
102
|
const picked = tools.filter((tool) => names.includes(tool.name));
|
|
@@ -452,6 +456,7 @@ export {
|
|
|
452
456
|
SCOOPS_ROOT,
|
|
453
457
|
ScoopWorkDoc,
|
|
454
458
|
askerName,
|
|
459
|
+
choosable,
|
|
455
460
|
coneOf,
|
|
456
461
|
createScoops,
|
|
457
462
|
findScoop,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/scoops/service.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type AgentChange,\n type AnyTask,\n type Conversation,\n type ConversationId,\n configure,\n defineDoc,\n type Harness,\n type TaskId,\n type ToolRegistration,\n type Tx,\n} from '@earendil-works/pi-durable';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport {\n type Agents,\n type AgentsState,\n freeFolder,\n live,\n type Mark,\n type ScoopRecord,\n scoopId,\n} from '../agents.ts';\nimport { CODEMODE } from '../codemode/index.ts';\nimport type { ProcessGroups } from '../kernel/groups.ts';\nimport { type Layout, LEGACY } from '../layout.ts';\nimport type { Licks } from '../licks/licks.ts';\nimport { McpAllowDoc, mcpEntries } from '../mcp/index.ts';\nimport type { Role } from '../roles/roles.ts';\nimport { visible } from './visible.ts';\n\nexport { SCOOPS_ROOT } from '../layout.ts';\nexport const LEDGER_SIZE = 512;\n\nexport type Feed = {\n scoop: string;\n created: Mark | null;\n report: boolean;\n target: string | null;\n channel: 'scoop-notify' | 'bash';\n from?: string;\n agent?: boolean;\n};\ntype Wait = { scoops: string[]; done: Record<string, string>; cone: string };\nexport type Answer = { code: number; out: string; err?: string; tasks?: number[] };\ntype WorkState = {\n feeds: Record<string, Feed>;\n waits: Record<string, Wait>;\n ledger: Record<string, Answer>;\n turns: Record<string, { at: number; count: number }>;\n};\n\nexport const ScoopWorkDoc = defineDoc<WorkState>({\n kind: 'slicc.scoops.work',\n version: 1,\n scope: 'session',\n initial: () => ({ feeds: {}, waits: {}, ledger: {}, turns: {} }),\n});\n\nexport interface ScoopTasks {\n anchor: AnyTask;\n reporter: AnyTask;\n wait: AnyTask;\n}\n\nexport interface ScoopsHost {\n harness: Harness;\n agents: Agents;\n licks: Licks;\n groups?: ProcessGroups;\n tools?: readonly ToolRegistration[];\n reads?: () => readonly string[];\n files?: Pick<ExecutionEnv, 'remove' | 'createDir'>;\n mcp?: (names: readonly string[]) => ToolRegistration[];\n layout?: Layout;\n enforced?: boolean;\n}\n\nexport interface Rewound {\n stopped: string[];\n restored: string[];\n}\n\nexport interface ScoopSummary {\n id: string;\n folder: string;\n name: string;\n role: string | null;\n busy: boolean;\n}\n\nexport interface Limits {\n maxLiveScoops: number;\n maxPerTurn: number;\n}\n\nexport interface SpawnRequest {\n cone: string;\n kind?: 'async' | 'sync';\n parent?: string;\n depth?: number;\n roots?: { write: string[]; read: string[] };\n cwd?: string;\n origin?: string;\n instructions?: string;\n target?: string | null;\n asker?: string | null;\n name: string;\n role?: Role;\n model?: { provider: string; modelId: string };\n thinking?: AgentChange['thinkingLevel'];\n tools?: string[];\n prompts: string[];\n fromAgent: boolean;\n request?: string;\n limits: Limits;\n}\n\nexport function workspace(folder: string, layout: Layout = LEGACY): string {\n return layout.workspace(folder);\n}\n\nexport async function tail(conversation: Conversation, context: Context): Promise<Mark> {\n const { entries } = await conversation.context(context);\n return { in: conversation.id, at: Number(entries.at(-1)?.id ?? 0) };\n}\n\nexport async function turnStart(conversation: Conversation, context: Context): Promise<number> {\n const { entries } = await conversation.context(context);\n return Number(entries.findLast((entry) => entry.kind === 'pi.user')?.id ?? 0);\n}\n\nexport function coneOf(state: Readonly<AgentsState>, conversation: number): string | undefined {\n return Object.entries(state.cones).find(([, cone]) => cone.conversation === conversation)?.[0];\n}\n\nexport function findScoop(\n state: Readonly<AgentsState>,\n handle: string,\n cone: string | null\n): [string, ScoopRecord] | undefined {\n const id = handle.startsWith('scoop:') ? handle : scoopId(handle);\n const reachable = (record: ScoopRecord | undefined): record is ScoopRecord =>\n !!record && live(record) && (cone === null || record.cone === cone);\n const record = state.scoops[id];\n if (reachable(record)) return [id, record];\n return Object.entries(state.scoops).find(\n ([, candidate]) => reachable(candidate) && candidate.name === handle\n );\n}\n\nfunction remember(work: WorkState, request: string | undefined, answer: Answer): void {\n if (!request) return;\n work.ledger[request] = answer;\n const keys = Object.keys(work.ledger);\n for (const key of keys.slice(0, Math.max(0, keys.length - LEDGER_SIZE))) delete work.ledger[key];\n}\n\ntype Core = { host: Promise<ScoopsHost>; tasks: () => ScoopTasks };\n\nasync function coneOfId(core: Core, coneId: string, context: Context): Promise<Conversation> {\n const { agents } = await core.host;\n const found = await agents.conversation(coneId, context);\n if (!found) throw new Error(`There is no cone ${coneId}.`);\n return found;\n}\n\nasync function isBusy(core: Core, conversation: number, context: Context): Promise<boolean> {\n const { harness } = await core.host;\n const handle = await harness.conversation(conversation as ConversationId, context);\n const view = await handle?.viewState(context);\n const run = (view?.value.docs['pi.live'] as { run?: unknown } | undefined)?.run;\n view?.dispose();\n return Boolean(run);\n}\n\nasync function answered(\n core: Core,\n request: string | undefined,\n context: Context\n): Promise<Answer | undefined> {\n if (!request) return undefined;\n const { harness } = await core.host;\n return (await harness.snapshot(ScoopWorkDoc, context))?.ledger[request];\n}\n\ntype NewFeed = Omit<Feed, 'channel' | 'from'> & {\n from: string;\n agent: boolean;\n prompt: string;\n request?: string;\n followUp?: boolean;\n};\n\nasync function addFeed(core: Core, tx: Tx, parent: number, feed: NewFeed): Promise<number> {\n const task = await tx.createTask(\n core.tasks().reporter as never,\n {\n scoop: feed.scoop,\n prompt: feed.prompt,\n ...(feed.followUp ? { followUp: true } : {}),\n ...(feed.request ? { request: feed.request } : {}),\n },\n {\n ownership: { kind: 'conversation' },\n conversationId: parent as ConversationId,\n background: true,\n }\n );\n (await tx.doc(ScoopWorkDoc)).feeds[String(task)] = {\n scoop: feed.scoop,\n created: feed.created,\n report: feed.report,\n target: feed.target,\n channel: 'scoop-notify',\n from: feed.from,\n agent: feed.agent,\n };\n return Number(task);\n}\n\nfunction selection(\n tools: readonly ToolRegistration[] | undefined,\n names: string[] | undefined,\n mcp?: (names: readonly string[]) => ToolRegistration[]\n) {\n if (!names || !tools) return undefined;\n const picked = tools.filter((tool) => names.includes(tool.name));\n const entries = mcpEntries(names) ?? [];\n if (!entries.length) return picked;\n const codemode = tools.find((tool) => tool.name === CODEMODE);\n if (codemode && !picked.includes(codemode)) picked.push(codemode);\n return [...picked, ...(mcp?.(entries) ?? [])];\n}\n\nfunction liveCount(state: Readonly<AgentsState>, cone: string): number {\n return Object.values(state.scoops).filter((scoop) => scoop.cone === cone && live(scoop)).length;\n}\n\nfunction claimTurn(\n work: WorkState,\n request: SpawnRequest,\n turn: number | null\n): Answer | undefined {\n if (turn === null) return undefined;\n const counted = work.turns[request.cone];\n const count = counted && counted.at === turn ? counted.count : 0;\n if (count >= request.limits.maxPerTurn)\n return {\n code: 1,\n out: `subagent: this turn already started ${count} scoops (the limit is ${request.limits.maxPerTurn})\\n`,\n };\n work.turns[request.cone] = { at: turn, count: count + 1 };\n return undefined;\n}\n\nfunction agentChange(\n request: SpawnRequest,\n folder: string,\n host: Pick<ScoopsHost, 'tools' | 'mcp' | 'layout'>\n): AgentChange {\n const { tools, mcp } = host;\n const model = request.model ?? modelOf(request.role?.model);\n const thinking = request.thinking ?? request.role?.thinking;\n const chosen = selection(tools, request.tools ?? request.role?.tools, mcp);\n const instructions = [request.role?.prompt, request.instructions].filter(Boolean).join('\\n\\n');\n return {\n cwd: request.cwd ?? workspace(folder, host.layout),\n ...(instructions ? { instructions } : {}),\n ...(model ? { model } : {}),\n ...(thinking ? { thinkingLevel: thinking as AgentChange['thinkingLevel'] } : {}),\n ...(chosen ? { tools: chosen } : {}),\n };\n}\n\nasync function narrowMcp(\n tx: Tx,\n state: Readonly<AgentsState>,\n cone: ConversationId,\n child: ConversationId,\n request: SpawnRequest\n): Promise<void> {\n const listed = mcpEntries(request.tools ?? request.role?.tools);\n const caller = state.scoops[request.parent ?? '']?.conversation as ConversationId | undefined;\n const inherited = (await tx.doc(McpAllowDoc, caller ?? cone)).allow;\n if (listed !== null || inherited !== null)\n (await tx.doc(McpAllowDoc, child)).allow = listed ?? inherited;\n}\n\nfunction scoopRecord(\n request: SpawnRequest,\n folder: string,\n conversation: number,\n anchor: number,\n created: Mark | null\n): ScoopRecord {\n return {\n name: request.name.trim() || folder,\n folder,\n cone: request.cone,\n conversation,\n anchor,\n role: request.role?.name ?? null,\n ...(request.role?.memory ? { memory: request.role.memory } : {}),\n ...(request.role?.memory && request.origin ? { origin: request.origin } : {}),\n ...(request.role?.context ? { context: request.role.context } : {}),\n kind: request.kind ?? 'async',\n parent: request.parent ?? request.cone,\n depth: request.depth ?? 1,\n roots: request.roots ?? null,\n created,\n dropped: null,\n gone: false,\n };\n}\n\nasync function spawn(core: Core, request: SpawnRequest, context: Context): Promise<Answer> {\n const done = await answered(core, request.request, context);\n if (done) return done;\n const host = await core.host;\n const { agents } = host;\n const parent = await coneOfId(core, request.cone, context);\n const count = liveCount(agents.state(), request.cone);\n if (count >= request.limits.maxLiveScoops)\n return {\n code: 1,\n out: `subagent: this cone already has ${count} live scoops (the limit is ${request.limits.maxLiveScoops}); stop one first\\n`,\n };\n const created = request.fromAgent ? await tail(parent, context) : null;\n const turn = request.fromAgent ? await turnStart(parent, context) : null;\n return agents.update(async (tx, doc) => {\n const work = await tx.doc(ScoopWorkDoc);\n const refused = claimTurn(work as WorkState, request, turn);\n if (refused) return refused;\n const folder = freeFolder(doc, request.name);\n const id = scoopId(folder);\n const anchor = await tx.createTask(core.tasks().anchor as never, null, {\n ownership: { kind: 'conversation' },\n conversationId: parent.id,\n background: true,\n });\n const child = await tx.createConversation({ ownership: { kind: 'task', taskId: anchor } });\n await configure(tx, child.id, agentChange(request, folder, host));\n await narrowMcp(tx, doc, parent.id, child.id, request);\n doc.scoops[id] = scoopRecord(request, folder, child.id, Number(anchor), created);\n const target =\n request.target === undefined\n ? request.fromAgent\n ? `cone:${request.cone}`\n : null\n : request.target;\n const made: number[] = [];\n const asker = request.asker === undefined ? target : request.asker;\n const from = askerName(doc, asker);\n for (const prompt of request.prompts)\n made.push(\n await addFeed(core, tx, parent.id, {\n scoop: id,\n created,\n report: target !== null,\n target,\n from,\n agent: asker !== null,\n prompt,\n })\n );\n const answer = { code: 0, out: `${folder}\\n`, tasks: made };\n remember(work as WorkState, request.request, answer);\n return answer;\n }, context);\n}\n\nexport function askerName(state: Readonly<AgentsState>, asker: string | null): string {\n if (asker === null) return 'terminal';\n if (asker.startsWith('cone:')) return state.cones[asker.slice('cone:'.length)]?.name ?? asker;\n return state.scoops[asker]?.name ?? asker;\n}\n\nconst missing = (id: string): Answer => ({\n code: 1,\n out: `subagent: there is no scoop ${id.replace('scoop:', '')}\\n`,\n});\n\nasync function feed(\n core: Core,\n id: string,\n prompt: string,\n options: { fromAgent: boolean; request: string; followUp: boolean; target: string | null },\n context: Context\n): Promise<Answer> {\n const done = await answered(core, options.request, context);\n if (done) return done;\n const { harness, agents } = await core.host;\n const record = agents.state().scoops[id];\n if (!record || !live(record)) return missing(id);\n const parent = await coneOfId(core, record.cone, context);\n const created = options.fromAgent ? await tail(parent, context) : null;\n const { target } = options;\n return harness.commit(async (tx) => {\n await addFeed(core, tx, parent.id, {\n scoop: id,\n created,\n report: target !== null,\n target,\n from: askerName(agents.state(), target),\n agent: target !== null,\n prompt,\n followUp: options.followUp,\n request: options.request,\n });\n const answer = { code: 0, out: `sent to ${record.folder}\\n` };\n remember((await tx.doc(ScoopWorkDoc)) as WorkState, options.request, answer);\n return answer;\n }, context);\n}\n\nasync function signal(core: Core, record: ScoopRecord, context: Context): Promise<void> {\n const { harness, groups } = await core.host;\n const conversation = await harness.conversation(record.conversation as ConversationId, context);\n await conversation?.abort(context, { background: true });\n await groups?.signal(record.conversation);\n}\n\nasync function stopFeeds(\n core: Core,\n scoop: string,\n keep: (feed: Feed) => Promise<boolean>,\n context: Context\n) {\n const { harness } = await core.host;\n const work = (await harness.snapshot(ScoopWorkDoc, context)) ?? { feeds: {} };\n const dropped: string[] = [];\n for (const [task, entry] of Object.entries(work.feeds)) {\n if (entry.scoop !== scoop || (await keep(entry))) continue;\n const record = await harness.getTask(Number(task) as TaskId, context);\n if (record && record.state.status !== 'terminal')\n await harness.abortTask(Number(task) as TaskId, context);\n dropped.push(task);\n }\n if (dropped.length)\n await harness.commit(async (tx) => {\n const doc = await tx.doc(ScoopWorkDoc);\n for (const task of dropped) delete doc.feeds[task];\n }, context);\n return dropped.length;\n}\n\nasync function stop(core: Core, id: string, fromAgent: boolean, context: Context): Promise<Answer> {\n const { agents, files, layout = LEGACY } = await core.host;\n const record = agents.state().scoops[id];\n if (!record || !live(record)) return missing(id);\n await stopFeeds(core, id, async () => false, context);\n await signal(core, record, context);\n const mark = fromAgent\n ? await tail(await coneOfId(core, record.cone, context), context)\n : { in: 0, at: 0 };\n await agents.update((_tx, state) => {\n (state.scoops[id] as ScoopRecord).dropped = mark;\n }, context);\n const place = layout.workspace(record.folder);\n const removed = await files?.remove(place, { recursive: true, force: true }, context);\n const folder = removed?.ok ? `removed ${place}` : `kept ${place}`;\n return {\n code: 0,\n out: `stopped ${record.folder}; its transcript is kept, ${folder}, and its reports stay in ${layout.reports(record.folder, record.cone)}\\n`,\n };\n}\n\nasync function rename(core: Core, id: string, name: string, context: Context): Promise<Answer> {\n const { agents } = await core.host;\n const record = agents.state().scoops[id];\n if (!record || !live(record)) return missing(id);\n const wanted = name.trim();\n if (!wanted) return { code: 2, out: 'subagent: rename needs a non-empty name\\n' };\n await agents.update((_tx, state) => {\n (state.scoops[id] as ScoopRecord).name = wanted;\n }, context);\n return { code: 0, out: `${record.folder} is now called ${wanted}\\n` };\n}\n\nasync function resetWorkspace(\n host: Pick<ScoopsHost, 'files' | 'layout'>,\n folder: string,\n context: Context\n): Promise<boolean> {\n const { files } = host;\n if (!files) return true;\n const place = workspace(folder, host.layout);\n const cleared = await files.remove(place, { recursive: true, force: true }, context);\n if (!cleared.ok) return false;\n if (host.layout?.users) return true;\n return (await files.createDir(place, { recursive: true }, context)).ok;\n}\n\ntype Seen = (mark: Mark | null) => Promise<boolean>;\n\nasync function rewindOne(\n core: Core,\n coneId: string,\n id: string,\n record: ScoopRecord,\n seen: Seen,\n result: Rewound,\n context: Context\n): Promise<Partial<ScoopRecord> | undefined> {\n const host = await core.host;\n const { harness, licks } = host;\n if (!(await seen(record.created))) {\n if (live(record)) {\n await stopFeeds(core, id, async () => false, context);\n await signal(core, record, context);\n result.stopped.push(record.name);\n }\n return { gone: true };\n }\n if (record.dropped !== null) {\n if (await seen(record.dropped)) return undefined;\n const reset = await resetWorkspace(host, record.folder, context);\n result.restored.push(\n reset ? record.name : `${record.name} (its working folder could not be reset)`\n );\n return { dropped: null };\n }\n const withdrawn = await stopFeeds(core, id, (entry) => seen(entry.created), context);\n if (!withdrawn) return undefined;\n const conversation = await harness.conversation(record.conversation as ConversationId, context);\n await conversation?.abort(context);\n await licks.forget(`cone:${coneId}`, 'scoop-notify', id, context);\n return undefined;\n}\n\nasync function rewound(\n core: Core,\n coneId: string,\n fork: Conversation,\n context: Context\n): Promise<Rewound> {\n const { harness, agents } = await core.host;\n const read = (id: number) =>\n harness.commit((tx) => tx.conversation(id as ConversationId), context);\n const seen: Seen = async (mark) =>\n mark === null || mark.in === 0 || (await visible(read, fork.id, mark));\n const result: Rewound = { stopped: [], restored: [] };\n const changes: [string, Partial<ScoopRecord>][] = [];\n for (const [id, record] of Object.entries(agents.state().scoops)) {\n if (record.cone !== coneId || record.gone) continue;\n const change = await rewindOne(core, coneId, id, record, seen, result, context);\n if (change) changes.push([id, change]);\n }\n if (changes.length)\n await agents.update((_tx, state) => {\n for (const [id, change] of changes) Object.assign(state.scoops[id] as ScoopRecord, change);\n }, context);\n return result;\n}\n\nasync function list(core: Core, coneId: string | null, context: Context): Promise<ScoopSummary[]> {\n const { agents } = await core.host;\n const out: ScoopSummary[] = [];\n for (const [id, record] of Object.entries(agents.state().scoops)) {\n if ((coneId !== null && record.cone !== coneId) || !live(record)) continue;\n out.push({\n id,\n folder: record.folder,\n name: record.name,\n role: record.role,\n busy: await isBusy(core, record.conversation, context),\n });\n }\n return out;\n}\n\nasync function working(core: Core, ids: readonly string[], context: Context): Promise<string[]> {\n const { harness, agents } = await core.host;\n const feeds = Object.values((await harness.snapshot(ScoopWorkDoc, context))?.feeds ?? {});\n const out: string[] = [];\n for (const id of ids) {\n const record = agents.state().scoops[id];\n if (!record) continue;\n if (\n feeds.some((entry) => entry.scoop === id) ||\n (await isBusy(core, record.conversation, context))\n )\n out.push(id);\n }\n return out;\n}\n\nasync function notifyWhenDone(\n core: Core,\n coneId: string,\n ids: string[],\n deadline: number,\n context: Context\n) {\n const { harness } = await core.host;\n const parent = await coneOfId(core, coneId, context);\n await harness.commit(\n (tx) =>\n tx.createTask(\n core.tasks().wait as never,\n { scoops: ids, cone: coneId, deadline },\n { ownership: { kind: 'conversation' }, conversationId: parent.id, background: true }\n ),\n context\n );\n}\n\nasync function detach(core: Core, tasks: readonly number[], target: string, context: Context) {\n const { harness } = await core.host;\n await harness.commit(async (tx) => {\n const work = await tx.doc(ScoopWorkDoc);\n for (const task of tasks) {\n const entry = work.feeds[String(task)];\n if (!entry) continue;\n entry.report = true;\n entry.target = target;\n entry.channel = 'bash';\n }\n }, context);\n}\n\nasync function note(core: Core, id: string, text: string, context: Context): Promise<Answer> {\n const { agents, licks } = await core.host;\n const record = agents.state().scoops[id];\n if (!record || !live(record)) return missing(id);\n const label = `${record.name} (${record.role ?? 'scoop'})`;\n await licks.deliver(\n {\n channel: 'scoop-notify',\n source: `${id}#note`,\n title: label,\n text: `[scoop ${label} progress]`,\n body: text,\n target: `cone:${record.cone}`,\n },\n context\n );\n return { code: 0, out: 'note sent to the cone\\n' };\n}\n\nexport function createScoops(host: Promise<ScoopsHost>, tasks: () => ScoopTasks) {\n const core: Core = { host, tasks };\n return {\n detach: (made: readonly number[], target: string, context: Context) =>\n detach(core, made, target, context),\n note: (id: string, text: string, context: Context) => note(core, id, text, context),\n spawn: (request: SpawnRequest, context: Context) => spawn(core, request, context),\n feed: (\n id: string,\n prompt: string,\n options: { fromAgent: boolean; request: string; followUp: boolean; target: string | null },\n context: Context\n ) => feed(core, id, prompt, options, context),\n stop: (id: string, fromAgent: boolean, context: Context) => stop(core, id, fromAgent, context),\n rename: (id: string, name: string, context: Context) => rename(core, id, name, context),\n rewound: (coneId: string, fork: Conversation, context: Context) =>\n rewound(core, coneId, fork, context),\n list: (coneId: string | null, context: Context) => list(core, coneId, context),\n busy: (conversation: number, context: Context) => isBusy(core, conversation, context),\n working: (ids: readonly string[], context: Context) => working(core, ids, context),\n notifyWhenDone: (coneId: string, ids: string[], deadline: number, context: Context) =>\n notifyWhenDone(core, coneId, ids, deadline, context),\n };\n}\n\nexport function modelOf(\n value: string | undefined\n): { provider: string; modelId: string } | undefined {\n if (!value) return undefined;\n const at = value.indexOf('/');\n if (at <= 0) return undefined;\n return { provider: value.slice(0, at), modelId: value.slice(at + 1) };\n}\n\nexport type Scoops = ReturnType<typeof createScoops>;\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type AgentChange,\n type AnyTask,\n type Conversation,\n type ConversationId,\n configure,\n defineDoc,\n type Harness,\n type TaskId,\n type ToolRegistration,\n type Tx,\n} from '@earendil-works/pi-durable';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport {\n type Agents,\n type AgentsState,\n freeFolder,\n live,\n type Mark,\n type ScoopRecord,\n scoopId,\n} from '../agents.ts';\nimport { CODEMODE } from '../codemode/index.ts';\nimport type { ProcessGroups } from '../kernel/groups.ts';\nimport { type Layout, LEGACY } from '../layout.ts';\nimport type { Licks } from '../licks/licks.ts';\nimport { McpAllowDoc, mcpEntries } from '../mcp/index.ts';\nimport type { Role } from '../roles/roles.ts';\nimport type { Choosable } from '../selection.ts';\nimport { visible } from './visible.ts';\n\nexport { SCOOPS_ROOT } from '../layout.ts';\nexport const LEDGER_SIZE = 512;\n\nexport type Feed = {\n scoop: string;\n created: Mark | null;\n report: boolean;\n target: string | null;\n channel: 'scoop-notify' | 'bash';\n from?: string;\n agent?: boolean;\n};\ntype Wait = { scoops: string[]; done: Record<string, string>; cone: string };\nexport type Answer = { code: number; out: string; err?: string; tasks?: number[] };\ntype WorkState = {\n feeds: Record<string, Feed>;\n waits: Record<string, Wait>;\n ledger: Record<string, Answer>;\n turns: Record<string, { at: number; count: number }>;\n};\n\nexport const ScoopWorkDoc = defineDoc<WorkState>({\n kind: 'slicc.scoops.work',\n version: 1,\n scope: 'session',\n initial: () => ({ feeds: {}, waits: {}, ledger: {}, turns: {} }),\n});\n\nexport interface ScoopTasks {\n anchor: AnyTask;\n reporter: AnyTask;\n wait: AnyTask;\n}\n\nexport type EnvOf = (\n conversationId: ConversationId,\n context: Context\n) => Promise<Pick<ExecutionEnv, 'canonicalPath'> | undefined>;\n\nexport interface ScoopsHost {\n harness: Harness;\n agents: Agents;\n licks: Licks;\n groups?: ProcessGroups;\n tools?: readonly ToolRegistration[];\n reads?: () => readonly string[];\n files?: Pick<ExecutionEnv, 'remove' | 'createDir'>;\n envOf?: EnvOf;\n mcp?: (names: readonly string[]) => ToolRegistration[];\n layout?: Layout;\n enforced?: boolean;\n}\n\nexport interface Rewound {\n stopped: string[];\n restored: string[];\n}\n\nexport interface ScoopSummary {\n id: string;\n folder: string;\n name: string;\n role: string | null;\n busy: boolean;\n}\n\nexport interface Limits {\n maxLiveScoops: number;\n maxPerTurn: number;\n}\n\nexport interface SpawnRequest {\n cone: string;\n kind?: 'async' | 'sync';\n parent?: string;\n depth?: number;\n roots?: { write: string[]; read: string[] };\n cwd?: string;\n origin?: string;\n instructions?: string;\n target?: string | null;\n asker?: string | null;\n name: string;\n role?: Role;\n model?: { provider: string; modelId: string };\n thinking?: AgentChange['thinkingLevel'];\n tools?: string[];\n prompts: string[];\n fromAgent: boolean;\n request?: string;\n limits: Limits;\n}\n\nexport function workspace(folder: string, layout: Layout = LEGACY): string {\n return layout.workspace(folder);\n}\n\nexport async function tail(conversation: Conversation, context: Context): Promise<Mark> {\n const { entries } = await conversation.context(context);\n return { in: conversation.id, at: Number(entries.at(-1)?.id ?? 0) };\n}\n\nexport async function turnStart(conversation: Conversation, context: Context): Promise<number> {\n const { entries } = await conversation.context(context);\n return Number(entries.findLast((entry) => entry.kind === 'pi.user')?.id ?? 0);\n}\n\nexport function coneOf(state: Readonly<AgentsState>, conversation: number): string | undefined {\n return Object.entries(state.cones).find(([, cone]) => cone.conversation === conversation)?.[0];\n}\n\nexport function findScoop(\n state: Readonly<AgentsState>,\n handle: string,\n cone: string | null\n): [string, ScoopRecord] | undefined {\n const id = handle.startsWith('scoop:') ? handle : scoopId(handle);\n const reachable = (record: ScoopRecord | undefined): record is ScoopRecord =>\n !!record && live(record) && (cone === null || record.cone === cone);\n const record = state.scoops[id];\n if (reachable(record)) return [id, record];\n return Object.entries(state.scoops).find(\n ([, candidate]) => reachable(candidate) && candidate.name === handle\n );\n}\n\nfunction remember(work: WorkState, request: string | undefined, answer: Answer): void {\n if (!request) return;\n work.ledger[request] = answer;\n const keys = Object.keys(work.ledger);\n for (const key of keys.slice(0, Math.max(0, keys.length - LEDGER_SIZE))) delete work.ledger[key];\n}\n\ntype Core = { host: Promise<ScoopsHost>; tasks: () => ScoopTasks };\n\nasync function coneOfId(core: Core, coneId: string, context: Context): Promise<Conversation> {\n const { agents } = await core.host;\n const found = await agents.conversation(coneId, context);\n if (!found) throw new Error(`There is no cone ${coneId}.`);\n return found;\n}\n\nasync function isBusy(core: Core, conversation: number, context: Context): Promise<boolean> {\n const { harness } = await core.host;\n const handle = await harness.conversation(conversation as ConversationId, context);\n const view = await handle?.viewState(context);\n const run = (view?.value.docs['pi.live'] as { run?: unknown } | undefined)?.run;\n view?.dispose();\n return Boolean(run);\n}\n\nasync function answered(\n core: Core,\n request: string | undefined,\n context: Context\n): Promise<Answer | undefined> {\n if (!request) return undefined;\n const { harness } = await core.host;\n return (await harness.snapshot(ScoopWorkDoc, context))?.ledger[request];\n}\n\ntype NewFeed = Omit<Feed, 'channel' | 'from'> & {\n from: string;\n agent: boolean;\n prompt: string;\n request?: string;\n followUp?: boolean;\n};\n\nasync function addFeed(core: Core, tx: Tx, parent: number, feed: NewFeed): Promise<number> {\n const task = await tx.createTask(\n core.tasks().reporter as never,\n {\n scoop: feed.scoop,\n prompt: feed.prompt,\n ...(feed.followUp ? { followUp: true } : {}),\n ...(feed.request ? { request: feed.request } : {}),\n },\n {\n ownership: { kind: 'conversation' },\n conversationId: parent as ConversationId,\n background: true,\n }\n );\n (await tx.doc(ScoopWorkDoc)).feeds[String(task)] = {\n scoop: feed.scoop,\n created: feed.created,\n report: feed.report,\n target: feed.target,\n channel: 'scoop-notify',\n from: feed.from,\n agent: feed.agent,\n };\n return Number(task);\n}\n\nexport function choosable(tools: readonly ToolRegistration[]): Choosable {\n const names = new Set(tools.map((tool) => tool.name));\n return (name) => names.has(name) || (mcpEntries([name]) ?? []).length > 0;\n}\n\nfunction selection(\n tools: readonly ToolRegistration[] | undefined,\n names: string[] | undefined,\n mcp?: (names: readonly string[]) => ToolRegistration[]\n) {\n if (!names || !tools) return undefined;\n const picked = tools.filter((tool) => names.includes(tool.name));\n const entries = mcpEntries(names) ?? [];\n if (!entries.length) return picked;\n const codemode = tools.find((tool) => tool.name === CODEMODE);\n if (codemode && !picked.includes(codemode)) picked.push(codemode);\n return [...picked, ...(mcp?.(entries) ?? [])];\n}\n\nfunction liveCount(state: Readonly<AgentsState>, cone: string): number {\n return Object.values(state.scoops).filter((scoop) => scoop.cone === cone && live(scoop)).length;\n}\n\nfunction claimTurn(\n work: WorkState,\n request: SpawnRequest,\n turn: number | null\n): Answer | undefined {\n if (turn === null) return undefined;\n const counted = work.turns[request.cone];\n const count = counted && counted.at === turn ? counted.count : 0;\n if (count >= request.limits.maxPerTurn)\n return {\n code: 1,\n out: `subagent: this turn already started ${count} scoops (the limit is ${request.limits.maxPerTurn})\\n`,\n };\n work.turns[request.cone] = { at: turn, count: count + 1 };\n return undefined;\n}\n\nfunction agentChange(\n request: SpawnRequest,\n folder: string,\n host: Pick<ScoopsHost, 'tools' | 'mcp' | 'layout'>\n): AgentChange {\n const { tools, mcp } = host;\n const model = request.model ?? modelOf(request.role?.model);\n const thinking = request.thinking ?? request.role?.thinking;\n const chosen = selection(tools, request.tools ?? request.role?.tools, mcp);\n const instructions = [request.role?.prompt, request.instructions].filter(Boolean).join('\\n\\n');\n return {\n cwd: request.cwd ?? workspace(folder, host.layout),\n ...(instructions ? { instructions } : {}),\n ...(model ? { model } : {}),\n ...(thinking ? { thinkingLevel: thinking as AgentChange['thinkingLevel'] } : {}),\n ...(chosen ? { tools: chosen } : {}),\n };\n}\n\nasync function narrowMcp(\n tx: Tx,\n state: Readonly<AgentsState>,\n cone: ConversationId,\n child: ConversationId,\n request: SpawnRequest\n): Promise<void> {\n const listed = mcpEntries(request.tools ?? request.role?.tools);\n const caller = state.scoops[request.parent ?? '']?.conversation as ConversationId | undefined;\n const inherited = (await tx.doc(McpAllowDoc, caller ?? cone)).allow;\n if (listed !== null || inherited !== null)\n (await tx.doc(McpAllowDoc, child)).allow = listed ?? inherited;\n}\n\nfunction scoopRecord(\n request: SpawnRequest,\n folder: string,\n conversation: number,\n anchor: number,\n created: Mark | null\n): ScoopRecord {\n return {\n name: request.name.trim() || folder,\n folder,\n cone: request.cone,\n conversation,\n anchor,\n role: request.role?.name ?? null,\n ...(request.role?.memory ? { memory: request.role.memory } : {}),\n ...(request.role?.memory && request.origin ? { origin: request.origin } : {}),\n ...(request.role?.context ? { context: request.role.context } : {}),\n kind: request.kind ?? 'async',\n parent: request.parent ?? request.cone,\n depth: request.depth ?? 1,\n roots: request.roots ?? null,\n created,\n dropped: null,\n gone: false,\n };\n}\n\nasync function spawn(core: Core, request: SpawnRequest, context: Context): Promise<Answer> {\n const done = await answered(core, request.request, context);\n if (done) return done;\n const host = await core.host;\n const { agents } = host;\n const parent = await coneOfId(core, request.cone, context);\n const count = liveCount(agents.state(), request.cone);\n if (count >= request.limits.maxLiveScoops)\n return {\n code: 1,\n out: `subagent: this cone already has ${count} live scoops (the limit is ${request.limits.maxLiveScoops}); stop one first\\n`,\n };\n const created = request.fromAgent ? await tail(parent, context) : null;\n const turn = request.fromAgent ? await turnStart(parent, context) : null;\n return agents.update(async (tx, doc) => {\n const work = await tx.doc(ScoopWorkDoc);\n const refused = claimTurn(work as WorkState, request, turn);\n if (refused) return refused;\n const folder = freeFolder(doc, request.name);\n const id = scoopId(folder);\n const anchor = await tx.createTask(core.tasks().anchor as never, null, {\n ownership: { kind: 'conversation' },\n conversationId: parent.id,\n background: true,\n });\n const child = await tx.createConversation({ ownership: { kind: 'task', taskId: anchor } });\n await configure(tx, child.id, agentChange(request, folder, host));\n await narrowMcp(tx, doc, parent.id, child.id, request);\n doc.scoops[id] = scoopRecord(request, folder, child.id, Number(anchor), created);\n const target =\n request.target === undefined\n ? request.fromAgent\n ? `cone:${request.cone}`\n : null\n : request.target;\n const made: number[] = [];\n const asker = request.asker === undefined ? target : request.asker;\n const from = askerName(doc, asker);\n for (const prompt of request.prompts)\n made.push(\n await addFeed(core, tx, parent.id, {\n scoop: id,\n created,\n report: target !== null,\n target,\n from,\n agent: asker !== null,\n prompt,\n })\n );\n const answer = { code: 0, out: `${folder}\\n`, tasks: made };\n remember(work as WorkState, request.request, answer);\n return answer;\n }, context);\n}\n\nexport function askerName(state: Readonly<AgentsState>, asker: string | null): string {\n if (asker === null) return 'terminal';\n if (asker.startsWith('cone:')) return state.cones[asker.slice('cone:'.length)]?.name ?? asker;\n return state.scoops[asker]?.name ?? asker;\n}\n\nconst missing = (id: string): Answer => ({\n code: 1,\n out: `subagent: there is no scoop ${id.replace('scoop:', '')}\\n`,\n});\n\nasync function feed(\n core: Core,\n id: string,\n prompt: string,\n options: { fromAgent: boolean; request: string; followUp: boolean; target: string | null },\n context: Context\n): Promise<Answer> {\n const done = await answered(core, options.request, context);\n if (done) return done;\n const { harness, agents } = await core.host;\n const record = agents.state().scoops[id];\n if (!record || !live(record)) return missing(id);\n const parent = await coneOfId(core, record.cone, context);\n const created = options.fromAgent ? await tail(parent, context) : null;\n const { target } = options;\n return harness.commit(async (tx) => {\n await addFeed(core, tx, parent.id, {\n scoop: id,\n created,\n report: target !== null,\n target,\n from: askerName(agents.state(), target),\n agent: target !== null,\n prompt,\n followUp: options.followUp,\n request: options.request,\n });\n const answer = { code: 0, out: `sent to ${record.folder}\\n` };\n remember((await tx.doc(ScoopWorkDoc)) as WorkState, options.request, answer);\n return answer;\n }, context);\n}\n\nasync function signal(core: Core, record: ScoopRecord, context: Context): Promise<void> {\n const { harness, groups } = await core.host;\n const conversation = await harness.conversation(record.conversation as ConversationId, context);\n await conversation?.abort(context, { background: true });\n await groups?.signal(record.conversation);\n}\n\nasync function stopFeeds(\n core: Core,\n scoop: string,\n keep: (feed: Feed) => Promise<boolean>,\n context: Context\n) {\n const { harness } = await core.host;\n const work = (await harness.snapshot(ScoopWorkDoc, context)) ?? { feeds: {} };\n const dropped: string[] = [];\n for (const [task, entry] of Object.entries(work.feeds)) {\n if (entry.scoop !== scoop || (await keep(entry))) continue;\n const record = await harness.getTask(Number(task) as TaskId, context);\n if (record && record.state.status !== 'terminal')\n await harness.abortTask(Number(task) as TaskId, context);\n dropped.push(task);\n }\n if (dropped.length)\n await harness.commit(async (tx) => {\n const doc = await tx.doc(ScoopWorkDoc);\n for (const task of dropped) delete doc.feeds[task];\n }, context);\n return dropped.length;\n}\n\nasync function stop(core: Core, id: string, fromAgent: boolean, context: Context): Promise<Answer> {\n const { agents, files, layout = LEGACY } = await core.host;\n const record = agents.state().scoops[id];\n if (!record || !live(record)) return missing(id);\n await stopFeeds(core, id, async () => false, context);\n await signal(core, record, context);\n const mark = fromAgent\n ? await tail(await coneOfId(core, record.cone, context), context)\n : { in: 0, at: 0 };\n await agents.update((_tx, state) => {\n (state.scoops[id] as ScoopRecord).dropped = mark;\n }, context);\n const place = layout.workspace(record.folder);\n const removed = await files?.remove(place, { recursive: true, force: true }, context);\n const folder = removed?.ok ? `removed ${place}` : `kept ${place}`;\n return {\n code: 0,\n out: `stopped ${record.folder}; its transcript is kept, ${folder}, and its reports stay in ${layout.reports(record.folder, record.cone)}\\n`,\n };\n}\n\nasync function rename(core: Core, id: string, name: string, context: Context): Promise<Answer> {\n const { agents } = await core.host;\n const record = agents.state().scoops[id];\n if (!record || !live(record)) return missing(id);\n const wanted = name.trim();\n if (!wanted) return { code: 2, out: 'subagent: rename needs a non-empty name\\n' };\n await agents.update((_tx, state) => {\n (state.scoops[id] as ScoopRecord).name = wanted;\n }, context);\n return { code: 0, out: `${record.folder} is now called ${wanted}\\n` };\n}\n\nasync function resetWorkspace(\n host: Pick<ScoopsHost, 'files' | 'layout'>,\n folder: string,\n context: Context\n): Promise<boolean> {\n const { files } = host;\n if (!files) return true;\n const place = workspace(folder, host.layout);\n const cleared = await files.remove(place, { recursive: true, force: true }, context);\n if (!cleared.ok) return false;\n if (host.layout?.users) return true;\n return (await files.createDir(place, { recursive: true }, context)).ok;\n}\n\ntype Seen = (mark: Mark | null) => Promise<boolean>;\n\nasync function rewindOne(\n core: Core,\n coneId: string,\n id: string,\n record: ScoopRecord,\n seen: Seen,\n result: Rewound,\n context: Context\n): Promise<Partial<ScoopRecord> | undefined> {\n const host = await core.host;\n const { harness, licks } = host;\n if (!(await seen(record.created))) {\n if (live(record)) {\n await stopFeeds(core, id, async () => false, context);\n await signal(core, record, context);\n result.stopped.push(record.name);\n }\n return { gone: true };\n }\n if (record.dropped !== null) {\n if (await seen(record.dropped)) return undefined;\n const reset = await resetWorkspace(host, record.folder, context);\n result.restored.push(\n reset ? record.name : `${record.name} (its working folder could not be reset)`\n );\n return { dropped: null };\n }\n const withdrawn = await stopFeeds(core, id, (entry) => seen(entry.created), context);\n if (!withdrawn) return undefined;\n const conversation = await harness.conversation(record.conversation as ConversationId, context);\n await conversation?.abort(context);\n await licks.forget(`cone:${coneId}`, 'scoop-notify', id, context);\n return undefined;\n}\n\nasync function rewound(\n core: Core,\n coneId: string,\n fork: Conversation,\n context: Context\n): Promise<Rewound> {\n const { harness, agents } = await core.host;\n const read = (id: number) =>\n harness.commit((tx) => tx.conversation(id as ConversationId), context);\n const seen: Seen = async (mark) =>\n mark === null || mark.in === 0 || (await visible(read, fork.id, mark));\n const result: Rewound = { stopped: [], restored: [] };\n const changes: [string, Partial<ScoopRecord>][] = [];\n for (const [id, record] of Object.entries(agents.state().scoops)) {\n if (record.cone !== coneId || record.gone) continue;\n const change = await rewindOne(core, coneId, id, record, seen, result, context);\n if (change) changes.push([id, change]);\n }\n if (changes.length)\n await agents.update((_tx, state) => {\n for (const [id, change] of changes) Object.assign(state.scoops[id] as ScoopRecord, change);\n }, context);\n return result;\n}\n\nasync function list(core: Core, coneId: string | null, context: Context): Promise<ScoopSummary[]> {\n const { agents } = await core.host;\n const out: ScoopSummary[] = [];\n for (const [id, record] of Object.entries(agents.state().scoops)) {\n if ((coneId !== null && record.cone !== coneId) || !live(record)) continue;\n out.push({\n id,\n folder: record.folder,\n name: record.name,\n role: record.role,\n busy: await isBusy(core, record.conversation, context),\n });\n }\n return out;\n}\n\nasync function working(core: Core, ids: readonly string[], context: Context): Promise<string[]> {\n const { harness, agents } = await core.host;\n const feeds = Object.values((await harness.snapshot(ScoopWorkDoc, context))?.feeds ?? {});\n const out: string[] = [];\n for (const id of ids) {\n const record = agents.state().scoops[id];\n if (!record) continue;\n if (\n feeds.some((entry) => entry.scoop === id) ||\n (await isBusy(core, record.conversation, context))\n )\n out.push(id);\n }\n return out;\n}\n\nasync function notifyWhenDone(\n core: Core,\n coneId: string,\n ids: string[],\n deadline: number,\n context: Context\n) {\n const { harness } = await core.host;\n const parent = await coneOfId(core, coneId, context);\n await harness.commit(\n (tx) =>\n tx.createTask(\n core.tasks().wait as never,\n { scoops: ids, cone: coneId, deadline },\n { ownership: { kind: 'conversation' }, conversationId: parent.id, background: true }\n ),\n context\n );\n}\n\nasync function detach(core: Core, tasks: readonly number[], target: string, context: Context) {\n const { harness } = await core.host;\n await harness.commit(async (tx) => {\n const work = await tx.doc(ScoopWorkDoc);\n for (const task of tasks) {\n const entry = work.feeds[String(task)];\n if (!entry) continue;\n entry.report = true;\n entry.target = target;\n entry.channel = 'bash';\n }\n }, context);\n}\n\nasync function note(core: Core, id: string, text: string, context: Context): Promise<Answer> {\n const { agents, licks } = await core.host;\n const record = agents.state().scoops[id];\n if (!record || !live(record)) return missing(id);\n const label = `${record.name} (${record.role ?? 'scoop'})`;\n await licks.deliver(\n {\n channel: 'scoop-notify',\n source: `${id}#note`,\n title: label,\n text: `[scoop ${label} progress]`,\n body: text,\n target: `cone:${record.cone}`,\n },\n context\n );\n return { code: 0, out: 'note sent to the cone\\n' };\n}\n\nexport function createScoops(host: Promise<ScoopsHost>, tasks: () => ScoopTasks) {\n const core: Core = { host, tasks };\n return {\n detach: (made: readonly number[], target: string, context: Context) =>\n detach(core, made, target, context),\n note: (id: string, text: string, context: Context) => note(core, id, text, context),\n spawn: (request: SpawnRequest, context: Context) => spawn(core, request, context),\n feed: (\n id: string,\n prompt: string,\n options: { fromAgent: boolean; request: string; followUp: boolean; target: string | null },\n context: Context\n ) => feed(core, id, prompt, options, context),\n stop: (id: string, fromAgent: boolean, context: Context) => stop(core, id, fromAgent, context),\n rename: (id: string, name: string, context: Context) => rename(core, id, name, context),\n rewound: (coneId: string, fork: Conversation, context: Context) =>\n rewound(core, coneId, fork, context),\n list: (coneId: string | null, context: Context) => list(core, coneId, context),\n busy: (conversation: number, context: Context) => isBusy(core, conversation, context),\n working: (ids: readonly string[], context: Context) => working(core, ids, context),\n notifyWhenDone: (coneId: string, ids: string[], deadline: number, context: Context) =>\n notifyWhenDone(core, coneId, ids, deadline, context),\n };\n}\n\nexport function modelOf(\n value: string | undefined\n): { provider: string; modelId: string } | undefined {\n if (!value) return undefined;\n const at = value.indexOf('/');\n if (at <= 0) return undefined;\n return { provider: value.slice(0, at), modelId: value.slice(at + 1) };\n}\n\nexport type Scoops = ReturnType<typeof createScoops>;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["record", "feed"]
|
|
7
7
|
}
|
|
@@ -5,8 +5,6 @@ import {
|
|
|
5
5
|
section,
|
|
6
6
|
ToolTask
|
|
7
7
|
} from "@earendil-works/pi-durable";
|
|
8
|
-
import { GUEST_GUARD } from "../guests/guard.js";
|
|
9
|
-
import { TITLES } from "../titles/extension.js";
|
|
10
8
|
import { listedLine, SECRETS_KIND } from "./view.js";
|
|
11
9
|
const SECRETS = "slicc-secrets";
|
|
12
10
|
const SECRETS_COMMAND = "/secrets";
|
|
@@ -102,32 +100,6 @@ function secretsEntry(names) {
|
|
|
102
100
|
function shellGrants(names) {
|
|
103
101
|
return (argv) => names.state.value.some((name) => name.exec === "shell") ? [...SHELL_GRANTS, ...argv] : argv;
|
|
104
102
|
}
|
|
105
|
-
const missing = (list) => [SECRETS, TITLES, GUEST_GUARD].filter(
|
|
106
|
-
(name) => !list.some((extension) => extension.name === name)
|
|
107
|
-
);
|
|
108
|
-
function pinned(change) {
|
|
109
|
-
const list = change.extensions;
|
|
110
|
-
if (!Array.isArray(list)) return change;
|
|
111
|
-
const added = missing(list);
|
|
112
|
-
return added.length ? { ...change, extensions: [...list, ...added.map((name) => ({ name }))] } : change;
|
|
113
|
-
}
|
|
114
|
-
async function repin(harness, storage, context) {
|
|
115
|
-
let fixed = 0;
|
|
116
|
-
let cursor;
|
|
117
|
-
do {
|
|
118
|
-
const page = await storage.scanConversations({}, 100, cursor, context);
|
|
119
|
-
for (const record of page.items) {
|
|
120
|
-
const conversation = await harness.conversation(record.id, context);
|
|
121
|
-
if (!conversation) continue;
|
|
122
|
-
const agent = await conversation.agent(context);
|
|
123
|
-
if (!missing(agent.extensions).length) continue;
|
|
124
|
-
await conversation.configure(pinned({ extensions: agent.extensions }), context);
|
|
125
|
-
fixed++;
|
|
126
|
-
}
|
|
127
|
-
cursor = page.next;
|
|
128
|
-
} while (cursor);
|
|
129
|
-
return fixed;
|
|
130
|
-
}
|
|
131
103
|
function secretsExtension(deps) {
|
|
132
104
|
return defineExtension({
|
|
133
105
|
name: SECRETS,
|
|
@@ -150,9 +122,7 @@ export {
|
|
|
150
122
|
SECRETS_KIND,
|
|
151
123
|
SHELL_GRANTS,
|
|
152
124
|
listedLine,
|
|
153
|
-
pinned,
|
|
154
125
|
renderSecrets,
|
|
155
|
-
repin,
|
|
156
126
|
scrubResult,
|
|
157
127
|
secretsEntry,
|
|
158
128
|
secretsExtension,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/secrets/extension.ts"],
|
|
4
|
-
"sourcesContent": ["import type {
|
|
5
|
-
"mappings": "AACA;AAAA,
|
|
4
|
+
"sourcesContent": ["import type { JsonValue } from '@earendil-works/chord';\nimport {\n defineExtension,\n type Extension,\n GenerationTask,\n hook,\n section,\n type ToolExecutionResult,\n ToolTask,\n} from '@earendil-works/pi-durable';\nimport type { Names, SecretName } from './names.ts';\nimport type { Scrub } from './scrub.ts';\nimport type { Vault } from './vault.ts';\nimport { listedLine, SECRETS_KIND } from './view.ts';\n\nexport { listedLine, SECRETS_KIND };\n\nexport const SECRETS = 'slicc-secrets';\nexport const SECRETS_COMMAND = '/secrets';\nexport const SHELL_GRANTS = ['fnox', 'exec', '--shell-grants', '--'] as const;\n\nexport const MODEL_LINE =\n \"Credentials are env vars (`$NAME`) holding placeholders; `secret list` shows them and where each works. The vault fills in the real value only in the listed header, for the listed domain, method and path, over HTTPS (curl, git, fetch). Adding a rule means editing `/etc/fnox/config.toml` with sudo, which the user approves. `fnox exec` gives real values only for secrets the user enabled for exec, and the secrets listed as 'in every bash' already hold real values in your commands. Everything is logged, and stored values are replaced by their placeholder in everything you see; edit around placeholders, since files show them too. Never ask the user to paste a credential into the chat.\";\n\ntype Leaf = { get(): string; set(text: string): void };\n\nfunction leaves(value: JsonValue, out: Leaf[], set: (next: JsonValue) => void): void {\n if (typeof value === 'string') {\n out.push({ get: () => value, set });\n return;\n }\n if (Array.isArray(value)) {\n value.forEach((item, at) => {\n leaves(item, out, (next) => {\n value[at] = next;\n });\n });\n return;\n }\n if (value && typeof value === 'object')\n for (const [key, item] of Object.entries(value))\n leaves(item as JsonValue, out, (next) => {\n (value as Record<string, JsonValue>)[key] = next;\n });\n}\n\nexport async function scrubResult(\n result: ToolExecutionResult,\n scrub: Pick<Scrub, 'texts'>\n): Promise<ToolExecutionResult | undefined> {\n const content = [...(result.output ?? [])];\n const diagnostics = [...(result.diagnostics ?? [])];\n const copy = (value: JsonValue | undefined) =>\n value === undefined ? undefined : (structuredClone(value) as JsonValue);\n const details = copy(result.details);\n const structured = copy(result.structuredOutput);\n const found: Leaf[] = [];\n content.forEach((item, at) => {\n if (item.type === 'text')\n found.push({ get: () => item.text, set: (text) => void (content[at] = { ...item, text }) });\n });\n diagnostics.forEach((item, at) => {\n found.push({\n get: () => item.message,\n set: (message) => void (diagnostics[at] = { ...item, message }),\n });\n });\n let replaced = details;\n if (details !== undefined)\n leaves(details, found, (next) => {\n replaced = next;\n });\n let output = structured;\n if (structured !== undefined)\n leaves(structured, found, (next) => {\n output = next;\n });\n const before = found.map((leaf) => leaf.get());\n const after = await scrub.texts(before);\n if (after.every((text, at) => text === before[at])) return undefined;\n found.forEach((leaf, at) => {\n leaf.set(after[at] as string);\n });\n const kept = result.output as ToolExecutionResult['output'] & unknown[];\n if (kept) kept.splice(0, kept.length, ...content);\n return {\n ...result,\n ...(kept ? { output: kept } : {}),\n ...(result.diagnostics ? { diagnostics } : {}),\n ...(replaced === undefined ? {} : { details: replaced }),\n ...(output === undefined ? {} : { structuredOutput: output }),\n };\n}\n\nfunction where(name: SecretName): string {\n const rules = name.rules.map((rule) =>\n [\n rule.domain,\n rule.header ? `header ${rule.header}` : '',\n rule.methods?.length ? rule.methods.join(' ') : '',\n rule.paths?.length ? rule.paths.join(' ') : '',\n ]\n .filter(Boolean)\n .join(', ')\n );\n const shell = name.exec === 'shell' ? ['in every bash'] : [];\n const parts = [...rules, ...shell];\n return parts.length ? `: ${parts.join('; ')}` : '';\n}\n\nexport function renderSecrets(names: readonly SecretName[]): string | undefined {\n if (!names.length) return undefined;\n const lines = names.map((name) => `- ${name.name} = ${name.placeholder}${where(name)}`);\n return [MODEL_LINE, '', ...lines].join('\\n');\n}\n\nexport function secretsEntry(names: readonly SecretName[]) {\n return {\n kind: SECRETS_KIND,\n data: { names: structuredClone(names) as unknown as JsonValue },\n model: [{ role: 'user' as const, content: listedLine(names), timestamp: Date.now() }],\n };\n}\n\nexport function shellGrants(names: Pick<Names, 'state'>) {\n return (argv: readonly string[]): readonly string[] =>\n names.state.value.some((name) => name.exec === 'shell') ? [...SHELL_GRANTS, ...argv] : argv;\n}\n\nexport function secretsExtension(deps: {\n names: Pick<Names, 'state'>;\n vault: Pick<Vault, 'ready' | 'texts'>;\n}): Extension {\n return defineExtension({\n name: SECRETS,\n sections: [section('secrets', () => renderSecrets(deps.names.state.value))],\n hooks: [\n hook(ToolTask, { afterTool: (_call, result) => scrubResult(result, deps.vault) }),\n hook(GenerationTask, {\n beforeRequest: async () => {\n await deps.vault.ready();\n return undefined;\n },\n }),\n ],\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|