@tt-a1i/openpi 0.1.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.
- package/README.md +643 -0
- package/SETUP.md +74 -0
- package/THIRD_PARTY_NOTICES.md +16 -0
- package/assets/openpi-package.png +0 -0
- package/assets/readme-hero-mobile.svg +72 -0
- package/assets/readme-hero.svg +118 -0
- package/assets/readme-runtime-mobile.svg +91 -0
- package/assets/readme-runtime.svg +111 -0
- package/extensions/ask-user/handoff.ts +205 -0
- package/extensions/ask-user/index.ts +1110 -0
- package/extensions/ask-user/limits.ts +89 -0
- package/extensions/ask-user/prompt.ts +76 -0
- package/extensions/background-terminals/index.ts +653 -0
- package/extensions/background-terminals/src/domain.ts +99 -0
- package/extensions/background-terminals/src/manager.ts +989 -0
- package/extensions/background-terminals/src/output.ts +84 -0
- package/extensions/background-terminals/src/prompt.ts +195 -0
- package/extensions/background-terminals/src/result-delivery.ts +43 -0
- package/extensions/background-terminals/src/runtime.ts +36 -0
- package/extensions/background-terminals/src/ui/output-view.ts +55 -0
- package/extensions/background-terminals/src/ui/ps.ts +642 -0
- package/extensions/background-terminals/src/ui/tool-result.ts +146 -0
- package/extensions/background-terminals/src/watch.ts +192 -0
- package/extensions/context-pivot/index.ts +222 -0
- package/extensions/copy-all/index.ts +65 -0
- package/extensions/cron/index.ts +173 -0
- package/extensions/cron/schedule.ts +127 -0
- package/extensions/file-mutation-display/index.ts +105 -0
- package/extensions/file-mutation-display/render.ts +107 -0
- package/extensions/file-search/index.ts +515 -0
- package/extensions/file-search/src/args.ts +129 -0
- package/extensions/file-search/src/binaries.ts +419 -0
- package/extensions/file-search/src/output.ts +142 -0
- package/extensions/file-search/src/process.ts +309 -0
- package/extensions/file-search/src/prompt.ts +53 -0
- package/extensions/git-info/index.ts +272 -0
- package/extensions/git-info/src/changed-files-view.ts +414 -0
- package/extensions/git-info/src/process.ts +107 -0
- package/extensions/git-info/src/refresh-coordinator.ts +13 -0
- package/extensions/git-info/src/runtime.ts +28 -0
- package/extensions/goal/controller.ts +794 -0
- package/extensions/goal/index.ts +521 -0
- package/extensions/goal/prompts.ts +122 -0
- package/extensions/goal/state.ts +763 -0
- package/extensions/goal/ui.ts +158 -0
- package/extensions/model-info/index.ts +234 -0
- package/extensions/plan-mode/bash-policy.ts +313 -0
- package/extensions/plan-mode/index.ts +539 -0
- package/extensions/post-edit/index.ts +129 -0
- package/extensions/sessions/LICENSE.upstream +21 -0
- package/extensions/sessions/git-stats.ts +226 -0
- package/extensions/sessions/index.ts +1092 -0
- package/extensions/sessions/sessions.ts +385 -0
- package/extensions/setup/index.ts +408 -0
- package/extensions/shared/activity-status.ts +65 -0
- package/extensions/shared/below-editor-navigation.ts +343 -0
- package/extensions/shared/child-session.ts +352 -0
- package/extensions/shared/context-utilization.ts +47 -0
- package/extensions/shared/dashboard-state.ts +102 -0
- package/extensions/shared/plan-mode-state.ts +65 -0
- package/extensions/shared/setup-config.ts +971 -0
- package/extensions/shared/subagent-roles.ts +22 -0
- package/extensions/shared/terminal-text.ts +38 -0
- package/extensions/shared/tool-call-timeout.ts +104 -0
- package/extensions/shared/worktree.ts +526 -0
- package/extensions/subagents/index.ts +1225 -0
- package/extensions/subagents/navigation.ts +121 -0
- package/extensions/subagents/src/agent-types.ts +543 -0
- package/extensions/subagents/src/backend.ts +63 -0
- package/extensions/subagents/src/backends/pi.ts +493 -0
- package/extensions/subagents/src/backends/stub.ts +296 -0
- package/extensions/subagents/src/by-the-way.ts +21 -0
- package/extensions/subagents/src/domain.ts +271 -0
- package/extensions/subagents/src/format.ts +48 -0
- package/extensions/subagents/src/manager.ts +769 -0
- package/extensions/subagents/src/prompt.ts +190 -0
- package/extensions/subagents/src/result-delivery.ts +20 -0
- package/extensions/subagents/src/runtime.ts +51 -0
- package/extensions/subagents/src/ui/takeover.ts +615 -0
- package/extensions/subagents/src/ui/transcript.ts +293 -0
- package/extensions/subagents/src/ui/wait-result.ts +89 -0
- package/extensions/suggestions/index.ts +172 -0
- package/extensions/suggestions/src/config.ts +12 -0
- package/extensions/suggestions/src/predictor.ts +147 -0
- package/extensions/suggestions/src/prompt.ts +20 -0
- package/extensions/suggestions/src/transcript.ts +233 -0
- package/extensions/suggestions/src/ui.ts +224 -0
- package/extensions/tasks/index.ts +512 -0
- package/extensions/tasks/tasks.ts +649 -0
- package/extensions/tasks/ui.ts +421 -0
- package/extensions/turn-time/index.ts +61 -0
- package/extensions/ui-customization/footer.ts +512 -0
- package/extensions/ui-customization/index.ts +217 -0
- package/extensions/workflows/acceptance.ts +298 -0
- package/extensions/workflows/artifacts.ts +225 -0
- package/extensions/workflows/controller.ts +210 -0
- package/extensions/workflows/dashboard.ts +1226 -0
- package/extensions/workflows/index.ts +1884 -0
- package/extensions/workflows/journal.ts +188 -0
- package/extensions/workflows/meta.ts +250 -0
- package/extensions/workflows/model.ts +423 -0
- package/extensions/workflows/navigation.ts +93 -0
- package/extensions/workflows/prompt.ts +212 -0
- package/extensions/workflows/replay-safety.ts +577 -0
- package/extensions/workflows/runner.ts +786 -0
- package/extensions/workflows/sandbox-child.cjs +402 -0
- package/extensions/workflows/sandbox.ts +397 -0
- package/extensions/workflows/serialization.ts +162 -0
- package/extensions/workflows/worktree-handoff.ts +216 -0
- package/package.json +87 -0
- package/scripts/prepare-effect-tsgo.mjs +16 -0
- package/skills/background-terminals/SKILL.md +30 -0
- package/skills/subagents/SKILL.md +15 -0
- package/themes/github-dark-default.json +89 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// This file is launched by sandbox.ts in Node permission mode. It deliberately
|
|
4
|
+
// has no filesystem/network/child-process permissions and receives workflow
|
|
5
|
+
// source only over a validated IPC channel.
|
|
6
|
+
const vm = require("node:vm");
|
|
7
|
+
const sendIpc =
|
|
8
|
+
typeof process.send === "function" ? process.send.bind(process) : undefined;
|
|
9
|
+
// If a future V8 escape exposes `process`, remove the convenient bridges to
|
|
10
|
+
// builtins, native bindings, parent signalling, and addons before any workflow
|
|
11
|
+
// source is compiled. The parent still enforces the authenticated IPC protocol.
|
|
12
|
+
for (const capability of [
|
|
13
|
+
"getBuiltinModule",
|
|
14
|
+
"binding",
|
|
15
|
+
"_linkedBinding",
|
|
16
|
+
"dlopen",
|
|
17
|
+
"kill",
|
|
18
|
+
"abort",
|
|
19
|
+
"send",
|
|
20
|
+
]) {
|
|
21
|
+
try {
|
|
22
|
+
Object.defineProperty(process, capability, {
|
|
23
|
+
value: undefined,
|
|
24
|
+
writable: false,
|
|
25
|
+
configurable: false,
|
|
26
|
+
});
|
|
27
|
+
} catch {
|
|
28
|
+
// The VM boundary and permission mode remain mandatory controls.
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const BOOTSTRAP = String.raw`
|
|
33
|
+
(function bootstrapWorkflowApi() {
|
|
34
|
+
"use strict";
|
|
35
|
+
const callHost = globalThis.__hostBridge;
|
|
36
|
+
delete globalThis.__hostBridge;
|
|
37
|
+
let nextRequestId = 0;
|
|
38
|
+
const unconsumed = new Set();
|
|
39
|
+
const inFlight = new Set();
|
|
40
|
+
|
|
41
|
+
function deepFreeze(value, depth = 0) {
|
|
42
|
+
if (!value || typeof value !== "object" || depth > 32 || Object.isFrozen(value)) return value;
|
|
43
|
+
Object.freeze(value);
|
|
44
|
+
for (const key of Object.keys(value)) deepFreeze(value[key], depth + 1);
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function requestAgent(promptValue, optionsValue = {}) {
|
|
49
|
+
const id = ++nextRequestId;
|
|
50
|
+
unconsumed.add(id);
|
|
51
|
+
let started;
|
|
52
|
+
const begin = () => {
|
|
53
|
+
unconsumed.delete(id);
|
|
54
|
+
if (!started) {
|
|
55
|
+
let payload;
|
|
56
|
+
try {
|
|
57
|
+
payload = JSON.stringify({
|
|
58
|
+
id,
|
|
59
|
+
prompt: typeof promptValue === "string" ? promptValue : String(promptValue ?? ""),
|
|
60
|
+
options: optionsValue && typeof optionsValue === "object" ? optionsValue : {},
|
|
61
|
+
});
|
|
62
|
+
} catch (error) {
|
|
63
|
+
started = Promise.reject(new Error("agent() arguments must be serializable: " + error.message));
|
|
64
|
+
return started;
|
|
65
|
+
}
|
|
66
|
+
inFlight.add(id);
|
|
67
|
+
// Re-wrap in a context-realm promise: chaining a host promise would
|
|
68
|
+
// expose the host Function constructor through .constructor.constructor.
|
|
69
|
+
started = new Promise((resolve, reject) => {
|
|
70
|
+
callHost("agent", payload).then(resolve, reject);
|
|
71
|
+
})
|
|
72
|
+
.then((json) => JSON.parse(json))
|
|
73
|
+
.finally(() => inFlight.delete(id));
|
|
74
|
+
}
|
|
75
|
+
return started;
|
|
76
|
+
};
|
|
77
|
+
return Object.freeze({
|
|
78
|
+
then(resolve, reject) {
|
|
79
|
+
return begin().then(resolve, reject);
|
|
80
|
+
},
|
|
81
|
+
catch(reject) {
|
|
82
|
+
return begin().catch(reject);
|
|
83
|
+
},
|
|
84
|
+
finally(callback) {
|
|
85
|
+
return begin().finally(callback);
|
|
86
|
+
},
|
|
87
|
+
get [Symbol.toStringTag]() {
|
|
88
|
+
return "Promise";
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function mapLimited(items, concurrency, invoke) {
|
|
94
|
+
const results = new Array(items.length);
|
|
95
|
+
let next = 0;
|
|
96
|
+
const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
|
|
97
|
+
while (true) {
|
|
98
|
+
const index = next++;
|
|
99
|
+
if (index >= items.length) return;
|
|
100
|
+
results[index] = await invoke(items[index], index);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
await Promise.all(workers);
|
|
104
|
+
return results;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const maxConcurrency = globalThis.__maxConcurrency;
|
|
108
|
+
delete globalThis.__maxConcurrency;
|
|
109
|
+
|
|
110
|
+
async function parallel(items, options = {}) {
|
|
111
|
+
if (!Array.isArray(items)) throw new Error("parallel() expects an array of zero-argument agent thunks");
|
|
112
|
+
const requested = options && typeof options.concurrency === "number"
|
|
113
|
+
? Math.floor(options.concurrency)
|
|
114
|
+
: maxConcurrency;
|
|
115
|
+
if (!Number.isFinite(requested) || requested < 1) {
|
|
116
|
+
throw new Error("parallel(): concurrency must be a positive integer");
|
|
117
|
+
}
|
|
118
|
+
const concurrency = Math.min(maxConcurrency, requested);
|
|
119
|
+
return mapLimited(items, concurrency, async (item) => {
|
|
120
|
+
if (typeof item !== "function") {
|
|
121
|
+
throw new Error("parallel() items must be zero-argument functions");
|
|
122
|
+
}
|
|
123
|
+
// A thunk that throws settles to null instead of rejecting the whole
|
|
124
|
+
// batch, so one bad item never discards every completed sibling's
|
|
125
|
+
// result. agent() itself never throws (it returns { ok:false }); this
|
|
126
|
+
// only catches user thunk code, e.g. JSON.parse on a non-JSON output.
|
|
127
|
+
try {
|
|
128
|
+
return await item();
|
|
129
|
+
} catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function pipeline(items, ...stages) {
|
|
136
|
+
if (!Array.isArray(items)) throw new Error("pipeline() expects an array of items as its first argument");
|
|
137
|
+
for (const stage of stages) {
|
|
138
|
+
if (typeof stage !== "function") throw new Error("pipeline() stages must be functions");
|
|
139
|
+
}
|
|
140
|
+
// Snapshot: mapLimited re-reads items[index] every iteration, so a stage
|
|
141
|
+
// that mutates the array it was given would manufacture nulls for items
|
|
142
|
+
// that never existed (truncating) or silently extend the run (appending).
|
|
143
|
+
const work = items.slice();
|
|
144
|
+
// Each item walks every stage on its own, with no barrier in between: item
|
|
145
|
+
// A can be in stage 3 while item B is still in stage 1. Wall-clock is the
|
|
146
|
+
// slowest single chain rather than the sum of each stage's slowest item.
|
|
147
|
+
//
|
|
148
|
+
// In-flight items are capped even though the host semaphore already
|
|
149
|
+
// serializes agent calls, because the host counts an agent call against the
|
|
150
|
+
// run budget when it is SUBMITTED, not when it runs. Releasing every chain
|
|
151
|
+
// at once would burn the budget on calls that are merely queued.
|
|
152
|
+
return mapLimited(work, Math.min(maxConcurrency, work.length || 1), async (item, index) => {
|
|
153
|
+
let value = item;
|
|
154
|
+
// A throwing stage drops just this item to null and skips its remaining
|
|
155
|
+
// stages; siblings are unaffected. Matches parallel()'s thunk semantics.
|
|
156
|
+
//
|
|
157
|
+
// The throw is narrated rather than swallowed: without it a script bug
|
|
158
|
+
// (a typo on an undefined field), a deliberate skip, and a genuinely
|
|
159
|
+
// failed agent are all one indistinguishable null, and the "how many
|
|
160
|
+
// dropped" count every script is told to report becomes a guess.
|
|
161
|
+
try {
|
|
162
|
+
for (const stage of stages) {
|
|
163
|
+
value = await stage(value, item, index);
|
|
164
|
+
}
|
|
165
|
+
return value;
|
|
166
|
+
} catch (error) {
|
|
167
|
+
log("pipeline: item " + index + " dropped — " + ((error && error.message) || String(error)));
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function phase(title) {
|
|
174
|
+
callHost("phase", JSON.stringify({ title: String(title) }));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function log(message) {
|
|
178
|
+
const text =
|
|
179
|
+
typeof message === "string"
|
|
180
|
+
? message
|
|
181
|
+
: message === undefined
|
|
182
|
+
? ""
|
|
183
|
+
: (() => {
|
|
184
|
+
try {
|
|
185
|
+
return typeof message === "object" && message !== null
|
|
186
|
+
? JSON.stringify(message)
|
|
187
|
+
: String(message);
|
|
188
|
+
} catch {
|
|
189
|
+
return String(message);
|
|
190
|
+
}
|
|
191
|
+
})();
|
|
192
|
+
callHost("log", JSON.stringify({ text }));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Synchronous read of a plain JSON string the host refreshes as agents
|
|
196
|
+
// settle, so a loop condition evaluated right after an awaited agent call
|
|
197
|
+
// sees that agent's spend. Returning a primitive keeps the host realm sealed.
|
|
198
|
+
// (No backticks in this file's comments: BOOTSTRAP is a template literal.)
|
|
199
|
+
function usage() {
|
|
200
|
+
let raw;
|
|
201
|
+
try {
|
|
202
|
+
raw = JSON.parse(callHost("usage", ""));
|
|
203
|
+
} catch {
|
|
204
|
+
raw = undefined;
|
|
205
|
+
}
|
|
206
|
+
const read = (key) => {
|
|
207
|
+
const value = raw && typeof raw === "object" ? raw[key] : undefined;
|
|
208
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
209
|
+
};
|
|
210
|
+
// Every field is coerced, so a missing or malformed snapshot reads as zero
|
|
211
|
+
// rather than undefined: a script doing arithmetic on it gets 0, not NaN.
|
|
212
|
+
return deepFreeze({
|
|
213
|
+
input: read("input"),
|
|
214
|
+
output: read("output"),
|
|
215
|
+
cacheRead: read("cacheRead"),
|
|
216
|
+
cacheWrite: read("cacheWrite"),
|
|
217
|
+
total: read("total"),
|
|
218
|
+
cost: read("cost"),
|
|
219
|
+
agents: read("agents"),
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const argsEnvelope = JSON.parse(globalThis.__argsJson);
|
|
224
|
+
const args = argsEnvelope.defined ? deepFreeze(argsEnvelope.value) : undefined;
|
|
225
|
+
delete globalThis.__argsJson;
|
|
226
|
+
const stringify = JSON.stringify;
|
|
227
|
+
function serializeResult(value) {
|
|
228
|
+
const seen = new WeakSet();
|
|
229
|
+
return stringify(value === undefined ? null : value, (_key, item) => {
|
|
230
|
+
if (typeof item === "bigint") return item.toString() + "n";
|
|
231
|
+
if (item && typeof item === "object") {
|
|
232
|
+
if (seen.has(item)) return "[circular]";
|
|
233
|
+
seen.add(item);
|
|
234
|
+
}
|
|
235
|
+
return item;
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
Object.defineProperties(globalThis, {
|
|
239
|
+
agent: { value: requestAgent, writable: false, configurable: false },
|
|
240
|
+
parallel: { value: parallel, writable: false, configurable: false },
|
|
241
|
+
pipeline: { value: pipeline, writable: false, configurable: false },
|
|
242
|
+
phase: { value: phase, writable: false, configurable: false },
|
|
243
|
+
log: { value: log, writable: false, configurable: false },
|
|
244
|
+
usage: { value: usage, writable: false, configurable: false },
|
|
245
|
+
args: { value: args, writable: false, configurable: false },
|
|
246
|
+
__workflowCheck: {
|
|
247
|
+
value: Object.freeze(() => ({
|
|
248
|
+
unconsumed: unconsumed.size,
|
|
249
|
+
inFlight: inFlight.size,
|
|
250
|
+
})),
|
|
251
|
+
writable: false,
|
|
252
|
+
configurable: false,
|
|
253
|
+
},
|
|
254
|
+
__workflowSerialize: {
|
|
255
|
+
value: Object.freeze(serializeResult),
|
|
256
|
+
writable: false,
|
|
257
|
+
configurable: false,
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
})();
|
|
261
|
+
`;
|
|
262
|
+
|
|
263
|
+
let initialized = false;
|
|
264
|
+
let token;
|
|
265
|
+
/**
|
|
266
|
+
* Latest host usage snapshot as a JSON string, refreshed on init and on every
|
|
267
|
+
* agent result. IPC is an ordered stream, so "the last one received" is the
|
|
268
|
+
* newest and no sequence number is needed.
|
|
269
|
+
*/
|
|
270
|
+
let usageJson = "{}";
|
|
271
|
+
/** Emission cap for the narrator; see the log bridge in run(). */
|
|
272
|
+
let logCount = 0;
|
|
273
|
+
const pendingAgents = new Map();
|
|
274
|
+
|
|
275
|
+
function send(message) {
|
|
276
|
+
sendIpc?.({ token, ...message });
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function fail(error) {
|
|
280
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
281
|
+
send({ kind: "error", error: message.slice(0, 16 * 1024) });
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
process.on("message", (message) => {
|
|
285
|
+
if (!message || typeof message !== "object") return;
|
|
286
|
+
if (!initialized) {
|
|
287
|
+
if (
|
|
288
|
+
message.kind !== "init" ||
|
|
289
|
+
typeof message.token !== "string" ||
|
|
290
|
+
typeof message.source !== "string" ||
|
|
291
|
+
typeof message.argsJson !== "string" ||
|
|
292
|
+
!Number.isSafeInteger(message.maxConcurrency) ||
|
|
293
|
+
message.maxConcurrency < 1 ||
|
|
294
|
+
message.maxConcurrency > 64
|
|
295
|
+
) {
|
|
296
|
+
process.exitCode = 1;
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
initialized = true;
|
|
300
|
+
token = message.token;
|
|
301
|
+
if (typeof message.usageJson === "string") usageJson = message.usageJson;
|
|
302
|
+
run(message.source, message.argsJson, message.maxConcurrency);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (message.token !== token || message.kind !== "agentResult") return;
|
|
306
|
+
if (typeof message.usageJson === "string") usageJson = message.usageJson;
|
|
307
|
+
const pending = pendingAgents.get(message.id);
|
|
308
|
+
if (!pending) return;
|
|
309
|
+
pendingAgents.delete(message.id);
|
|
310
|
+
if (typeof message.resultJson === "string")
|
|
311
|
+
pending.resolve(message.resultJson);
|
|
312
|
+
else
|
|
313
|
+
pending.reject(
|
|
314
|
+
new Error(
|
|
315
|
+
typeof message.error === "string" ? message.error : "Agent IPC failed",
|
|
316
|
+
),
|
|
317
|
+
);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
function run(source, argsJson, maxConcurrency) {
|
|
321
|
+
try {
|
|
322
|
+
const sandbox = Object.create(null);
|
|
323
|
+
sandbox.__argsJson = argsJson;
|
|
324
|
+
sandbox.__maxConcurrency = maxConcurrency;
|
|
325
|
+
sandbox.__hostBridge = (kind, payloadJson) => {
|
|
326
|
+
if (kind === "phase") {
|
|
327
|
+
send({ kind: "phase", payloadJson });
|
|
328
|
+
return undefined;
|
|
329
|
+
}
|
|
330
|
+
if (kind === "log") {
|
|
331
|
+
// Capped in the CHILD, where the memory is. process.send queues on the
|
|
332
|
+
// IPC pipe, and a synchronous log loop never yields, so the queue grows
|
|
333
|
+
// until the 128MB heap dies with SIGABRT — taking every completed
|
|
334
|
+
// agent's output with it. Measured: 1e6 logs delivers ~2k of them and
|
|
335
|
+
// then aborts the run. The cap is ~100x the 100-line display window, so
|
|
336
|
+
// nothing a reader would have seen is lost.
|
|
337
|
+
if (++logCount > 10000) return undefined;
|
|
338
|
+
send({ kind: "log", payloadJson });
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
if (kind === "usage") return usageJson;
|
|
342
|
+
if (kind !== "agent")
|
|
343
|
+
return Promise.reject(new Error("Unknown workflow operation"));
|
|
344
|
+
let id;
|
|
345
|
+
try {
|
|
346
|
+
id = JSON.parse(payloadJson).id;
|
|
347
|
+
} catch {
|
|
348
|
+
return Promise.reject(new Error("Invalid agent request"));
|
|
349
|
+
}
|
|
350
|
+
return new Promise((resolve, reject) => {
|
|
351
|
+
pendingAgents.set(id, { resolve, reject });
|
|
352
|
+
send({ kind: "agent", payloadJson });
|
|
353
|
+
});
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const context = vm.createContext(sandbox, {
|
|
357
|
+
name: "pi-workflow",
|
|
358
|
+
codeGeneration: { strings: false, wasm: false },
|
|
359
|
+
});
|
|
360
|
+
new vm.Script(BOOTSTRAP, {
|
|
361
|
+
filename: "workflow-bootstrap.js",
|
|
362
|
+
}).runInContext(context, { timeout: 1000 });
|
|
363
|
+
const workflow = vm.compileFunction(
|
|
364
|
+
`"use strict";\nreturn (async function workflow() {\n${source}\n})();`,
|
|
365
|
+
["agent", "parallel", "pipeline", "phase", "log", "usage", "args"],
|
|
366
|
+
{ filename: "workflow-script.js", parsingContext: context },
|
|
367
|
+
);
|
|
368
|
+
context.__workflowBody = workflow;
|
|
369
|
+
const invoke = `
|
|
370
|
+
(() => {
|
|
371
|
+
const workflowBody = globalThis.__workflowBody;
|
|
372
|
+
delete globalThis.__workflowBody;
|
|
373
|
+
globalThis.__workflowPromise = Promise.resolve(
|
|
374
|
+
workflowBody(agent, parallel, pipeline, phase, log, usage, args),
|
|
375
|
+
).then(async (value) => {
|
|
376
|
+
await Promise.resolve();
|
|
377
|
+
const pending = __workflowCheck();
|
|
378
|
+
if (pending.unconsumed > 0) {
|
|
379
|
+
throw new Error("Workflow created " + pending.unconsumed + " unawaited agent() call(s)");
|
|
380
|
+
}
|
|
381
|
+
if (pending.inFlight > 0) {
|
|
382
|
+
throw new Error("Workflow returned before " + pending.inFlight + " agent call(s) settled");
|
|
383
|
+
}
|
|
384
|
+
return __workflowSerialize(value);
|
|
385
|
+
});
|
|
386
|
+
})();
|
|
387
|
+
`;
|
|
388
|
+
new vm.Script(invoke, { filename: "workflow-invoke.js" }).runInContext(
|
|
389
|
+
context,
|
|
390
|
+
{ timeout: 1000 },
|
|
391
|
+
);
|
|
392
|
+
Promise.resolve(context.__workflowPromise)
|
|
393
|
+
.then((resultJson) => {
|
|
394
|
+
if (typeof resultJson !== "string")
|
|
395
|
+
throw new Error("Workflow result was not serializable");
|
|
396
|
+
send({ kind: "result", resultJson });
|
|
397
|
+
})
|
|
398
|
+
.catch(fail);
|
|
399
|
+
} catch (error) {
|
|
400
|
+
fail(error);
|
|
401
|
+
}
|
|
402
|
+
}
|