@agimon-ai/po 0.0.0-alpha.32 → 0.0.0-alpha.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/catalog/index.json +67 -55
  2. package/dist/src/builders/isolate/index.cjs +17 -5
  3. package/dist/src/builders/isolate/index.cjs.map +1 -1
  4. package/dist/src/builders/isolate/index.mjs +17 -5
  5. package/dist/src/builders/isolate/index.mjs.map +1 -1
  6. package/dist/src/builders/npm/index.cjs +28 -39
  7. package/dist/src/builders/npm/index.cjs.map +1 -1
  8. package/dist/src/builders/npm/index.mjs +29 -40
  9. package/dist/src/builders/npm/index.mjs.map +1 -1
  10. package/dist/src/builders/server/nativePush.cjs +106 -0
  11. package/dist/src/builders/server/nativePush.cjs.map +1 -0
  12. package/dist/src/builders/server/nativePush.mjs +105 -0
  13. package/dist/src/builders/server/nativePush.mjs.map +1 -0
  14. package/dist/src/builders/server/runtime.cjs +21 -0
  15. package/dist/src/builders/server/runtime.cjs.map +1 -1
  16. package/dist/src/builders/server/runtime.mjs +21 -0
  17. package/dist/src/builders/server/runtime.mjs.map +1 -1
  18. package/dist/src/cli/server/processGuards.cjs +2 -2
  19. package/dist/src/cli/server/processGuards.cjs.map +1 -1
  20. package/dist/src/cli/server/processGuards.mjs +2 -2
  21. package/dist/src/cli/server/processGuards.mjs.map +1 -1
  22. package/dist/src/composition/layerPackageInstaller/index.cjs +45 -0
  23. package/dist/src/composition/layerPackageInstaller/index.cjs.map +1 -1
  24. package/dist/src/composition/layerPackageInstaller/index.mjs +45 -1
  25. package/dist/src/composition/layerPackageInstaller/index.mjs.map +1 -1
  26. package/dist/src/composition/syncService/index.cjs +22 -0
  27. package/dist/src/composition/syncService/index.cjs.map +1 -1
  28. package/dist/src/composition/syncService/index.d.mts.map +1 -1
  29. package/dist/src/composition/syncService/index.mjs +22 -1
  30. package/dist/src/composition/syncService/index.mjs.map +1 -1
  31. package/package.json +20 -20
@@ -2,7 +2,7 @@
2
2
  "version": 1,
