@ai-ecoverse/slicc-agent 3.7.0 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +46 -1
  2. package/dist/client.js +2 -1
  3. package/dist/client.js.map +2 -2
  4. package/dist/control.js +3 -2
  5. package/dist/control.js.map +2 -2
  6. package/dist/host.js +11 -2
  7. package/dist/host.js.map +2 -2
  8. package/dist/main.js +10 -2
  9. package/dist/main.js.map +2 -2
  10. package/dist/roles/frontmatter.js +61 -35
  11. package/dist/roles/frontmatter.js.map +3 -3
  12. package/dist/roles/roles.js +1 -0
  13. package/dist/roles/roles.js.map +2 -2
  14. package/dist/scoops/extension.js +40 -29
  15. package/dist/scoops/extension.js.map +2 -2
  16. package/dist/scoops/from.js +5 -0
  17. package/dist/scoops/from.js.map +7 -0
  18. package/dist/scoops/index.js +9 -4
  19. package/dist/scoops/index.js.map +2 -2
  20. package/dist/scoops/service.js +11 -1
  21. package/dist/scoops/service.js.map +2 -2
  22. package/dist/scoops/sync.js +1 -0
  23. package/dist/scoops/sync.js.map +2 -2
  24. package/dist/services.js.map +2 -2
  25. package/dist/skills/index.js +172 -0
  26. package/dist/skills/index.js.map +7 -0
  27. package/dist/skills/skills.js +162 -0
  28. package/dist/skills/skills.js.map +7 -0
  29. package/dist/skills/templates.js +97 -0
  30. package/dist/skills/templates.js.map +7 -0
  31. package/dist/spectrum/agent-port.js +1 -1
  32. package/dist/spectrum/agent-port.js.map +2 -2
  33. package/dist/spectrum/messages.js +20 -6
  34. package/dist/spectrum/messages.js.map +2 -2
  35. package/dist/types/client.d.ts +2 -1
  36. package/dist/types/control.d.ts +2 -1
  37. package/dist/types/host.d.ts +2 -0
  38. package/dist/types/roles/roles.d.ts +1 -0
  39. package/dist/types/scoops/extension.d.ts +19 -9
  40. package/dist/types/scoops/from.d.ts +5 -0
  41. package/dist/types/scoops/index.d.ts +2 -1
  42. package/dist/types/scoops/service.d.ts +4 -0
  43. package/dist/types/services.d.ts +6 -0
  44. package/dist/types/skills/index.d.ts +45 -0
  45. package/dist/types/skills/skills.d.ts +32 -0
  46. package/dist/types/skills/templates.d.ts +18 -0
  47. package/dist/types/spectrum/messages.d.ts +2 -1
  48. package/package.json +1 -1
  49. package/packages/vfs-root/prompts/parallel-review.md +59 -0
  50. package/packages/vfs-root/prompts/review-loop.md +48 -0
  51. package/packages/vfs-root/skills/agent/SKILL.md +1 -1
  52. package/packages/vfs-root/skills/licks/SKILL.md +49 -0
  53. package/packages/vfs-root/skills/skill-authoring/SKILL.md +76 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/roles/roles.ts"],
