@ai-ecoverse/slicc-agent 3.41.0 → 3.42.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +27 -4
  2. package/bin/agent +9 -4
  3. package/dist/browser/guard.js +106 -0
  4. package/dist/browser/guard.js.map +7 -0
  5. package/dist/browser/origins.js +41 -0
  6. package/dist/browser/origins.js.map +7 -0
  7. package/dist/browser/shell.js +179 -0
  8. package/dist/browser/shell.js.map +7 -0
  9. package/dist/client.js +4 -1
  10. package/dist/client.js.map +2 -2
  11. package/dist/host.js +59 -21
  12. package/dist/host.js.map +3 -3
  13. package/dist/index.js +6 -0
  14. package/dist/index.js.map +2 -2
  15. package/dist/main.js +6 -0
  16. package/dist/main.js.map +2 -2
  17. package/dist/owner.js +2 -1
  18. package/dist/owner.js.map +2 -2
  19. package/dist/readonly.js +13 -0
  20. package/dist/readonly.js.map +2 -2
  21. package/dist/remote/host.js +10 -4
  22. package/dist/remote/host.js.map +2 -2
  23. package/dist/remote/pages.js +216 -0
  24. package/dist/remote/pages.js.map +7 -0
  25. package/dist/scoops/cli.js +17 -0
  26. package/dist/scoops/cli.js.map +2 -2
  27. package/dist/scoops/index.js +4 -2
  28. package/dist/scoops/index.js.map +2 -2
  29. package/dist/scoops/requests.js +14 -2
  30. package/dist/scoops/requests.js.map +2 -2
  31. package/dist/scoops/service.js.map +1 -1
  32. package/dist/services.js +2 -0
  33. package/dist/services.js.map +2 -2
  34. package/dist/spectrum/index.js +7 -0
  35. package/dist/spectrum/index.js.map +3 -3
  36. package/dist/spectrum/page-port.js +132 -0
  37. package/dist/spectrum/page-port.js.map +7 -0
  38. package/dist/sprinkles/index.js +1 -2
  39. package/dist/sprinkles/index.js.map +2 -2
  40. package/dist/types/browser/guard.d.ts +9 -0
  41. package/dist/types/browser/origins.d.ts +5 -0
  42. package/dist/types/browser/shell.d.ts +7 -0
  43. package/dist/types/client.d.ts +2 -0
  44. package/dist/types/host.d.ts +8 -3
  45. package/dist/types/index.d.ts +3 -2
  46. package/dist/types/owner.d.ts +1 -0
  47. package/dist/types/readonly.d.ts +2 -0
  48. package/dist/types/remote/host.d.ts +10 -0
  49. package/dist/types/remote/pages.d.ts +76 -0
  50. package/dist/types/scoops/cli.d.ts +7 -0
  51. package/dist/types/scoops/index.d.ts +1 -0
  52. package/dist/types/scoops/requests.d.ts +2 -1
  53. package/dist/types/scoops/service.d.ts +1 -0
  54. package/dist/types/services.d.ts +2 -0
  55. package/dist/types/spectrum/index.d.ts +4 -0
  56. package/dist/types/spectrum/page-port.d.ts +32 -0
  57. package/dist/types/sprinkles/index.d.ts +1 -1
  58. package/dist/worker.js +2 -1
  59. package/dist/worker.js.map +2 -2
  60. package/package.json +2 -2
  61. package/packages/vfs-root/skills/agent/SKILL.md +2 -0
  62. package/packages/vfs-root/skills/browser/SKILL.md +1 -0
  63. package/packages/vfs-root/skills/sprinkles/SKILL.md +1 -1
@@ -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 { type CliOptions, createCli, RUN_DIR } from './cli.ts';\nimport { guardExtension, scoopsExtension, scoopTasks } from './extension.ts';\nimport { type ControlPlane, controlPlane, PROTOCOL } 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 sprinkle?: CliOptions['sprinkle'];\n memory?: CliOptions['memory'];\n gelatiere?: CliOptions['gelatiere'];\n freezer?: CliOptions['freezer'];\n models?: CliOptions['models'];\n mcp?: (names: readonly string[]) => ToolRegistration[];\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 = [\n 'agent',\n 'subagent',\n 'sprinkle',\n 'memory',\n 'gelatiere',\n 'freezer',\n 'models',\n];\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 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) {\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\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 files: env,\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, '/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 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.sprinkle ? { sprinkle: options.sprinkle } : {}),\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 });\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 { type CliOptions, createCli, RUN_DIR } from './cli.ts';\nimport { guardExtension, scoopsExtension, scoopTasks } from './extension.ts';\nimport { type ControlPlane, controlPlane, PROTOCOL } 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 sprinkle?: CliOptions['sprinkle'];\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}\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 = [\n 'agent',\n 'subagent',\n 'sprinkle',\n 'memory',\n 'gelatiere',\n 'freezer',\n 'models',\n 'panel',\n];\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 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) {\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\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 files: env,\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, '/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 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.sprinkle ? { sprinkle: options.sprinkle } : {}),\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 ...(options.panel ? { panel: options.panel } : {}),\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
  }
@@ -1,7 +1,15 @@
1
1
  const CONTROL_DIR = "/var/lib/slicc/agent/requests";
2
2
  const PROTOCOL = "slicc-agent/1";
3
3
  const STALE_MS = 10 * 6e4;
4
- const COMMAND_NAMES = /* @__PURE__ */ new Set(["subagent", "sprinkle", "memory", "gelatiere", "freezer", "models"]);
4
+ const COMMAND_NAMES = /* @__PURE__ */ new Set([
5
+ "subagent",
6
+ "sprinkle",
7
+ "memory",
8
+ "gelatiere",
9
+ "freezer",
10
+ "models",
11
+ "panel"
12
+ ]);
5
13
  const HEADER = 6;
