@ai-ecoverse/slicc-agent 6.13.2 → 6.13.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/agent CHANGED
@@ -53,7 +53,15 @@ done
53
53
 
54
54
  count=0
55
55
  seen=0
56
- while [ ! -s "$dir/out/$id.code" ]; do
56
+ code=""
57
+ while true; do
58
+ if [ -s "$dir/out/$id.code" ]; then
59
+ read -r code < "$dir/out/$id.code" || true
60
+ case "$code" in
61
+ ''|*[!0-9]*) ;;
62
+ *) break ;;
63
+ esac
64
+ fi
57
65
  sleep 0.1
58
66
  count=$((count + 1))
59
67
  if [ $seen = 0 ] && [ -f "$dir/out/$id.ack" ]; then seen=1; fi
@@ -71,7 +79,6 @@ while [ ! -s "$dir/out/$id.code" ]; do
71
79
  after=""
72
80
  fi
73
81
  done
74
- read -r code < "$dir/out/$id.code"
75
82
  if [ -f "$dir/out/$id.err" ]; then
76
83
  cat "$dir/out/$id.out"
77
84
  cat "$dir/out/$id.err" >&2
@@ -64,12 +64,44 @@ function controlPlane(env, runner, options = {}) {
64
64
  return next;
65
65
  };