3
3
  "packages": {
4
4
  "@agimon-ai/po": {
5
- "version": "0.0.0-alpha.32",
5
+ "version": "0.0.0-alpha.34",
6
6
  "dependencies": [
7
7
  "@agimon-ai/po-artifact",
8
8
  "@agimon-ai/po-autostop",
@@ -23,7 +23,7 @@
23
23
  ]
24
24
  },
25
25
  "@agimon-ai/po-artifact": {
26
- "version": "0.0.0-alpha.30",
26
+ "version": "0.0.0-alpha.32",
27
27
  "dependencies": [
28
28
  "@agimon-ai/po-core",
29
29
  "@agimon-ai/po-web-components",
@@ -31,7 +31,7 @@
31
31
  ]
32
32
  },
33
33
  "@agimon-ai/po-author": {
34
- "version": "0.0.0-alpha.31",
34
+ "version": "0.0.0-alpha.33",
35
35
  "dependencies": [
36
36
  "@agimon-ai/po-artifact",
37
37
  "@agimon-ai/po-core",
@@ -41,7 +41,7 @@
41
41
  ]
42
42
  },
43
43
  "@agimon-ai/po-autocompact": {
44
- "version": "0.0.0-alpha.31",
44
+ "version": "0.0.0-alpha.33",
45
45
  "dependencies": [
46
46
  "@agimon-ai/po-artifact",
47
47
  "@agimon-ai/po-config",
@@ -49,24 +49,24 @@
49
49
  ]
50
50
  },
51
51
  "@agimon-ai/po-autostop": {
52
- "version": "0.0.0-alpha.31",
52
+ "version": "0.0.0-alpha.33",
53
53
  "dependencies": [
54
54
  "@agimon-ai/po-core"
55
55
  ]
56
56
  },
57
57
  "@agimon-ai/po-browse": {
58
- "version": "0.0.1-alpha.29",
58
+ "version": "0.0.1-alpha.31",
59
59
  "dependencies": [
60
60
  "@agimon-ai/po-core",
61
61
  "@agimon-ai/po-web-components"
62
62
  ]
63
63
  },
64
64
  "@agimon-ai/po-build": {
65
- "version": "0.0.0-alpha.31",
65
+ "version": "0.0.0-alpha.33",
66
66
  "dependencies": []
67
67
  },
68
68
  "@agimon-ai/po-cache": {
69
- "version": "0.0.0-alpha.31",
69
+ "version": "0.0.0-alpha.33",
70
70
  "dependencies": [
71
71
  "@agimon-ai/po-config",
72
72
  "@agimon-ai/po-core",
@@ -74,11 +74,11 @@
74
74
  ]
75
75
  },
76
76
  "@agimon-ai/po-claude-bridge": {
77
- "version": "0.1.1-alpha.11",
77
+ "version": "0.1.1-alpha.13",
78
78
  "dependencies": []
79
79
  },
80
80
  "@agimon-ai/po-codemode": {
81
- "version": "0.0.1-alpha.4",
81
+ "version": "0.0.1-alpha.6",
82
82
  "dependencies": [
83
83
  "@agimon-ai/po-config",
84
84
  "@agimon-ai/po-core",
@@ -86,7 +86,7 @@
86
86
  ]
87
87
  },
88
88
  "@agimon-ai/po-computer-use": {
89
- "version": "0.0.0-alpha.31",
89
+ "version": "0.0.0-alpha.33",
90
90
  "dependencies": [
91
91
  "@agimon-ai/po-config",
92
92
  "@agimon-ai/po-core",
@@ -96,28 +96,36 @@
96
96
  ]
97
97
  },
98
98
  "@agimon-ai/po-config": {
99
- "version": "0.0.0-alpha.31",
99
+ "version": "0.0.0-alpha.33",
100
100
  "dependencies": [
101
101
  "@agimon-ai/po-core",
102
102
  "@agimon-ai/po-web-components"
103
103
  ]
104
104
  },
105
105
  "@agimon-ai/po-core": {
106
- "version": "0.0.0-alpha.31",
106
+ "version": "0.0.0-alpha.33",
107
107
  "dependencies": [
108
108
  "@agimon-ai/po-web-security"
109
109
  ]
110
110
  },
111
111
  "@agimon-ai/po-domain": {
112
- "version": "0.0.0-alpha.31",
112
+ "version": "0.0.0-alpha.33",
113
113
  "dependencies": [
114
114
  "@agimon-ai/po-config",
115
115
  "@agimon-ai/po-core",
116
116
  "@agimon-ai/po-minor-mode"
117
117
  ]
118
118
  },
119
+ "@agimon-ai/po-dynamic-workflow": {
120
+ "version": "0.0.1-alpha.1",
121
+ "dependencies": [
122
+ "@agimon-ai/po-core",
123
+ "@agimon-ai/po-native-components",
124
+ "@agimon-ai/po-web-components"
125
+ ]
126
+ },
119
127
  "@agimon-ai/po-edit": {
120
- "version": "0.0.0-alpha.31",
128
+ "version": "0.0.0-alpha.33",
121
129
  "dependencies": [
122
130
  "@agimon-ai/po-core",
123
131
  "@agimon-ai/po-hashline",
@@ -126,7 +134,7 @@
126
134
  ]
127
135
  },
128
136
  "@agimon-ai/po-file-edit": {
129
- "version": "0.0.0-alpha.31",
137
+ "version": "0.0.0-alpha.33",
130
138
  "dependencies": [
131
139
  "@agimon-ai/po-config",
132
140
  "@agimon-ai/po-core",
@@ -136,7 +144,7 @@
136
144
  ]
137
145
  },
138
146
  "@agimon-ai/po-git": {
139
- "version": "0.0.0-alpha.31",
147
+ "version": "0.0.0-alpha.33",
140
148
  "dependencies": [
141
149
  "@agimon-ai/po-core",
142
150
  "@agimon-ai/po-session",
@@ -145,7 +153,7 @@
145
153
  ]
146
154
  },
147
155
  "@agimon-ai/po-goal": {
148
- "version": "0.0.0-alpha.31",
156
+ "version": "0.0.0-alpha.33",
149
157
  "dependencies": [
150
158
  "@agimon-ai/po-core",
151
159
  "@agimon-ai/po-minor-mode",
@@ -154,7 +162,7 @@
154
162
  ]
155
163
  },
156
164
  "@agimon-ai/po-grep": {
157
- "version": "0.0.0-alpha.31",
165
+ "version": "0.0.0-alpha.33",
158
166
  "dependencies": [
159
167
  "@agimon-ai/po-core",
160
168
  "@agimon-ai/po-hashline",
@@ -163,11 +171,11 @@
163
171
  ]
164
172
  },
165
173
  "@agimon-ai/po-hashline": {
166
- "version": "0.0.0-alpha.31",
174
+ "version": "0.0.0-alpha.33",
167
175
  "dependencies": []
168
176
  },
169
177
  "@agimon-ai/po-help": {
170
- "version": "0.0.0-alpha.31",
178
+ "version": "0.0.0-alpha.33",
171
179
  "dependencies": [
172
180
  "@agimon-ai/po-core",
173
181
  "@agimon-ai/po-minor-mode",
@@ -175,14 +183,14 @@
175
183
  ]
176
184
  },
177
185
  "@agimon-ai/po-hook": {
178
- "version": "0.0.0-alpha.31",
186
+ "version": "0.0.0-alpha.33",
179
187
  "dependencies": [
180
188
  "@agimon-ai/po-config",
181
189
  "@agimon-ai/po-core"
182
190
  ]
183
191
  },
184
192
  "@agimon-ai/po-image-gen": {
185
- "version": "0.0.0-alpha.30",
193
+ "version": "0.0.0-alpha.32",
186
194
  "dependencies": [
187
195
  "@agimon-ai/po-core",
188
196
  "@agimon-ai/po-web-components",
@@ -190,7 +198,7 @@
190
198
  ]
191
199
  },
192
200
  "@agimon-ai/po-log": {
193
- "version": "0.0.0-alpha.31",
201
+ "version": "0.0.0-alpha.33",
194
202
  "dependencies": [
195
203
  "@agimon-ai/po-core",
196
204
  "@agimon-ai/po-web-components",
@@ -198,7 +206,7 @@
198
206
  ]
199
207
  },
200
208
  "@agimon-ai/po-loop": {
201
- "version": "0.0.0-alpha.31",
209
+ "version": "0.0.0-alpha.33",
202
210
  "dependencies": [
203
211
  "@agimon-ai/po-core",
204
212
  "@agimon-ai/po-minor-mode",
@@ -207,7 +215,7 @@
207
215
  ]
208
216
  },
209
217
  "@agimon-ai/po-major-mode": {
210
- "version": "0.0.0-alpha.31",
218
+ "version": "0.0.0-alpha.33",
211
219
  "dependencies": [
212
220
  "@agimon-ai/po-config",
213
221
  "@agimon-ai/po-core",
@@ -215,7 +223,7 @@
215
223
  ]
216
224
  },
217
225
  "@agimon-ai/po-mcp": {
218
- "version": "0.0.0-alpha.31",
226
+ "version": "0.0.0-alpha.33",
219
227
  "dependencies": [
220
228
  "@agimon-ai/po-config",
221
229
  "@agimon-ai/po-core",
@@ -225,36 +233,36 @@
225
233
  ]
226
234
  },
227
235
  "@agimon-ai/po-mcp-proxy": {
228
- "version": "0.0.1-alpha.10",
236
+ "version": "0.0.1-alpha.12",
229
237
  "dependencies": []
230
238
  },
231
239
  "@agimon-ai/po-minor-mode": {
232
- "version": "0.0.0-alpha.31",
240
+ "version": "0.0.0-alpha.33",
233
241
  "dependencies": [
234
242
  "@agimon-ai/po-core"
235
243
  ]
236
244
  },
237
245
  "@agimon-ai/po-model-guidance": {
238
- "version": "0.0.0-alpha.31",
246
+ "version": "0.0.0-alpha.33",
239
247
  "dependencies": [
240
248
  "@agimon-ai/po-config",
241
249
  "@agimon-ai/po-core"
242
250
  ]
243
251
  },
244
252
  "@agimon-ai/po-native-components": {
245
- "version": "0.0.0-alpha.31",
253
+ "version": "0.0.0-alpha.33",
246
254
  "dependencies": [
247
255
  "@agimon-ai/po-web-components"
248
256
  ]
249
257
  },
250
258
  "@agimon-ai/po-notification": {
251
- "version": "0.0.0-alpha.31",
259
+ "version": "0.0.0-alpha.33",
252
260
  "dependencies": [
253
261
  "@agimon-ai/po-core"
254
262
  ]
255
263
  },
256
264
  "@agimon-ai/po-plan": {
257
- "version": "0.0.0-alpha.31",
265
+ "version": "0.0.0-alpha.33",
258
266
  "dependencies": [
259
267
  "@agimon-ai/po-config",
260
268
  "@agimon-ai/po-core",
@@ -264,7 +272,7 @@
264
272
  ]
265
273
  },
266
274
  "@agimon-ai/po-profile": {
267
- "version": "0.0.0-alpha.31",
275
+ "version": "0.0.0-alpha.33",
268
276
  "dependencies": [
269
277
  "@agimon-ai/po-config",
270
278
  "@agimon-ai/po-core",
@@ -272,7 +280,7 @@
272
280
  ]
273
281
  },
274
282
  "@agimon-ai/po-prompt": {
275
- "version": "0.0.0-alpha.31",
283
+ "version": "0.0.0-alpha.33",
276
284
  "dependencies": [
277
285
  "@agimon-ai/po-core",
278
286
  "@agimon-ai/po-web-components",
@@ -280,7 +288,7 @@
280
288
  ]
281
289
  },
282
290
  "@agimon-ai/po-read": {
283
- "version": "0.0.0-alpha.31",
291
+ "version": "0.0.0-alpha.33",
284
292
  "dependencies": [
285
293
  "@agimon-ai/po-config",
286
294
  "@agimon-ai/po-core",
@@ -290,7 +298,7 @@
290
298
  ]
291
299
  },
292
300
  "@agimon-ai/po-runner": {
293
- "version": "0.0.0-alpha.31",
301
+ "version": "0.0.0-alpha.33",
294
302
  "dependencies": [
295
303
  "@agimon-ai/po-core",
296
304
  "@agimon-ai/po-ui",
@@ -299,19 +307,19 @@
299
307
  ]
300
308
  },
301
309
  "@agimon-ai/po-sandbox": {
302
- "version": "0.0.0-alpha.31",
310
+ "version": "0.0.0-alpha.33",
303
311
  "dependencies": [
304
312
  "@agimon-ai/po-core"
305
313
  ]
306
314
  },
307
315
  "@agimon-ai/po-session": {
308
- "version": "0.0.0-alpha.31",
316
+ "version": "0.0.0-alpha.33",
309
317
  "dependencies": [
310
318
  "@agimon-ai/po-core"
311
319
  ]
312
320
  },
313
321
  "@agimon-ai/po-skill": {
314
- "version": "0.0.0-alpha.31",
322
+ "version": "0.0.0-alpha.33",
315
323
  "dependencies": [
316
324
  "@agimon-ai/po-config",
317
325
  "@agimon-ai/po-core",
@@ -320,7 +328,7 @@
320
328
  ]
321
329
  },
322
330
  "@agimon-ai/po-style-system": {
323
- "version": "0.0.0-alpha.31",
331
+ "version": "0.0.0-alpha.33",
324
332
  "dependencies": [
325
333
  "@agimon-ai/po-core",
326
334
  "@agimon-ai/po-web-components",
@@ -328,7 +336,7 @@
328
336
  ]
329
337
  },
330
338
  "@agimon-ai/po-task": {
331
- "version": "0.0.0-alpha.31",
339
+ "version": "0.0.0-alpha.33",
332
340
  "dependencies": [
333
341
  "@agimon-ai/po-core",
334
342
  "@agimon-ai/po-ui",
@@ -336,7 +344,7 @@
336
344
  ]
337
345
  },
338
346
  "@agimon-ai/po-team": {
339
- "version": "0.0.0-alpha.31",
347
+ "version": "0.0.0-alpha.33",
340
348
  "dependencies": [
341
349
  "@agimon-ai/po-cache",
342
350
  "@agimon-ai/po-config",
@@ -348,7 +356,7 @@
348
356
  ]
349
357
  },
350
358
  "@agimon-ai/po-template-advanced": {
351
- "version": "0.0.0-alpha.31",
359
+ "version": "0.0.0-alpha.33",
352
360
  "dependencies": [
353
361
  "@agimon-ai/po-core",
354
362
  "@agimon-ai/po-web-components",
@@ -356,7 +364,7 @@
356
364
  ]
357
365
  },
358
366
  "@agimon-ai/po-template-elegant": {
359
- "version": "0.0.0-alpha.31",
367
+ "version": "0.0.0-alpha.33",
360
368
  "dependencies": [
361
369
  "@agimon-ai/po-core",
362
370
  "@agimon-ai/po-web-components",
@@ -364,7 +372,7 @@
364
372
  ]
365
373
  },
366
374
  "@agimon-ai/po-ui": {
367
- "version": "0.0.0-alpha.31",
375
+ "version": "0.0.0-alpha.33",
368
376
  "dependencies": [
369
377
  "@agimon-ai/po-core",
370
378
  "@agimon-ai/po-hashline",
@@ -373,7 +381,7 @@
373
381
  ]
374
382
  },
375
383
  "@agimon-ai/po-user-feedback": {
376
- "version": "0.0.0-alpha.31",
384
+ "version": "0.0.0-alpha.33",
377
385
  "dependencies": [
378
386
  "@agimon-ai/po-core",
379
387
  "@agimon-ai/po-minor-mode",
@@ -382,14 +390,17 @@
382
390
  ]
383
391
  },
384
392
  "@agimon-ai/po-vibe-lint": {
385
- "version": "0.0.1-alpha.0",
393
+ "version": "0.0.1-alpha.2",
386
394
  "dependencies": [
387
395
  "@agimon-ai/po-config",
388
- "@agimon-ai/po-core"
396
+ "@agimon-ai/po-core",
397
+ "@agimon-ai/po-hashline",
398
+ "@agimon-ai/po-web-components",
399
+ "@agimon-ai/po-web-security"
389
400
  ]
390
401
  },
391
402
  "@agimon-ai/po-voice": {
392
- "version": "0.0.0-alpha.31",
403
+ "version": "0.0.0-alpha.33",
393
404
  "dependencies": [
394
405
  "@agimon-ai/po-config",
395
406
  "@agimon-ai/po-core",
@@ -401,11 +412,11 @@
401
412
  ]
402
413
  },
403
414
  "@agimon-ai/po-web-components": {
404
- "version": "0.0.0-alpha.31",
415
+ "version": "0.0.0-alpha.33",
405
416
  "dependencies": []
406
417
  },
407
418
  "@agimon-ai/po-web-dashboard": {
408
- "version": "0.0.0-alpha.31",
419
+ "version": "0.0.0-alpha.33",
409
420
  "dependencies": [
410
421
  "@agimon-ai/po-core",
411
422
  "@agimon-ai/po-web-components",
@@ -413,18 +424,18 @@
413
424
  ]
414
425
  },
415
426
  "@agimon-ai/po-web-search": {
416
- "version": "0.0.0-alpha.30",
427
+ "version": "0.0.0-alpha.32",
417
428
  "dependencies": [
418
429
  "@agimon-ai/po-core",
419
430
  "@agimon-ai/po-web-components"
420
431
  ]
421
432
  },
422
433
  "@agimon-ai/po-web-security": {
423
- "version": "0.0.0-alpha.31",
434
+ "version": "0.0.0-alpha.33",
424
435
  "dependencies": []
425
436
  },
426
437
  "@agimon-ai/po-workflow": {
427
- "version": "0.0.0-alpha.31",
438
+ "version": "0.0.0-alpha.33",
428
439
  "dependencies": [
429
440
  "@agimon-ai/po-core",
430
441
  "@agimon-ai/po-minor-mode",
@@ -440,6 +451,7 @@
440
451
  "@agimon-ai/po-browse",
441
452
  "@agimon-ai/po-codemode",
442
453
  "@agimon-ai/po-computer-use",
454
+ "@agimon-ai/po-dynamic-workflow",
443
455
  "@agimon-ai/po-edit",
444
456
  "@agimon-ai/po-file-edit",
445
457
  "@agimon-ai/po-git",
@@ -4,7 +4,8 @@ const require_membrane = require("./membrane.cjs");
4
4
  let node_vm = require("node:vm");
5
5
  node_vm = require_runtime.__toESM(node_vm, 1);
6
6
  //#region src/builders/isolate/index.ts
7
- /** The sync timeout trails the guard deadline, so a guarded loop always ends through the guard. */
7
+ /** Long asynchronous runs do not get an hours-long synchronous watchdog. */
8
+ const MAX_SYNC_TIMEOUT_MS = 12e4;
8
9
  const SYNC_TIMEOUT_MARGIN_MS = 100;
9
10
  /** Guards consult the host abort check this often; the deadline itself is checked on every call. */
10
11
  const ABORT_CHECK_MASK = 1023;
@@ -59,7 +60,7 @@ function locate(stack, files) {
59
60
  function describe(error, files, aborted, deadlineMs) {
60
61
  if (error instanceof LimitReached) return error.kind === "timeout" ? failure("timeout", "TimeoutError", `Script exceeded ${deadlineMs} ms.`) : failure("aborted", "AbortError", "Script was aborted.");
61
62
  const record = typeof error === "object" && error !== null ? error : {};
62
- if (record.code === "ERR_SCRIPT_EXECUTION_TIMEOUT" || record.name === "LimitError") return aborted ? failure("aborted", "AbortError", "Script was aborted.") : failure("timeout", "TimeoutError", `Script exceeded ${deadlineMs} ms.`);
63
+ if (record.code === "ERR_SCRIPT_EXECUTION_TIMEOUT" || record.name === "LimitError") return aborted ? failure("aborted", "AbortError", "Script was aborted.") : failure("timeout", "TimeoutError", `Script exceeded ${record.code === "ERR_SCRIPT_EXECUTION_TIMEOUT" ? Math.min(deadlineMs, MAX_SYNC_TIMEOUT_MS) : deadlineMs} ms.`);
63
64
  const name = typeof record.name === "string" ? record.name : "Error";
64
65
  const message = typeof record.message === "string" ? record.message : String(error);
65
66
  const found = locate(typeof record.stack === "string" ? record.stack : "", files);
@@ -94,7 +95,16 @@ async function runScript(request) {
94
95
  });
95
96
  const deadline = Date.now() + request.deadlineMs;
96
97
  let stopped = false;
97
- const shouldStop = () => stopped || request.signal.aborted || Date.now() > deadline;
98
+ let lastYield = Date.now();
99
+ let syncTimedOut = false;
100
+ const heartbeat = setInterval(() => {
101
+ lastYield = Date.now();
102
+ }, 1e3);
103
+ heartbeat.unref();
104
+ const shouldStop = () => {
105
+ syncTimedOut ||= Date.now() - lastYield > MAX_SYNC_TIMEOUT_MS;
106
+ return stopped || syncTimedOut || request.signal.aborted || Date.now() > deadline;
107
+ };
98
108
  const modules = /* @__PURE__ */ new Map();
99
109
  const imports = /* @__PURE__ */ new Map();
100
110
  const loadImport = async (specifier) => {
@@ -148,18 +158,20 @@ async function runScript(request) {
148
158
  if (request.signal.aborted) onAbort();
149
159
  else request.signal.addEventListener("abort", onAbort, { once: true });
150
160
  });
151
- const completion = new node_vm.default.Script(main.code, { filename: SCRIPT_FILE }).runInContext(context, { timeout: request.deadlineMs + SYNC_TIMEOUT_MARGIN_MS });
161
+ const completion = new node_vm.default.Script(main.code, { filename: SCRIPT_FILE }).runInContext(context, { timeout: Math.min(request.deadlineMs, MAX_SYNC_TIMEOUT_MS) + SYNC_TIMEOUT_MARGIN_MS });
152
162
  completion.catch(() => void 0);
153
163
  const value = await Promise.race([completion, limit]);
164
+ if (shouldStop()) throw new LimitReached(request.signal.aborted ? "aborted" : "timeout");
154
165
  return {
155
166
  ok: true,
156
167
  value: membrane.exportValue(value)
157
168
  };
158
169
  } catch (error) {
159
- return describe(error, files, request.signal.aborted, request.deadlineMs);
170
+ return describe(error, files, request.signal.aborted, syncTimedOut ? MAX_SYNC_TIMEOUT_MS : request.deadlineMs);
160
171
  } finally {
161
172
  stopped = true;
162
173
  clearTimeout(timer);
174
+ clearInterval(heartbeat);
163
175
  if (onAbort) request.signal.removeEventListener("abort", onAbort);
164
176
  membrane.end();
165
177
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["GUARD_NAME","compileModule","ScriptCompileError","vm","createMembrane","IMPORT_NAME"],"sources":["../../../../src/builders/isolate/index.ts"],"sourcesContent":["import vm from 'node:vm';\n\nimport type {\n PoIsolateErrorKind,\n PoIsolateRequest,\n PoIsolateResult,\n PoIsolateService,\n} from '@agimon-ai/po-core/platform';\n\nimport { compileModule, GUARD_NAME, IMPORT_NAME, ScriptCompileError, type CompiledScript } from './compile';\nimport { createMembrane, type ScriptRealm } from './membrane';\n\n/** The sync timeout trails the guard deadline, so a guarded loop always ends through the guard. */\nconst SYNC_TIMEOUT_MARGIN_MS = 100;\n/** Guards consult the host abort check this often; the deadline itself is checked on every call. */\nconst ABORT_CHECK_MASK = 1023;\nconst SCRIPT_FILE = 'script.js';\n\n// Runs once per context before user code; captures `Date.now` so the script cannot tamper with it.\nconst PRELUDE = `(function (shouldStop, deadline) {\n 'use strict';\n const now = Date.now;\n let tripped = false;\n let calls = 0;\n class LimitError extends Error {\n constructor() { super('Execution limit reached'); this.name = 'LimitError'; }\n }\n Object.defineProperty(globalThis, '${GUARD_NAME}', {\n value: function () {\n if (tripped || now() > deadline || ((++calls & ${ABORT_CHECK_MASK}) === 0 && shouldStop())) {\n tripped = true;\n throw new LimitError();\n }\n },\n });\n})`;\n\nclass LimitReached extends Error {\n constructor(readonly kind: 'timeout' | 'aborted') {\n super(kind === 'timeout' ? 'Script exceeded its deadline.' : 'Script was aborted.');\n }\n}\n\nfunction failure(\n kind: PoIsolateErrorKind,\n name: string,\n message: string,\n extra: { line?: number; stack?: string } = {},\n): PoIsolateResult {\n return { ok: false, error: { kind, name, message, ...extra } };\n}\n\n/** A compiled body the run evaluated, by its vm filename; imported ones carry their id. */\ntype RunFiles = Map<string, { readonly id?: string; readonly compiled: CompiledScript }>;\n\n/** The innermost stack frame in one of the run's files, as a line of that file's compiled code. */\nfunction locate(stack: string, files: RunFiles) {\n for (const frame of stack.split('\\n')) {\n for (const [filename, file] of files) {\n for (const prefix of [' ', '(']) {\n const at = frame.indexOf(`${prefix}${filename}:`);\n if (at < 0) continue;\n const line = Number(/^\\d+/u.exec(frame.slice(at + prefix.length + filename.length + 1))?.[0]);\n if (line > 0) return { file, line };\n }\n }\n }\n return undefined;\n}\n\nfunction describe(error: unknown, files: RunFiles, aborted: boolean, deadlineMs: number): PoIsolateResult {\n if (error instanceof LimitReached) {\n return error.kind === 'timeout'\n ? failure('timeout', 'TimeoutError', `Script exceeded ${deadlineMs} ms.`)\n : failure('aborted', 'AbortError', 'Script was aborted.');\n }\n const record = (typeof error === 'object' && error !== null ? error : {}) as {\n name?: unknown;\n message?: unknown;\n code?: unknown;\n stack?: unknown;\n };\n if (record.code === 'ERR_SCRIPT_EXECUTION_TIMEOUT' || record.name === 'LimitError') {\n return aborted\n ? failure('aborted', 'AbortError', 'Script was aborted.')\n : failure('timeout', 'TimeoutError', `Script exceeded ${deadlineMs} ms.`);\n }\n const name = typeof record.name === 'string' ? record.name : 'Error';\n const message = typeof record.message === 'string' ? record.message : String(error);\n const stack = typeof record.stack === 'string' ? record.stack : '';\n const found = locate(stack, files);\n if (!found) return failure('error', name, message);\n const at = found.file.compiled.code.split('\\n')[found.line - 1]?.trim().slice(0, 200) ?? '';\n const line = found.file.compiled.lineOf(found.line);\n if (found.file.id === undefined) {\n return failure('error', name, at ? `${message}\\n at: ${at}` : message, line === undefined ? {} : { line });\n }\n return failure('error', name, `${message}\\n at ${found.file.id}:${line ?? '?'}: ${at}`);\n}\n\n/** Runs one script on the main thread in a fresh `vm` context with guarded code and wrapped globals. */\nexport async function runScript(request: PoIsolateRequest): Promise<PoIsolateResult> {\n const files: RunFiles = new Map();\n let main: CompiledScript;\n try {\n main = compileModule(request.source);\n files.set(SCRIPT_FILE, { compiled: main });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return failure('compile', error instanceof ScriptCompileError ? error.name : 'SyntaxError', message);\n }\n const context = vm.createContext(Object.create(null), {\n name: 'po-codemode',\n codeGeneration: { strings: false, wasm: false },\n });\n const realm = vm.runInContext('({ Error, Promise, Object, Array, Function })', context) as ScriptRealm;\n const membrane = createMembrane(realm, { deny: request.deny, onDiagnostic: request.onDiagnostic });\n const deadline = Date.now() + request.deadlineMs;\n let stopped = false;\n const shouldStop = (): boolean => stopped || request.signal.aborted || Date.now() > deadline;\n const modules = new Map<string, unknown>();\n const imports = new Map<string, Promise<unknown>>();\n // Imported bodies share this context, its guard and deadline; each id compiles once per run.\n const loadImport = async (specifier: string): Promise<unknown> => {\n if (!request.resolveImport) throw new Error('import() is not available on this host.');\n if (shouldStop()) throw new Error('The exec run ended.');\n const resolved = await request.resolveImport(String(specifier));\n if (shouldStop()) throw new Error('The exec run ended.');\n const { id } = resolved;\n if (modules.has(id)) return modules.get(id);\n if (resolved.kind === 'module') {\n modules.set(id, resolved.exports);\n return resolved.exports;\n }\n let compiled: CompiledScript;\n try {\n compiled = compileModule(resolved.source, 'import');\n } catch (error) {\n throw new SyntaxError(`${id}: ${error instanceof Error ? error.message : String(error)}`);\n }\n files.set(id, { id, compiled });\n const loaded = new vm.Script(compiled.code, { filename: id }).runInContext(context) as Record<string, unknown>;\n loaded.default = loaded;\n modules.set(id, loaded);\n return loaded;\n };\n const importScript = (specifier: unknown): Promise<unknown> => {\n const key = String(specifier);\n let pending = imports.get(key);\n if (!pending) {\n pending = loadImport(key);\n imports.set(key, pending);\n void pending.catch(() => imports.delete(key));\n }\n return pending;\n };\n let timer: ReturnType<typeof setTimeout> | undefined;\n let onAbort: (() => void) | undefined;\n try {\n (vm.runInContext(PRELUDE, context) as (stop: unknown, at: number) => void)(membrane.wrap(shouldStop), deadline);\n Object.defineProperty(context, IMPORT_NAME, { value: membrane.wrap(importScript) });\n for (const [name, value] of Object.entries(request.globals)) {\n Object.defineProperty(context, name, { value: membrane.wrap(value), enumerable: true });\n }\n const limit = new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => reject(new LimitReached('timeout')), request.deadlineMs);\n onAbort = () => reject(new LimitReached('aborted'));\n if (request.signal.aborted) onAbort();\n else request.signal.addEventListener('abort', onAbort, { once: true });\n });\n const script = new vm.Script(main.code, { filename: SCRIPT_FILE });\n const completion = script.runInContext(context, {\n timeout: request.deadlineMs + SYNC_TIMEOUT_MARGIN_MS,\n }) as Promise<unknown>;\n completion.catch(() => undefined);\n const value = await Promise.race([completion, limit]);\n return { ok: true, value: membrane.exportValue(value) };\n } catch (error) {\n return describe(error, files, request.signal.aborted, request.deadlineMs);\n } finally {\n stopped = true;\n clearTimeout(timer);\n if (onAbort) request.signal.removeEventListener('abort', onAbort);\n membrane.end();\n }\n}\n\n/** Node binding of `po/isolate`: live objects on the main thread, guarded against runaway loops. */\nexport function createNodeIsolate(): PoIsolateService {\n return { abiVersion: 1, liveObjects: true, run: runScript };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["GUARD_NAME","compileModule","ScriptCompileError","vm","createMembrane","IMPORT_NAME"],"sources":["../../../../src/builders/isolate/index.ts"],"sourcesContent":["import vm from 'node:vm';\n\nimport type {\n PoIsolateErrorKind,\n PoIsolateRequest,\n PoIsolateResult,\n PoIsolateService,\n} from '@agimon-ai/po-core/platform';\n\nimport { compileModule, GUARD_NAME, IMPORT_NAME, ScriptCompileError, type CompiledScript } from './compile';\nimport { createMembrane, type ScriptRealm } from './membrane';\n\n/** Long asynchronous runs do not get an hours-long synchronous watchdog. */\nconst MAX_SYNC_TIMEOUT_MS = 120_000;\nconst SYNC_TIMEOUT_MARGIN_MS = 100;\n/** Guards consult the host abort check this often; the deadline itself is checked on every call. */\nconst ABORT_CHECK_MASK = 1023;\nconst SCRIPT_FILE = 'script.js';\n\n// Runs once per context before user code; captures `Date.now` so the script cannot tamper with it.\nconst PRELUDE = `(function (shouldStop, deadline) {\n 'use strict';\n const now = Date.now;\n let tripped = false;\n let calls = 0;\n class LimitError extends Error {\n constructor() { super('Execution limit reached'); this.name = 'LimitError'; }\n }\n Object.defineProperty(globalThis, '${GUARD_NAME}', {\n value: function () {\n if (tripped || now() > deadline || ((++calls & ${ABORT_CHECK_MASK}) === 0 && shouldStop())) {\n tripped = true;\n throw new LimitError();\n }\n },\n });\n})`;\n\nclass LimitReached extends Error {\n constructor(readonly kind: 'timeout' | 'aborted') {\n super(kind === 'timeout' ? 'Script exceeded its deadline.' : 'Script was aborted.');\n }\n}\n\nfunction failure(\n kind: PoIsolateErrorKind,\n name: string,\n message: string,\n extra: { line?: number; stack?: string } = {},\n): PoIsolateResult {\n return { ok: false, error: { kind, name, message, ...extra } };\n}\n\n/** A compiled body the run evaluated, by its vm filename; imported ones carry their id. */\ntype RunFiles = Map<string, { readonly id?: string; readonly compiled: CompiledScript }>;\n\n/** The innermost stack frame in one of the run's files, as a line of that file's compiled code. */\nfunction locate(stack: string, files: RunFiles) {\n for (const frame of stack.split('\\n')) {\n for (const [filename, file] of files) {\n for (const prefix of [' ', '(']) {\n const at = frame.indexOf(`${prefix}${filename}:`);\n if (at < 0) continue;\n const line = Number(/^\\d+/u.exec(frame.slice(at + prefix.length + filename.length + 1))?.[0]);\n if (line > 0) return { file, line };\n }\n }\n }\n return undefined;\n}\n\nfunction describe(error: unknown, files: RunFiles, aborted: boolean, deadlineMs: number): PoIsolateResult {\n if (error instanceof LimitReached) {\n return error.kind === 'timeout'\n ? failure('timeout', 'TimeoutError', `Script exceeded ${deadlineMs} ms.`)\n : failure('aborted', 'AbortError', 'Script was aborted.');\n }\n const record = (typeof error === 'object' && error !== null ? error : {}) as {\n name?: unknown;\n message?: unknown;\n code?: unknown;\n stack?: unknown;\n };\n if (record.code === 'ERR_SCRIPT_EXECUTION_TIMEOUT' || record.name === 'LimitError') {\n return aborted\n ? failure('aborted', 'AbortError', 'Script was aborted.')\n : failure(\n 'timeout',\n 'TimeoutError',\n `Script exceeded ${record.code === 'ERR_SCRIPT_EXECUTION_TIMEOUT' ? Math.min(deadlineMs, MAX_SYNC_TIMEOUT_MS) : deadlineMs} ms.`,\n );\n }\n const name = typeof record.name === 'string' ? record.name : 'Error';\n const message = typeof record.message === 'string' ? record.message : String(error);\n const stack = typeof record.stack === 'string' ? record.stack : '';\n const found = locate(stack, files);\n if (!found) return failure('error', name, message);\n const at = found.file.compiled.code.split('\\n')[found.line - 1]?.trim().slice(0, 200) ?? '';\n const line = found.file.compiled.lineOf(found.line);\n if (found.file.id === undefined) {\n return failure('error', name, at ? `${message}\\n at: ${at}` : message, line === undefined ? {} : { line });\n }\n return failure('error', name, `${message}\\n at ${found.file.id}:${line ?? '?'}: ${at}`);\n}\n\n/** Runs one script on the main thread in a fresh `vm` context with guarded code and wrapped globals. */\nexport async function runScript(request: PoIsolateRequest): Promise<PoIsolateResult> {\n const files: RunFiles = new Map();\n let main: CompiledScript;\n try {\n main = compileModule(request.source);\n files.set(SCRIPT_FILE, { compiled: main });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return failure('compile', error instanceof ScriptCompileError ? error.name : 'SyntaxError', message);\n }\n const context = vm.createContext(Object.create(null), {\n name: 'po-codemode',\n codeGeneration: { strings: false, wasm: false },\n });\n const realm = vm.runInContext('({ Error, Promise, Object, Array, Function })', context) as ScriptRealm;\n const membrane = createMembrane(realm, { deny: request.deny, onDiagnostic: request.onDiagnostic });\n const deadline = Date.now() + request.deadlineMs;\n let stopped = false;\n let lastYield = Date.now();\n let syncTimedOut = false;\n // A guarded callback resumed after an await has the same synchronous budget as the initial body.\n const heartbeat = setInterval(() => {\n lastYield = Date.now();\n }, 1_000);\n heartbeat.unref();\n const shouldStop = (): boolean => {\n syncTimedOut ||= Date.now() - lastYield > MAX_SYNC_TIMEOUT_MS;\n return stopped || syncTimedOut || request.signal.aborted || Date.now() > deadline;\n };\n const modules = new Map<string, unknown>();\n const imports = new Map<string, Promise<unknown>>();\n // Imported bodies share this context, its guard and deadline; each id compiles once per run.\n const loadImport = async (specifier: string): Promise<unknown> => {\n if (!request.resolveImport) throw new Error('import() is not available on this host.');\n if (shouldStop()) throw new Error('The exec run ended.');\n const resolved = await request.resolveImport(String(specifier));\n if (shouldStop()) throw new Error('The exec run ended.');\n const { id } = resolved;\n if (modules.has(id)) return modules.get(id);\n if (resolved.kind === 'module') {\n modules.set(id, resolved.exports);\n return resolved.exports;\n }\n let compiled: CompiledScript;\n try {\n compiled = compileModule(resolved.source, 'import');\n } catch (error) {\n throw new SyntaxError(`${id}: ${error instanceof Error ? error.message : String(error)}`);\n }\n files.set(id, { id, compiled });\n const loaded = new vm.Script(compiled.code, { filename: id }).runInContext(context) as Record<string, unknown>;\n loaded.default = loaded;\n modules.set(id, loaded);\n return loaded;\n };\n const importScript = (specifier: unknown): Promise<unknown> => {\n const key = String(specifier);\n let pending = imports.get(key);\n if (!pending) {\n pending = loadImport(key);\n imports.set(key, pending);\n void pending.catch(() => imports.delete(key));\n }\n return pending;\n };\n let timer: ReturnType<typeof setTimeout> | undefined;\n let onAbort: (() => void) | undefined;\n try {\n (vm.runInContext(PRELUDE, context) as (stop: unknown, at: number) => void)(membrane.wrap(shouldStop), deadline);\n Object.defineProperty(context, IMPORT_NAME, { value: membrane.wrap(importScript) });\n for (const [name, value] of Object.entries(request.globals)) {\n Object.defineProperty(context, name, { value: membrane.wrap(value), enumerable: true });\n }\n const limit = new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => reject(new LimitReached('timeout')), request.deadlineMs);\n onAbort = () => reject(new LimitReached('aborted'));\n if (request.signal.aborted) onAbort();\n else request.signal.addEventListener('abort', onAbort, { once: true });\n });\n const script = new vm.Script(main.code, { filename: SCRIPT_FILE });\n const completion = script.runInContext(context, {\n timeout: Math.min(request.deadlineMs, MAX_SYNC_TIMEOUT_MS) + SYNC_TIMEOUT_MARGIN_MS,\n }) as Promise<unknown>;\n completion.catch(() => undefined);\n const value = await Promise.race([completion, limit]);\n if (shouldStop()) throw new LimitReached(request.signal.aborted ? 'aborted' : 'timeout');\n return { ok: true, value: membrane.exportValue(value) };\n } catch (error) {\n return describe(error, files, request.signal.aborted, syncTimedOut ? MAX_SYNC_TIMEOUT_MS : request.deadlineMs);\n } finally {\n stopped = true;\n clearTimeout(timer);\n clearInterval(heartbeat);\n if (onAbort) request.signal.removeEventListener('abort', onAbort);\n membrane.end();\n }\n}\n\n/** Node binding of `po/isolate`: live objects on the main thread, guarded against runaway loops. */\nexport function createNodeIsolate(): PoIsolateService {\n return { abiVersion: 1, liveObjects: true, run: runScript };\n}\n"],"mappings":"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"}
@@ -2,7 +2,8 @@ import { GUARD_NAME, IMPORT_NAME, ScriptCompileError, compileModule } from "./co
2
2
  import { createMembrane } from "./membrane.mjs";
3
3
  import vm from "node:vm";
4
4
  //#region src/builders/isolate/index.ts
5
- /** The sync timeout trails the guard deadline, so a guarded loop always ends through the guard. */
5
+ /** Long asynchronous runs do not get an hours-long synchronous watchdog. */
6
+ const MAX_SYNC_TIMEOUT_MS = 12e4;
6
7
  const SYNC_TIMEOUT_MARGIN_MS = 100;
7
8
  /** Guards consult the host abort check this often; the deadline itself is checked on every call. */
8
9
  const ABORT_CHECK_MASK = 1023;
@@ -57,7 +58,7 @@ function locate(stack, files) {
57
58
  function describe(error, files, aborted, deadlineMs) {
58
59
  if (error instanceof LimitReached) return error.kind === "timeout" ? failure("timeout", "TimeoutError", `Script exceeded ${deadlineMs} ms.`) : failure("aborted", "AbortError", "Script was aborted.");
59
60
  const record = typeof error === "object" && error !== null ? error : {};
60
- if (record.code === "ERR_SCRIPT_EXECUTION_TIMEOUT" || record.name === "LimitError") return aborted ? failure("aborted", "AbortError", "Script was aborted.") : failure("timeout", "TimeoutError", `Script exceeded ${deadlineMs} ms.`);
61
+ if (record.code === "ERR_SCRIPT_EXECUTION_TIMEOUT" || record.name === "LimitError") return aborted ? failure("aborted", "AbortError", "Script was aborted.") : failure("timeout", "TimeoutError", `Script exceeded ${record.code === "ERR_SCRIPT_EXECUTION_TIMEOUT" ? Math.min(deadlineMs, MAX_SYNC_TIMEOUT_MS) : deadlineMs} ms.`);
61
62
  const name = typeof record.name === "string" ? record.name : "Error";
62
63
  const message = typeof record.message === "string" ? record.message : String(error);
63
64
  const found = locate(typeof record.stack === "string" ? record.stack : "", files);
@@ -92,7 +93,16 @@ async function runScript(request) {
92
93
  });
93
94
  const deadline = Date.now() + request.deadlineMs;
94
95
  let stopped = false;
95
- const shouldStop = () => stopped || request.signal.aborted || Date.now() > deadline;
96
+ let lastYield = Date.now();
97
+ let syncTimedOut = false;
98
+ const heartbeat = setInterval(() => {
99
+ lastYield = Date.now();
100
+ }, 1e3);
101
+ heartbeat.unref();
102
+ const shouldStop = () => {
103
+ syncTimedOut ||= Date.now() - lastYield > MAX_SYNC_TIMEOUT_MS;
104
+ return stopped || syncTimedOut || request.signal.aborted || Date.now() > deadline;
105
+ };
96
106
  const modules = /* @__PURE__ */ new Map();
97
107
  const imports = /* @__PURE__ */ new Map();
98
108
  const loadImport = async (specifier) => {
@@ -146,18 +156,20 @@ async function runScript(request) {
146
156
  if (request.signal.aborted) onAbort();
147
157
  else request.signal.addEventListener("abort", onAbort, { once: true });
148
158
  });
149
- const completion = new vm.Script(main.code, { filename: SCRIPT_FILE }).runInContext(context, { timeout: request.deadlineMs + SYNC_TIMEOUT_MARGIN_MS });
159
+ const completion = new vm.Script(main.code, { filename: SCRIPT_FILE }).runInContext(context, { timeout: Math.min(request.deadlineMs, MAX_SYNC_TIMEOUT_MS) + SYNC_TIMEOUT_MARGIN_MS });
150
160
  completion.catch(() => void 0);
151
161
  const value = await Promise.race([completion, limit]);
162
+ if (shouldStop()) throw new LimitReached(request.signal.aborted ? "aborted" : "timeout");
152
163
  return {
153
164
  ok: true,
154
165
  value: membrane.exportValue(value)
155
166
  };
156
167
  } catch (error) {
157
- return describe(error, files, request.signal.aborted, request.deadlineMs);
168
+ return describe(error, files, request.signal.aborted, syncTimedOut ? MAX_SYNC_TIMEOUT_MS : request.deadlineMs);
158
169
  } finally {
159
170
  stopped = true;
160
171
  clearTimeout(timer);
172
+ clearInterval(heartbeat);
161
173
  if (onAbort) request.signal.removeEventListener("abort", onAbort);
162
174
  membrane.end();
163
175
  }