6
14
  function parseRequest(id, bytes) {
7
15
  const decoder = new TextDecoder();
@@ -24,10 +32,12 @@ function parseRequest(id, bytes) {
24
32
  at = end + 1;
25
33
  }
26
34
  const process = Number(pid);
35
+ const tab = caller.startsWith("tab:") ? caller.slice(4) : null;
27
36
  return {
28
37
  id,
29
38
  as: COMMAND_NAMES.has(as) ? as : "agent",
30
- caller,
39
+ caller: tab === null ? caller : "",
40
+ ...tab ? { tab } : {},
31
41
  cwd: cwd || "/",
32
42
  pid: Number.isInteger(process) && process > 0 ? process : null,
33
43
  argv,
@@ -50,6 +60,8 @@ function controlPlane(env, runner, options = {}) {
50
60
  };
51
61
  async function answer(id, result, context) {
52
62
  await env.writeFile(`${responses}/${id}.out`, result.out, context);
63
+ if (result.err !== void 0)
64
+ await env.writeFile(`${responses}/${id}.err`, result.err, context);
53
65
  await env.writeFile(`${responses}/${id}.code`, String(result.code), context);
54
66
  await env.remove(`${requests}/${id}`, { force: true }, context);
55
67
  await env.remove(`${responses}/${id}.ack`, { force: true }, context);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/scoops/requests.ts"],
4
- "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { ExecutionEnv, FileWatcher } from '@earendil-works/pi-durable/env';\nimport type { Answer } from './service.ts';\n\nexport const CONTROL_DIR = '/var/lib/slicc/agent/requests';\nexport const PROTOCOL = 'slicc-agent/1';\nexport const STALE_MS = 10 * 60_000;\nconst COMMAND_NAMES = new Set(['subagent', 'sprinkle', 'memory', 'gelatiere', 'freezer', 'models']);\n\nexport interface Request {\n id: string;\n as: 'agent' | 'subagent' | 'sprinkle' | 'memory' | 'gelatiere' | 'freezer' | 'models';\n caller: string;\n cwd: string;\n pid: number | null;\n argv: string[];\n stdin: string;\n}\n\nconst HEADER = 6;\n\nexport function parseRequest(id: string, bytes: Uint8Array): Request | undefined {\n const decoder = new TextDecoder();\n const fields: string[] = [];\n let at = 0;\n while (fields.length < HEADER) {\n const end = bytes.indexOf(0, at);\n if (end < 0) return undefined;\n fields.push(decoder.decode(bytes.subarray(at, end)));\n at = end + 1;\n }\n const [version, as, caller, cwd, pid, count] = fields;\n const argc = Number(count);\n if (version !== PROTOCOL || !Number.isInteger(argc) || argc < 0) return undefined;\n const argv: string[] = [];\n while (argv.length < argc) {\n const end = bytes.indexOf(0, at);\n if (end < 0) return undefined;\n argv.push(decoder.decode(bytes.subarray(at, end)));\n at = end + 1;\n }\n const process = Number(pid);\n return {\n id,\n as: COMMAND_NAMES.has(as as string) ? (as as Request['as']) : 'agent',\n caller: caller as string,\n cwd: cwd || '/',\n pid: Number.isInteger(process) && process > 0 ? process : null,\n argv,\n stdin: decoder.decode(bytes.subarray(at)),\n };\n}\n\nexport interface Runner {\n run(request: Request, context: Context): Promise<Answer>;\n blocking(request: Request): boolean;\n}\n\nexport interface ControlPlane {\n start(context: Context): Promise<void>;\n sweep(context: Context): Promise<void>;\n close(context: Context): Promise<void>;\n}\n\nexport interface PlaneOptions {\n dir?: string;\n now?: () => number;\n sweepEvery?: number;\n mirror?: (context: Context) => Promise<void>;\n}\n\nexport function controlPlane(\n env: ExecutionEnv,\n runner: Runner,\n options: PlaneOptions = {}\n): ControlPlane {\n const dir = options.dir ?? CONTROL_DIR;\n const requests = `${dir}/in`;\n const responses = `${dir}/out`;\n const now = options.now ?? Date.now;\n const handled = new Set<string>();\n let watcher: FileWatcher | undefined;\n let timer: ReturnType<typeof setInterval> | undefined;\n let lock: Promise<unknown> = Promise.resolve();\n const serial = <T>(operation: () => Promise<T>): Promise<T> => {\n const next = lock.then(operation, operation);\n lock = next.catch(() => undefined);\n return next;\n };\n\n async function answer(id: string, result: Answer, context: Context) {\n await env.writeFile(`${responses}/${id}.out`, result.out, context);\n await env.writeFile(`${responses}/${id}.code`, String(result.code), context);\n await env.remove(`${requests}/${id}`, { force: true }, context);\n await env.remove(`${responses}/${id}.ack`, { force: true }, context);\n await options.mirror?.(context).catch(() => undefined);\n }\n\n async function handle(id: string, context: Context) {\n if (handled.has(id)) return;\n handled.add(id);\n try {\n const bytes = await env.readBinaryFile(`${requests}/${id}`, context);\n if (!bytes.ok) return;\n await env.writeFile(`${responses}/${id}.ack`, '', context);\n const request = parseRequest(id, bytes.value);\n if (!request) {\n await answer(\n id,\n { code: 2, out: 'agent: this request is not understood; update the agent command\\n' },\n context\n );\n return;\n }\n const result = runner.blocking(request)\n ? await runner.run(request, context)\n : await serial(() => runner.run(request, context));\n await answer(id, result, context);\n } finally {\n handled.delete(id);\n }\n }\n\n async function scan(context: Context) {\n const listed = await env.listDir(requests, context);\n if (!listed.ok) return;\n for (const entry of listed.value.sort((a, b) => a.name.localeCompare(b.name)))\n if (entry.kind === 'file' && !entry.name.startsWith('.'))\n void handle(entry.name, context).catch(() => undefined);\n }\n\n async function sweep(context: Context) {\n for (const folder of [requests, responses]) {\n const listed = await env.listDir(folder, context);\n if (!listed.ok) continue;\n for (const entry of listed.value) {\n const stale = now() - entry.mtimeMs > STALE_MS;\n const removable = folder === responses || entry.name.startsWith('.tmp-');\n if (stale && removable)\n await env.remove(`${folder}/${entry.name}`, { force: true }, context);\n }\n }\n }\n\n return {\n async start(context) {\n await env.createDir(requests, { recursive: true }, context);\n await env.createDir(responses, { recursive: true }, context);\n const leftovers = await env.listDir(requests, context);\n if (leftovers.ok)\n for (const entry of leftovers.value)\n if (entry.name.startsWith('.tmp-'))\n await env.remove(`${requests}/${entry.name}`, { force: true }, context);\n await sweep(context);\n const watched = await env.watch(\n [{ path: requests }],\n () => void scan(context).catch(() => undefined),\n context\n );\n if (watched.ok) watcher = watched.value;\n await scan(context);\n timer = setInterval(\n () => void sweep(context).catch(() => undefined),\n options.sweepEvery ?? 60_000\n );\n },\n sweep,\n async close(context) {\n clearInterval(timer);\n await watcher?.close(context);\n await lock;\n },\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { ExecutionEnv, FileWatcher } from '@earendil-works/pi-durable/env';\nimport type { Answer } from './service.ts';\n\nexport const CONTROL_DIR = '/var/lib/slicc/agent/requests';\nexport const PROTOCOL = 'slicc-agent/1';\nexport const STALE_MS = 10 * 60_000;\nconst COMMAND_NAMES = new Set([\n 'subagent',\n 'sprinkle',\n 'memory',\n 'gelatiere',\n 'freezer',\n 'models',\n 'panel',\n]);\n\nexport interface Request {\n id: string;\n as: 'agent' | 'subagent' | 'sprinkle' | 'memory' | 'gelatiere' | 'freezer' | 'models' | 'panel';\n caller: string;\n tab?: string | null;\n cwd: string;\n pid: number | null;\n argv: string[];\n stdin: string;\n}\n\nconst HEADER = 6;\n\nexport function parseRequest(id: string, bytes: Uint8Array): Request | undefined {\n const decoder = new TextDecoder();\n const fields: string[] = [];\n let at = 0;\n while (fields.length < HEADER) {\n const end = bytes.indexOf(0, at);\n if (end < 0) return undefined;\n fields.push(decoder.decode(bytes.subarray(at, end)));\n at = end + 1;\n }\n const [version, as, caller, cwd, pid, count] = fields;\n const argc = Number(count);\n if (version !== PROTOCOL || !Number.isInteger(argc) || argc < 0) return undefined;\n const argv: string[] = [];\n while (argv.length < argc) {\n const end = bytes.indexOf(0, at);\n if (end < 0) return undefined;\n argv.push(decoder.decode(bytes.subarray(at, end)));\n at = end + 1;\n }\n const process = Number(pid);\n const tab = (caller as string).startsWith('tab:') ? (caller as string).slice(4) : null;\n return {\n id,\n as: COMMAND_NAMES.has(as as string) ? (as as Request['as']) : 'agent',\n caller: tab === null ? (caller as string) : '',\n ...(tab ? { tab } : {}),\n cwd: cwd || '/',\n pid: Number.isInteger(process) && process > 0 ? process : null,\n argv,\n stdin: decoder.decode(bytes.subarray(at)),\n };\n}\n\nexport interface Runner {\n run(request: Request, context: Context): Promise<Answer>;\n blocking(request: Request): boolean;\n}\n\nexport interface ControlPlane {\n start(context: Context): Promise<void>;\n sweep(context: Context): Promise<void>;\n close(context: Context): Promise<void>;\n}\n\nexport interface PlaneOptions {\n dir?: string;\n now?: () => number;\n sweepEvery?: number;\n mirror?: (context: Context) => Promise<void>;\n}\n\nexport function controlPlane(\n env: ExecutionEnv,\n runner: Runner,\n options: PlaneOptions = {}\n): ControlPlane {\n const dir = options.dir ?? CONTROL_DIR;\n const requests = `${dir}/in`;\n const responses = `${dir}/out`;\n const now = options.now ?? Date.now;\n const handled = new Set<string>();\n let watcher: FileWatcher | undefined;\n let timer: ReturnType<typeof setInterval> | undefined;\n let lock: Promise<unknown> = Promise.resolve();\n const serial = <T>(operation: () => Promise<T>): Promise<T> => {\n const next = lock.then(operation, operation);\n lock = next.catch(() => undefined);\n return next;\n };\n\n async function answer(id: string, result: Answer, context: Context) {\n await env.writeFile(`${responses}/${id}.out`, result.out, context);\n if (result.err !== undefined)\n await env.writeFile(`${responses}/${id}.err`, result.err, context);\n await env.writeFile(`${responses}/${id}.code`, String(result.code), context);\n await env.remove(`${requests}/${id}`, { force: true }, context);\n await env.remove(`${responses}/${id}.ack`, { force: true }, context);\n await options.mirror?.(context).catch(() => undefined);\n }\n\n async function handle(id: string, context: Context) {\n if (handled.has(id)) return;\n handled.add(id);\n try {\n const bytes = await env.readBinaryFile(`${requests}/${id}`, context);\n if (!bytes.ok) return;\n await env.writeFile(`${responses}/${id}.ack`, '', context);\n const request = parseRequest(id, bytes.value);\n if (!request) {\n await answer(\n id,\n { code: 2, out: 'agent: this request is not understood; update the agent command\\n' },\n context\n );\n return;\n }\n const result = runner.blocking(request)\n ? await runner.run(request, context)\n : await serial(() => runner.run(request, context));\n await answer(id, result, context);\n } finally {\n handled.delete(id);\n }\n }\n\n async function scan(context: Context) {\n const listed = await env.listDir(requests, context);\n if (!listed.ok) return;\n for (const entry of listed.value.sort((a, b) => a.name.localeCompare(b.name)))\n if (entry.kind === 'file' && !entry.name.startsWith('.'))\n void handle(entry.name, context).catch(() => undefined);\n }\n\n async function sweep(context: Context) {\n for (const folder of [requests, responses]) {\n const listed = await env.listDir(folder, context);\n if (!listed.ok) continue;\n for (const entry of listed.value) {\n const stale = now() - entry.mtimeMs > STALE_MS;\n const removable = folder === responses || entry.name.startsWith('.tmp-');\n if (stale && removable)\n await env.remove(`${folder}/${entry.name}`, { force: true }, context);\n }\n }\n }\n\n return {\n async start(context) {\n await env.createDir(requests, { recursive: true }, context);\n await env.createDir(responses, { recursive: true }, context);\n const leftovers = await env.listDir(requests, context);\n if (leftovers.ok)\n for (const entry of leftovers.value)\n if (entry.name.startsWith('.tmp-'))\n await env.remove(`${requests}/${entry.name}`, { force: true }, context);\n await sweep(context);\n const watched = await env.watch(\n [{ path: requests }],\n () => void scan(context).catch(() => undefined),\n context\n );\n if (watched.ok) watcher = watched.value;\n await scan(context);\n timer = setInterval(\n () => void sweep(context).catch(() => undefined),\n options.sweepEvery ?? 60_000\n );\n },\n sweep,\n async close(context) {\n clearInterval(timer);\n await watcher?.close(context);\n await lock;\n },\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -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 { 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 const SCOOPS_ROOT = '/scoops';\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; 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}\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): string {\n return `${SCOOPS_ROOT}/${folder}/workspace`;\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 tools: readonly ToolRegistration[] | undefined,\n mcp?: (names: readonly string[]) => ToolRegistration[]\n): AgentChange {\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),\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\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 { agents, tools, mcp } = await core.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, tools, mcp));\n await narrowMcp(tx, doc, parent.id, child.id, request);\n doc.scoops[id] = {\n name: request.name.trim() || folder,\n folder,\n cone: request.cone,\n conversation: child.id,\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 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 } = 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 removed = await files?.remove(\n workspace(record.folder),\n { recursive: true, force: true },\n context\n );\n const folder = removed?.ok\n ? `removed ${workspace(record.folder)}`\n : `kept ${workspace(record.folder)}`;\n return {\n code: 0,\n out: `stopped ${record.folder}; its transcript is kept, ${folder}, and its reports stay in ${SCOOPS_ROOT}/${record.folder}/reports\\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 files: ScoopsHost['files'],\n folder: string,\n context: Context\n): Promise<boolean> {\n if (!files) return true;\n const cleared = await files.remove(workspace(folder), { recursive: true, force: true }, context);\n if (!cleared.ok) return false;\n return (await files.createDir(workspace(folder), { 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 { harness, licks, files } = await core.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(files, 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"],
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 { 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 const SCOOPS_ROOT = '/scoops';\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}\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): string {\n return `${SCOOPS_ROOT}/${folder}/workspace`;\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 tools: readonly ToolRegistration[] | undefined,\n mcp?: (names: readonly string[]) => ToolRegistration[]\n): AgentChange {\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),\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\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 { agents, tools, mcp } = await core.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, tools, mcp));\n await narrowMcp(tx, doc, parent.id, child.id, request);\n doc.scoops[id] = {\n name: request.name.trim() || folder,\n folder,\n cone: request.cone,\n conversation: child.id,\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 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 } = 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 removed = await files?.remove(\n workspace(record.folder),\n { recursive: true, force: true },\n context\n );\n const folder = removed?.ok\n ? `removed ${workspace(record.folder)}`\n : `kept ${workspace(record.folder)}`;\n return {\n code: 0,\n out: `stopped ${record.folder}; its transcript is kept, ${folder}, and its reports stay in ${SCOOPS_ROOT}/${record.folder}/reports\\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 files: ScoopsHost['files'],\n folder: string,\n context: Context\n): Promise<boolean> {\n if (!files) return true;\n const cleared = await files.remove(workspace(folder), { recursive: true, force: true }, context);\n if (!cleared.ok) return false;\n return (await files.createDir(workspace(folder), { 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 { harness, licks, files } = await core.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(files, 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
5
  "mappings": "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",
6
6
  "names": ["record", "feed"]
7
7
  }
package/dist/services.js CHANGED
@@ -5,11 +5,13 @@ const AgentControl = defineService("slicc.agent.control");
5
5
  const AgentTranscript = defineService("slicc.agent.transcript");
6
6
  const AgentSessions = defineService("slicc.agent.sessions");
7
7
  const AgentRemoteService = defineService("slicc.agent.remote");
8
+ const AgentPagesService = defineService("slicc.agent.pages");
8
9
  const AGENT_SESSION = "agent";
9
10
  const AgentSettings = defineService("slicc.agent.settings");
10
11
  export {
11
12
  AGENT_SESSION,
12
13
  AgentControl,
14
+ AgentPagesService,
13
15
  AgentRemoteService,
14
16
  AgentSessions,
15
17
  AgentSettings,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/services.ts"],
4
- "sourcesContent": ["import {\n type Context,\n defineService,\n type JsonValue,\n type ReplicatedState,\n} from '@earendil-works/chord';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport type { ChangesView } from './changes/git.ts';\nimport type { FrozenCone } from './freezer/index.ts';\nimport type { MemoryEntry, MemoryScope, MemoryTag } from './memory/format.ts';\nimport type { AgentRemote } from './remote/host.ts';\nimport type { Sprinkle } from './sprinkles/kind.ts';\n\nexport type SendMode = 'steer' | 'followUp' | 'reject';\n\nexport interface SendRequest {\n text: string;\n whenBusy: SendMode;\n requestId: string | null;\n agentId?: string | null;\n}\n\nexport interface OperationError {\n code: string;\n message: string;\n}\n\nexport type SendResponse =\n | { accepted: true; submissionId: string; error: null }\n | { accepted: false; submissionId: null; error: OperationError };\n\nexport type PromptResult =\n | { status: 'done'; text: string; reason: null }\n | { status: 'unanswered'; text: null; reason: string };\n\nexport interface AgentControl {\n send(request: SendRequest, context: Context): Promise<SendResponse>;\n wait(submissionId: string, context: Context): Promise<PromptResult>;\n withdraw(\n submissionId: string,\n context: Context\n ): Promise<{ outcome: 'withdrawn' | 'already_placed' | 'not_found' }>;\n abort(context: Context): Promise<void>;\n compact(instructions: string | null, context: Context): Promise<SendResponse>;\n compactAgent(\n agentId: string,\n instructions: string | null,\n context: Context\n ): Promise<SendResponse>;\n reset(handoff: string | null, context: Context): Promise<void>;\n configure(change: AgentSettingsChange, context: Context): Promise<void>;\n rewind(messageId: string | null, context: Context): Promise<Rewound>;\n rewindAgent(agentId: string, messageId: string | null, context: Context): Promise<Rewound>;\n resolveLick(\n lickId: string,\n state: 'confirmed' | 'dismissed',\n context: Context\n ): Promise<LickResolved>;\n resolveAgentLick(\n agentId: string,\n lickId: string,\n state: 'confirmed' | 'dismissed',\n context: Context\n ): Promise<LickResolved>;\n webhook(\n name: string,\n delivery: WebhookRequest,\n context: Context\n ): Promise<{ delivered: boolean }>;\n stopAgent(agentId: string, context: Context): Promise<void>;\n selectCone(agentId: string, context: Context): Promise<void>;\n createCone(name: string, context: Context): Promise<Created>;\n createScoop(parentId: string, name: string, context: Context): Promise<Created>;\n drop(agentId: string, context: Context): Promise<Created>;\n unqueue(\n agentId: string | null,\n submissionId: string,\n context: Context\n ): Promise<{ outcome: 'withdrawn' | 'already_placed' | 'not_found' }>;\n sprinkleSend(\n sprinkleId: string,\n payload: { action: string; data: JsonValue | null; target: string | null },\n context: Context\n ): Promise<{ delivered: boolean }>;\n sprinkleCall(\n sprinkleId: string,\n method: string,\n args: JsonValue[],\n context: Context\n ): Promise<JsonValue>;\n memorySave(draft: MemoryDraft, context: Context): Promise<MemoryEntry>;\n memoryRemove(id: string, context: Context): Promise<{ removed: boolean }>;\n freeze(agentId: string, context: Context): Promise<Created>;\n newChat(agentId: string, context: Context): Promise<Created>;\n thaw(frozenId: string, context: Context): Promise<Created>;\n discard(frozenId: string, context: Context): Promise<Created>;\n changesOpen(context: Context): Promise<void>;\n changeAccept(path: string, context: Context): Promise<Created>;\n changeRevert(path: string, context: Context): Promise<Created>;\n monitorOpen(lease: string | null, resync: boolean, context: Context): Promise<MonitorLease>;\n monitorClose(lease: string, context: Context): Promise<void>;\n}\n\nexport interface MonitorLease {\n lease: string;\n ttlMs: number;\n}\n\nexport interface MonitorAgent {\n id: string;\n name: string;\n kind: 'cone' | 'scoop';\n parentId: string | null;\n model: string | null;\n running: boolean;\n queued: number;\n tool: string | null;\n retry: { attempt: number; since: number; error: string } | null;\n failed: string | null;\n context: number | null;\n}\n\nexport interface MonitorTask {\n id: string;\n kind: string;\n detail: string | null;\n agentId: string | null;\n owner: string | null;\n status: 'pending' | 'running' | 'waiting' | 'completing';\n phase: string;\n background: boolean;\n aborting: number | null;\n since: number;\n waits: number;\n}\n\nexport interface MonitorProcess {\n pid: number;\n ppid: number;\n pgid: number;\n user: string | null;\n agentId: string | null;\n command: string;\n state: 'S' | 'Z';\n started: number;\n zombie: number | null;\n tty: boolean;\n rss: number | null;\n}\n\nexport interface MonitorProblem {\n id: string;\n at: number;\n agentId: string | null;\n kind: 'task' | 'retry' | 'turn';\n message: string;\n}\n\nexport interface MonitorFeed {\n open: boolean;\n at: number;\n usage: { tokens: number; cost: number; free: boolean } | null;\n agents: MonitorAgent[];\n tasks: MonitorTask[];\n processes: MonitorProcess[] | null;\n problems: MonitorProblem[];\n paused: boolean;\n}\n\nexport interface MemoryDraft {\n id: string | null;\n scope: string;\n section: string;\n title: string;\n body: string;\n tag: MemoryTag | null;\n}\n\nexport type Created = { id: string; error: null } | { id: null; error: string };\n\nexport interface AgentSummary {\n id: string;\n name: string;\n kind: 'cone' | 'scoop';\n parentId: string | null;\n role: string | null;\n}\n\nexport interface AgentsSummary {\n active: string;\n agents: AgentSummary[];\n}\n\nexport type LickResolved =\n | { done: true; text: string; error: null }\n | { done: false; text: null; error: string };\n\nexport interface WebhookRequest {\n id: string | null;\n headers: Record<string, string>;\n body: string;\n}\n\nexport type Rewound =\n | { done: true; text: string; reason: null }\n | { done: false; text: null; reason: 'busy' | 'no-turn' };\n\nexport interface AgentSettingsChange {\n model: { provider: string; modelId: string } | null;\n thinkingLevel: 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | null;\n agentId?: string | null;\n}\n\nexport const AgentControl = defineService<AgentControl>('slicc.agent.control');\n\nexport type Delivered = 'run' | 'steer' | 'follow-up';\n\nexport interface AgentTranscript {\n readonly state: ReplicatedState<ConversationView>;\n readonly deliveries: ReplicatedState<Record<string, Delivered>>;\n readonly agents: ReplicatedState<AgentsSummary>;\n readonly views: ReplicatedState<Record<string, ConversationView>>;\n readonly commands: ReplicatedState<Command[]>;\n readonly sprinkles: ReplicatedState<Sprinkle[]>;\n readonly memories: ReplicatedState<MemoryEntry[]>;\n readonly memoryScopes: ReplicatedState<MemoryScope[]>;\n readonly frozen: ReplicatedState<FrozenCone[]>;\n readonly changes: ReplicatedState<ChangesView>;\n readonly monitor: ReplicatedState<MonitorFeed>;\n}\n\nexport interface Command {\n name: string;\n description: string;\n kind: 'prompt' | 'skill';\n}\n\nexport const AgentTranscript = defineService<AgentTranscript>('slicc.agent.transcript');\n\nexport interface AgentSessions {\n attach(sessionId: string, context: Context): Promise<void>;\n detach(context: Context): Promise<void>;\n}\n\nexport const AgentSessions = defineService<AgentSessions>('slicc.agent.sessions');\n\nexport const AgentRemoteService = defineService<AgentRemote>('slicc.agent.remote');\n\nexport const AGENT_SESSION = 'agent';\n\nexport interface ModelChoice {\n id: string;\n label: string;\n provider: string;\n kind: 'chat' | 'classifier';\n reasoning: boolean;\n images?: boolean;\n contextWindow: number;\n}\n\nexport interface AccountState {\n id: string;\n provider: string;\n identity: string;\n status: 'connected' | 'disconnected' | 'expired';\n auth: 'api-key' | 'oauth';\n needs: 'cors-free-transport' | null;\n group?: 'providers' | 'mcp';\n secret?: 'api-key' | 'token';\n}\n\nexport interface BudgetState {\n provider: string;\n percent: number;\n window: 'weekly';\n resets: string;\n}\n\nexport interface SignIn {\n clientId: string;\n scopes: string;\n imsEnvironment: string;\n}\n\nexport interface McpSignIn {\n url: string;\n state: string;\n}\n\nexport interface SettingsState {\n models: ModelChoice[];\n accounts: AccountState[];\n budget?: BudgetState | null;\n}\n\nexport interface AgentSettings {\n readonly state: ReplicatedState<SettingsState>;\n connect(\n providerId: string,\n secret: string,\n region: string | null,\n context: Context\n ): Promise<void>;\n disconnect(providerId: string, context: Context): Promise<void>;\n signIn(providerId: string, context: Context): Promise<SignIn | McpSignIn | null>;\n}\n\nexport const AgentSettings = defineService<AgentSettings>('slicc.agent.settings');\n"],
5
- "mappings": "AAAA;AAAA,EAEE;AAAA,OAGK;AAgNA,MAAM,eAAe,cAA4B,qBAAqB;AAwBtE,MAAM,kBAAkB,cAA+B,wBAAwB;AAO/E,MAAM,gBAAgB,cAA6B,sBAAsB;AAEzE,MAAM,qBAAqB,cAA2B,oBAAoB;AAE1E,MAAM,gBAAgB;AA2DtB,MAAM,gBAAgB,cAA6B,sBAAsB;",
4
+ "sourcesContent": ["import {\n type Context,\n defineService,\n type JsonValue,\n type ReplicatedState,\n} from '@earendil-works/chord';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport type { ChangesView } from './changes/git.ts';\nimport type { FrozenCone } from './freezer/index.ts';\nimport type { MemoryEntry, MemoryScope, MemoryTag } from './memory/format.ts';\nimport type { AgentRemote } from './remote/host.ts';\nimport type { AgentPages } from './remote/pages.ts';\nimport type { Sprinkle } from './sprinkles/kind.ts';\n\nexport type SendMode = 'steer' | 'followUp' | 'reject';\n\nexport interface SendRequest {\n text: string;\n whenBusy: SendMode;\n requestId: string | null;\n agentId?: string | null;\n}\n\nexport interface OperationError {\n code: string;\n message: string;\n}\n\nexport type SendResponse =\n | { accepted: true; submissionId: string; error: null }\n | { accepted: false; submissionId: null; error: OperationError };\n\nexport type PromptResult =\n | { status: 'done'; text: string; reason: null }\n | { status: 'unanswered'; text: null; reason: string };\n\nexport interface AgentControl {\n send(request: SendRequest, context: Context): Promise<SendResponse>;\n wait(submissionId: string, context: Context): Promise<PromptResult>;\n withdraw(\n submissionId: string,\n context: Context\n ): Promise<{ outcome: 'withdrawn' | 'already_placed' | 'not_found' }>;\n abort(context: Context): Promise<void>;\n compact(instructions: string | null, context: Context): Promise<SendResponse>;\n compactAgent(\n agentId: string,\n instructions: string | null,\n context: Context\n ): Promise<SendResponse>;\n reset(handoff: string | null, context: Context): Promise<void>;\n configure(change: AgentSettingsChange, context: Context): Promise<void>;\n rewind(messageId: string | null, context: Context): Promise<Rewound>;\n rewindAgent(agentId: string, messageId: string | null, context: Context): Promise<Rewound>;\n resolveLick(\n lickId: string,\n state: 'confirmed' | 'dismissed',\n context: Context\n ): Promise<LickResolved>;\n resolveAgentLick(\n agentId: string,\n lickId: string,\n state: 'confirmed' | 'dismissed',\n context: Context\n ): Promise<LickResolved>;\n webhook(\n name: string,\n delivery: WebhookRequest,\n context: Context\n ): Promise<{ delivered: boolean }>;\n stopAgent(agentId: string, context: Context): Promise<void>;\n selectCone(agentId: string, context: Context): Promise<void>;\n createCone(name: string, context: Context): Promise<Created>;\n createScoop(parentId: string, name: string, context: Context): Promise<Created>;\n drop(agentId: string, context: Context): Promise<Created>;\n unqueue(\n agentId: string | null,\n submissionId: string,\n context: Context\n ): Promise<{ outcome: 'withdrawn' | 'already_placed' | 'not_found' }>;\n sprinkleSend(\n sprinkleId: string,\n payload: { action: string; data: JsonValue | null; target: string | null },\n context: Context\n ): Promise<{ delivered: boolean }>;\n sprinkleCall(\n sprinkleId: string,\n method: string,\n args: JsonValue[],\n context: Context\n ): Promise<JsonValue>;\n memorySave(draft: MemoryDraft, context: Context): Promise<MemoryEntry>;\n memoryRemove(id: string, context: Context): Promise<{ removed: boolean }>;\n freeze(agentId: string, context: Context): Promise<Created>;\n newChat(agentId: string, context: Context): Promise<Created>;\n thaw(frozenId: string, context: Context): Promise<Created>;\n discard(frozenId: string, context: Context): Promise<Created>;\n changesOpen(context: Context): Promise<void>;\n changeAccept(path: string, context: Context): Promise<Created>;\n changeRevert(path: string, context: Context): Promise<Created>;\n monitorOpen(lease: string | null, resync: boolean, context: Context): Promise<MonitorLease>;\n monitorClose(lease: string, context: Context): Promise<void>;\n}\n\nexport interface MonitorLease {\n lease: string;\n ttlMs: number;\n}\n\nexport interface MonitorAgent {\n id: string;\n name: string;\n kind: 'cone' | 'scoop';\n parentId: string | null;\n model: string | null;\n running: boolean;\n queued: number;\n tool: string | null;\n retry: { attempt: number; since: number; error: string } | null;\n failed: string | null;\n context: number | null;\n}\n\nexport interface MonitorTask {\n id: string;\n kind: string;\n detail: string | null;\n agentId: string | null;\n owner: string | null;\n status: 'pending' | 'running' | 'waiting' | 'completing';\n phase: string;\n background: boolean;\n aborting: number | null;\n since: number;\n waits: number;\n}\n\nexport interface MonitorProcess {\n pid: number;\n ppid: number;\n pgid: number;\n user: string | null;\n agentId: string | null;\n command: string;\n state: 'S' | 'Z';\n started: number;\n zombie: number | null;\n tty: boolean;\n rss: number | null;\n}\n\nexport interface MonitorProblem {\n id: string;\n at: number;\n agentId: string | null;\n kind: 'task' | 'retry' | 'turn';\n message: string;\n}\n\nexport interface MonitorFeed {\n open: boolean;\n at: number;\n usage: { tokens: number; cost: number; free: boolean } | null;\n agents: MonitorAgent[];\n tasks: MonitorTask[];\n processes: MonitorProcess[] | null;\n problems: MonitorProblem[];\n paused: boolean;\n}\n\nexport interface MemoryDraft {\n id: string | null;\n scope: string;\n section: string;\n title: string;\n body: string;\n tag: MemoryTag | null;\n}\n\nexport type Created = { id: string; error: null } | { id: null; error: string };\n\nexport interface AgentSummary {\n id: string;\n name: string;\n kind: 'cone' | 'scoop';\n parentId: string | null;\n role: string | null;\n}\n\nexport interface AgentsSummary {\n active: string;\n agents: AgentSummary[];\n}\n\nexport type LickResolved =\n | { done: true; text: string; error: null }\n | { done: false; text: null; error: string };\n\nexport interface WebhookRequest {\n id: string | null;\n headers: Record<string, string>;\n body: string;\n}\n\nexport type Rewound =\n | { done: true; text: string; reason: null }\n | { done: false; text: null; reason: 'busy' | 'no-turn' };\n\nexport interface AgentSettingsChange {\n model: { provider: string; modelId: string } | null;\n thinkingLevel: 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | null;\n agentId?: string | null;\n}\n\nexport const AgentControl = defineService<AgentControl>('slicc.agent.control');\n\nexport type Delivered = 'run' | 'steer' | 'follow-up';\n\nexport interface AgentTranscript {\n readonly state: ReplicatedState<ConversationView>;\n readonly deliveries: ReplicatedState<Record<string, Delivered>>;\n readonly agents: ReplicatedState<AgentsSummary>;\n readonly views: ReplicatedState<Record<string, ConversationView>>;\n readonly commands: ReplicatedState<Command[]>;\n readonly sprinkles: ReplicatedState<Sprinkle[]>;\n readonly memories: ReplicatedState<MemoryEntry[]>;\n readonly memoryScopes: ReplicatedState<MemoryScope[]>;\n readonly frozen: ReplicatedState<FrozenCone[]>;\n readonly changes: ReplicatedState<ChangesView>;\n readonly monitor: ReplicatedState<MonitorFeed>;\n}\n\nexport interface Command {\n name: string;\n description: string;\n kind: 'prompt' | 'skill';\n}\n\nexport const AgentTranscript = defineService<AgentTranscript>('slicc.agent.transcript');\n\nexport interface AgentSessions {\n attach(sessionId: string, context: Context): Promise<void>;\n detach(context: Context): Promise<void>;\n}\n\nexport const AgentSessions = defineService<AgentSessions>('slicc.agent.sessions');\n\nexport const AgentRemoteService = defineService<AgentRemote>('slicc.agent.remote');\n\nexport const AgentPagesService = defineService<AgentPages>('slicc.agent.pages');\n\nexport const AGENT_SESSION = 'agent';\n\nexport interface ModelChoice {\n id: string;\n label: string;\n provider: string;\n kind: 'chat' | 'classifier';\n reasoning: boolean;\n images?: boolean;\n contextWindow: number;\n}\n\nexport interface AccountState {\n id: string;\n provider: string;\n identity: string;\n status: 'connected' | 'disconnected' | 'expired';\n auth: 'api-key' | 'oauth';\n needs: 'cors-free-transport' | null;\n group?: 'providers' | 'mcp';\n secret?: 'api-key' | 'token';\n}\n\nexport interface BudgetState {\n provider: string;\n percent: number;\n window: 'weekly';\n resets: string;\n}\n\nexport interface SignIn {\n clientId: string;\n scopes: string;\n imsEnvironment: string;\n}\n\nexport interface McpSignIn {\n url: string;\n state: string;\n}\n\nexport interface SettingsState {\n models: ModelChoice[];\n accounts: AccountState[];\n budget?: BudgetState | null;\n}\n\nexport interface AgentSettings {\n readonly state: ReplicatedState<SettingsState>;\n connect(\n providerId: string,\n secret: string,\n region: string | null,\n context: Context\n ): Promise<void>;\n disconnect(providerId: string, context: Context): Promise<void>;\n signIn(providerId: string, context: Context): Promise<SignIn | McpSignIn | null>;\n}\n\nexport const AgentSettings = defineService<AgentSettings>('slicc.agent.settings');\n"],
5
+ "mappings": "AAAA;AAAA,EAEE;AAAA,OAGK;AAiNA,MAAM,eAAe,cAA4B,qBAAqB;AAwBtE,MAAM,kBAAkB,cAA+B,wBAAwB;AAO/E,MAAM,gBAAgB,cAA6B,sBAAsB;AAEzE,MAAM,qBAAqB,cAA2B,oBAAoB;AAE1E,MAAM,oBAAoB,cAA0B,mBAAmB;AAEvE,MAAM,gBAAgB;AA2DtB,MAAM,gBAAgB,cAA6B,sBAAsB;",
6
6
  "names": []
7
7
  }
@@ -2,6 +2,7 @@ import { AgentAdapter } from "./agent-port.js";
2
2
  import { ChangesAdapter } from "./changes-port.js";
3
3
  import { MemoryAdapter } from "./memory-port.js";
4
4
  import { MonitorAdapter } from "./monitor-port.js";
5
+ import { PageAdapter } from "./page-port.js";
5
6
  import { SettingsAdapter } from "./settings-port.js";
6
7
  import { SprinkleAdapter } from "./sprinkle-port.js";
7
8
  import { TrayAdapter } from "./tray-port.js";
@@ -21,13 +22,18 @@ import {
21
22
  MonitorAdapter as MonitorAdapter2,
22
23
  monitorSnapshot
23
24
  } from "./monitor-port.js";
25
+ import {
26
+ PageAdapter as PageAdapter2
27
+ } from "./page-port.js";
24
28
  import { SETTINGS_KEY, SettingsAdapter as SettingsAdapter2 } from "./settings-port.js";
25
29
  import { SprinkleAdapter as SprinkleAdapter2 } from "./sprinkle-port.js";
26
30
  import { TrayAdapter as TrayAdapter2 } from "./tray-port.js";
27
31
  function createAgentModel(connection, options = {}) {
28
32
  const agent = new AgentAdapter(connection);
33
+ const run = options.page?.run;
29
34
  return {
30
35
  agent,
36
+ page: options.page && run ? new PageAdapter(connection, agent, { ...options.page, run }) : null,
31
37
  settings: new SettingsAdapter(
32
38
  connection.settings,
33
39
  agent,
@@ -50,6 +56,7 @@ export {
50
56
  ChangesAdapter2 as ChangesAdapter,
51
57
  MemoryAdapter2 as MemoryAdapter,
52
58
  MonitorAdapter2 as MonitorAdapter,
59
+ PageAdapter2 as PageAdapter,
53
60
  SETTINGS_KEY,
54
61
  SettingsAdapter2 as SettingsAdapter,
55
62
  SprinkleAdapter2 as SprinkleAdapter,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/spectrum/index.ts"],
4
- "sourcesContent": ["import type { MonitorSection } from '@ai-ecoverse/slicc-spectrum/ui';\nimport type { AgentConnection } from '../client.ts';\nimport { AgentAdapter } from './agent-port.ts';\nimport { ChangesAdapter } from './changes-port.ts';\nimport { MemoryAdapter } from './memory-port.ts';\nimport { MonitorAdapter } from './monitor-port.ts';\nimport { type Login, SettingsAdapter } from './settings-port.ts';\nimport { SprinkleAdapter } from './sprinkle-port.ts';\nimport { TrayAdapter } from './tray-port.ts';\n\nexport { AgentAdapter, CONE } from './agent-port.ts';\nexport { ChangesAdapter, type ChangesEvents } from './changes-port.ts';\nexport { MemoryAdapter } from './memory-port.ts';\nexport {\n assistant,\n errorAction,\n errorPart,\n isBusy,\n queued,\n textOf,\n toMessages,\n} from './messages.ts';\nexport {\n MonitorAdapter,\n type MonitorAdapterOptions,\n type MonitorInput,\n monitorSnapshot,\n} from './monitor-port.ts';\nexport { type Login, SETTINGS_KEY, SettingsAdapter } from './settings-port.ts';\nexport { SprinkleAdapter } from './sprinkle-port.ts';\nexport { TrayAdapter } from './tray-port.ts';\n\nexport function createAgentModel(\n connection: AgentConnection,\n options: {\n storage?: Pick<Storage, 'getItem' | 'setItem'> | null;\n login?: Login;\n monitor?: { extra?: () => readonly MonitorSection[] };\n } = {}\n): {\n agent: AgentAdapter;\n settings: SettingsAdapter;\n tray: TrayAdapter;\n sprinkles: SprinkleAdapter;\n memory: MemoryAdapter;\n changes: ChangesAdapter;\n monitor: MonitorAdapter;\n} {\n const agent = new AgentAdapter(connection);\n return {\n agent,\n settings: new SettingsAdapter(\n connection.settings,\n agent,\n options.storage ?? null,\n options.login ?? null\n ),\n tray: new TrayAdapter(connection.settings),\n sprinkles: new SprinkleAdapter(connection),\n memory: new MemoryAdapter(connection),\n changes: new ChangesAdapter(connection),\n monitor: new MonitorAdapter(\n connection,\n options.monitor?.extra ? { extra: options.monitor.extra } : {}\n ),\n };\n}\n"],
5
- "mappings": "AAEA,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB;AAC9B,SAAS,sBAAsB;AAC/B,SAAqB,uBAAuB;AAC5C,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAE5B,SAAS,gBAAAA,eAAc,YAAY;AACnC,SAAS,kBAAAC,uBAA0C;AACnD,SAAS,iBAAAC,sBAAqB;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE,kBAAAC;AAAA,EAGA;AAAA,OACK;AACP,SAAqB,cAAc,mBAAAC,wBAAuB;AAC1D,SAAS,mBAAAC,wBAAuB;AAChC,SAAS,eAAAC,oBAAmB;AAErB,SAAS,iBACd,YACA,UAII,CAAC,GASL;AACA,QAAM,QAAQ,IAAI,aAAa,UAAU;AACzC,SAAO;AAAA,IACL;AAAA,IACA,UAAU,IAAI;AAAA,MACZ,WAAW;AAAA,MACX;AAAA,MACA,QAAQ,WAAW;AAAA,MACnB,QAAQ,SAAS;AAAA,IACnB;AAAA,IACA,MAAM,IAAI,YAAY,WAAW,QAAQ;AAAA,IACzC,WAAW,IAAI,gBAAgB,UAAU;AAAA,IACzC,QAAQ,IAAI,cAAc,UAAU;AAAA,IACpC,SAAS,IAAI,eAAe,UAAU;AAAA,IACtC,SAAS,IAAI;AAAA,MACX;AAAA,MACA,QAAQ,SAAS,QAAQ,EAAE,OAAO,QAAQ,QAAQ,MAAM,IAAI,CAAC;AAAA,IAC/D;AAAA,EACF;AACF;",
6
- "names": ["AgentAdapter", "ChangesAdapter", "MemoryAdapter", "MonitorAdapter", "SettingsAdapter", "SprinkleAdapter", "TrayAdapter"]
4
+ "sourcesContent": ["import type { MonitorSection } from '@ai-ecoverse/slicc-spectrum/ui';\nimport type { AgentConnection } from '../client.ts';\nimport { AgentAdapter } from './agent-port.ts';\nimport { ChangesAdapter } from './changes-port.ts';\nimport { MemoryAdapter } from './memory-port.ts';\nimport { MonitorAdapter } from './monitor-port.ts';\nimport { PageAdapter, type PageOptions } from './page-port.ts';\nimport { type Login, SettingsAdapter } from './settings-port.ts';\nimport { SprinkleAdapter } from './sprinkle-port.ts';\nimport { TrayAdapter } from './tray-port.ts';\n\nexport { AgentAdapter, CONE } from './agent-port.ts';\nexport { ChangesAdapter, type ChangesEvents } from './changes-port.ts';\nexport { MemoryAdapter } from './memory-port.ts';\nexport {\n assistant,\n errorAction,\n errorPart,\n isBusy,\n queued,\n textOf,\n toMessages,\n} from './messages.ts';\nexport {\n MonitorAdapter,\n type MonitorAdapterOptions,\n type MonitorInput,\n monitorSnapshot,\n} from './monitor-port.ts';\nexport {\n PageAdapter,\n type PageDocument,\n type PageOptions,\n type PageRun,\n type PageTarget,\n} from './page-port.ts';\nexport { type Login, SETTINGS_KEY, SettingsAdapter } from './settings-port.ts';\nexport { SprinkleAdapter } from './sprinkle-port.ts';\nexport { TrayAdapter } from './tray-port.ts';\n\nexport function createAgentModel(\n connection: AgentConnection,\n options: {\n storage?: Pick<Storage, 'getItem' | 'setItem'> | null;\n login?: Login;\n monitor?: { extra?: () => readonly MonitorSection[] };\n page?: PageOptions;\n } = {}\n): {\n agent: AgentAdapter;\n settings: SettingsAdapter;\n tray: TrayAdapter;\n sprinkles: SprinkleAdapter;\n memory: MemoryAdapter;\n changes: ChangesAdapter;\n monitor: MonitorAdapter;\n page: PageAdapter | null;\n} {\n const agent = new AgentAdapter(connection);\n const run = options.page?.run;\n return {\n agent,\n page: options.page && run ? new PageAdapter(connection, agent, { ...options.page, run }) : null,\n settings: new SettingsAdapter(\n connection.settings,\n agent,\n options.storage ?? null,\n options.login ?? null\n ),\n tray: new TrayAdapter(connection.settings),\n sprinkles: new SprinkleAdapter(connection),\n memory: new MemoryAdapter(connection),\n changes: new ChangesAdapter(connection),\n monitor: new MonitorAdapter(\n connection,\n options.monitor?.extra ? { extra: options.monitor.extra } : {}\n ),\n };\n}\n"],
5
+ "mappings": "AAEA,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB;AAC9B,SAAS,sBAAsB;AAC/B,SAAS,mBAAqC;AAC9C,SAAqB,uBAAuB;AAC5C,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAE5B,SAAS,gBAAAA,eAAc,YAAY;AACnC,SAAS,kBAAAC,uBAA0C;AACnD,SAAS,iBAAAC,sBAAqB;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE,kBAAAC;AAAA,EAGA;AAAA,OACK;AACP;AAAA,EACE,eAAAC;AAAA,OAKK;AACP,SAAqB,cAAc,mBAAAC,wBAAuB;AAC1D,SAAS,mBAAAC,wBAAuB;AAChC,SAAS,eAAAC,oBAAmB;AAErB,SAAS,iBACd,YACA,UAKI,CAAC,GAUL;AACA,QAAM,QAAQ,IAAI,aAAa,UAAU;AACzC,QAAM,MAAM,QAAQ,MAAM;AAC1B,SAAO;AAAA,IACL;AAAA,IACA,MAAM,QAAQ,QAAQ,MAAM,IAAI,YAAY,YAAY,OAAO,EAAE,GAAG,QAAQ,MAAM,IAAI,CAAC,IAAI;AAAA,IAC3F,UAAU,IAAI;AAAA,MACZ,WAAW;AAAA,MACX;AAAA,MACA,QAAQ,WAAW;AAAA,MACnB,QAAQ,SAAS;AAAA,IACnB;AAAA,IACA,MAAM,IAAI,YAAY,WAAW,QAAQ;AAAA,IACzC,WAAW,IAAI,gBAAgB,UAAU;AAAA,IACzC,QAAQ,IAAI,cAAc,UAAU;AAAA,IACpC,SAAS,IAAI,eAAe,UAAU;AAAA,IACtC,SAAS,IAAI;AAAA,MACX;AAAA,MACA,QAAQ,SAAS,QAAQ,EAAE,OAAO,QAAQ,QAAQ,MAAM,IAAI,CAAC;AAAA,IAC/D;AAAA,EACF;AACF;",
6
+ "names": ["AgentAdapter", "ChangesAdapter", "MemoryAdapter", "MonitorAdapter", "PageAdapter", "SettingsAdapter", "SprinkleAdapter", "TrayAdapter"]
7
7
  }
@@ -0,0 +1,132 @@
1
+ import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
2
+ const REPORT_MS = 250;
3
+ const POLL_MS = 1e4;
4
+ async function quietly(call) {
5
+ try {
6
+ await call();
7
+ } catch {
8
+ }
9
+ }
10
+ const ACTIVITY = ["pointerdown", "keydown"];
11
+ const FOCUS = ["focus", "blur"];
12
+ class PageAdapter {
13
+ #connection;
14
+ #agent;
15
+ #options;
16
+ #run;
17
+ #now;
18
+ #off = [];
19
+ #stream = null;
20
+ #lastActive;
21
+ #timer;
22
+ #disposed = false;
23
+ done;
24
+ constructor(connection, agent, options) {
25
+ this.#connection = connection;
26
+ this.#agent = agent;
27
+ this.#options = options;
28
+ this.#run = options.run;
29
+ this.#now = options.now ?? Date.now;
30
+ this.#lastActive = this.#now();
31
+ const doc = options.document ?? globalThis.document;
32
+ const win = options.window ?? globalThis.window;
33
+ const schedule = () => this.#schedule();
34
+ const active = () => {
35
+ this.#lastActive = this.#now();
36
+ this.#schedule();
37
+ };
38
+ if (doc) this.#listen(doc, "visibilitychange", schedule);
39
+ if (win) {
40
+ for (const type of FOCUS) this.#listen(win, type, schedule);
41
+ for (const type of ACTIVITY) this.#listen(win, type, active);
42
+ }
43
+ this.#off.push(agent.on("active", schedule));
44
+ const agents = connection.agents?.subscribe(schedule);
45
+ if (agents) this.#off.push(agents);
46
+ this.done = this.#loop().catch(() => void 0);
47
+ }
48
+ #listen(target, type, listener) {
49
+ target.addEventListener(type, listener, true);
50
+ this.#off.push(() => target.removeEventListener(type, listener, true));
51
+ }
52
+ #cone() {
53
+ const summary = this.#connection.agents?.value;
54
+ const agents = summary?.agents ?? [];
55
+ let id = this.#agent.active();
56
+ for (let hops = 0; id && hops <= agents.length; hops++) {
57
+ const found = agents.find((agent) => agent.id === id);
58
+ if (!found || found.kind === "cone") return id;
59
+ id = found.parentId;
60
+ }
61
+ return summary?.active ?? id;
62
+ }
63
+ state() {
64
+ const doc = this.#options.document ?? globalThis.document;
65
+ return {
66
+ tab: this.#options.tab,
67
+ kinds: ["panel"],
68
+ cone: this.#cone(),
69
+ visible: doc ? doc.visibilityState === "visible" : false,
70
+ focused: doc ? doc.hasFocus() : false,
71
+ lastActive: this.#lastActive
72
+ };
73
+ }
74
+ #schedule() {
75
+ if (this.#disposed || this.#timer !== void 0) return;
76
+ this.#timer = setTimeout(() => {
77
+ this.#timer = void 0;
78
+ const stream = this.#stream;
79
+ if (!stream || this.#disposed) return;
80
+ void quietly(() => this.#connection.pages?.report(stream, this.state(), BACKGROUND_CONTEXT));
81
+ }, this.#options.reportMs ?? REPORT_MS);
82
+ }
83
+ async #loop() {
84
+ const pages = this.#connection.pages;
85
+ if (!pages) return;
86
+ while (!this.#disposed) {
87
+ const opened = await pages.open(this.state(), BACKGROUND_CONTEXT);
88
+ if ("error" in opened) return;
89
+ this.#stream = opened.stream;
90
+ if (this.#disposed) break;
91
+ let done = false;
92
+ while (!done && !this.#disposed) {
93
+ const batch = await pages.next(
94
+ opened.stream,
95
+ this.#options.pollMs ?? POLL_MS,
96
+ BACKGROUND_CONTEXT
97
+ );
98
+ for (const request of batch.requests) void this.#handle(opened.stream, request);
99
+ done = batch.done;
100
+ }
101
+ }
102
+ const stream = this.#stream;
103
+ if (stream) await pages.close(stream, BACKGROUND_CONTEXT);
104
+ }
105
+ async #handle(stream, request) {
106
+ let result;
107
+ try {
108
+ result = await this.#run(request.argv, request.caller);
109
+ } catch (error) {
110
+ result = { stdout: "", stderr: `panel: ${error.message}
111
+ `, code: 1 };
112
+ }
113
+ await quietly(
114
+ () => this.#connection.pages?.answer(stream, request.id, result, BACKGROUND_CONTEXT)
115
+ );
116
+ }
117
+ dispose() {
118
+ this.#disposed = true;
119
+ clearTimeout(this.#timer);
120
+ this.#timer = void 0;
121
+ for (const off of this.#off.splice(0)) off();
122
+ const stream = this.#stream;
123
+ if (stream) void quietly(() => this.#connection.pages?.close(stream, BACKGROUND_CONTEXT));
124
+ return this.done;
125
+ }
126
+ }
127
+ export {
128
+ POLL_MS,
129
+ PageAdapter,
130
+ REPORT_MS
131
+ };
132
+ //# sourceMappingURL=page-port.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/spectrum/page-port.ts"],
4
+ "sourcesContent": ["import { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { AgentConnection } from '../client.ts';\nimport type { PageCaller, PageRequest, PageResult, PageTab } from '../remote/pages.ts';\nimport type { AgentAdapter } from './agent-port.ts';\n\nexport const REPORT_MS = 250;\nexport const POLL_MS = 10000;\n\nexport type PageRun = (\n argv: readonly string[],\n caller: PageCaller\n) => PageResult | Promise<PageResult>;\n\nexport interface PageTarget {\n addEventListener(type: string, listener: () => void, options?: boolean): void;\n removeEventListener(type: string, listener: () => void, options?: boolean): void;\n}\n\nexport interface PageDocument extends PageTarget {\n readonly visibilityState: string;\n hasFocus(): boolean;\n}\n\nexport interface PageOptions {\n tab: string;\n run?: PageRun;\n document?: PageDocument;\n window?: PageTarget;\n now?: () => number;\n reportMs?: number;\n pollMs?: number;\n}\n\nasync function quietly(call: () => Promise<void> | undefined): Promise<void> {\n try {\n await call();\n } catch {}\n}\n\nconst ACTIVITY = ['pointerdown', 'keydown'];\nconst FOCUS = ['focus', 'blur'];\n\nexport class PageAdapter {\n readonly #connection: AgentConnection;\n readonly #agent: AgentAdapter;\n readonly #options: PageOptions;\n readonly #run: PageRun;\n readonly #now: () => number;\n readonly #off: (() => void)[] = [];\n #stream: string | null = null;\n #lastActive: number;\n #timer: ReturnType<typeof setTimeout> | undefined;\n #disposed = false;\n readonly done: Promise<void>;\n\n constructor(\n connection: AgentConnection,\n agent: AgentAdapter,\n options: PageOptions & { run: PageRun }\n ) {\n this.#connection = connection;\n this.#agent = agent;\n this.#options = options;\n this.#run = options.run;\n this.#now = options.now ?? Date.now;\n this.#lastActive = this.#now();\n const doc = options.document ?? (globalThis as { document?: PageDocument }).document;\n const win = options.window ?? (globalThis as { window?: PageTarget }).window;\n const schedule = () => this.#schedule();\n const active = () => {\n this.#lastActive = this.#now();\n this.#schedule();\n };\n if (doc) this.#listen(doc, 'visibilitychange', schedule);\n if (win) {\n for (const type of FOCUS) this.#listen(win, type, schedule);\n for (const type of ACTIVITY) this.#listen(win, type, active);\n }\n this.#off.push(agent.on('active', schedule));\n const agents = connection.agents?.subscribe(schedule);\n if (agents) this.#off.push(agents);\n this.done = this.#loop().catch(() => undefined);\n }\n\n #listen(target: PageTarget, type: string, listener: () => void): void {\n target.addEventListener(type, listener, true);\n this.#off.push(() => target.removeEventListener(type, listener, true));\n }\n\n #cone(): string | null {\n const summary = this.#connection.agents?.value;\n const agents = summary?.agents ?? [];\n let id: string | null = this.#agent.active();\n for (let hops = 0; id && hops <= agents.length; hops++) {\n const found = agents.find((agent) => agent.id === id);\n if (!found || found.kind === 'cone') return id;\n id = found.parentId;\n }\n return summary?.active ?? id;\n }\n\n state(): PageTab {\n const doc = this.#options.document ?? (globalThis as { document?: PageDocument }).document;\n return {\n tab: this.#options.tab,\n kinds: ['panel'],\n cone: this.#cone(),\n visible: doc ? doc.visibilityState === 'visible' : false,\n focused: doc ? doc.hasFocus() : false,\n lastActive: this.#lastActive,\n };\n }\n\n #schedule(): void {\n if (this.#disposed || this.#timer !== undefined) return;\n this.#timer = setTimeout(() => {\n this.#timer = undefined;\n const stream = this.#stream;\n if (!stream || this.#disposed) return;\n void quietly(() => this.#connection.pages?.report(stream, this.state(), BACKGROUND_CONTEXT));\n }, this.#options.reportMs ?? REPORT_MS);\n }\n\n async #loop(): Promise<void> {\n const pages = this.#connection.pages;\n if (!pages) return;\n while (!this.#disposed) {\n const opened = await pages.open(this.state(), BACKGROUND_CONTEXT);\n if ('error' in opened) return;\n this.#stream = opened.stream;\n if (this.#disposed) break;\n let done = false;\n while (!done && !this.#disposed) {\n const batch = await pages.next(\n opened.stream,\n this.#options.pollMs ?? POLL_MS,\n BACKGROUND_CONTEXT\n );\n for (const request of batch.requests) void this.#handle(opened.stream, request);\n done = batch.done;\n }\n }\n const stream = this.#stream;\n if (stream) await pages.close(stream, BACKGROUND_CONTEXT);\n }\n\n async #handle(stream: string, request: PageRequest): Promise<void> {\n let result: PageResult;\n try {\n result = await this.#run(request.argv, request.caller);\n } catch (error) {\n result = { stdout: '', stderr: `panel: ${(error as Error).message}\\n`, code: 1 };\n }\n await quietly(() =>\n this.#connection.pages?.answer(stream, request.id, result, BACKGROUND_CONTEXT)\n );\n }\n\n dispose(): Promise<void> {\n this.#disposed = true;\n clearTimeout(this.#timer);\n this.#timer = undefined;\n for (const off of this.#off.splice(0)) off();\n const stream = this.#stream;\n if (stream) void quietly(() => this.#connection.pages?.close(stream, BACKGROUND_CONTEXT));\n return this.done;\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }