runline 0.26.0 → 0.27.1
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/dist/core/engine.d.ts +9 -0
- package/dist/core/engine.js +34 -9
- package/dist/plugin/types.d.ts +7 -0
- package/dist/sdk.d.ts +2 -0
- package/package.json +1 -1
package/dist/core/engine.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export interface ExecuteResult {
|
|
|
8
8
|
export interface EngineOptions {
|
|
9
9
|
timeoutMs?: number;
|
|
10
10
|
memoryLimitBytes?: number;
|
|
11
|
+
/** Delivered to every action's `ctx.context` for this run. */
|
|
12
|
+
context?: unknown;
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* One resolved action call, observed at the engine's dispatch point.
|
|
@@ -44,6 +46,13 @@ export interface EngineHooks {
|
|
|
44
46
|
* ignores resourceLimits, so the watchdog is the only memory backstop.
|
|
45
47
|
*/
|
|
46
48
|
export declare function shouldArmRssWatchdog(versions?: Partial<Record<string, string>>): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Ceiling on an armed timeout: setTimeout stores its delay in an int32,
|
|
51
|
+
* so past ~24.8 days it overflows and fires at 0ms. Oversized values are
|
|
52
|
+
* rejected with an error that teaches the caller to pass 0 — the explicit
|
|
53
|
+
* "no timeout" — instead of overflowing into an instant kill.
|
|
54
|
+
*/
|
|
55
|
+
export declare const MAX_TIMEOUT_MS: number;
|
|
47
56
|
/**
|
|
48
57
|
* Executes agent code in a node:worker_threads worker.
|
|
49
58
|
*
|
package/dist/core/engine.js
CHANGED
|
@@ -48,6 +48,13 @@ function workerShape(pluginNames, helpData, memoryLimitMb) {
|
|
|
48
48
|
export function shouldArmRssWatchdog(versions = process.versions) {
|
|
49
49
|
return Boolean(versions.bun);
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Ceiling on an armed timeout: setTimeout stores its delay in an int32,
|
|
53
|
+
* so past ~24.8 days it overflows and fires at 0ms. Oversized values are
|
|
54
|
+
* rejected with an error that teaches the caller to pass 0 — the explicit
|
|
55
|
+
* "no timeout" — instead of overflowing into an instant kill.
|
|
56
|
+
*/
|
|
57
|
+
export const MAX_TIMEOUT_MS = 2 ** 31 - 1;
|
|
51
58
|
// Extra slack on top of the configured memory limit for the RSS watchdog,
|
|
52
59
|
// since whole-process RSS includes the host's own working set.
|
|
53
60
|
const RSS_WATCHDOG_SLACK_BYTES = 128 * 1024 * 1024;
|
|
@@ -149,6 +156,16 @@ export class ExecutionEngine {
|
|
|
149
156
|
const timeoutMs = options?.timeoutMs ?? this.config.timeoutMs;
|
|
150
157
|
const memoryLimitBytes = options?.memoryLimitBytes ?? this.config.memoryLimitBytes;
|
|
151
158
|
const logs = [];
|
|
159
|
+
if (!Number.isFinite(timeoutMs) ||
|
|
160
|
+
timeoutMs < 0 ||
|
|
161
|
+
timeoutMs > MAX_TIMEOUT_MS) {
|
|
162
|
+
return {
|
|
163
|
+
result: null,
|
|
164
|
+
error: `Invalid timeoutMs: ${timeoutMs}. ` +
|
|
165
|
+
`Pass 0 (no timeout) or 1..${MAX_TIMEOUT_MS} ms.`,
|
|
166
|
+
logs,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
152
169
|
const plugins = this.registry.listPlugins();
|
|
153
170
|
const pluginNames = plugins.map((p) => p.name);
|
|
154
171
|
const helpData = buildHelpData(plugins);
|
|
@@ -190,21 +207,28 @@ export class ExecutionEngine {
|
|
|
190
207
|
settled = true;
|
|
191
208
|
clearTimeout(timeoutTimer);
|
|
192
209
|
clearInterval(rssTimer);
|
|
210
|
+
worker.unref?.();
|
|
193
211
|
worker.off("message", onMessage);
|
|
194
212
|
worker.off("error", onError);
|
|
195
213
|
worker.off("exit", onExit);
|
|
196
214
|
this.release(entry, pooled, retire);
|
|
197
215
|
resolve(r);
|
|
198
216
|
};
|
|
217
|
+
// The acquire-time unref must not let the host exit mid-run: hold
|
|
218
|
+
// the loop for the run's duration, whether or not a timer is armed.
|
|
219
|
+
worker.ref?.();
|
|
199
220
|
// A spinning body blocks the worker's event loop, so it can never be
|
|
200
221
|
// asked to stop — terminate is the only remedy and the worker is gone.
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
222
|
+
// 0 means no timeout: the caller explicitly opted out of the backstop.
|
|
223
|
+
const timeoutTimer = timeoutMs === 0
|
|
224
|
+
? undefined
|
|
225
|
+
: setTimeout(() => {
|
|
226
|
+
finish({
|
|
227
|
+
result: null,
|
|
228
|
+
error: `Execution timed out after ${timeoutMs}ms`,
|
|
229
|
+
logs,
|
|
230
|
+
}, true);
|
|
231
|
+
}, timeoutMs);
|
|
208
232
|
let rssTimer;
|
|
209
233
|
if (shouldArmRssWatchdog()) {
|
|
210
234
|
const baselineRss = process.memoryUsage().rss;
|
|
@@ -243,7 +267,7 @@ export class ExecutionEngine {
|
|
|
243
267
|
logs.push(`[${msg.level}] ${msg.line}`);
|
|
244
268
|
}
|
|
245
269
|
else if (msg.t === "invoke") {
|
|
246
|
-
this.invokeAction(msg.path, msg.args).then((value) => {
|
|
270
|
+
this.invokeAction(msg.path, msg.args, options?.context).then((value) => {
|
|
247
271
|
let serialized;
|
|
248
272
|
try {
|
|
249
273
|
serialized = toPlainJson(value);
|
|
@@ -305,7 +329,7 @@ export class ExecutionEngine {
|
|
|
305
329
|
reply({ t: "run", runId, body });
|
|
306
330
|
});
|
|
307
331
|
}
|
|
308
|
-
async invokeAction(path, rawArgs) {
|
|
332
|
+
async invokeAction(path, rawArgs, context) {
|
|
309
333
|
// A call written with no arguments reaches the action as `{}`, not
|
|
310
334
|
// `undefined`, so plugins can read `input.foo` without guarding.
|
|
311
335
|
const args = normalizeActionInput(rawArgs);
|
|
@@ -317,6 +341,7 @@ export class ExecutionEngine {
|
|
|
317
341
|
const connection = this.resolveConnection(plugin);
|
|
318
342
|
const ctx = {
|
|
319
343
|
connection,
|
|
344
|
+
context,
|
|
320
345
|
log: {
|
|
321
346
|
info: (msg) => console.log(`[${plugin.name}] ${msg}`),
|
|
322
347
|
warn: (msg) => console.warn(`[${plugin.name}] ${msg}`),
|
package/dist/plugin/types.d.ts
CHANGED
|
@@ -51,6 +51,13 @@ export interface ConnectionConfig {
|
|
|
51
51
|
}
|
|
52
52
|
export interface ActionContext {
|
|
53
53
|
connection: ConnectionConfig;
|
|
54
|
+
/**
|
|
55
|
+
* Opaque per-run value from the embedder's `execute()` options.
|
|
56
|
+
* Host-supplied and invisible to worker code, so actions may
|
|
57
|
+
* derive authority from it (e.g. per-call identity on a shared
|
|
58
|
+
* engine). Absent when the embedder passed none.
|
|
59
|
+
*/
|
|
60
|
+
context?: unknown;
|
|
54
61
|
log: {
|
|
55
62
|
info(msg: string): void;
|
|
56
63
|
warn(msg: string): void;
|
package/dist/sdk.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ export interface RunlineOptions {
|
|
|
24
24
|
export interface RunlineExecuteOptions {
|
|
25
25
|
timeoutMs?: number;
|
|
26
26
|
memoryLimitBytes?: number;
|
|
27
|
+
/** Delivered to every action's `ctx.context` for this run. */
|
|
28
|
+
context?: unknown;
|
|
27
29
|
}
|
|
28
30
|
export declare class Runline {
|
|
29
31
|
private _registry;
|