@ai-ecoverse/slicc-agent 6.18.17 → 6.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,250 @@
1
+ const MAX_SOURCE = 512 * 1024;
2
+ const MAX_AGENTS = 1e3;
3
+ const MAX_ITEMS = 4096;
4
+ const MAX_SCHEMA = 64 * 1024;
5
+ const DEFAULT_CONCURRENCY = 8;
6
+ function maxConcurrency(cores = globalThis.navigator?.hardwareConcurrency ?? 8) {
7
+ return Math.min(16, Math.max(8, cores * 4));
8
+ }
9
+ function skipQuoted(source, at) {
10
+ const quote = source[at];
11
+ for (let i = at + 1; i < source.length; i++) {
12
+ if (source[i] === "\\") i++;
13
+ else if (source[i] === quote) return i;
14
+ }
15
+ return source.length;
16
+ }
17
+ function block(source, start) {
18
+ let depth = 0;
19
+ for (let i = start; i < source.length; i++) {
20
+ const char = source[i];
21
+ if (char === '"' || char === "'" || char === "`") i = skipQuoted(source, i);
22
+ else if (char === "{") depth++;
23
+ else if (char === "}" && --depth === 0) return source.slice(start + 1, i);
24
+ }
25
+ return null;
26
+ }
27
+ function field(text, name) {
28
+ const found = new RegExp(
29
+ `(?:^|[\\s,{])["']?${name}["']?\\s*:\\s*(["'\`])((?:\\\\.|(?!\\1)[^\\\\])*)\\1`
30
+ ).exec(text);
31
+ return found ? found[2] : null;
32
+ }
33
+ function parseMeta(source) {
34
+ if (source.length > MAX_SOURCE) throw new Error("a workflow is at most 512 KiB");
35
+ const found = /^\s*export\s+const\s+meta\s*=\s*\{/.exec(source.replace(/^#![^\n]*/, ""));
36
+ if (!found) throw new Error("a workflow starts with export const meta = { name, description }");
37
+ const offset = source.length - source.replace(/^#![^\n]*/, "").length;
38
+ const text = block(source, offset + found[0].length - 1);
39
+ if (text === null) throw new Error("export const meta has no closing brace");
40
+ const name = field(text, "name");
41
+ const description = field(text, "description");
42
+ if (!name?.trim()) throw new Error("meta.name must be a non-empty string");
43
+ if (!description?.trim()) throw new Error("meta.description must be a non-empty string");
44
+ return { name, description };
45
+ }
46
+ function stripExports(source) {
47
+ return source.replace(/^#![^\n]*/, "").replace(/\bexport\s+(const|let|var|function|async\s+function|class)\b/g, "$1");
48
+ }
49
+ const PRELUDE = `
50
+ class WorkflowError extends Error {
51
+ constructor(message) {
52
+ super(message);
53
+ this.name = 'WorkflowError';
54
+ }
55
+ }
56
+ const RealDate = globalThis.Date;
57
+ const RealMath = globalThis.Math;
58
+ const banned = (what) => () => {
59
+ throw new WorkflowError(what + ' is not allowed in a workflow; pass time or randomness in args');
60
+ };
61
+ const Date = function Date(...given) {
62
+ if (new.target === undefined) banned('Date()')();
63
+ if (given.length === 0) banned('new Date()')();
64
+ return new RealDate(...given);
65
+ };
66
+ Date.prototype = RealDate.prototype;
67
+ Date.UTC = RealDate.UTC;
68
+ Date.parse = RealDate.parse;
69
+ Date.now = banned('Date.now()');
70
+ Object.freeze(Date);
71
+ const Math = {};
72
+ for (const key of Object.getOwnPropertyNames(RealMath)) Math[key] = RealMath[key];
73
+ Math.random = banned('Math.random()');
74
+ Object.freeze(Math);
75
+ const crypto = Object.freeze({
76
+ getRandomValues: banned('crypto.getRandomValues()'),
77
+ randomUUID: banned('crypto.randomUUID()'),
78
+ });
79
+ const performance = Object.freeze({ now: banned('performance.now()') });
80
+ const setTimeout = banned('setTimeout()');
81
+ const setInterval = banned('setInterval()');
82
+ const OPTIONS = ['label', 'phase', 'schema', 'model', 'effort', 'agentType', 'isolation'];
83
+ const lanes = config.concurrency;
84
+ let started = 0;
85
+ let spent = 0;
86
+ let current = null;
87
+ let active = 0;
88
+ const waiting = [];
89
+ const acquire = async () => {
90
+ if (active < lanes) {
91
+ active++;
92
+ return;
93
+ }
94
+ await new Promise((resume) => waiting.push(resume));
95
+ };
96
+ const release = () => {
97
+ const next = waiting.shift();
98
+ if (next) next();
99
+ else active--;
100
+ };
101
+ const textOf = (values) =>
102
+ values.map((value) => (typeof value === 'string' ? value : JSON.stringify(value))).join(' ');
103
+ const note = (kind, text) => {
104
+ tell({ kind, text }).then(undefined, () => undefined);
105
+ };
106
+ const gate = () => {
107
+ if (config.budget !== null && spent >= config.budget)
108
+ throw new WorkflowError('agent(): the budget of ' + config.budget + ' output tokens is spent');
109
+ };
110
+ const failed = (error) => {
111
+ note('log', '[error] ' + (error && error.message ? error.message : String(error)));
112
+ return null;
113
+ };
114
+ const console = Object.freeze({
115
+ log: (...values) => note('log', textOf(values)),
116
+ info: (...values) => note('log', textOf(values)),
117
+ debug: (...values) => note('log', textOf(values)),
118
+ warn: (...values) => note('log', '[warn] ' + textOf(values)),
119
+ error: (...values) => note('log', '[error] ' + textOf(values)),
120
+ });
121
+ const budget = Object.freeze({
122
+ total: config.budget,
123
+ spent: () => spent,
124
+ remaining: () => (config.budget === null ? Infinity : Math.max(0, config.budget - spent)),
125
+ });
126
+ function phase(title) {
127
+ current = String(title);
128
+ note('phase', current);
129
+ }
130
+ function log(message) {
131
+ note('log', String(message));
132
+ }
133
+ function workflow() {
134
+ throw new WorkflowError('workflow(): nested workflows are not supported in SLICC');
135
+ }
136
+ async function agent(prompt, options) {
137
+ if (typeof prompt !== 'string' || !prompt.trim()) throw new WorkflowError('agent() needs a prompt string');
138
+ const given = options === undefined || options === null ? {} : options;
139
+ if (typeof given !== 'object' || Array.isArray(given)) throw new WorkflowError('agent(): options must be an object');
140
+ for (const key of Object.keys(given))
141
+ if (!OPTIONS.includes(key)) throw new WorkflowError('agent(): unknown option ' + key);
142
+ if (given.isolation !== undefined) throw new WorkflowError('agent(): isolation is not supported in SLICC');
143
+ if (started >= ${MAX_AGENTS}) throw new WorkflowError('agent(): a workflow starts at most ${MAX_AGENTS} agents');
144
+ gate();
145
+ started++;
146
+ await acquire();
147
+ try {
148
+ gate();
149
+ const call = { ...given, prompt };
150
+ if (call.phase === undefined && current !== null) call.phase = current;
151
+ try {
152
+ const answer = await host(call);
153
+ spent += answer.outputTokens;
154
+ return answer.value;
155
+ } catch (error) {
156
+ spent += error && typeof error.outputTokens === 'number' ? error.outputTokens : 0;
157
+ throw error;
158
+ }
159
+ } finally {
160
+ release();
161
+ }
162
+ }
163
+ async function parallel(thunks) {
164
+ if (!Array.isArray(thunks) || thunks.some((thunk) => typeof thunk !== 'function'))
165
+ throw new WorkflowError('parallel() expects an array of functions');
166
+ if (thunks.length > ${MAX_ITEMS}) throw new WorkflowError('parallel(): at most ${MAX_ITEMS} items per call');
167
+ return Promise.all(
168
+ thunks.map(async (thunk) => {
169
+ try {
170
+ return await thunk();
171
+ } catch (error) {
172
+ return failed(error);
173
+ }
174
+ })
175
+ );
176
+ }
177
+ async function pipeline(items, ...stages) {
178
+ if (!Array.isArray(items)) throw new WorkflowError('pipeline() expects an array as its first argument');
179
+ if (items.length > ${MAX_ITEMS}) throw new WorkflowError('pipeline(): at most ${MAX_ITEMS} items per call');
180
+ if (stages.some((stage) => typeof stage !== 'function')) throw new WorkflowError('pipeline(): every stage must be a function');
181
+ return Promise.all(
182
+ items.map(async (item, index) => {
183
+ let value = item;
184
+ for (const stage of stages) {
185
+ try {
186
+ value = await stage(value, item, index);
187
+ } catch (error) {
188
+ return failed(error);
189
+ }
190
+ if (value === null) return null;
191
+ }
192
+ return value;
193
+ })
194
+ );
195
+ }
196
+ return Object.freeze({
197
+ agent, parallel, pipeline, phase, log, workflow, console, budget, args: config.args,
198
+ Date, Math, crypto, performance, setTimeout, setInterval, WorkflowError,
199
+ });
200
+ `;
201
+ const PUBLIC = [
202
+ "agent",
203
+ "parallel",
204
+ "pipeline",
205
+ "phase",
206
+ "log",
207
+ "workflow",
208
+ "console",
209
+ "budget",
210
+ "args",
211
+ "Date",
212
+ "Math",
213
+ "crypto",
214
+ "performance",
215
+ "setTimeout",
216
+ "setInterval",
217
+ "WorkflowError"
218
+ ];
219
+ function buildCode(source, config) {
220
+ return [
221
+ "{",
222
+ `const __api = (function (config, host, tell) {${PRELUDE}})(Object.freeze(${JSON.stringify(config)}), __agent, __notify);`,
223
+ "{",
224
+ "const tools = undefined;",
225
+ "const __agent = undefined;",
226
+ "const __notify = undefined;",
227
+ `const { ${PUBLIC.join(", ")} } = __api;`,
228
+ "const __main = async () => {",
229
+ stripExports(source),
230
+ "};",
231
+ "const __value = await __main();",
232
+ "return __value === undefined ? null : __value;",
233
+ "}",
234
+ "}"
235
+ ].join("\n");
236
+ }
237
+ export {
238
+ DEFAULT_CONCURRENCY,
239
+ MAX_AGENTS,
240
+ MAX_ITEMS,
241
+ MAX_SCHEMA,
242
+ MAX_SOURCE,
243
+ PRELUDE,
244
+ PUBLIC,
245
+ buildCode,
246
+ maxConcurrency,
247
+ parseMeta,
248
+ stripExports
249
+ };
250
+ //# sourceMappingURL=script.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/workflow/script.ts"],
4
+ "sourcesContent": ["export const MAX_SOURCE = 512 * 1024;\nexport const MAX_AGENTS = 1000;\nexport const MAX_ITEMS = 4096;\nexport const MAX_SCHEMA = 64 * 1024;\nexport const DEFAULT_CONCURRENCY = 8;\n\nexport interface Meta {\n name: string;\n description: string;\n}\n\nexport interface Config {\n args?: unknown;\n budget: number | null;\n concurrency: number;\n}\n\nexport function maxConcurrency(cores = globalThis.navigator?.hardwareConcurrency ?? 8): number {\n return Math.min(16, Math.max(8, cores * 4));\n}\n\nfunction skipQuoted(source: string, at: number): number {\n const quote = source[at];\n for (let i = at + 1; i < source.length; i++) {\n if (source[i] === '\\\\') i++;\n else if (source[i] === quote) return i;\n }\n return source.length;\n}\n\nfunction block(source: string, start: number): string | null {\n let depth = 0;\n for (let i = start; i < source.length; i++) {\n const char = source[i] as string;\n if (char === '\"' || char === \"'\" || char === '`') i = skipQuoted(source, i);\n else if (char === '{') depth++;\n else if (char === '}' && --depth === 0) return source.slice(start + 1, i);\n }\n return null;\n}\n\nfunction field(text: string, name: string): string | null {\n const found = new RegExp(\n `(?:^|[\\\\s,{])[\"']?${name}[\"']?\\\\s*:\\\\s*([\"'\\`])((?:\\\\\\\\.|(?!\\\\1)[^\\\\\\\\])*)\\\\1`\n ).exec(text);\n return found ? (found[2] as string) : null;\n}\n\nexport function parseMeta(source: string): Meta {\n if (source.length > MAX_SOURCE) throw new Error('a workflow is at most 512 KiB');\n const found = /^\\s*export\\s+const\\s+meta\\s*=\\s*\\{/.exec(source.replace(/^#![^\\n]*/, ''));\n if (!found) throw new Error('a workflow starts with export const meta = { name, description }');\n const offset = source.length - source.replace(/^#![^\\n]*/, '').length;\n const text = block(source, offset + found[0].length - 1);\n if (text === null) throw new Error('export const meta has no closing brace');\n const name = field(text, 'name');\n const description = field(text, 'description');\n if (!name?.trim()) throw new Error('meta.name must be a non-empty string');\n if (!description?.trim()) throw new Error('meta.description must be a non-empty string');\n return { name, description };\n}\n\nexport function stripExports(source: string): string {\n return source\n .replace(/^#![^\\n]*/, '')\n .replace(/\\bexport\\s+(const|let|var|function|async\\s+function|class)\\b/g, '$1');\n}\n\nexport const PRELUDE = `\nclass WorkflowError extends Error {\n constructor(message) {\n super(message);\n this.name = 'WorkflowError';\n }\n}\nconst RealDate = globalThis.Date;\nconst RealMath = globalThis.Math;\nconst banned = (what) => () => {\n throw new WorkflowError(what + ' is not allowed in a workflow; pass time or randomness in args');\n};\nconst Date = function Date(...given) {\n if (new.target === undefined) banned('Date()')();\n if (given.length === 0) banned('new Date()')();\n return new RealDate(...given);\n};\nDate.prototype = RealDate.prototype;\nDate.UTC = RealDate.UTC;\nDate.parse = RealDate.parse;\nDate.now = banned('Date.now()');\nObject.freeze(Date);\nconst Math = {};\nfor (const key of Object.getOwnPropertyNames(RealMath)) Math[key] = RealMath[key];\nMath.random = banned('Math.random()');\nObject.freeze(Math);\nconst crypto = Object.freeze({\n getRandomValues: banned('crypto.getRandomValues()'),\n randomUUID: banned('crypto.randomUUID()'),\n});\nconst performance = Object.freeze({ now: banned('performance.now()') });\nconst setTimeout = banned('setTimeout()');\nconst setInterval = banned('setInterval()');\nconst OPTIONS = ['label', 'phase', 'schema', 'model', 'effort', 'agentType', 'isolation'];\nconst lanes = config.concurrency;\nlet started = 0;\nlet spent = 0;\nlet current = null;\nlet active = 0;\nconst waiting = [];\nconst acquire = async () => {\n if (active < lanes) {\n active++;\n return;\n }\n await new Promise((resume) => waiting.push(resume));\n};\nconst release = () => {\n const next = waiting.shift();\n if (next) next();\n else active--;\n};\nconst textOf = (values) =>\n values.map((value) => (typeof value === 'string' ? value : JSON.stringify(value))).join(' ');\nconst note = (kind, text) => {\n tell({ kind, text }).then(undefined, () => undefined);\n};\nconst gate = () => {\n if (config.budget !== null && spent >= config.budget)\n throw new WorkflowError('agent(): the budget of ' + config.budget + ' output tokens is spent');\n};\nconst failed = (error) => {\n note('log', '[error] ' + (error && error.message ? error.message : String(error)));\n return null;\n};\nconst console = Object.freeze({\n log: (...values) => note('log', textOf(values)),\n info: (...values) => note('log', textOf(values)),\n debug: (...values) => note('log', textOf(values)),\n warn: (...values) => note('log', '[warn] ' + textOf(values)),\n error: (...values) => note('log', '[error] ' + textOf(values)),\n});\nconst budget = Object.freeze({\n total: config.budget,\n spent: () => spent,\n remaining: () => (config.budget === null ? Infinity : Math.max(0, config.budget - spent)),\n});\nfunction phase(title) {\n current = String(title);\n note('phase', current);\n}\nfunction log(message) {\n note('log', String(message));\n}\nfunction workflow() {\n throw new WorkflowError('workflow(): nested workflows are not supported in SLICC');\n}\nasync function agent(prompt, options) {\n if (typeof prompt !== 'string' || !prompt.trim()) throw new WorkflowError('agent() needs a prompt string');\n const given = options === undefined || options === null ? {} : options;\n if (typeof given !== 'object' || Array.isArray(given)) throw new WorkflowError('agent(): options must be an object');\n for (const key of Object.keys(given))\n if (!OPTIONS.includes(key)) throw new WorkflowError('agent(): unknown option ' + key);\n if (given.isolation !== undefined) throw new WorkflowError('agent(): isolation is not supported in SLICC');\n if (started >= ${MAX_AGENTS}) throw new WorkflowError('agent(): a workflow starts at most ${MAX_AGENTS} agents');\n gate();\n started++;\n await acquire();\n try {\n gate();\n const call = { ...given, prompt };\n if (call.phase === undefined && current !== null) call.phase = current;\n try {\n const answer = await host(call);\n spent += answer.outputTokens;\n return answer.value;\n } catch (error) {\n spent += error && typeof error.outputTokens === 'number' ? error.outputTokens : 0;\n throw error;\n }\n } finally {\n release();\n }\n}\nasync function parallel(thunks) {\n if (!Array.isArray(thunks) || thunks.some((thunk) => typeof thunk !== 'function'))\n throw new WorkflowError('parallel() expects an array of functions');\n if (thunks.length > ${MAX_ITEMS}) throw new WorkflowError('parallel(): at most ${MAX_ITEMS} items per call');\n return Promise.all(\n thunks.map(async (thunk) => {\n try {\n return await thunk();\n } catch (error) {\n return failed(error);\n }\n })\n );\n}\nasync function pipeline(items, ...stages) {\n if (!Array.isArray(items)) throw new WorkflowError('pipeline() expects an array as its first argument');\n if (items.length > ${MAX_ITEMS}) throw new WorkflowError('pipeline(): at most ${MAX_ITEMS} items per call');\n if (stages.some((stage) => typeof stage !== 'function')) throw new WorkflowError('pipeline(): every stage must be a function');\n return Promise.all(\n items.map(async (item, index) => {\n let value = item;\n for (const stage of stages) {\n try {\n value = await stage(value, item, index);\n } catch (error) {\n return failed(error);\n }\n if (value === null) return null;\n }\n return value;\n })\n );\n}\nreturn Object.freeze({\n agent, parallel, pipeline, phase, log, workflow, console, budget, args: config.args,\n Date, Math, crypto, performance, setTimeout, setInterval, WorkflowError,\n});\n`;\n\nexport const PUBLIC = [\n 'agent',\n 'parallel',\n 'pipeline',\n 'phase',\n 'log',\n 'workflow',\n 'console',\n 'budget',\n 'args',\n 'Date',\n 'Math',\n 'crypto',\n 'performance',\n 'setTimeout',\n 'setInterval',\n 'WorkflowError',\n];\n\nexport function buildCode(source: string, config: Config): string {\n return [\n '{',\n `const __api = (function (config, host, tell) {${PRELUDE}})(Object.freeze(${JSON.stringify(config)}), __agent, __notify);`,\n '{',\n 'const tools = undefined;',\n 'const __agent = undefined;',\n 'const __notify = undefined;',\n `const { ${PUBLIC.join(', ')} } = __api;`,\n 'const __main = async () => {',\n stripExports(source),\n '};',\n 'const __value = await __main();',\n 'return __value === undefined ? null : __value;',\n '}',\n '}',\n ].join('\\n');\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,132 @@
1
+ import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
2
+ import { UsageDoc } from "@earendil-works/pi-durable";
3
+ import { Check, Errors } from "typebox/schema";
4
+ import { findRole } from "../roles/roles.js";
5
+ import { modelOf } from "../scoops/service.js";
6
+ import { MAX_SCHEMA } from "./script.js";
7
+ const EFFORT = {
8
+ low: "low",
9
+ medium: "medium",
10
+ high: "high",
11
+ xhigh: "xhigh",
12
+ max: "xhigh"
13
+ };
14
+ class StepError extends Error {
15
+ outputTokens;
16
+ constructor(message, outputTokens2 = 0) {
17
+ super(message);
18
+ this.outputTokens = outputTokens2;
19
+ }
20
+ }
21
+ function schemaOf(value) {
22
+ if (value === void 0) return void 0;
23
+ if (typeof value !== "object" || value === null || Array.isArray(value))
24
+ throw new StepError("agent(): schema must be a JSON Schema object");
25
+ if (JSON.stringify(value).length > MAX_SCHEMA)
26
+ throw new StepError("agent(): schema is at most 64 KiB");
27
+ return value;
28
+ }
29
+ function instruction(schema) {
30
+ return `
31
+
32
+ End your work with a final answer that is only JSON, with no other text and no code fence, matching this JSON Schema:
33
+ ${JSON.stringify(schema)}`;
34
+ }
35
+ const OPTIONS = /* @__PURE__ */ new Set(["prompt", "label", "phase", "schema", "model", "effort", "agentType"]);
36
+ function checked(call) {
37
+ if (typeof call !== "object" || call === null || Array.isArray(call))
38
+ throw new StepError("agent(): options must be an object");
39
+ const given = call;
40
+ if (typeof given.prompt !== "string" || !given.prompt.trim())
41
+ throw new StepError("agent() needs a prompt string");
42
+ if ("isolation" in given) throw new StepError("agent(): isolation is not supported in SLICC");
43
+ const unknown = Object.keys(given).find((key) => !OPTIONS.has(key));
44
+ if (unknown) throw new StepError(`agent(): unknown option ${unknown}`);
45
+ return given;
46
+ }
47
+ function plan(given, roles) {
48
+ const call = checked(given);
49
+ const role = call.agentType === void 0 ? void 0 : findRole(roles.roles, call.agentType);
50
+ if (call.agentType !== void 0 && !role)
51
+ throw new StepError(`agent(): there is no agent role ${call.agentType}`);
52
+ const model = call.model === void 0 ? void 0 : modelOf(String(call.model));
53
+ if (call.model !== void 0 && !model)
54
+ throw new StepError(`agent(): model must be provider/model, not ${call.model}`);
55
+ const thinking = call.effort === void 0 ? void 0 : EFFORT[String(call.effort)];
56
+ if (call.effort !== void 0 && !thinking)
57
+ throw new StepError("agent(): effort must be low, medium, high, xhigh or max");
58
+ const schema = schemaOf(call.schema);
59
+ const label = typeof call.label === "string" && call.label.trim() ? call.label : void 0;
60
+ return {
61
+ name: label ?? role?.name ?? "workflow",
62
+ prompt: schema ? `${call.prompt}${instruction(schema)}` : call.prompt,
63
+ ...role ? { role } : {},
64
+ ...model ? { model } : {},
65
+ ...thinking ? { thinking } : {},
66
+ ...schema ? { schema } : {}
67
+ };
68
+ }
69
+ function parseAnswer(text, schema) {
70
+ const fenced = /^\s*```(?:json)?\s*\n([\s\S]*?)\n\s*```\s*$/.exec(text);
71
+ const body = fenced ? fenced[1] : text;
72
+ let value;
73
+ try {
74
+ value = JSON.parse(body.trim());
75
+ } catch {
76
+ throw new Error(`agent(): the answer is not JSON: ${body.trim().slice(0, 120)}`);
77
+ }
78
+ if (!Check(schema, value)) {
79
+ const [, errors] = Errors(schema, value);
80
+ const detail = errors.slice(0, 1).map((error) => `: ${error.instancePath || "/"} ${error.message}`).join("");
81
+ throw new Error(`agent(): the answer does not match the schema${detail}`);
82
+ }
83
+ return value;
84
+ }
85
+ async function outputTokens(harness, conversation, context) {
86
+ const usage = await harness.snapshot(UsageDoc, conversation, context);
87
+ return Object.values(usage?.models ?? {}).reduce(
88
+ (sum, entry) => sum + (entry.output ?? 0),
89
+ 0
90
+ );
91
+ }
92
+ async function cleanup(host, folder) {
93
+ const id = `scoop:${folder}`;
94
+ const record = host.agents.state().scoops[id];
95
+ await host.scoops.stop(id, false, BACKGROUND_CONTEXT).catch(() => void 0);
96
+ if (record)
97
+ await host.env.remove(
98
+ host.layout.scoopRoot(record.folder),
99
+ { recursive: true, force: true },
100
+ BACKGROUND_CONTEXT
101
+ ).catch(() => void 0);
102
+ }
103
+ async function settle(host, folder, task, planned, wait, context) {
104
+ const result = await wait(task);
105
+ const record = host.agents.state().scoops[`scoop:${folder}`];
106
+ const spent = record ? await outputTokens(host.harness, record.conversation, context) : 0;
107
+ if (result.failed)
108
+ throw new StepError(`agent(): the agent ended without an answer: ${result.failed}`, spent);
109
+ if (!planned.schema) return { value: result.text, outputTokens: spent };
110
+ try {
111
+ return { value: parseAnswer(result.text, planned.schema), outputTokens: spent };
112
+ } catch (error) {
113
+ throw new StepError(error.message, spent);
114
+ }
115
+ }
116
+ async function waitReport(wait, task, context) {
117
+ const settled = await wait(task, context);
118
+ const outcome = settled.state.outcome;
119
+ return outcome.status === "completed" && outcome.result ? outcome.result : { text: "", failed: outcome.status, path: null };
120
+ }
121
+ export {
122
+ EFFORT,
123
+ StepError,
124
+ cleanup,
125
+ instruction,
126
+ outputTokens,
127
+ parseAnswer,
128
+ plan,
129
+ settle,
130
+ waitReport
131
+ };
132
+ //# sourceMappingURL=step.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/workflow/step.ts"],
4
+ "sourcesContent": ["import type { Context, JsonValue } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ConversationId, Harness, TaskId } from '@earendil-works/pi-durable';\nimport { UsageDoc } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { Check, Errors } from 'typebox/schema';\nimport type { Agents } from '../agents.ts';\nimport type { Layout } from '../layout.ts';\nimport { findRole, type Role, type Roles } from '../roles/roles.ts';\nimport type { ReportResult } from '../scoops/extension.ts';\nimport { modelOf, type Scoops } from '../scoops/service.ts';\nimport { MAX_SCHEMA } from './script.ts';\n\nexport const EFFORT: Record<string, string> = {\n low: 'low',\n medium: 'medium',\n high: 'high',\n xhigh: 'xhigh',\n max: 'xhigh',\n};\n\nexport interface AgentCall {\n prompt: string;\n label?: string;\n phase?: string;\n schema?: JsonValue;\n model?: string;\n effort?: string;\n agentType?: string;\n}\n\nexport interface StepHost {\n harness: Harness;\n agents: Agents;\n scoops: Scoops;\n roles: (context: Context) => Promise<Roles>;\n env: Pick<ExecutionEnv, 'remove'>;\n layout: Layout;\n}\n\nexport class StepError extends Error {\n readonly outputTokens: number;\n\n constructor(message: string, outputTokens = 0) {\n super(message);\n this.outputTokens = outputTokens;\n }\n}\n\nexport interface Planned {\n name: string;\n prompt: string;\n role?: Role;\n model?: { provider: string; modelId: string };\n thinking?: string;\n schema?: object;\n}\n\nfunction schemaOf(value: JsonValue | undefined): object | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== 'object' || value === null || Array.isArray(value))\n throw new StepError('agent(): schema must be a JSON Schema object');\n if (JSON.stringify(value).length > MAX_SCHEMA)\n throw new StepError('agent(): schema is at most 64 KiB');\n return value;\n}\n\nexport function instruction(schema: object): string {\n return `\\n\\nEnd your work with a final answer that is only JSON, with no other text and no code fence, matching this JSON Schema:\\n${JSON.stringify(schema)}`;\n}\n\nconst OPTIONS = new Set(['prompt', 'label', 'phase', 'schema', 'model', 'effort', 'agentType']);\n\nfunction checked(call: unknown): AgentCall {\n if (typeof call !== 'object' || call === null || Array.isArray(call))\n throw new StepError('agent(): options must be an object');\n const given = call as Record<string, unknown>;\n if (typeof given.prompt !== 'string' || !given.prompt.trim())\n throw new StepError('agent() needs a prompt string');\n if ('isolation' in given) throw new StepError('agent(): isolation is not supported in SLICC');\n const unknown = Object.keys(given).find((key) => !OPTIONS.has(key));\n if (unknown) throw new StepError(`agent(): unknown option ${unknown}`);\n return given as unknown as AgentCall;\n}\n\nexport function plan(given: unknown, roles: Roles): Planned {\n const call = checked(given);\n const role = call.agentType === undefined ? undefined : findRole(roles.roles, call.agentType);\n if (call.agentType !== undefined && !role)\n throw new StepError(`agent(): there is no agent role ${call.agentType}`);\n const model = call.model === undefined ? undefined : modelOf(String(call.model));\n if (call.model !== undefined && !model)\n throw new StepError(`agent(): model must be provider/model, not ${call.model}`);\n const thinking = call.effort === undefined ? undefined : EFFORT[String(call.effort)];\n if (call.effort !== undefined && !thinking)\n throw new StepError('agent(): effort must be low, medium, high, xhigh or max');\n const schema = schemaOf(call.schema);\n const label = typeof call.label === 'string' && call.label.trim() ? call.label : undefined;\n return {\n name: label ?? role?.name ?? 'workflow',\n prompt: schema ? `${call.prompt}${instruction(schema)}` : call.prompt,\n ...(role ? { role } : {}),\n ...(model ? { model } : {}),\n ...(thinking ? { thinking } : {}),\n ...(schema ? { schema } : {}),\n };\n}\n\nexport function parseAnswer(text: string, schema: object): JsonValue {\n const fenced = /^\\s*```(?:json)?\\s*\\n([\\s\\S]*?)\\n\\s*```\\s*$/.exec(text);\n const body = (fenced ? fenced[1] : text) as string;\n let value: JsonValue;\n try {\n value = JSON.parse(body.trim()) as JsonValue;\n } catch {\n throw new Error(`agent(): the answer is not JSON: ${body.trim().slice(0, 120)}`);\n }\n if (!Check(schema as never, value)) {\n const [, errors] = Errors(schema as never, value) as unknown as [\n boolean,\n { instancePath: string; message: string }[],\n ];\n const detail = errors\n .slice(0, 1)\n .map((error) => `: ${error.instancePath || '/'} ${error.message}`)\n .join('');\n throw new Error(`agent(): the answer does not match the schema${detail}`);\n }\n return value;\n}\n\nexport async function outputTokens(\n harness: Harness,\n conversation: number,\n context: Context\n): Promise<number> {\n const usage = await harness.snapshot(UsageDoc, conversation as ConversationId, context);\n return Object.values(usage?.models ?? {}).reduce(\n (sum, entry) => sum + ((entry as { output?: number }).output ?? 0),\n 0\n );\n}\n\nexport async function cleanup(host: StepHost, folder: string): Promise<void> {\n const id = `scoop:${folder}`;\n const record = host.agents.state().scoops[id];\n await host.scoops.stop(id, false, BACKGROUND_CONTEXT).catch(() => undefined);\n if (record)\n await host.env\n .remove(\n host.layout.scoopRoot(record.folder),\n { recursive: true, force: true },\n BACKGROUND_CONTEXT\n )\n .catch(() => undefined);\n}\n\nexport async function settle(\n host: StepHost,\n folder: string,\n task: number,\n planned: Planned,\n wait: (task: number) => Promise<ReportResult>,\n context: Context\n): Promise<{ value: JsonValue; outputTokens: number }> {\n const result = await wait(task);\n const record = host.agents.state().scoops[`scoop:${folder}`];\n const spent = record ? await outputTokens(host.harness, record.conversation, context) : 0;\n if (result.failed)\n throw new StepError(`agent(): the agent ended without an answer: ${result.failed}`, spent);\n if (!planned.schema) return { value: result.text, outputTokens: spent };\n try {\n return { value: parseAnswer(result.text, planned.schema), outputTokens: spent };\n } catch (error) {\n throw new StepError((error as Error).message, spent);\n }\n}\n\nexport async function waitReport(\n wait: (id: TaskId<ReportResult>, context: Context) => Promise<{ state: unknown }>,\n task: number,\n context: Context\n): Promise<ReportResult> {\n const settled = (await wait(task as TaskId<ReportResult>, context)) as {\n state: { outcome: { status: string; result?: ReportResult } };\n };\n const outcome = settled.state.outcome;\n return outcome.status === 'completed' && outcome.result\n ? outcome.result\n : { text: '', failed: outcome.status, path: null };\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["outputTokens"]
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-agent",
3
- "version": "6.18.17",
3
+ "version": "6.19.0",
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,9 +65,9 @@
65
65
  "release": "semantic-release"
66
66
  },
67
67
  "devDependencies": {
68
- "@ai-ecoverse/slicc-kernel": "1.143.0",
68
+ "@ai-ecoverse/slicc-kernel": "1.143.2",
69
69
  "@ai-ecoverse/slicc-scrub": "1.0.0",
70
- "@ai-ecoverse/slicc-shared-web": "1.29.16",
70
+ "@ai-ecoverse/slicc-shared-web": "1.30.1",
71
71
  "@ai-ecoverse/slicc-spectrum": "1.104.3",
72
72
  "@ai-ecoverse/wasi-fnox": "1.39.0-6",
73
73
  "@ai-ecoverse/wasm-bash": "5.3.0-22",
@@ -79,13 +79,13 @@
79
79
  "fake-indexeddb": "6.2.5",
80
80
  "lefthook": "2.2.2",
81
81
  "monocart-coverage-reports": "2.13.2",
82
- "playwright-core": "1.64.0",
82
+ "playwright-core": "1.64.1",
83
83
  "semantic-release": "25.0.9",
84
84
  "slicc-agent-5": "npm:@ai-ecoverse/slicc-agent@5.10.1",
85
85
  "typescript": "7.0.2"
86
86
  },
87
87
  "dependencies": {
88
- "@ai-ecoverse/slicc-browsers": "1.15.0",
88
+ "@ai-ecoverse/slicc-browsers": "1.15.1",
89
89
  "@ai-ecoverse/slicc-vault": "1.15.2",
90
90
  "@aws-sdk/middleware-websocket": "^3.972.54",
91
91
  "@earendil-works/chord": "1.3.0",
@@ -73,6 +73,10 @@ A scoop can post a progress note to its cone; it arrives as a lick:
73
73
  agent send parent "Halfway: the parser is done, starting on the tests"
74
74
  ```
75
75
 
76
+ ## Workflows
77
+
78
+ A Claude Code `.workflow.js` runs as a durable background run that survives a reload: `agent workflow run <file.js> [--args <json>] [--budget <tokens>] [--concurrency <n>] [--wait]`. Its `agent()` calls are scoops. `agent workflow list`, `status <run>`, `wait <run>`, `stop <run>` and `save <run> <name>` manage runs, and `agent workflow help` lists the script's globals. A cone can also use the `workflow` tool.
79
+
76
80
  ## Limits
77
81
 
78
82
  A cone can have 16 live scoops and start 64 per turn (`subagents.maxLiveScoops` and `subagents.maxPerTurn` in `~/.pi/agent/settings.json`).