pi-ultracode 0.1.2 → 0.3.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,678 @@
1
+ import { parentPort, workerData } from "node:worker_threads";
2
+ import vm from "node:vm";
3
+ import { AsyncLocalStorage } from "node:async_hooks";
4
+
5
+ const POLICY = "WORKFLOW_POLICY_ERROR";
6
+ const ABORT = "WORKFLOW_ABORTED";
7
+ const STALL = "WORKFLOW_STALLED";
8
+ const CHECKPOINT = "__ultracodeCheckpoint";
9
+ const MAX_ITEMS_PER_CALL = 4096;
10
+ const MAX_RESERVE_AGENTS = 1024;
11
+
12
+ let aborted = false;
13
+ let finished = false;
14
+ let nextRpcId = 0;
15
+ let checkpointCount = 0;
16
+ let hostCallCount = 0;
17
+ let stickyFatalError;
18
+ const rpcWaiters = new Map();
19
+ const orchestrationPromises = new Set();
20
+ const executionContext = new AsyncLocalStorage();
21
+
22
+ const heartbeat = setInterval(() => sendHeartbeat(), 25);
23
+ heartbeat.unref?.();
24
+
25
+ parentPort.on("message", (message) => {
26
+ if (!message || typeof message !== "object") return;
27
+ if (message.type === "abort") {
28
+ aborted = true;
29
+ for (const waiter of rpcWaiters.values()) waiter.reject(makeAbortError());
30
+ rpcWaiters.clear();
31
+ return;
32
+ }
33
+ if (message.type === "rpcResult") {
34
+ const waiter = rpcWaiters.get(message.id);
35
+ if (!waiter) return;
36
+ rpcWaiters.delete(message.id);
37
+ if (message.ok) {
38
+ waiter.resolve(message.value);
39
+ } else {
40
+ const error = reviveError(message.error);
41
+ if (isPolicy(error)) markFatal(error);
42
+ waiter.reject(error);
43
+ }
44
+ }
45
+ });
46
+
47
+ runRoot().catch((error) => finish(false, undefined, error));
48
+
49
+ async function runRoot() {
50
+ const rootName = typeof workerData.name === "string" && workerData.name ? workerData.name : "workflow";
51
+ const result = await executeBody(workerData.body, workerData.args, 0, rootName);
52
+ if (stickyFatalError) throw stickyFatalError;
53
+ const unobserved = [...orchestrationPromises].filter((record) => !record.observed);
54
+ if (unobserved.length > 0) {
55
+ throw makePolicyError(
56
+ `workflow completed with ${unobserved.length} unawaited orchestration promise(s); await or return every agent(), parallel(), pipeline(), and workflow() call`,
57
+ );
58
+ }
59
+ const pending = [...orchestrationPromises].filter((record) => !record.settled);
60
+ if (pending.length > 0) {
61
+ throw makePolicyError(
62
+ `workflow completed with ${pending.length} pending orchestration promise(s); await or return the full promise chain`,
63
+ );
64
+ }
65
+ finish(true, result);
66
+ }
67
+
68
+ async function executeBody(body, bodyArgs, depth, name, scopePath = "$") {
69
+ throwIfAborted();
70
+ const parent = executionContext.getStore();
71
+ const context = createExecutionScope({
72
+ workflowPath: [...(parent?.workflowPath ?? []), name || `workflow-${depth}`],
73
+ depth,
74
+ currentPhase: parent?.currentPhase,
75
+ reservationIds: Array.isArray(parent?.reservationIds) ? [...parent.reservationIds] : [],
76
+ scopePath,
77
+ });
78
+ return executionContext.run(context, async () => {
79
+ const scriptContext = buildScriptContext(bodyArgs);
80
+ const wrapped = `(async () => {\n${body}\n})()`;
81
+ return await new vm.Script(wrapped, { filename: `${name || "workflow"}.js` }).runInContext(scriptContext);
82
+ });
83
+ }
84
+
85
+ function buildScriptContext(bodyArgs) {
86
+ const scriptContext = vm.createContext({}, {
87
+ codeGeneration: { strings: false, wasm: false },
88
+ });
89
+ const install = new vm.Script(`
90
+ (bridge, rawArgs, cwdValue) => {
91
+ const localize = (value, seen = new WeakMap()) => {
92
+ if (value === null || typeof value !== "object") return value
93
+ if (seen.has(value)) return seen.get(value)
94
+ if (Array.isArray(value)) {
95
+ const output = []
96
+ seen.set(value, output)
97
+ for (const item of value) output.push(localize(item, seen))
98
+ return output
99
+ }
100
+ const output = {}
101
+ seen.set(value, output)
102
+ for (const [key, child] of Object.entries(value)) {
103
+ Object.defineProperty(output, key, {
104
+ value: localize(child, seen),
105
+ enumerable: true,
106
+ writable: true,
107
+ configurable: true,
108
+ })
109
+ }
110
+ return output
111
+ }
112
+ const localError = (value) => {
113
+ const error = new Error(value && typeof value.message === "string" ? value.message : String(value))
114
+ if (value && typeof value.name === "string") error.name = value.name
115
+ if (value && typeof value.code === "string") error.code = value.code
116
+ return error
117
+ }
118
+ const orchestration = (name, callSite, values) => {
119
+ let hostThenable
120
+ try {
121
+ hostThenable = bridge[name](callSite, ...values)
122
+ } catch (error) {
123
+ hostThenable = { then: (_resolve, reject) => reject(error) }
124
+ }
125
+ let localized
126
+ const consume = () => {
127
+ if (!localized) {
128
+ localized = Promise.resolve(hostThenable).then(
129
+ (value) => localize(value),
130
+ (error) => { throw localError(error) },
131
+ )
132
+ }
133
+ return localized
134
+ }
135
+ return Object.freeze({
136
+ then: (resolve, reject) => consume().then(resolve, reject),
137
+ catch: (reject) => consume().catch(reject),
138
+ finally: (callback) => consume().finally(callback),
139
+ get [Symbol.toStringTag]() { return "Promise" },
140
+ })
141
+ }
142
+ const sync = (name, values) => {
143
+ try {
144
+ return localize(bridge[name](...values))
145
+ } catch (error) {
146
+ throw localError(error)
147
+ }
148
+ }
149
+ const invoke = (callSite, fn, thisArg, values) => {
150
+ try {
151
+ return bridge.invoke(callSite, fn, thisArg, values)
152
+ } catch (error) {
153
+ throw localError(error)
154
+ }
155
+ }
156
+ const sandboxLog = (value) => sync("log", [String(value)])
157
+ Object.defineProperties(globalThis, {
158
+ __ultracodeAgent: { value: (callSite, ...values) => orchestration("agent", callSite, values) },
159
+ __ultracodeParallel: { value: (callSite, ...values) => orchestration("parallel", callSite, values) },
160
+ __ultracodePipeline: { value: (callSite, ...values) => orchestration("pipeline", callSite, values) },
161
+ __ultracodeWorkflow: { value: (callSite, ...values) => orchestration("workflow", callSite, values) },
162
+ __ultracodeLoop: { value: (site) => sync("loop", [site]) },
163
+ __ultracodeInvoke: { value: (callSite, fn, thisArg, ...values) => invoke(callSite, fn, thisArg, values) },
164
+ phase: { value: (value) => sync("phase", [value]) },
165
+ log: { value: sandboxLog },
166
+ args: { value: localize(rawArgs), enumerable: true },
167
+ cwd: { value: cwdValue, enumerable: true },
168
+ process: { value: Object.freeze({ cwd: () => cwdValue }) },
169
+ console: { value: Object.freeze({
170
+ log: sandboxLog,
171
+ info: sandboxLog,
172
+ warn: (value) => sandboxLog("[warn] " + String(value)),
173
+ error: (value) => sandboxLog("[error] " + String(value)),
174
+ }) },
175
+ structuredClone: { value: (value) => localize(value) },
176
+ Date: { value: undefined },
177
+ Function: { value: undefined },
178
+ eval: { value: undefined },
179
+ require: { value: undefined },
180
+ performance: { value: undefined },
181
+ crypto: { value: undefined },
182
+ Intl: { value: undefined },
183
+ Temporal: { value: undefined },
184
+ WeakRef: { value: undefined },
185
+ FinalizationRegistry: { value: undefined },
186
+ WebAssembly: { value: undefined },
187
+ ArrayBuffer: { value: undefined },
188
+ SharedArrayBuffer: { value: undefined },
189
+ DataView: { value: undefined },
190
+ Atomics: { value: undefined },
191
+ Int8Array: { value: undefined },
192
+ Uint8Array: { value: undefined },
193
+ Uint8ClampedArray: { value: undefined },
194
+ Int16Array: { value: undefined },
195
+ Uint16Array: { value: undefined },
196
+ Int32Array: { value: undefined },
197
+ Uint32Array: { value: undefined },
198
+ Float32Array: { value: undefined },
199
+ Float64Array: { value: undefined },
200
+ BigInt64Array: { value: undefined },
201
+ BigUint64Array: { value: undefined },
202
+ ${JSON.stringify(CHECKPOINT)}: { value: () => sync("checkpoint", []) },
203
+ })
204
+ Object.defineProperties(Promise, {
205
+ all: { value: undefined },
206
+ allSettled: { value: undefined },
207
+ race: { value: undefined },
208
+ any: { value: undefined },
209
+ })
210
+ Object.defineProperties(Promise.prototype, {
211
+ constructor: { value: undefined },
212
+ catch: { value: undefined },
213
+ finally: { value: undefined },
214
+ })
215
+ Object.freeze(Promise.prototype)
216
+ Object.freeze(Promise)
217
+ Object.defineProperty(Math, "random", {
218
+ value: () => { throw new Error("Math.random() is non-deterministic and forbidden in workflow scripts; vary randomness by agent index instead.") },
219
+ configurable: false,
220
+ writable: false,
221
+ })
222
+ Object.freeze(Math)
223
+ }
224
+ `, { filename: "workflow-bootstrap.js" }).runInContext(scriptContext);
225
+ install(Object.freeze({
226
+ agent,
227
+ parallel,
228
+ pipeline,
229
+ workflow,
230
+ phase,
231
+ log: logLine,
232
+ checkpoint,
233
+ loop: markLoopIteration,
234
+ invoke: invokeHelper,
235
+ }), bodyArgs, workerData.cwd);
236
+ return scriptContext;
237
+ }
238
+
239
+ function checkpoint() {
240
+ throwIfAborted();
241
+ checkpointCount++;
242
+ const limit = Number.isInteger(workerData.checkpointLimit) ? workerData.checkpointLimit : 1_000_000;
243
+ if (checkpointCount > limit) {
244
+ throw makePolicyError(`workflow exceeded the ${limit}-operation script checkpoint limit (runaway loop?)`);
245
+ }
246
+ if ((checkpointCount & 1023) === 0) sendHeartbeat();
247
+ }
248
+
249
+ function agent(callSite, prompt, options = {}) {
250
+ return startOrchestration(() => agentImpl(callSite, prompt, options));
251
+ }
252
+
253
+ async function agentImpl(callSite, prompt, options = {}) {
254
+ throwIfAborted();
255
+ if (typeof prompt !== "string") throw new TypeError("agent prompt must be a string");
256
+ if (options == null) options = {};
257
+ if (typeof options !== "object") throw new TypeError("agent options must be an object");
258
+ const execution = executionContext.getStore() ?? createExecutionScope({ scopePath: "$" });
259
+ const callPath = allocateCallPath(execution, "agent", "a", callSite);
260
+ const opts = { ...options };
261
+ const assignedPhase = typeof opts.phase === "string" ? opts.phase : execution.currentPhase;
262
+ return await rpc("agent", {
263
+ callPath,
264
+ prompt,
265
+ options: opts,
266
+ assignedPhase,
267
+ workflowPath: execution.workflowPath ?? [],
268
+ reservationIds: Array.isArray(execution.reservationIds) ? execution.reservationIds : [],
269
+ });
270
+ }
271
+
272
+ function parallel(callSite, thunks, options = {}) {
273
+ return startOrchestration(() => parallelImpl(callSite, thunks, options));
274
+ }
275
+
276
+ async function parallelImpl(callSite, thunks, options = {}) {
277
+ throwIfAborted();
278
+ if (!Array.isArray(thunks)) throw new TypeError("parallel() expects an array of functions");
279
+ if (thunks.length > MAX_ITEMS_PER_CALL) {
280
+ throw makePolicyError(`parallel() accepts at most ${MAX_ITEMS_PER_CALL} items (got ${thunks.length})`);
281
+ }
282
+ if (thunks.some((thunk) => typeof thunk !== "function")) {
283
+ throw new TypeError("parallel() expects an array of functions, not promises. Wrap each call: () => agent(...)");
284
+ }
285
+ const execution = executionContext.getStore() ?? createExecutionScope({ scopePath: "$" });
286
+ const reserveAgents = normalizeReserveAgents(options, thunks.length);
287
+ const rawPanelSite = typeof callSite === "string" && callSite ? callSite : "parallel:dynamic";
288
+ const [panelSource, panelLoops = ""] = rawPanelSite.split("@", 2);
289
+ const panelSite = `${panelSource}#${thunks.length}:${reserveAgents}${panelLoops ? `@${panelLoops}` : ""}`;
290
+ const panelCallPath = allocateCallPath(execution, "parallel", "p", panelSite);
291
+ if (reserveAgents < thunks.length) {
292
+ throw makePolicyError(`parallel reserveAgents must be at least the number of thunks (${thunks.length}); got ${reserveAgents}`);
293
+ }
294
+ const parentReservationIds = Array.isArray(execution.reservationIds) ? execution.reservationIds : [];
295
+ const panel = await rpc("reservePanel", {
296
+ callPath: panelCallPath,
297
+ reserveAgents,
298
+ branchCount: thunks.length,
299
+ parentReservationIds,
300
+ });
301
+ const panelReservationId = requireString(panel?.panelReservationId, "panelReservationId");
302
+ const branchReservationIds = Array.isArray(panel?.branchReservationIds) ? panel.branchReservationIds : [];
303
+ if (branchReservationIds.length !== thunks.length || branchReservationIds.some((id) => typeof id !== "string")) {
304
+ throw makePolicyError("workflow host returned an invalid parallel reservation panel");
305
+ }
306
+
307
+ let completed = false;
308
+ const branchOutcomes = Array.from({ length: thunks.length }, () => "pending");
309
+ try {
310
+ const results = await Promise.all(thunks.map(async (thunk, index) => {
311
+ const branchReservationId = branchReservationIds[index];
312
+ const childContext = createExecutionScope({
313
+ workflowPath: [...(execution.workflowPath ?? [])],
314
+ depth: execution.depth,
315
+ currentPhase: execution.currentPhase,
316
+ reservationIds: [branchReservationId, panelReservationId].filter(Boolean),
317
+ scopePath: `${panelCallPath}/b:${index}`,
318
+ loopIterations: execution.loopIterations,
319
+ });
320
+ return executionContext.run(childContext, async () => {
321
+ try {
322
+ throwIfAborted();
323
+ const value = await thunk();
324
+ branchOutcomes[index] = "success";
325
+ await rpc("completePanelBranch", {
326
+ callPath: panelCallPath,
327
+ branchIndex: index,
328
+ outcome: "success",
329
+ });
330
+ return value;
331
+ } catch (error) {
332
+ if (isFatal(error)) throw error;
333
+ branchOutcomes[index] = "failed";
334
+ await rpc("completePanelBranch", {
335
+ callPath: panelCallPath,
336
+ branchIndex: index,
337
+ outcome: "failed",
338
+ });
339
+ logLine(`parallel[${index}] failed: ${messageOf(error)}`);
340
+ return null;
341
+ } finally {
342
+ await rpc("releasePanel", { reservationId: branchReservationId }).catch(() => undefined);
343
+ }
344
+ });
345
+ }));
346
+ completed = true;
347
+ return results;
348
+ } finally {
349
+ const release = rpc("releasePanel", {
350
+ reservationId: panelReservationId,
351
+ callPath: panelCallPath,
352
+ completed,
353
+ branchOutcomes,
354
+ });
355
+ if (completed) await release;
356
+ else await release.catch(() => undefined);
357
+ }
358
+ }
359
+
360
+ function pipeline(callSite, items, ...stages) {
361
+ return startOrchestration(() => pipelineImpl(callSite, items, ...stages));
362
+ }
363
+
364
+ async function pipelineImpl(callSite, items, ...stages) {
365
+ throwIfAborted();
366
+ if (!Array.isArray(items)) throw new TypeError("pipeline() expects an array as the first argument");
367
+ if (items.length > MAX_ITEMS_PER_CALL) {
368
+ throw makePolicyError(`pipeline() accepts at most ${MAX_ITEMS_PER_CALL} items (got ${items.length})`);
369
+ }
370
+ if (stages.some((stage) => typeof stage !== "function")) {
371
+ throw new TypeError("pipeline() stages must be functions: pipeline(items, item => ..., result => ...)");
372
+ }
373
+ const execution = executionContext.getStore() ?? createExecutionScope({ scopePath: "$" });
374
+ const pipelineCallPath = allocateCallPath(execution, "pipeline", "l", callSite);
375
+ return await Promise.all(items.map(async (item, index) => {
376
+ let value = item;
377
+ for (const [stageIndex, stage] of stages.entries()) {
378
+ try {
379
+ throwIfAborted();
380
+ const stageContext = createExecutionScope({
381
+ workflowPath: [...(execution.workflowPath ?? [])],
382
+ depth: execution.depth,
383
+ currentPhase: execution.currentPhase,
384
+ reservationIds: Array.isArray(execution.reservationIds) ? [...execution.reservationIds] : [],
385
+ scopePath: `${pipelineCallPath}/i:${index}/s:${stageIndex}`,
386
+ loopIterations: execution.loopIterations,
387
+ });
388
+ value = await executionContext.run(stageContext, () => stage(value, item, index));
389
+ throwIfAborted();
390
+ } catch (error) {
391
+ if (isFatal(error)) throw error;
392
+ logLine(`pipeline[${index}] failed: ${messageOf(error)}`);
393
+ return null;
394
+ }
395
+ }
396
+ return value;
397
+ }));
398
+ }
399
+
400
+ function workflow(callSite, nameOrRef, nestedArgs) {
401
+ return startOrchestration(() => workflowImpl(callSite, nameOrRef, nestedArgs));
402
+ }
403
+
404
+ async function workflowImpl(callSite, nameOrRef, nestedArgs) {
405
+ throwIfAborted();
406
+ const execution = executionContext.getStore() ?? createExecutionScope({ scopePath: "$" });
407
+ const workflowCallPath = allocateCallPath(execution, "workflow", "w", callSite);
408
+ if ((execution.depth ?? 0) >= 1) {
409
+ throw new Error("workflow() nesting is one level deep only; cannot call workflow() inside a child workflow");
410
+ }
411
+ let argsSnapshot;
412
+ try {
413
+ argsSnapshot = structuredClone(nestedArgs);
414
+ } catch (error) {
415
+ throw new TypeError(`workflow() args must be structured-cloneable: ${messageOf(error)}`);
416
+ }
417
+ const loaded = await rpc("loadWorkflow", {
418
+ nameOrRef,
419
+ callPath: workflowCallPath,
420
+ args: argsSnapshot,
421
+ });
422
+ logLine(`▸ nested workflow: ${loaded.meta.name}`);
423
+ const childResult = await executeBody(
424
+ loaded.body,
425
+ argsSnapshot,
426
+ (execution.depth ?? 0) + 1,
427
+ loaded.meta.name,
428
+ requireString(loaded.callPath, "nested workflow durable callPath"),
429
+ );
430
+ let outputSnapshot;
431
+ try {
432
+ outputSnapshot = structuredClone(childResult);
433
+ } catch (error) {
434
+ throw makePolicyError(`nested workflow output must be structured-cloneable: ${messageOf(error)}`);
435
+ }
436
+ await rpc("validateOutput", { value: outputSnapshot });
437
+ return outputSnapshot;
438
+ }
439
+
440
+ function createExecutionScope(input) {
441
+ const phaseState = input.phaseState ?? { value: input.currentPhase };
442
+ const scope = {
443
+ workflowPath: Array.isArray(input.workflowPath) ? input.workflowPath : [],
444
+ depth: Number.isInteger(input.depth) ? input.depth : 0,
445
+ phaseState,
446
+ reservationIds: Array.isArray(input.reservationIds) ? input.reservationIds : [],
447
+ scopePath: typeof input.scopePath === "string" ? input.scopePath : "$",
448
+ counters: Object.create(null),
449
+ loopIterations: { ...(input.loopIterations ?? {}) },
450
+ orchestrationState: input.orchestrationState ?? { active: 0 },
451
+ };
452
+ Object.defineProperty(scope, "currentPhase", {
453
+ get: () => phaseState.value,
454
+ set: (value) => { phaseState.value = value },
455
+ });
456
+ return scope;
457
+ }
458
+
459
+ function allocateCallPath(execution, counter, segment, callSite) {
460
+ const rawSite = typeof callSite === "string" && callSite ? callSite : `${counter}:dynamic`;
461
+ const [site, loopSuffix = ""] = rawSite.split("@", 2);
462
+ const loopIdentity = loopSuffix
463
+ ? `@${loopSuffix.split(",").map((loopSite) => `${loopSite}=${execution.loopIterations[loopSite] ?? -1}`).join(",")}`
464
+ : "";
465
+ const identifiedSite = `${site}${loopIdentity}`;
466
+ const key = `${segment}:${identifiedSite}`;
467
+ const occurrence = execution.counters[key] ?? 0;
468
+ execution.counters[key] = occurrence + 1;
469
+ return `${execution.scopePath}/${segment}:${identifiedSite}:${occurrence}`;
470
+ }
471
+
472
+ function invokeHelper(callSite, fn, thisArg, values) {
473
+ if (typeof fn !== "function") throw new TypeError("workflow helper must be a function");
474
+ if (!Array.isArray(values)) throw new TypeError("workflow helper args must be an array");
475
+ const execution = executionContext.getStore() ?? createExecutionScope({ scopePath: "$" });
476
+ const invocationPath = allocateCallPath(execution, "invoke", "h", callSite);
477
+ const childContext = createExecutionScope({
478
+ workflowPath: [...(execution.workflowPath ?? [])],
479
+ depth: execution.depth,
480
+ phaseState: execution.phaseState,
481
+ reservationIds: [...(execution.reservationIds ?? [])],
482
+ scopePath: invocationPath,
483
+ loopIterations: execution.loopIterations,
484
+ orchestrationState: execution.orchestrationState,
485
+ });
486
+ return executionContext.run(childContext, () => Reflect.apply(fn, thisArg, values));
487
+ }
488
+
489
+ function markLoopIteration(site) {
490
+ if (typeof site !== "string" || !site) throw new TypeError("workflow loop site must be a string");
491
+ const execution = executionContext.getStore();
492
+ if (!execution) throw makePolicyError("workflow loop executed without an execution scope");
493
+ execution.loopIterations[site] = (execution.loopIterations[site] ?? -1) + 1;
494
+ }
495
+
496
+ function phase(title) {
497
+ if (typeof title !== "string") throw new TypeError("phase title must be a string");
498
+ consumeHostCall();
499
+ const execution = executionContext.getStore();
500
+ if (execution) execution.currentPhase = title;
501
+ parentPort.postMessage({ type: "event", op: "phase", payload: { title } });
502
+ }
503
+
504
+ function logLine(message) {
505
+ consumeHostCall();
506
+ parentPort.postMessage({ type: "event", op: "log", payload: { message: String(message) } });
507
+ }
508
+
509
+ function normalizeReserveAgents(options, fallback) {
510
+ if (options == null) return fallback;
511
+ if (typeof options !== "object") throw new TypeError("parallel() options must be an object");
512
+ const value = options.reserveAgents;
513
+ if (value === undefined || value === null) return fallback;
514
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0 || value > MAX_RESERVE_AGENTS) {
515
+ throw makePolicyError(`parallel reserveAgents must be an integer between 0 and ${MAX_RESERVE_AGENTS}`);
516
+ }
517
+ return value;
518
+ }
519
+
520
+ function rpc(op, payload) {
521
+ throwIfAborted();
522
+ consumeHostCall();
523
+ const id = ++nextRpcId;
524
+ return new Promise((resolve, reject) => {
525
+ rpcWaiters.set(id, { resolve, reject });
526
+ parentPort.postMessage({ type: "rpc", id, op, payload });
527
+ });
528
+ }
529
+
530
+ function consumeHostCall() {
531
+ hostCallCount++;
532
+ const limit = Number.isInteger(workerData.hostCallLimit) ? workerData.hostCallLimit : 10_000;
533
+ if (hostCallCount > limit) {
534
+ throw makePolicyError(`workflow exceeded the ${limit} host-call limit`);
535
+ }
536
+ }
537
+
538
+ function startOrchestration(factory) {
539
+ const execution = executionContext.getStore() ?? createExecutionScope({ scopePath: "$" });
540
+ if (execution.orchestrationState.active > 0) {
541
+ return trackOrchestration(Promise.reject(makePolicyError(
542
+ "native concurrent orchestration in one scope is non-deterministic; use parallel() or pipeline()",
543
+ )));
544
+ }
545
+ execution.orchestrationState.active++;
546
+ let promise;
547
+ try {
548
+ promise = Promise.resolve(factory());
549
+ } catch (error) {
550
+ promise = Promise.reject(error);
551
+ }
552
+ promise.then(
553
+ () => { execution.orchestrationState.active--; },
554
+ () => { execution.orchestrationState.active--; },
555
+ );
556
+ return trackOrchestration(promise);
557
+ }
558
+
559
+ function trackOrchestration(promise) {
560
+ const record = { observed: false, settled: false, promise };
561
+ orchestrationPromises.add(record);
562
+ // Observe settlement immediately so fast failures are contained and root
563
+ // completion can reject detached chains that still have live host work.
564
+ promise.then(
565
+ () => { record.settled = true; },
566
+ () => { record.settled = true; },
567
+ );
568
+ const observe = () => {
569
+ record.observed = true;
570
+ };
571
+ return Object.freeze({
572
+ then(onFulfilled, onRejected) {
573
+ observe();
574
+ return promise.then(onFulfilled, onRejected);
575
+ },
576
+ catch(onRejected) {
577
+ observe();
578
+ return trackOrchestration(promise.catch(onRejected));
579
+ },
580
+ finally(onFinally) {
581
+ observe();
582
+ return trackOrchestration(promise.finally(onFinally));
583
+ },
584
+ get [Symbol.toStringTag]() {
585
+ return "Promise";
586
+ },
587
+ });
588
+ }
589
+
590
+ function throwIfAborted() {
591
+ if (stickyFatalError) throw stickyFatalError;
592
+ if (aborted) throw makeAbortError();
593
+ }
594
+
595
+ function makePolicyError(message) {
596
+ const error = new Error(message);
597
+ error.name = "WorkflowPolicyError";
598
+ error.code = POLICY;
599
+ markFatal(error);
600
+ return error;
601
+ }
602
+
603
+ function markFatal(error) {
604
+ if (!isPolicy(error)) return;
605
+ stickyFatalError ??= error;
606
+ aborted = true;
607
+ for (const waiter of rpcWaiters.values()) waiter.reject(stickyFatalError);
608
+ rpcWaiters.clear();
609
+ try {
610
+ parentPort.postMessage({ type: "fatal", error: serializeError(stickyFatalError) });
611
+ } catch {
612
+ // Parent is already gone; exit/result handling will still carry the error.
613
+ }
614
+ }
615
+
616
+ function makeAbortError(message = "workflow aborted") {
617
+ const error = new Error(message);
618
+ error.name = "WorkflowAbortError";
619
+ error.code = ABORT;
620
+ return error;
621
+ }
622
+
623
+ function reviveError(value) {
624
+ const error = new Error(value?.message ?? String(value ?? "unknown error"));
625
+ error.name = value?.name ?? "Error";
626
+ if (value?.code) error.code = value.code;
627
+ if (value?.stack) error.stack = value.stack;
628
+ return error;
629
+ }
630
+
631
+ function serializeError(error) {
632
+ return {
633
+ name: error?.name ?? "Error",
634
+ message: messageOf(error),
635
+ code: error?.code,
636
+ stack: error?.stack,
637
+ };
638
+ }
639
+
640
+ function isFatal(error) {
641
+ return error?.code === POLICY || error?.code === ABORT || error?.code === STALL;
642
+ }
643
+
644
+ function isPolicy(error) {
645
+ return error?.code === POLICY;
646
+ }
647
+
648
+ function messageOf(error) {
649
+ return error instanceof Error ? error.message : String(error);
650
+ }
651
+
652
+ function requireString(value, name) {
653
+ if (typeof value !== "string") throw makePolicyError(`workflow host returned invalid ${name}`);
654
+ return value;
655
+ }
656
+
657
+ function sendHeartbeat() {
658
+ if (!finished) parentPort.postMessage({ type: "heartbeat", at: Date.now(), checkpoints: checkpointCount });
659
+ }
660
+
661
+ function finish(ok, value, error) {
662
+ if (finished) return;
663
+ finished = true;
664
+ clearInterval(heartbeat);
665
+ if (stickyFatalError) {
666
+ parentPort.postMessage({ type: "result", ok: false, error: serializeError(stickyFatalError) });
667
+ return;
668
+ }
669
+ if (ok) {
670
+ try {
671
+ parentPort.postMessage({ type: "result", ok: true, value: structuredClone(value) });
672
+ } catch (cloneError) {
673
+ parentPort.postMessage({ type: "result", ok: false, error: serializeError(cloneError) });
674
+ }
675
+ } else {
676
+ parentPort.postMessage({ type: "result", ok: false, error: serializeError(error) });
677
+ }
678
+ }
@@ -7,6 +7,7 @@
7
7
 
8
8
  import { defineTool, type ToolDefinition } from "@earendil-works/pi-coding-agent";
9
9
  import type { Static, TSchema } from "typebox";
10
+ import { assertStructuredOutputLimit } from "./value-limits.ts";
10
11
 
11
12
  export interface StructuredOutputCapture<T = unknown> {
12
13
  value: T | undefined;
@@ -36,6 +37,7 @@ export function createStructuredOutputTool<TSchemaDef extends TSchema>({
36
37
  ],
37
38
  parameters: schema,
38
39
  async execute(_toolCallId, params) {
40
+ assertStructuredOutputLimit(params);
39
41
  capture.value = params;
40
42
  capture.called = true;
41
43
  return {