66
66
  async function answer(id, result, context) {
67
- await env.writeFile(`${responses}/${id}.out`, result.out, context);
68
- if (result.err !== void 0)
69
- await env.writeFile(`${responses}/${id}.err`, result.err, context);
67
+ const codePath = `${responses}/${id}.code`;
70
68
  const staged = `${responses}/.tmp-${id}.code`;
71
- await env.writeFile(staged, String(result.code), context);
72
- await env.renameFile(staged, `${responses}/${id}.code`, context);
69
+ const publish = async (code2) => {
70
+ const body = String(code2);
71
+ for (let attempt = 0; attempt < 2; attempt++) {
72
+ const stage = attempt === 0 ? staged : `${responses}/.tmp-${id}-${attempt}.code`;
73
+ const written = await env.writeFile(stage, body, context);
74
+ if (!written.ok) continue;
75
+ let renamed = await env.renameFile(stage, codePath, context);
76
+ if (!renamed.ok) renamed = await env.renameFile(stage, codePath, context);
77
+ if (renamed.ok) return true;
78
+ await env.remove(stage, { force: true }, context);
79
+ }
80
+ return (await env.writeFile(codePath, body, context)).ok;
81
+ };
82
+ let code = result.code;
83
+ const out = await env.writeFile(`${responses}/${id}.out`, result.out, context);
84
+ if (!out.ok) {
85
+ code = 1;
86
+ await env.writeFile(
87
+ `${responses}/${id}.err`,
88
+ `agent: ${out.error.code}: ${out.error.message}
89
+ `,
90
+ context
91
+ );
92
+ } else if (result.err !== void 0) {
93
+ const err = await env.writeFile(`${responses}/${id}.err`, result.err, context);
94
+ if (!err.ok) {
95
+ code = 1;
96
+ await env.writeFile(
97
+ `${responses}/${id}.err`,
98
+ `agent: ${err.error.code}: ${err.error.message}
99
+ `,
100
+ context
101
+ );
102
+ }
103
+ }
104
+ if (!await publish(code)) throw new Error(`couldn't place ${codePath}`);
73
105
  await env.remove(`${requests}/${id}`, { force: true }, context);
74
106
  await env.remove(`${responses}/${id}.ack`, { force: true }, context);
75
107
  await options.mirror?.(context).catch(() => void 0);
@@ -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 { CronRun } from '../licks/crontab.ts';\nimport type { Answer } from './service.ts';\n\nexport const CONTROL_DIR = '/var/lib/slicc/agent/requests';\nexport const PROTOCOL = 'slicc-agent/2';\nexport const PROTOCOL_MARK = 'slicc-agent/';\nexport const STALE_MS = 10 * 60_000;\nconst COMMAND_NAMES = new Set(['subagent', 'memory', 'gelatiere', 'freezer', 'models', 'panel']);\n\nexport interface Request {\n id: string;\n as: 'agent' | 'subagent' | '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 user?: string;\n cron?: CronRun;\n}\n\nconst HEADERS: Record<string, number> = { 'slicc-agent/1': 6, [PROTOCOL]: 7 };\n\nexport function cronOf(field: string): CronRun | undefined {\n const [job, due, missed, extra] = field.split(' ');\n const count = Number(missed);\n if (!job || !due || extra !== undefined || !Number.isInteger(count) || count < 0)\n return undefined;\n return { job, due, missed: count };\n}\n\nexport function parseRequest(id: string, bytes: Uint8Array): Request | undefined {\n const decoder = new TextDecoder();\n const fields: string[] = [];\n let at = 0;\n let header = 1;\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 header = HEADERS[fields[0] as string] ?? 0;\n }\n if (!header) return undefined;\n const [, as, caller, cwd, pid] = fields;\n const cron = header === 7 ? cronOf(fields[5] as string) : undefined;\n const argc = Number(fields.at(-1));\n if (!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 ...(cron ? { cron } : {}),\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 user?: string;\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 const staged = `${responses}/.tmp-${id}.code`;\n await env.writeFile(staged, String(result.code), context);\n await env.renameFile(staged, `${responses}/${id}.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 parsed = parseRequest(id, bytes.value);\n const request = parsed && options.user ? { ...parsed, user: options.user } : parsed;\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
- "names": []
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { ExecutionEnv, FileWatcher } from '@earendil-works/pi-durable/env';\nimport type { CronRun } from '../licks/crontab.ts';\nimport type { Answer } from './service.ts';\n\nexport const CONTROL_DIR = '/var/lib/slicc/agent/requests';\nexport const PROTOCOL = 'slicc-agent/2';\nexport const PROTOCOL_MARK = 'slicc-agent/';\nexport const STALE_MS = 10 * 60_000;\nconst COMMAND_NAMES = new Set(['subagent', 'memory', 'gelatiere', 'freezer', 'models', 'panel']);\n\nexport interface Request {\n id: string;\n as: 'agent' | 'subagent' | '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 user?: string;\n cron?: CronRun;\n}\n\nconst HEADERS: Record<string, number> = { 'slicc-agent/1': 6, [PROTOCOL]: 7 };\n\nexport function cronOf(field: string): CronRun | undefined {\n const [job, due, missed, extra] = field.split(' ');\n const count = Number(missed);\n if (!job || !due || extra !== undefined || !Number.isInteger(count) || count < 0)\n return undefined;\n return { job, due, missed: count };\n}\n\nexport function parseRequest(id: string, bytes: Uint8Array): Request | undefined {\n const decoder = new TextDecoder();\n const fields: string[] = [];\n let at = 0;\n let header = 1;\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 header = HEADERS[fields[0] as string] ?? 0;\n }\n if (!header) return undefined;\n const [, as, caller, cwd, pid] = fields;\n const cron = header === 7 ? cronOf(fields[5] as string) : undefined;\n const argc = Number(fields.at(-1));\n if (!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 ...(cron ? { cron } : {}),\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 user?: string;\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 const codePath = `${responses}/${id}.code`;\n const staged = `${responses}/.tmp-${id}.code`;\n const publish = async (code: number) => {\n const body = String(code);\n for (let attempt = 0; attempt < 2; attempt++) {\n const stage = attempt === 0 ? staged : `${responses}/.tmp-${id}-${attempt}.code`;\n const written = await env.writeFile(stage, body, context);\n if (!written.ok) continue;\n let renamed = await env.renameFile(stage, codePath, context);\n if (!renamed.ok) renamed = await env.renameFile(stage, codePath, context);\n if (renamed.ok) return true;\n await env.remove(stage, { force: true }, context);\n }\n return (await env.writeFile(codePath, body, context)).ok;\n };\n let code = result.code;\n const out = await env.writeFile(`${responses}/${id}.out`, result.out, context);\n if (!out.ok) {\n code = 1;\n await env.writeFile(\n `${responses}/${id}.err`,\n `agent: ${out.error.code}: ${out.error.message}\\n`,\n context\n );\n } else if (result.err !== undefined) {\n const err = await env.writeFile(`${responses}/${id}.err`, result.err, context);\n if (!err.ok) {\n code = 1;\n await env.writeFile(\n `${responses}/${id}.err`,\n `agent: ${err.error.code}: ${err.error.message}\\n`,\n context\n );\n }\n }\n if (!(await publish(code))) throw new Error(`couldn't place ${codePath}`);\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 parsed = parseRequest(id, bytes.value);\n const request = parsed && options.user ? { ...parsed, user: options.user } : parsed;\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
+ "names": ["code"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-agent",
3
- "version": "6.13.2",
3
+ "version": "6.13.3",
4
4
  "description": "SLICC's agent core on pi durable: an agent worker, session storage, remoting and pi extensions",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -65,7 +65,7 @@
65
65
  "release": "semantic-release"
66
66
  },
67
67
  "devDependencies": {
68
- "@ai-ecoverse/slicc-kernel": "1.130.1",
68
+ "@ai-ecoverse/slicc-kernel": "1.130.2",
69
69
  "@ai-ecoverse/slicc-scrub": "1.0.0",
70
70
  "@ai-ecoverse/slicc-shared-web": "1.24.2",
71
71
  "@ai-ecoverse/slicc-spectrum": "1.95.0",