4
- "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { type FrontmatterValue, parseFrontmatter } from './frontmatter.ts';\n\nexport type RoleSource = 'builtin' | 'package' | 'user' | 'project';\nexport type Thinking = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';\n\nexport interface Role {\n name: string;\n description: string;\n prompt: string;\n mode: 'replace' | 'append';\n tools?: string[];\n model?: string;\n thinking?: Thinking;\n aliases: string[];\n output?: string;\n source: RoleSource;\n path: string;\n}\n\nexport interface Roles {\n roles: Role[];\n warnings: string[];\n limits: { maxLiveScoops: number; maxPerTurn: number };\n}\n\nexport const DEFAULT_LIMITS = { maxLiveScoops: 16, maxPerTurn: 64 };\n\nconst TOOLS: Record<string, string> = {\n read: 'read',\n write: 'write',\n edit: 'edit',\n bash: 'bash',\n grep: 'bash',\n find: 'bash',\n ls: 'bash',\n};\n\nconst THINKING = new Set<string>(['off', 'minimal', 'low', 'medium', 'high', 'xhigh']);\n\nconst APPLIED = new Set([\n 'name',\n 'description',\n 'tools',\n 'model',\n 'thinking',\n 'systemPromptMode',\n 'aliases',\n 'output',\n 'package',\n 'advertise',\n 'inheritProjectContext',\n 'inheritSkills',\n 'skills',\n 'defaultReads',\n]);\n\nconst LATER: Record<string, string> = {\n memory: 'per-agent memory comes with SLICC\u2019s memory panel',\n};\n\nexport type RoleFiles = Pick<ExecutionEnv, 'readTextFile' | 'listDir'>;\n\nexport interface RoleSourceDir {\n source: RoleSource;\n dir: string;\n}\n\nfunction list(value: FrontmatterValue | undefined): string[] {\n if (Array.isArray(value)) return value;\n if (typeof value === 'string')\n return value\n .split(',')\n .map((item) => item.trim())\n .filter(Boolean);\n return [];\n}\n\nfunction tools(names: string[], where: string, warnings: string[]): string[] {\n const mapped: string[] = [];\n for (const name of names) {\n const tool = TOOLS[name];\n if (tool) {\n if (!mapped.includes(tool)) mapped.push(tool);\n } else warnings.push(`${where}: tool \"${name}\" isn't available in SLICC; it's left out`);\n }\n return mapped;\n}\n\nexport function parseRole(\n text: string,\n source: RoleSource,\n path: string,\n warnings: string[]\n): Role | undefined {\n const { fields, body, problems } = parseFrontmatter(text);\n for (const problem of problems) warnings.push(`${path}: ${problem}`);\n const name = fields.name;\n const description = fields.description;\n if (typeof name !== 'string' || typeof description !== 'string') {\n warnings.push(`${path}: an agent file needs a name and a description; skipped`);\n return undefined;\n }\n for (const key of Object.keys(fields))\n if (!APPLIED.has(key))\n warnings.push(\n `${path}: \"${key}\" ${LATER[key] ? `is not read yet (${LATER[key]})` : \"isn't supported in SLICC; it's ignored\"}`\n );\n const thinking =\n typeof fields.thinking === 'string' && THINKING.has(fields.thinking)\n ? (fields.thinking as Thinking)\n : undefined;\n if (fields.thinking !== undefined && !thinking)\n warnings.push(`${path}: thinking \"${String(fields.thinking)}\" is unknown; ignored`);\n const pkg = typeof fields.package === 'string' && fields.package ? `${fields.package}.` : '';\n return {\n name: `${pkg}${name}`,\n description,\n prompt: body,\n mode: fields.systemPromptMode === 'append' ? 'append' : 'replace',\n ...(fields.tools !== undefined ? { tools: tools(list(fields.tools), path, warnings) } : {}),\n ...(typeof fields.model === 'string' ? { model: fields.model } : {}),\n ...(thinking ? { thinking } : {}),\n aliases: list(fields.aliases),\n ...(typeof fields.output === 'string' ? { output: fields.output } : {}),\n source,\n path,\n };\n}\n\nasync function markdown(\n files: RoleFiles,\n dir: string,\n context: Context,\n depth = 0\n): Promise<string[]> {\n if (dir.endsWith('.md')) return [dir];\n const listed = await files.listDir(dir, context);\n if (!listed.ok || depth > 4) return [];\n const out: string[] = [];\n for (const info of listed.value) {\n const path = `${dir}/${info.name}`;\n if (info.kind === 'directory') out.push(...(await markdown(files, path, context, depth + 1)));\n else if (info.name.endsWith('.md') && !info.name.endsWith('.chain.md')) out.push(path);\n }\n return out.sort();\n}\n\ntype Override = {\n model?: unknown;\n thinking?: unknown;\n tools?: unknown;\n systemPrompt?: unknown;\n description?: unknown;\n disabled?: unknown;\n};\n\ninterface Settings {\n overrides: Record<string, Override>;\n limits: Roles['limits'];\n}\n\nfunction positive(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : fallback;\n}\n\nasync function settings(\n files: RoleFiles,\n path: string,\n context: Context,\n warnings: string[]\n): Promise<Settings> {\n const read = await files.readTextFile(path, context);\n const empty = { overrides: {}, limits: { ...DEFAULT_LIMITS } };\n if (!read.ok) return empty;\n let parsed: {\n subagents?: {\n agentOverrides?: Record<string, Override>;\n maxLiveScoops?: unknown;\n maxPerTurn?: unknown;\n };\n };\n try {\n parsed = JSON.parse(read.value);\n } catch (error) {\n warnings.push(`${path}: not valid JSON (${(error as Error).message}); overrides ignored`);\n return empty;\n }\n const subagents = parsed.subagents ?? {};\n return {\n overrides: subagents.agentOverrides ?? {},\n limits: {\n maxLiveScoops: positive(subagents.maxLiveScoops, DEFAULT_LIMITS.maxLiveScoops),\n maxPerTurn: positive(subagents.maxPerTurn, DEFAULT_LIMITS.maxPerTurn),\n },\n };\n}\n\nfunction apply(role: Role, override: Override | undefined, warnings: string[]): Role | undefined {\n if (!override) return role;\n if (override.disabled === true) return undefined;\n const next: Role = { ...role };\n if (typeof override.model === 'string') next.model = override.model;\n if (typeof override.thinking === 'string' && THINKING.has(override.thinking))\n next.thinking = override.thinking as Thinking;\n if (typeof override.description === 'string') next.description = override.description;\n if (typeof override.systemPrompt === 'string') next.prompt = override.systemPrompt;\n if (Array.isArray(override.tools))\n next.tools = tools(override.tools.map(String), `override for ${role.name}`, warnings);\n return next;\n}\n\nexport interface LoadOptions {\n files: RoleFiles;\n builtin: () => Promise<{ path: string; text: string }[]>;\n dirs: RoleSourceDir[];\n settingsPath: string;\n}\n\nconst ORDER: RoleSource[] = ['builtin', 'package', 'user', 'project'];\n\nexport async function loadRoles(options: LoadOptions, context: Context): Promise<Roles> {\n const warnings: string[] = [];\n const byName = new Map<string, Role>();\n const add = (role: Role | undefined) => {\n if (role) byName.set(role.name, role);\n };\n for (const { path, text } of await options.builtin())\n add(parseRole(text, 'builtin', path, warnings));\n const dirs = [...options.dirs].sort((a, b) => ORDER.indexOf(a.source) - ORDER.indexOf(b.source));\n for (const { source, dir } of dirs)\n for (const path of await markdown(options.files, dir, context)) {\n const read = await options.files.readTextFile(path, context);\n if (read.ok) add(parseRole(read.value, source, path, warnings));\n }\n const { overrides, limits } = await settings(\n options.files,\n options.settingsPath,\n context,\n warnings\n );\n const roles: Role[] = [];\n for (const role of byName.values()) {\n const applied = apply(role, overrides[role.name], warnings);\n if (applied) roles.push(applied);\n }\n return { roles, warnings, limits };\n}\n\nexport function findRole(roles: readonly Role[], name: string): Role | undefined {\n return (\n roles.find((role) => role.name === name) ?? roles.find((role) => role.aliases.includes(name))\n );\n}\n\nasync function manifestDirs(files: RoleFiles, root: string, context: Context): Promise<string[]> {\n const top = await files.listDir(root, context);\n if (!top.ok) return [];\n const out: string[] = [];\n for (const entry of top.value) {\n if (entry.kind !== 'directory' || entry.name.startsWith('.')) continue;\n if (!entry.name.startsWith('@')) {\n out.push(`${root}/${entry.name}`);\n continue;\n }\n const scoped = await files.listDir(`${root}/${entry.name}`, context);\n if (scoped.ok)\n for (const inner of scoped.value)\n if (inner.kind === 'directory') out.push(`${root}/${entry.name}/${inner.name}`);\n }\n return out;\n}\n\ntype Manifest = {\n 'pi-subagents'?: { agents?: unknown };\n pi?: { subagents?: { agents?: unknown } };\n};\n\nexport function agentPaths(dir: string, text: string): string[] {\n let manifest: Manifest;\n try {\n manifest = JSON.parse(text) as Manifest;\n } catch {\n return [];\n }\n const agents = manifest['pi-subagents']?.agents ?? manifest.pi?.subagents?.agents;\n const listed = Array.isArray(agents) ? agents : typeof agents === 'string' ? [agents] : [];\n return listed\n .filter((path): path is string => typeof path === 'string')\n .map((path) => `${dir}/${path.replace(/^\\.\\//, '').replace(/\\/$/, '')}`);\n}\n\nexport async function packageDirs(\n files: RoleFiles,\n root: string,\n context: Context\n): Promise<string[]> {\n const out: string[] = [];\n for (const dir of await manifestDirs(files, root, context)) {\n const read = await files.readTextFile(`${dir}/package.json`, context);\n if (read.ok) out.push(...agentPaths(dir, read.value));\n }\n return out;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { type FrontmatterValue, parseFrontmatter } from './frontmatter.ts';\n\nexport type RoleSource = 'builtin' | 'package' | 'user' | 'project';\nexport type Thinking = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';\n\nexport interface Role {\n name: string;\n description: string;\n prompt: string;\n mode: 'replace' | 'append';\n tools?: string[];\n model?: string;\n thinking?: Thinking;\n aliases: string[];\n output?: string;\n source: RoleSource;\n path: string;\n}\n\nexport interface Roles {\n roles: Role[];\n warnings: string[];\n limits: { maxLiveScoops: number; maxPerTurn: number };\n}\n\nexport const DEFAULT_LIMITS = { maxLiveScoops: 16, maxPerTurn: 64 };\n\nconst TOOLS: Record<string, string> = {\n read: 'read',\n write: 'write',\n edit: 'edit',\n bash: 'bash',\n grep: 'bash',\n find: 'bash',\n ls: 'bash',\n};\n\nconst THINKING = new Set<string>(['off', 'minimal', 'low', 'medium', 'high', 'xhigh']);\n\nconst APPLIED = new Set([\n 'name',\n 'description',\n 'tools',\n 'model',\n 'thinking',\n 'systemPromptMode',\n 'aliases',\n 'output',\n 'package',\n 'advertise',\n 'inheritProjectContext',\n 'inheritSkills',\n 'skills',\n 'defaultReads',\n]);\n\nconst LATER: Record<string, string> = {\n memory: 'per-agent memory comes with SLICC\u2019s memory panel',\n};\n\nexport type RoleFiles = Pick<ExecutionEnv, 'readTextFile' | 'listDir'>;\n\nexport interface RoleSourceDir {\n source: RoleSource;\n dir: string;\n}\n\nfunction list(value: FrontmatterValue | undefined): string[] {\n if (Array.isArray(value)) return value;\n if (typeof value === 'string')\n return value\n .split(',')\n .map((item) => item.trim())\n .filter(Boolean);\n return [];\n}\n\nfunction tools(names: string[], where: string, warnings: string[]): string[] {\n const mapped: string[] = [];\n for (const name of names) {\n const tool = TOOLS[name];\n if (tool) {\n if (!mapped.includes(tool)) mapped.push(tool);\n } else warnings.push(`${where}: tool \"${name}\" isn't available in SLICC; it's left out`);\n }\n return mapped;\n}\n\nexport function parseRole(\n text: string,\n source: RoleSource,\n path: string,\n warnings: string[]\n): Role | undefined {\n const { fields, body, problems } = parseFrontmatter(text);\n for (const problem of problems) warnings.push(`${path}: ${problem}`);\n const name = fields.name;\n const description = fields.description;\n if (typeof name !== 'string' || typeof description !== 'string') {\n warnings.push(`${path}: an agent file needs a name and a description; skipped`);\n return undefined;\n }\n for (const key of Object.keys(fields))\n if (!APPLIED.has(key))\n warnings.push(\n `${path}: \"${key}\" ${LATER[key] ? `is not read yet (${LATER[key]})` : \"isn't supported in SLICC; it's ignored\"}`\n );\n const thinking =\n typeof fields.thinking === 'string' && THINKING.has(fields.thinking)\n ? (fields.thinking as Thinking)\n : undefined;\n if (fields.thinking !== undefined && !thinking)\n warnings.push(`${path}: thinking \"${String(fields.thinking)}\" is unknown; ignored`);\n const pkg = typeof fields.package === 'string' && fields.package ? `${fields.package}.` : '';\n return {\n name: `${pkg}${name}`,\n description,\n prompt: body,\n mode: fields.systemPromptMode === 'append' ? 'append' : 'replace',\n ...(fields.tools !== undefined ? { tools: tools(list(fields.tools), path, warnings) } : {}),\n ...(typeof fields.model === 'string' ? { model: fields.model } : {}),\n ...(thinking ? { thinking } : {}),\n aliases: list(fields.aliases),\n ...(typeof fields.output === 'string' ? { output: fields.output } : {}),\n source,\n path,\n };\n}\n\nasync function markdown(\n files: RoleFiles,\n dir: string,\n context: Context,\n depth = 0\n): Promise<string[]> {\n if (dir.endsWith('.md')) return [dir];\n const listed = await files.listDir(dir, context);\n if (!listed.ok || depth > 4) return [];\n const out: string[] = [];\n for (const info of listed.value) {\n const path = `${dir}/${info.name}`;\n if (info.kind === 'directory') out.push(...(await markdown(files, path, context, depth + 1)));\n else if (info.name.endsWith('.md') && !info.name.endsWith('.chain.md')) out.push(path);\n }\n return out.sort();\n}\n\ntype Override = {\n model?: unknown;\n thinking?: unknown;\n tools?: unknown;\n systemPrompt?: unknown;\n description?: unknown;\n disabled?: unknown;\n};\n\ninterface Settings {\n overrides: Record<string, Override>;\n limits: Roles['limits'];\n}\n\nfunction positive(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : fallback;\n}\n\nasync function settings(\n files: RoleFiles,\n path: string,\n context: Context,\n warnings: string[]\n): Promise<Settings> {\n const read = await files.readTextFile(path, context);\n const empty = { overrides: {}, limits: { ...DEFAULT_LIMITS } };\n if (!read.ok) return empty;\n let parsed: {\n subagents?: {\n agentOverrides?: Record<string, Override>;\n maxLiveScoops?: unknown;\n maxPerTurn?: unknown;\n };\n };\n try {\n parsed = JSON.parse(read.value);\n } catch (error) {\n warnings.push(`${path}: not valid JSON (${(error as Error).message}); overrides ignored`);\n return empty;\n }\n const subagents = parsed.subagents ?? {};\n return {\n overrides: subagents.agentOverrides ?? {},\n limits: {\n maxLiveScoops: positive(subagents.maxLiveScoops, DEFAULT_LIMITS.maxLiveScoops),\n maxPerTurn: positive(subagents.maxPerTurn, DEFAULT_LIMITS.maxPerTurn),\n },\n };\n}\n\nfunction apply(role: Role, override: Override | undefined, warnings: string[]): Role | undefined {\n if (!override) return role;\n if (override.disabled === true) return undefined;\n const next: Role = { ...role };\n if (typeof override.model === 'string') next.model = override.model;\n if (typeof override.thinking === 'string' && THINKING.has(override.thinking))\n next.thinking = override.thinking as Thinking;\n if (typeof override.description === 'string') next.description = override.description;\n if (typeof override.systemPrompt === 'string') next.prompt = override.systemPrompt;\n if (Array.isArray(override.tools))\n next.tools = tools(override.tools.map(String), `override for ${role.name}`, warnings);\n return next;\n}\n\nexport interface LoadOptions {\n files: RoleFiles;\n builtin: () => Promise<{ path: string; text: string }[]>;\n dirs: RoleSourceDir[];\n settingsPath: string;\n}\n\nconst ORDER: RoleSource[] = ['builtin', 'package', 'user', 'project'];\n\nexport async function loadRoles(options: LoadOptions, context: Context): Promise<Roles> {\n const warnings: string[] = [];\n const byName = new Map<string, Role>();\n const add = (role: Role | undefined) => {\n if (role) byName.set(role.name, role);\n };\n for (const { path, text } of await options.builtin())\n add(parseRole(text, 'builtin', path, warnings));\n const dirs = [...options.dirs].sort((a, b) => ORDER.indexOf(a.source) - ORDER.indexOf(b.source));\n for (const { source, dir } of dirs)\n for (const path of await markdown(options.files, dir, context)) {\n const read = await options.files.readTextFile(path, context);\n if (read.ok) add(parseRole(read.value, source, path, warnings));\n }\n const { overrides, limits } = await settings(\n options.files,\n options.settingsPath,\n context,\n warnings\n );\n const roles: Role[] = [];\n for (const role of byName.values()) {\n const applied = apply(role, overrides[role.name], warnings);\n if (applied) roles.push(applied);\n }\n return { roles, warnings, limits };\n}\n\nexport function findRole(roles: readonly Role[], name: string): Role | undefined {\n return (\n roles.find((role) => role.name === name) ?? roles.find((role) => role.aliases.includes(name))\n );\n}\n\nexport async function manifestDirs(\n files: RoleFiles,\n root: string,\n context: Context\n): Promise<string[]> {\n const top = await files.listDir(root, context);\n if (!top.ok) return [];\n const out: string[] = [];\n for (const entry of top.value) {\n if (entry.kind !== 'directory' || entry.name.startsWith('.')) continue;\n if (!entry.name.startsWith('@')) {\n out.push(`${root}/${entry.name}`);\n continue;\n }\n const scoped = await files.listDir(`${root}/${entry.name}`, context);\n if (scoped.ok)\n for (const inner of scoped.value)\n if (inner.kind === 'directory') out.push(`${root}/${entry.name}/${inner.name}`);\n }\n return out;\n}\n\ntype Manifest = {\n 'pi-subagents'?: { agents?: unknown };\n pi?: { subagents?: { agents?: unknown } };\n};\n\nexport function agentPaths(dir: string, text: string): string[] {\n let manifest: Manifest;\n try {\n manifest = JSON.parse(text) as Manifest;\n } catch {\n return [];\n }\n const agents = manifest['pi-subagents']?.agents ?? manifest.pi?.subagents?.agents;\n const listed = Array.isArray(agents) ? agents : typeof agents === 'string' ? [agents] : [];\n return listed\n .filter((path): path is string => typeof path === 'string')\n .map((path) => `${dir}/${path.replace(/^\\.\\//, '').replace(/\\/$/, '')}`);\n}\n\nexport async function packageDirs(\n files: RoleFiles,\n root: string,\n context: Context\n): Promise<string[]> {\n const out: string[] = [];\n for (const dir of await manifestDirs(files, root, context)) {\n const read = await files.readTextFile(`${dir}/package.json`, context);\n if (read.ok) out.push(...agentPaths(dir, read.value));\n }\n return out;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -9,9 +9,9 @@ import { answerText } from "../agent.js";
9
9
  import { AgentsDoc, live } from "../agents.js";
10
10
  import { HOME } from "../kernel/env.js";
11
11
  import { normalize, resolve } from "../kernel/paths.js";
12
+ import { FROM_KIND } from "./from.js";
12
13
  import { SCOOPS_ROOT, ScoopWorkDoc, workspace } from "./service.js";
13
14
  const PREVIEW = 1e3;
14
- const ADVERTISED = 16;
15
15
  function scoopFacts(record) {
16
16
  return [
17
17
  `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.`,
@@ -39,6 +39,23 @@ function reportLick(record, id, report, route = {}) {
39
39
  ...report.failed && report.failed !== "aborted" ? { severity: "warn" } : {}
40
40
  };
41
41
  }
42
+ async function noteWhenPlaced(runtime, submission, conversation, note, context) {
43
+ const { from } = note;
44
+ while (from && !note.noted && !context.abortSignal?.aborted) {
45
+ const record = await submission.status(context);
46
+ if (record.status !== "queued") {
47
+ if (!("entry" in record) || record.entry === void 0) return;
48
+ const entry = record.entry;
49
+ await runtime.commit(async (tx) => {
50
+ await tx.appendEntry(conversation, { kind: FROM_KIND, data: { from, entry } });
51
+ return { status: "running", checkpoint: { phase: "deliver", noted: true } };
52
+ }, context).catch(() => void 0);
53
+ return;
54
+ }
55
+ await new Promise((done) => setTimeout(done, NOTE_POLL_MS));
56
+ }
57
+ }
58
+ const NOTE_POLL_MS = 100;
42
59
  function waitTask(lookup) {
43
60
  return defineTask({
44
61
  name: "slicc.scoop-wait",
@@ -138,6 +155,7 @@ function scoopTasks(lookup) {
138
155
  return;
139
156
  }
140
157
  const requestId = task.input.request ? `subagent:${task.input.request}` : `feed:${task.id}`;
158
+ const from = (await runtime.snapshot(ScoopWorkDoc, context))?.feeds[String(task.id)]?.from;
141
159
  const submission = await handle.submit(
142
160
  {
143
161
  type: "input",
@@ -147,7 +165,15 @@ function scoopTasks(lookup) {
147
165
  },
148
166
  context
149
167
  );
168
+ const noting = noteWhenPlaced(
169
+ runtime,
170
+ submission,
171
+ record?.conversation,
172
+ { from, noted: task.state.checkpoint.noted },
173
+ context
174
+ );
150
175
  const settled = await submission.wait(context);
176
+ await noting;
151
177
  await runtime.commit(async (tx) => {
152
178
  if (settled.status !== "done" || settled.type !== "input")
153
179
  return {
@@ -221,22 +247,9 @@ async function whose(input, context) {
221
247
  const scoop = Object.values(state?.scoops ?? {}).find(
222
248
  (record) => record.conversation === input.conversationId
223
249
  );
224
- const cone = Object.values(state?.cones ?? {}).some(
225
- (record) => record.conversation === input.conversationId
226
- );
227
- return { scoop, cone };
250
+ return { scoop };
228
251
  }
229
- function rolesSection(roles) {
230
- const shown = roles.slice(0, ADVERTISED);
231
- return [
232
- "Roles for `subagent spawn --agent <role>`:",
233
- ...shown.map(
234
- (role) => `- ${role.name}${role.aliases.length ? ` (also ${role.aliases.join(", ")})` : ""}: ${role.description}`
235
- ),
236
- ...roles.length > shown.length ? [`\u2026 and ${roles.length - shown.length} more; \`subagent list --agents\` shows them all.`] : []
237
- ].join("\n");
238
- }
239
- function scoopsExtension(tasks, sections) {
252
+ function scoopsExtension(tasks) {
240
253
  return defineExtension({
241
254
  name: "slicc-scoops",
242
255
  tasks: [tasks.anchor, tasks.reporter, tasks.wait],
@@ -244,20 +257,15 @@ function scoopsExtension(tasks, sections) {
244
257
  section("scoop", async (input, context) => {
245
258
  const { scoop } = await whose(input, context);
246
259
  return scoop ? scoopFacts(scoop) : void 0;
247
- }),
248
- section("subagent", async (input, context) => {
249
- const { cone } = await whose(input, context);
250
- if (!cone) return void 0;
251
- return [sections.skill(), rolesSection(sections.roles())].filter(Boolean).join("\n\n");
252
260
  })
253
261
  ]
254
262
  });
255
263
  }
256
264
  const WRITES = /* @__PURE__ */ new Set(["write", "edit"]);
257
- function allowed(record, coneCwd, path, writing) {
265
+ function allowed(record, coneCwd, path, writing, reads = []) {
258
266
  const own = `${SCOOPS_ROOT}/${record.folder}`;
259
267
  const write = [own, "/tmp", ...record.roots?.write ?? []];
260
- const roots = writing ? write : [...write, coneCwd, ...record.roots?.read ?? []];
268
+ const roots = writing ? write : [...write, coneCwd, ...record.roots?.read ?? [], ...reads];
261
269
  return roots.some((root) => path === root || path.startsWith(`${root === "/" ? "" : root}/`));
262
270
  }
263
271
  function refusal(value) {
@@ -268,16 +276,18 @@ function guardExtension(lookup, tools) {
268
276
  (tool) => wrapTool(tool, (inner) => ({
269
277
  ...inner,
270
278
  async execute(args, api, context) {
271
- const { agents } = await lookup();
279
+ const { agents, reads } = await lookup();
272
280
  const record = Object.values(agents.state().scoops).find(
273
281
  (scoop) => scoop.conversation === api.conversationId
274
282
  );
275
283
  if (!record) return inner.execute(args, api, context);
276
284
  const path = String(args.path);
277
- let target = normalize(resolve(workspace(record.folder), path));
278
- const canonical = await api.env?.canonicalPath(target, context);
279
- if (canonical?.ok) target = canonical.value;
280
- if (allowed(record, HOME, target, WRITES.has(inner.name)))
285
+ const asked = normalize(resolve(workspace(record.folder), path));
286
+ const canonical = await api.env?.canonicalPath(asked, context);
287
+ const target = canonical?.ok ? canonical.value : asked;
288
+ const writing = WRITES.has(inner.name);
289
+ const skills = reads?.() ?? [];
290
+ if (allowed(record, HOME, target, writing, skills) || allowed(record, HOME, asked, writing, skills))
281
291
  return inner.execute(args, api, context);
282
292
  return refusal(
283
293
  `${target} is outside this scoop's folders. ${inner.name} reaches ${SCOOPS_ROOT}/${record.folder} and /tmp${WRITES.has(inner.name) ? "" : `, and reads ${HOME}`}.`
@@ -288,11 +298,12 @@ function guardExtension(lookup, tools) {
288
298
  return defineExtension({ name: "slicc-scoop-guard", wraps });
289
299
  }
290
300
  export {
301
+ NOTE_POLL_MS,
291
302
  PREVIEW,
292
303
  allowed,
293
304
  guardExtension,
305
+ noteWhenPlaced,
294
306
  reportLick,
295
- rolesSection,
296
307
  scoopFacts,
297
308
  scoopTasks,
298
309
  scoopsExtension,
@@ -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 section,\n type ToolRegistration,\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 { LickEvent } from '../licks/licks.ts';\nimport type { Role } from '../roles/roles.ts';\nimport { type Feed, SCOOPS_ROOT, type ScoopsHost, ScoopWorkDoc, workspace } from './service.ts';\n\nexport const PREVIEW = 1000;\nconst ADVERTISED = 16;\n\ntype Lookup = () => Promise<ScoopsHost>;\n\nexport function scoopFacts(record: Pick<ScoopRecord, 'name' | 'folder' | 'role'>): string {\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 `Work in ${workspace(record.folder)}. Your read, write and edit tools only reach ${SCOOPS_ROOT}/${record.folder} and /tmp, and read ${HOME}.`,\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\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' }\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 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 settled = await submission.wait(context);\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 } = 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 ? `${SCOOPS_ROOT}/${record.folder}/reports/${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 const cone = Object.values(state?.cones ?? {}).some(\n (record) => record.conversation === input.conversationId\n );\n return { scoop, cone };\n}\n\nexport function rolesSection(roles: readonly Role[]): string {\n const shown = roles.slice(0, ADVERTISED);\n return [\n 'Roles for `subagent spawn --agent <role>`:',\n ...shown.map(\n (role) =>\n `- ${role.name}${role.aliases.length ? ` (also ${role.aliases.join(', ')})` : ''}: ${role.description}`\n ),\n ...(roles.length > shown.length\n ? [`\u2026 and ${roles.length - shown.length} more; \\`subagent list --agents\\` shows them all.`]\n : []),\n ].join('\\n');\n}\n\nexport interface ScoopSections {\n roles(): readonly Role[];\n skill(): string;\n}\n\nexport function scoopsExtension(\n tasks: ReturnType<typeof scoopTasks>,\n sections: ScoopSections\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) : undefined;\n }),\n section('subagent', async (input, context) => {\n const { cone } = await whose(input, context);\n if (!cone) return undefined;\n return [sections.skill(), rolesSection(sections.roles())].filter(Boolean).join('\\n\\n');\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): boolean {\n const own = `${SCOOPS_ROOT}/${record.folder}`;\n const write = [own, '/tmp', ...(record.roots?.write ?? [])];\n const roots = writing ? write : [...write, coneCwd, ...(record.roots?.read ?? [])];\n return roots.some((root) => path === root || path.startsWith(`${root === '/' ? '' : root}/`));\n}\n\nfunction refusal(value: string) {\n return { content: [{ 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 } = await lookup();\n const record = Object.values(agents.state().scoops).find(\n (scoop) => scoop.conversation === api.conversationId\n );\n if (!record) return inner.execute(args, api, context);\n const path = String((args as { path: unknown }).path);\n let target = normalize(resolve(workspace(record.folder), path));\n const canonical = await api.env?.canonicalPath(target, context);\n if (canonical?.ok) target = canonical.value;\n if (allowed(record, HOME, target, WRITES.has(inner.name)))\n return inner.execute(args, api, context);\n return refusal(\n `${target} is outside this scoop's folders. ${inner.name} reaches ${SCOOPS_ROOT}/${record.folder} and /tmp${WRITES.has(inner.name) ? '' : `, and reads ${HOME}`}.`\n );\n },\n }))\n );\n return defineExtension({ name: 'slicc-scoop-guard', wraps });\n}\n"],
5
- "mappings": "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",
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 { LickEvent } from '../licks/licks.ts';\nimport { FROM_KIND } from './from.ts';\nimport { type Feed, SCOOPS_ROOT, type ScoopsHost, ScoopWorkDoc, workspace } from './service.ts';\n\nexport const PREVIEW = 1000;\n\ntype Lookup = () => Promise<ScoopsHost>;\n\nexport function scoopFacts(record: Pick<ScoopRecord, 'name' | 'folder' | 'role'>): string {\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 `Work in ${workspace(record.folder)}. Your read, write and edit tools only reach ${SCOOPS_ROOT}/${record.folder} and /tmp, and read ${HOME}.`,\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; 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, { kind: FROM_KIND, data: { from, entry } });\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 from = (await runtime.snapshot(ScoopWorkDoc, context))?.feeds[String(task.id)]?.from;\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, 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 } = 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 ? `${SCOOPS_ROOT}/${record.folder}/reports/${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(tasks: ReturnType<typeof scoopTasks>): 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) : 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): boolean {\n const own = `${SCOOPS_ROOT}/${record.folder}`;\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 { content: [{ 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 } = await lookup();\n const record = Object.values(agents.state().scoops).find(\n (scoop) => scoop.conversation === api.conversationId\n );\n if (!record) return inner.execute(args, api, context);\n const path = String((args as { path: unknown }).path);\n const asked = normalize(resolve(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, HOME, target, writing, skills) ||\n allowed(record, HOME, asked, writing, skills)\n )\n return inner.execute(args, api, context);\n return refusal(\n `${target} is outside this scoop's folders. ${inner.name} reaches ${SCOOPS_ROOT}/${record.folder} and /tmp${WRITES.has(inner.name) ? '' : `, and reads ${HOME}`}.`\n );\n },\n }))\n );\n return defineExtension({ name: 'slicc-scoop-guard', wraps });\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["work"]
7
7
  }
@@ -0,0 +1,5 @@
1
+ const FROM_KIND = "slicc.from";
2
+ export {
3
+ FROM_KIND
4
+ };
5
+ //# sourceMappingURL=from.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/scoops/from.ts"],
4
+ "sourcesContent": ["export const FROM_KIND = 'slicc.from';\n\nexport type From = { from: string; entry: number };\n"],
5
+ "mappings": "AAAO,MAAM,YAAY;",
6
+ "names": []
7
+ }
@@ -52,15 +52,21 @@ function setupScoops(registry, licks, fileTools = []) {
52
52
  const tasks = scoopTasks(lookup);
53
53
  const scoops = createScoops(host, () => tasks);
54
54
  let cached = { roles: [], warnings: [], limits: { ...DEFAULT_LIMITS } };
55
- let skill = "";
56
- registry.install(scoopsExtension(tasks, { roles: () => cached.roles, skill: () => skill }));
55
+ registry.install(scoopsExtension(tasks));
57
56
  registry.install(guardExtension(lookup, fileTools));
58
57
  return {
59
58
  scoops,
60
59
  async attach(options, context) {
61
60
  const { harness, agents, groups, env, home } = options;
62
61
  const assets = options.assets ?? packageAssets();
63
- bind({ harness, agents, licks, tools: fileTools, ...groups ? { groups } : {} });
62
+ bind({
63
+ harness,
64
+ agents,
65
+ licks,
66
+ tools: fileTools,
67
+ groups,
68
+ reads: options.reads
69
+ });
64
70
  const builtin = async () => {
65
71
  const out = [];
66
72
  for (const name of BUILTIN_ROLES) {
@@ -87,7 +93,6 @@ function setupScoops(registry, licks, fileTools = []) {
87
93
  );
88
94
  return cached;
89
95
  };
90
- skill = (await assets("packages/vfs-root/skills/agent/SKILL.md").catch(() => "")).replace(/^---[\s\S]*?---\n/, "").trim();
91
96
  await roles(context).catch(() => cached);
92
97
  const script = await assets("bin/agent").catch(() => void 0);
93
98
  if (script)
@@ -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 { Licks } from '../licks/licks.ts';\nimport { DEFAULT_LIMITS, loadRoles, packageDirs, type Roles } from '../roles/roles.ts';\nimport { createCli, RUN_DIR } from './cli.ts';\nimport { guardExtension, scoopsExtension, scoopTasks } from './extension.ts';\nimport { type ControlPlane, controlPlane } from './requests.ts';\nimport { createScoops, type Scoops, type ScoopsHost } from './service.ts';\n\nexport const BUILTIN_ROLES = ['scout', 'worker', 'reviewer', 'oracle', 'delegate'];\nexport const PNPM_HOME = '/home/.local/share/pnpm';\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 pnpmHome?: string;\n controlDir?: string;\n sweepEvery?: number;\n alive?: (pid: number) => Promise<boolean>;\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'];\n\nasync function install(env: ExecutionEnv, script: string, pnpmHome: string, context: Context) {\n await env.createDir(`${pnpmHome}/bin`, { recursive: true }, context);\n for (const name of COMMANDS) {\n const path = `${pnpmHome}/bin/${name}`;\n await env.writeFile(path, script, context);\n await env.exec(['chmod', '+x', path], undefined, 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\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 skill = '';\n registry.install(scoopsExtension(tasks, { roles: () => cached.roles, skill: () => skill }));\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 bind({ harness, agents, licks, tools: fileTools, ...(groups ? { groups } : {}) });\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, '/node_modules', using)).map((dir) => ({\n source: 'package' as const,\n dir,\n })),\n { source: 'user', dir: `${home}/.pi/agent/agents` },\n ],\n settingsPath: `${home}/.pi/agent/settings.json`,\n },\n using\n );\n return cached;\n };\n skill = (await assets('packages/vfs-root/skills/agent/SKILL.md').catch(() => ''))\n .replace(/^---[\\s\\S]*?---\\n/, '')\n .trim();\n await roles(context).catch(() => cached);\n const script = await assets('bin/agent').catch(() => undefined);\n if (script)\n await install(env, script, options.pnpmHome ?? PNPM_HOME, context).catch(() => undefined);\n const cli = createCli({\n env,\n harness,\n agents,\n scoops,\n roles: () => roles(context),\n ...(options.alive ? { alive: options.alive } : {}),\n });\n const plane = controlPlane(env, cli, {\n ...(options.controlDir ? { dir: options.controlDir } : {}),\n ...(options.sweepEvery ? { sweepEvery: options.sweepEvery } : {}),\n mirror: mirror(agents, env),\n });\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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",
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 { Licks } from '../licks/licks.ts';\nimport { DEFAULT_LIMITS, loadRoles, packageDirs, type Roles } from '../roles/roles.ts';\nimport { createCli, RUN_DIR } from './cli.ts';\nimport { guardExtension, scoopsExtension, scoopTasks } from './extension.ts';\nimport { type ControlPlane, controlPlane } from './requests.ts';\nimport { createScoops, type Scoops, type ScoopsHost } from './service.ts';\n\nexport const BUILTIN_ROLES = ['scout', 'worker', 'reviewer', 'oracle', 'delegate'];\nexport const PNPM_HOME = '/home/.local/share/pnpm';\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 pnpmHome?: string;\n controlDir?: string;\n sweepEvery?: number;\n alive?: (pid: number) => Promise<boolean>;\n reads: () => readonly string[];\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'];\n\nasync function install(env: ExecutionEnv, script: string, pnpmHome: string, context: Context) {\n await env.createDir(`${pnpmHome}/bin`, { recursive: true }, context);\n for (const name of COMMANDS) {\n const path = `${pnpmHome}/bin/${name}`;\n await env.writeFile(path, script, context);\n await env.exec(['chmod', '+x', path], undefined, 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\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 registry.install(scoopsExtension(tasks));\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 bind({\n harness,\n agents,\n licks,\n tools: fileTools,\n groups,\n reads: options.reads,\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, '/node_modules', using)).map((dir) => ({\n source: 'package' as const,\n dir,\n })),\n { source: 'user', dir: `${home}/.pi/agent/agents` },\n ],\n settingsPath: `${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 await install(env, script, options.pnpmHome ?? PNPM_HOME, context).catch(() => undefined);\n const cli = createCli({\n env,\n harness,\n agents,\n scoops,\n roles: () => roles(context),\n ...(options.alive ? { alive: options.alive } : {}),\n });\n const plane = controlPlane(env, cli, {\n ...(options.controlDir ? { dir: options.controlDir } : {}),\n ...(options.sweepEvery ? { sweepEvery: options.sweepEvery } : {}),\n mirror: mirror(agents, env),\n });\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
  }
@@ -84,7 +84,8 @@ async function addFeed(core, tx, parent, feed2) {
84
84
  created: feed2.created,
85
85
  report: feed2.report,
86
86
  target: feed2.target,
87
- channel: "scoop-notify"
87
+ channel: "scoop-notify",
88
+ from: feed2.from
88
89
  };
89
90
  return Number(task);
90
91
  }
@@ -165,6 +166,7 @@ async function spawn(core, request, context) {
165
166
  };
166
167
  const target = request.target === void 0 ? request.fromAgent ? `cone:${request.cone}` : null : request.target;
167
168
  const made = [];
169
+ const from = askerName(doc, request.asker === void 0 ? target : request.asker);
168
170
  for (const prompt of request.prompts)
169
171
  made.push(
170
172
  await addFeed(core, tx, parent.id, {
@@ -172,6 +174,7 @@ async function spawn(core, request, context) {
172
174
  created,
173
175
  report: target !== null,
174
176
  target,
177
+ from,
175
178
  prompt
176
179
  })
177
180
  );
@@ -181,6 +184,11 @@ async function spawn(core, request, context) {
181
184
  return answer;
182
185
  }, context);
183
186
  }
187
+ function askerName(state, asker) {
188
+ if (asker === null) return "terminal";
189
+ if (asker.startsWith("cone:")) return state.cones[asker.slice("cone:".length)]?.name ?? asker;
190
+ return state.scoops[asker]?.name ?? asker;
191
+ }
184
192
  const missing = (id) => ({
185
193
  code: 1,
186
194
  out: `subagent: there is no scoop ${id.replace("scoop:", "")}
@@ -201,6 +209,7 @@ async function feed(core, id, prompt, options, context) {
201
209
  created,
202
210
  report: target !== null,
203
211
  target,
212
+ from: askerName(agents.state(), target),
204
213
  prompt,
205
214
  followUp: options.followUp,
206
215
  request: options.request
@@ -398,6 +407,7 @@ export {
398
407
  LEDGER_SIZE,
399
408
  SCOOPS_ROOT,
400
409
  ScoopWorkDoc,
410
+ askerName,
401
411
  coneOf,
402
412
  createScoops,
403
413
  findScoop,