@draht/rlm 2026.7.30 → 2026.10.4-2

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/session.d.ts CHANGED
@@ -41,7 +41,6 @@ export declare function truncateStdout(stdout: string, limit: number): string;
41
41
  */
42
42
  export declare function pythonReprToValue(repr: string): unknown;
43
43
  export declare class RlmSession {
44
- private readonly opts;
45
44
  private readonly child;
46
45
  private readonly workdir;
47
46
  private readonly stdoutTruncateChars;
@@ -63,6 +62,7 @@ export declare class RlmSession {
63
62
  private subCallCount;
64
63
  private rssPollTimer;
65
64
  private rssPollInFlight;
65
+ private readonly opts;
66
66
  constructor(opts: RlmSessionOptions);
67
67
  private onStdoutChunk;
68
68
  private markTerminated;
@@ -135,8 +135,8 @@ export declare class RlmSession {
135
135
  * report both a network connect attempt and an out-of-workdir file read
136
136
  * failing. Throws `RlmSandboxViolationError` (fail-closed, refusing to run
137
137
  * at all) if:
138
- * - the driver never responds (crash/hang -- guarded by the same
139
- * wall-clock timeout as a normal step, via `withStepTimeout`),
138
+ * - the driver never responds (crash/hang -- guarded by the step
139
+ * wall-clock timeout, floored at `MIN_SELF_TEST_TIMEOUT_MS`),
140
140
  * - the response isn't a well-formed `self_test_result`, or
141
141
  * - either check reports the sandbox did NOT block the attempt.
142
142
  *
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AASH,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AA4GhF;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI1D;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAIpE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CASvD;AAoND,qBAAa,UAAU;IAwBV,OAAO,CAAC,QAAQ,CAAC,IAAI;IAvBjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAA6B;IACnE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IAEtC,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,gBAAgB,CAAsB;IAC9C,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,eAAe,CAAS;IAEhC,YAA6B,IAAI,EAAE,iBAAiB,EAyEnD;IAED,OAAO,CAAC,aAAa;IAqBrB,OAAO,CAAC,cAAc;IAatB;;;;;;;;;OASG;IACH,OAAO,CAAC,gBAAgB;IAKxB;;;;;;OAMG;IACH,OAAO,CAAC,eAAe;YAST,OAAO;IAoBrB;;;;;;;;;OASG;IACH,OAAO,CAAC,kBAAkB;IAW1B;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAYvB;;;;;;;;;OASG;IACH,OAAO,CAAC,eAAe;IAwBvB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,IAAI;IAOZ;;;;OAIG;YACW,OAAO;IAKrB,sHAAsH;YACxG,iBAAiB;YA4BjB,WAAW;IAOzB;;;;;;;;;;;;;;;;;;;;;OAqBG;YACW,WAAW;IAyBzB,OAAO,CAAC,YAAY;IAWpB,iFAAiF;IAC3E,IAAI,IAAI,OAAO,CAAC,eAAe,CAAC,CAyBrC;IAED,iGAAiG;IAC3F,GAAG,IAAI,OAAO,CAAC,SAAS,CAAC,CAoD9B;IAED,4GAA4G;IAC5G,OAAO,IAAI,IAAI,CAUd;CACD"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AASH,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAkHhF;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI1D;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAIpE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CASvD;AAwND,qBAAa,UAAU;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IACrD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAA6B;IACnE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IAEtC,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,gBAAgB,CAAsB;IAC9C,OAAO,CAAC,SAAS,CAAmC;IACpD,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,eAAe,CAAS;IAEhC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;IAEzC,YAAY,IAAI,EAAE,iBAAiB,EA0ElC;IAED,OAAO,CAAC,aAAa;IAqBrB,OAAO,CAAC,cAAc;IAatB;;;;;;;;;OASG;IACH,OAAO,CAAC,gBAAgB;IAKxB;;;;;;OAMG;IACH,OAAO,CAAC,eAAe;YAST,OAAO;IAoBrB;;;;;;;;;OASG;IACH,OAAO,CAAC,kBAAkB;IAW1B;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAYvB;;;;;;;;;OASG;IACH,OAAO,CAAC,eAAe;IAwBvB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,IAAI;IAOZ;;;;OAIG;YACW,OAAO;IAKrB,sHAAsH;YACxG,iBAAiB;YA4BjB,WAAW;IAOzB;;;;;;;;;;;;;;;;;;;;;OAqBG;YACW,WAAW;IA4BzB,OAAO,CAAC,YAAY;IAWpB,iFAAiF;IAC3E,IAAI,IAAI,OAAO,CAAC,eAAe,CAAC,CAyBrC;IAED,iGAAiG;IAC3F,GAAG,IAAI,OAAO,CAAC,SAAS,CAAC,CAoD9B;IAED,4GAA4G;IAC5G,OAAO,IAAI,IAAI,CAUd;CACD"}
package/dist/session.js CHANGED
@@ -25,6 +25,12 @@ const DEFAULT_STDOUT_TRUNCATE_CHARS = 2000;
25
25
  // which is explicitly a backstop, not the primary mechanism, and isn't used
26
26
  // on macOS at all per the plan).
27
27
  const DEFAULT_STEP_TIMEOUT_MS = 30_000;
28
+ /**
29
+ * The startup self-test spawns python3 and drives a 2 s socket probe, so it
30
+ * is bounded by at least this much even when a caller sets a tiny step
31
+ * budget -- otherwise a slow start would be reported as a sandbox violation.
32
+ */
33
+ const MIN_SELF_TEST_TIMEOUT_MS = 10_000;
28
34
  const DEFAULT_MAX_RSS_BYTES = 256 * 1024 * 1024;
29
35
  const DEFAULT_RSS_POLL_INTERVAL_MS = 250;
30
36
  /**
@@ -148,9 +154,10 @@ class PyReprParseError extends Error {
148
154
  }
149
155
  /** Minimal recursive-descent parser for a subset of Python literal syntax. */
150
156
  class PyReprParser {
157
+ i = 0;
158
+ src;
151
159
  constructor(src) {
152
160
  this.src = src;
153
- this.i = 0;
154
161
  }
155
162
  skipWs() {
156
163
  while (this.i < this.src.length && /\s/.test(this.src[this.i]))
@@ -362,17 +369,30 @@ class PyReprParser {
362
369
  }
363
370
  }
364
371
  export class RlmSession {
372
+ child;
373
+ workdir;
374
+ stdoutTruncateChars;
375
+ maxIterations;
376
+ stepTimeoutMs;
377
+ maxRssBytes;
378
+ rssPollIntervalMs;
379
+ maxSubCalls;
380
+ maxTotalCostUsd;
381
+ getAccumulatedCostUsd;
382
+ history = [];
383
+ ready;
384
+ buffer = "";
385
+ messageQueue = [];
386
+ waiters = [];
387
+ terminated = false;
388
+ terminationError = null;
389
+ lastFinal = null;
390
+ subCallCount = 0;
391
+ rssPollTimer;
392
+ rssPollInFlight = false;
393
+ opts;
365
394
  constructor(opts) {
366
395
  this.opts = opts;
367
- this.history = [];
368
- this.buffer = "";
369
- this.messageQueue = [];
370
- this.waiters = [];
371
- this.terminated = false;
372
- this.terminationError = null;
373
- this.lastFinal = null;
374
- this.subCallCount = 0;
375
- this.rssPollInFlight = false;
376
396
  this.stdoutTruncateChars = opts.stdoutTruncateChars ?? DEFAULT_STDOUT_TRUNCATE_CHARS;
377
397
  this.maxIterations = opts.maxIterations ?? DEFAULT_MAX_ITERATIONS;
378
398
  this.stepTimeoutMs = opts.stepTimeoutMs ?? DEFAULT_STEP_TIMEOUT_MS;
@@ -574,15 +594,15 @@ export class RlmSession {
574
594
  * `nextMessage()` wait) -- the explicit `reject` here is what actually
575
595
  * unblocks the caller in that case, not just a formality.
576
596
  */
577
- withStepTimeout(promise) {
578
- if (!(this.stepTimeoutMs > 0))
597
+ withStepTimeout(promise, timeoutMs = this.stepTimeoutMs) {
598
+ if (!(timeoutMs > 0))
579
599
  return promise;
580
600
  return new Promise((resolve, reject) => {
581
601
  const timer = setTimeout(() => {
582
- const error = new RlmStepTimeoutError(`RlmSession: step exceeded the configured wall-clock timeout of ${this.stepTimeoutMs}ms -- killed.`);
602
+ const error = new RlmStepTimeoutError(`RlmSession: step exceeded the configured wall-clock timeout of ${timeoutMs}ms -- killed.`);
583
603
  this.killAndTerminate(error);
584
604
  reject(error);
585
- }, this.stepTimeoutMs);
605
+ }, timeoutMs);
586
606
  timer.unref();
587
607
  promise.then((value) => {
588
608
  clearTimeout(timer);
@@ -658,8 +678,8 @@ export class RlmSession {
658
678
  * report both a network connect attempt and an out-of-workdir file read
659
679
  * failing. Throws `RlmSandboxViolationError` (fail-closed, refusing to run
660
680
  * at all) if:
661
- * - the driver never responds (crash/hang -- guarded by the same
662
- * wall-clock timeout as a normal step, via `withStepTimeout`),
681
+ * - the driver never responds (crash/hang -- guarded by the step
682
+ * wall-clock timeout, floored at `MIN_SELF_TEST_TIMEOUT_MS`),
663
683
  * - the response isn't a well-formed `self_test_result`, or
664
684
  * - either check reports the sandbox did NOT block the attempt.
665
685
  *
@@ -677,7 +697,7 @@ export class RlmSession {
677
697
  this.send({ type: "self_test" });
678
698
  let message;
679
699
  try {
680
- message = await this.withStepTimeout(this.nextMessage());
700
+ message = await this.withStepTimeout(this.nextMessage(), this.stepTimeoutMs > 0 ? Math.max(this.stepTimeoutMs, MIN_SELF_TEST_TIMEOUT_MS) : 0);
681
701
  }
682
702
  catch (err) {
683
703
  throw new RlmSandboxViolationError("RlmSession: OS-level sandbox startup self-test did not complete " +
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `RlmSession` — the RLM root loop.\n *\n * root-LLM-produces-code -> REPL-executes -> truncated-stdout ->\n * history-append -> FINAL-check, per\n * .planning/phases/26-rlm-core-primitives/26-01-PLAN.md, Architecture\n * section 4. Talks to `../python/repl_driver.py` over the newline-delimited\n * JSON wire protocol documented in that file's module docstring (and\n * section 2-3 of the plan).\n */\n\nimport { type ChildProcess, execFile } from \"node:child_process\";\nimport { mkdtempSync, rmSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { spawnSandboxed } from \"./sandbox.js\";\nimport type { RlmHistoryEntry, RlmResult, RlmSessionOptions } from \"./types.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst DRIVER_PATH = join(__dirname, \"..\", \"python\", \"repl_driver.py\");\n\nconst DEFAULT_MAX_ITERATIONS = 24;\nconst DEFAULT_STDOUT_TRUNCATE_CHARS = 2000;\n\n// Resource-limit defaults (Phase 28, Architecture section 3). These are the\n// REAL enforcement mechanisms -- Node-side wall-clock + RSS polling, not any\n// Python-level RLIMIT/timer (see repl_driver.py's Linux-only RLIMIT_AS,\n// which is explicitly a backstop, not the primary mechanism, and isn't used\n// on macOS at all per the plan).\nconst DEFAULT_STEP_TIMEOUT_MS = 30_000;\nconst DEFAULT_MAX_RSS_BYTES = 256 * 1024 * 1024;\nconst DEFAULT_RSS_POLL_INTERVAL_MS = 250;\n\n/**\n * Thrown internally when a step's Node-side wall-clock timeout fires and the\n * driver subprocess is hard-killed. Caught in `run()` and mapped to\n * `RlmResult.kind === \"timeout\"` -- never surfaced to callers directly.\n */\nclass RlmStepTimeoutError extends Error {}\n\n/**\n * Thrown internally when a `maxSubCalls`/`maxTotalCostUsd` pre-check fails\n * and the driver subprocess is hard-killed as a result. Caught in `run()`\n * and mapped to `RlmResult.kind === \"budget_exhausted\"` -- never surfaced to\n * callers directly.\n */\nclass RlmBudgetExhaustedError extends Error {}\n\n/**\n * Thrown internally when the OS-level sandbox's runtime startup self-test\n * (Phase 28 Architecture section 1) doesn't confirm the sandbox is actually\n * enforcing for this process -- either the driver never answered, answered\n * with something other than a well-formed `self_test_result`, or reported\n * that a network connect or an out-of-workdir file read *succeeded*. Caught\n * in `run()` and mapped to `RlmResult.kind === \"sandbox_violation\"`. This is\n * distinct from the synchronous `SandboxUnavailableError` thrown by\n * `spawnSandboxed` itself (missing wrapper binary/profile/interpreter) --\n * that one fails closed before any process exists; this one fails closed\n * after a process exists but before it's ever trusted with real\n * root-LLM-authored code.\n */\nclass RlmSandboxViolationError extends Error {}\n\n/**\n * Reads the driver subprocess's current resident-set size in bytes, or\n * `null` if it can't be determined right now (process already exited,\n * `ps`/`/proc` unavailable, a transient read race, etc.). `null` always\n * means \"skip this poll\" -- never treated as \"assume 0 bytes\" or \"assume\n * over the limit\", since either guess could produce a wrong kill/no-kill\n * decision.\n *\n * Linux: reads `/proc/<pid>/status`'s `VmRSS` line directly (no subprocess\n * spawn needed). Every other platform (macOS in practice): shells out to\n * `ps -o rss= -p <pid>` (BSD `ps` reports RSS in KiB) since there's no\n * `/proc` to read. See Phase 28 Architecture section 3.\n */\nfunction readRssBytes(pid: number): Promise<number | null> {\n\tif (process.platform === \"linux\") {\n\t\treturn readFile(`/proc/${pid}/status`, \"utf8\").then(\n\t\t\t(status) => {\n\t\t\t\tconst match = /^VmRSS:\\s*(\\d+)\\s*kB$/m.exec(status);\n\t\t\t\treturn match ? Number(match[1]) * 1024 : null;\n\t\t\t},\n\t\t\t() => null,\n\t\t);\n\t}\n\treturn new Promise((resolve) => {\n\t\texecFile(\"ps\", [\"-o\", \"rss=\", \"-p\", String(pid)], { encoding: \"utf8\" }, (err, stdout) => {\n\t\t\tif (err) {\n\t\t\t\tresolve(null);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst trimmed = stdout.trim().split(\"\\n\")[0]?.trim();\n\t\t\tif (!trimmed) {\n\t\t\t\tresolve(null);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst kb = Number(trimmed);\n\t\t\tresolve(Number.isFinite(kb) ? kb * 1024 : null);\n\t\t});\n\t});\n}\n\n/** One newline-delimited-JSON message in either direction of the wire protocol. */\ninterface DriverMessage {\n\ttype: string;\n\t[key: string]: unknown;\n}\n\n/** The `exec_result.final` payload shape (see repl_driver.py). */\ninterface DriverFinalPayload {\n\tkind: \"value\" | \"var\";\n\t/** For \"value\": `str(answer)`. For \"var\": `repr(globals_dict[name])`. */\n\tvalue: string;\n\tname?: string;\n}\n\ninterface DriverExecResult extends DriverMessage {\n\ttype: \"exec_result\";\n\tstdout: string;\n\terror: string | null;\n\tfinal: DriverFinalPayload | null;\n}\n\n/**\n * Extracts Python source from a root-LLM response.\n *\n * Per R26-RLM.4, the response is expected to contain a ```repl or ```python\n * fenced block (possibly surrounded by prose explaining the plan). Falls\n * back to the whole (trimmed) response when no fence is present, so mocked\n * `rootLlm` callbacks in tests can return raw code directly.\n */\nexport function extractPythonCode(response: string): string {\n\tconst match = /```(?:repl|python)[ \\t]*\\r?\\n([\\s\\S]*?)```/.exec(response);\n\tif (!match) return response.trim();\n\treturn match[1].replace(/\\r?\\n$/, \"\");\n}\n\n/**\n * Truncates captured stdout to `limit` characters, appending an explicit\n * marker for how many characters were cut — matching the `[truncated N\n * chars]` format R28-SBX.4 (Phase 28) will enforce as a hard cap. This phase\n * only applies it as a plain formatting rule, not a safety mechanism.\n */\nexport function truncateStdout(stdout: string, limit: number): string {\n\tif (stdout.length <= limit) return stdout;\n\tconst omitted = stdout.length - limit;\n\treturn `${stdout.slice(0, limit)}\\n[truncated ${omitted} chars]`;\n}\n\n/**\n * Restricted Python-literal parser used to turn a `FINAL_VAR`-produced\n * `repr()` string back into a real JS value.\n *\n * Handles the JSON-safe subset the plan calls out: `None`/`True`/`False`,\n * int/float, single- or double-quoted strings (with the common backslash\n * escapes), lists, tuples (mapped to arrays — JS has no tuple type), and\n * dicts (mapped to plain objects; keys are coerced with `String(...)`).\n * Anything it can't parse (custom object reprs like `<Foo object at\n * 0x...>`, sets, NaN/Infinity, etc.) falls back to the raw repr string\n * rather than throwing, so a result is never lost — see the module-level\n * note in this file's neighboring test for why raw-string fallback beats\n * failing the whole run.\n */\nexport function pythonReprToValue(repr: string): unknown {\n\ttry {\n\t\tconst parser = new PyReprParser(repr);\n\t\tconst value = parser.parseValue();\n\t\tparser.expectEnd();\n\t\treturn value;\n\t} catch {\n\t\treturn repr;\n\t}\n}\n\nclass PyReprParseError extends Error {}\n\n/** Minimal recursive-descent parser for a subset of Python literal syntax. */\nclass PyReprParser {\n\tprivate i = 0;\n\tconstructor(private readonly src: string) {}\n\n\tprivate skipWs(): void {\n\t\twhile (this.i < this.src.length && /\\s/.test(this.src[this.i])) this.i++;\n\t}\n\n\texpectEnd(): void {\n\t\tthis.skipWs();\n\t\tif (this.i !== this.src.length) {\n\t\t\tthrow new PyReprParseError(`unexpected trailing characters at ${this.i}`);\n\t\t}\n\t}\n\n\tparseValue(): unknown {\n\t\tthis.skipWs();\n\t\tconst c = this.src[this.i];\n\t\tif (c === undefined) throw new PyReprParseError(\"unexpected end of repr\");\n\t\tif (c === \"'\" || c === '\"') return this.parseString();\n\t\tif (c === \"[\") return this.parseSequence(\"[\", \"]\");\n\t\tif (c === \"(\") return this.parseSequence(\"(\", \")\");\n\t\tif (c === \"{\") return this.parseDictOrSet();\n\t\tif (this.src.startsWith(\"None\", this.i)) {\n\t\t\tthis.i += 4;\n\t\t\treturn null;\n\t\t}\n\t\tif (this.src.startsWith(\"True\", this.i)) {\n\t\t\tthis.i += 4;\n\t\t\treturn true;\n\t\t}\n\t\tif (this.src.startsWith(\"False\", this.i)) {\n\t\t\tthis.i += 5;\n\t\t\treturn false;\n\t\t}\n\t\treturn this.parseNumber();\n\t}\n\n\tprivate parseString(): string {\n\t\tconst quote = this.src[this.i];\n\t\tthis.i++;\n\t\tlet out = \"\";\n\t\twhile (this.i < this.src.length && this.src[this.i] !== quote) {\n\t\t\tconst ch = this.src[this.i];\n\t\t\tif (ch === \"\\\\\") {\n\t\t\t\tthis.i++;\n\t\t\t\tconst esc = this.src[this.i];\n\t\t\t\tswitch (esc) {\n\t\t\t\t\tcase \"n\":\n\t\t\t\t\t\tout += \"\\n\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"t\":\n\t\t\t\t\t\tout += \"\\t\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"r\":\n\t\t\t\t\t\tout += \"\\r\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"b\":\n\t\t\t\t\t\tout += \"\\b\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"f\":\n\t\t\t\t\t\tout += \"\\f\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"v\":\n\t\t\t\t\t\tout += \"\\v\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"0\":\n\t\t\t\t\t\tout += \"\\0\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"\\\\\":\n\t\t\t\t\t\tout += \"\\\\\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"'\":\n\t\t\t\t\t\tout += \"'\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '\"':\n\t\t\t\t\t\tout += '\"';\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"x\": {\n\t\t\t\t\t\tout += String.fromCharCode(Number.parseInt(this.src.slice(this.i + 1, this.i + 3), 16));\n\t\t\t\t\t\tthis.i += 2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"u\": {\n\t\t\t\t\t\tout += String.fromCharCode(Number.parseInt(this.src.slice(this.i + 1, this.i + 5), 16));\n\t\t\t\t\t\tthis.i += 4;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"U\": {\n\t\t\t\t\t\tout += String.fromCodePoint(Number.parseInt(this.src.slice(this.i + 1, this.i + 9), 16));\n\t\t\t\t\t\tthis.i += 8;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tout += esc ?? \"\";\n\t\t\t\t}\n\t\t\t\tthis.i++;\n\t\t\t} else {\n\t\t\t\tout += ch;\n\t\t\t\tthis.i++;\n\t\t\t}\n\t\t}\n\t\tif (this.src[this.i] !== quote) throw new PyReprParseError(\"unterminated string literal\");\n\t\tthis.i++;\n\t\treturn out;\n\t}\n\n\tprivate parseNumber(): number {\n\t\tconst start = this.i;\n\t\tif (this.src[this.i] === \"-\" || this.src[this.i] === \"+\") this.i++;\n\t\twhile (this.i < this.src.length && /[0-9]/.test(this.src[this.i])) this.i++;\n\t\tif (this.src[this.i] === \".\") {\n\t\t\tthis.i++;\n\t\t\twhile (this.i < this.src.length && /[0-9]/.test(this.src[this.i])) this.i++;\n\t\t}\n\t\tif (this.src[this.i] === \"e\" || this.src[this.i] === \"E\") {\n\t\t\tthis.i++;\n\t\t\tif (this.src[this.i] === \"-\" || this.src[this.i] === \"+\") this.i++;\n\t\t\twhile (this.i < this.src.length && /[0-9]/.test(this.src[this.i])) this.i++;\n\t\t}\n\t\tconst numStr = this.src.slice(start, this.i);\n\t\tif (!/^[+-]?[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?$/.test(numStr)) {\n\t\t\tthrow new PyReprParseError(`invalid number literal at ${start}`);\n\t\t}\n\t\treturn Number(numStr);\n\t}\n\n\t/** Parses `[...]` (list) or `(...)` (tuple) into a JS array. */\n\tprivate parseSequence(_open: string, close: string): unknown[] {\n\t\tthis.i++; // opening bracket\n\t\tconst out: unknown[] = [];\n\t\tthis.skipWs();\n\t\tif (this.src[this.i] === close) {\n\t\t\tthis.i++;\n\t\t\treturn out;\n\t\t}\n\t\twhile (true) {\n\t\t\tout.push(this.parseValue());\n\t\t\tthis.skipWs();\n\t\t\tif (this.src[this.i] === \",\") {\n\t\t\t\tthis.i++;\n\t\t\t\tthis.skipWs();\n\t\t\t\tif (this.src[this.i] === close) {\n\t\t\t\t\tthis.i++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (this.src[this.i] === close) {\n\t\t\t\tthis.i++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tthrow new PyReprParseError(`expected ',' or '${close}' at ${this.i}`);\n\t\t}\n\t\treturn out;\n\t}\n\n\t/** Parses `{...}` as a dict (if any `key: value` pair is found) or a set (mapped to an array). */\n\tprivate parseDictOrSet(): unknown {\n\t\tthis.i++; // {\n\t\tthis.skipWs();\n\t\tif (this.src[this.i] === \"}\") {\n\t\t\tthis.i++;\n\t\t\treturn {};\n\t\t}\n\t\tconst firstKeyOrValue = this.parseValue();\n\t\tthis.skipWs();\n\t\tif (this.src[this.i] === \":\") {\n\t\t\tthis.i++;\n\t\t\tconst out: Record<string, unknown> = {};\n\t\t\tout[String(firstKeyOrValue)] = this.parseValue();\n\t\t\tthis.skipWs();\n\t\t\twhile (this.src[this.i] === \",\") {\n\t\t\t\tthis.i++;\n\t\t\t\tthis.skipWs();\n\t\t\t\tif (this.src[this.i] === \"}\") break;\n\t\t\t\tconst key = this.parseValue();\n\t\t\t\tthis.skipWs();\n\t\t\t\tif (this.src[this.i] !== \":\") throw new PyReprParseError(`expected ':' at ${this.i}`);\n\t\t\t\tthis.i++;\n\t\t\t\tout[String(key)] = this.parseValue();\n\t\t\t\tthis.skipWs();\n\t\t\t}\n\t\t\tif (this.src[this.i] !== \"}\") throw new PyReprParseError(\"expected '}' to close dict\");\n\t\t\tthis.i++;\n\t\t\treturn out;\n\t\t}\n\t\tconst out: unknown[] = [firstKeyOrValue];\n\t\tthis.skipWs();\n\t\twhile (this.src[this.i] === \",\") {\n\t\t\tthis.i++;\n\t\t\tthis.skipWs();\n\t\t\tif (this.src[this.i] === \"}\") break;\n\t\t\tout.push(this.parseValue());\n\t\t\tthis.skipWs();\n\t\t}\n\t\tif (this.src[this.i] !== \"}\") throw new PyReprParseError(\"expected '}' to close set\");\n\t\tthis.i++;\n\t\treturn out;\n\t}\n}\n\ninterface PendingWaiter {\n\tresolve: (message: DriverMessage) => void;\n\treject: (error: Error) => void;\n}\n\nexport class RlmSession {\n\tprivate readonly child: ChildProcess;\n\tprivate readonly workdir: string;\n\tprivate readonly stdoutTruncateChars: number;\n\tprivate readonly maxIterations: number;\n\tprivate readonly stepTimeoutMs: number;\n\tprivate readonly maxRssBytes: number;\n\tprivate readonly rssPollIntervalMs: number;\n\tprivate readonly maxSubCalls: number | undefined;\n\tprivate readonly maxTotalCostUsd: number | undefined;\n\tprivate readonly getAccumulatedCostUsd: (() => number) | undefined;\n\tprivate readonly history: RlmHistoryEntry[] = [];\n\tprivate readonly ready: Promise<void>;\n\n\tprivate buffer = \"\";\n\tprivate messageQueue: DriverMessage[] = [];\n\tprivate waiters: PendingWaiter[] = [];\n\tprivate terminated = false;\n\tprivate terminationError: Error | null = null;\n\tprivate lastFinal: DriverFinalPayload | null = null;\n\tprivate subCallCount = 0;\n\tprivate rssPollTimer: NodeJS.Timeout | undefined;\n\tprivate rssPollInFlight = false;\n\n\tconstructor(private readonly opts: RlmSessionOptions) {\n\t\tthis.stdoutTruncateChars = opts.stdoutTruncateChars ?? DEFAULT_STDOUT_TRUNCATE_CHARS;\n\t\tthis.maxIterations = opts.maxIterations ?? DEFAULT_MAX_ITERATIONS;\n\t\tthis.stepTimeoutMs = opts.stepTimeoutMs ?? DEFAULT_STEP_TIMEOUT_MS;\n\t\tthis.maxRssBytes = opts.maxRssBytes ?? DEFAULT_MAX_RSS_BYTES;\n\t\tthis.rssPollIntervalMs = opts.rssPollIntervalMs ?? DEFAULT_RSS_POLL_INTERVAL_MS;\n\t\tthis.maxSubCalls = opts.maxSubCalls;\n\t\tthis.maxTotalCostUsd = opts.maxTotalCostUsd;\n\t\tthis.getAccumulatedCostUsd = opts.getAccumulatedCostUsd;\n\n\t\t// The session's scratch workdir -- the only place the OS-sandboxed\n\t\t// driver process may read *and* write freely (see sandbox.ts /\n\t\t// ../sandbox/macos.sb). Created fresh per session and cleaned up in\n\t\t// dispose(). Not caller-configurable (yet): Phase 28 scopes the\n\t\t// sandbox to this session-owned directory, not an arbitrary\n\t\t// caller-supplied path.\n\t\tthis.workdir = mkdtempSync(join(tmpdir(), \"rlm-session-\"));\n\n\t\t// spawnSandboxed wraps python3/repl_driver.py in the platform's\n\t\t// OS-level sandbox (sandbox-exec on macOS, unshare/bwrap on Linux) --\n\t\t// see sandbox.ts's module docstring for why this, and not\n\t\t// repl_driver.py's Python-level guardrails, is the real security\n\t\t// boundary. It throws SandboxUnavailableError synchronously (never\n\t\t// returns a fallback unwrapped command) if the sandbox can't be\n\t\t// established, which is exactly what should happen here: this\n\t\t// constructor must fail closed, not silently spawn the driver\n\t\t// unsandboxed.\n\t\tthis.child = spawnSandboxed({ driverPath: DRIVER_PATH, workdir: this.workdir });\n\n\t\tthis.child.stdout?.setEncoding(\"utf8\");\n\t\tthis.child.stdout?.on(\"data\", (chunk: string) => this.onStdoutChunk(chunk));\n\n\t\tthis.child.stderr?.setEncoding(\"utf8\");\n\t\tthis.child.stderr?.on(\"data\", (chunk: string) => {\n\t\t\t// Driver-side crashes (as opposed to in-REPL exceptions, which are\n\t\t\t// reported via exec_result.error) surface here. Don't throw on\n\t\t\t// stray stderr output alone -- just make it visible for debugging.\n\t\t\tconsole.error(`[rlm repl_driver stderr] ${chunk}`);\n\t\t});\n\n\t\tthis.child.once(\"exit\", (code, signal) => {\n\t\t\tthis.markTerminated(new Error(`RlmSession: python3 driver exited (code=${code}, signal=${signal})`));\n\t\t});\n\t\tthis.child.once(\"error\", (err) => {\n\t\t\tthis.markTerminated(new Error(`RlmSession: failed to spawn python3 driver: ${err.message}`));\n\t\t});\n\n\t\t// Node-side RSS polling (Phase 28 Architecture section 3) -- the real\n\t\t// memory enforcement mechanism, running for the whole lifetime of the\n\t\t// child process, independent of any single step's wall-clock timeout.\n\t\tthis.startRssPolling();\n\n\t\t// Tell the driver how aggressively to cap captured stdout *before* any\n\t\t// real step runs (see repl_driver.py's `configure` handling / Phase 28\n\t\t// Architecture section 5) -- fire-and-forget: writes to child.stdin are\n\t\t// delivered strictly in order, so this is guaranteed to be processed\n\t\t// before the `self_test`/`exec` messages sent next.\n\t\tthis.send({ type: \"configure\", stdoutTruncateChars: this.stdoutTruncateChars });\n\n\t\t// Runtime startup self-test (Phase 28 Architecture section 1) -- runs\n\t\t// before `context` is even seeded, let alone before any root-LLM-\n\t\t// authored `exec`. If the OS sandbox isn't actually enforcing for this\n\t\t// process, `runSelfTest()` throws and `seedContext()` never runs, so no\n\t\t// step ever executes against an unconfined driver. See\n\t\t// `RlmSandboxViolationError`'s doc comment for how this differs from\n\t\t// `spawnSandboxed`'s synchronous fail-closed throw.\n\t\tthis.ready = this.runSelfTest().then(() => this.seedContext());\n\t\t// Attach a no-op rejection handler so Node doesn't report this as an\n\t\t// unhandled rejection when the failure path is exercised without every\n\t\t// caller immediately awaiting `run()`/`step()` -- `await this.ready`\n\t\t// elsewhere still observes the same rejection (a promise notifies every\n\t\t// attached reaction, not just the first).\n\t\tthis.ready.catch(() => {});\n\t}\n\n\tprivate onStdoutChunk(chunk: string): void {\n\t\tthis.buffer += chunk;\n\t\tlet newlineIndex = this.buffer.indexOf(\"\\n\");\n\t\twhile (newlineIndex !== -1) {\n\t\t\tconst line = this.buffer.slice(0, newlineIndex);\n\t\t\tthis.buffer = this.buffer.slice(newlineIndex + 1);\n\t\t\tnewlineIndex = this.buffer.indexOf(\"\\n\");\n\t\t\tif (!line.trim()) continue;\n\t\t\tlet message: DriverMessage;\n\t\t\ttry {\n\t\t\t\tmessage = JSON.parse(line) as DriverMessage;\n\t\t\t} catch {\n\t\t\t\tthis.markTerminated(new Error(`RlmSession: driver emitted non-JSON output: ${line}`));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst waiter = this.waiters.shift();\n\t\t\tif (waiter) waiter.resolve(message);\n\t\t\telse this.messageQueue.push(message);\n\t\t}\n\t}\n\n\tprivate markTerminated(error: Error): void {\n\t\tif (this.terminated) return;\n\t\tthis.terminated = true;\n\t\tthis.terminationError = error;\n\t\tif (this.rssPollTimer) {\n\t\t\tclearInterval(this.rssPollTimer);\n\t\t\tthis.rssPollTimer = undefined;\n\t\t}\n\t\tconst waiters = this.waiters;\n\t\tthis.waiters = [];\n\t\tfor (const waiter of waiters) waiter.reject(error);\n\t}\n\n\t/**\n\t * Hard-kills the driver subprocess and marks the session terminated with\n\t * `error` in one step. Used by every resource-limit enforcement path (RSS\n\t * ceiling, wall-clock timeout, budget exhaustion) -- these are Node-side\n\t * hard kills, not attempts at graceful in-process recovery (Phase 28\n\t * Architecture sections 3 and 6). `markTerminated`'s first-write-wins\n\t * guard means whichever of these fires first supplies the message callers\n\t * actually see, even though the child's own `exit` handler will also fire\n\t * shortly after and try to (harmlessly, redundantly) mark terminated too.\n\t */\n\tprivate killAndTerminate(error: Error): void {\n\t\tthis.markTerminated(error);\n\t\tthis.child.kill(\"SIGKILL\");\n\t}\n\n\t/**\n\t * Starts the Node-side RSS poll loop (Phase 28 Architecture section 3) --\n\t * the real memory enforcement mechanism, not any Python-level\n\t * `RLIMIT_AS`. Runs for the child process's whole lifetime; `unref()`d so\n\t * it never by itself keeps the host Node process alive past everything\n\t * else finishing. A `maxRssBytes <= 0` disables polling entirely.\n\t */\n\tprivate startRssPolling(): void {\n\t\tif (!(this.maxRssBytes > 0)) return;\n\t\tconst timer = setInterval(() => {\n\t\t\tvoid this.pollRss();\n\t\t}, this.rssPollIntervalMs);\n\t\ttimer.unref();\n\t\tthis.rssPollTimer = timer;\n\t}\n\n\tprivate async pollRss(): Promise<void> {\n\t\tif (this.terminated || this.rssPollInFlight) return;\n\t\tconst pid = this.child.pid;\n\t\tif (!pid) return;\n\t\tthis.rssPollInFlight = true;\n\t\ttry {\n\t\t\tconst rssBytes = await readRssBytes(pid);\n\t\t\tif (rssBytes === null || rssBytes <= this.maxRssBytes) return;\n\t\t\tthis.killAndTerminate(\n\t\t\t\tnew Error(\n\t\t\t\t\t`RlmSession: python3 driver exceeded the configured RSS ceiling of ${this.maxRssBytes} bytes ` +\n\t\t\t\t\t\t`(observed ${rssBytes} bytes) -- killed. This is a resource-limit kill (Phase 28 Architecture ` +\n\t\t\t\t\t\t\"section 3), not the OS sandbox itself failing.\",\n\t\t\t\t),\n\t\t\t);\n\t\t} finally {\n\t\t\tthis.rssPollInFlight = false;\n\t\t}\n\t}\n\n\t/**\n\t * Pre-dispatch sub-call budget check (Phase 28 Architecture section 6,\n\t * R28-SBX.5): called *before* an `llm_query_request` is ever forwarded to\n\t * `opts.llmQuery`, so the (budget+1)th attempted sub-call never actually\n\t * dispatches -- the session is killed right here instead. Deliberately\n\t * NOT also checked before dispatching a whole step: a step that never\n\t * attempts another sub-call can still legitimately finish (e.g. call\n\t * FINAL immediately) even after the budget is otherwise exhausted, and\n\t * pre-emptively killing such a step here would be wrong.\n\t */\n\tprivate checkSubCallBudget(): void {\n\t\tif (this.maxSubCalls === undefined) return;\n\t\tif (this.subCallCount < this.maxSubCalls) return;\n\t\tconst error = new RlmBudgetExhaustedError(\n\t\t\t`RlmSession: llm_query sub-call budget exhausted (maxSubCalls=${this.maxSubCalls}) -- refusing to ` +\n\t\t\t\t`dispatch sub-call #${this.subCallCount + 1} and killing the driver subprocess.`,\n\t\t);\n\t\tthis.killAndTerminate(error);\n\t\tthrow error;\n\t}\n\n\t/**\n\t * Pre-dispatch cost budget check (Phase 28 Architecture section 6,\n\t * R28-SBX.5): called before dispatching a step's `rootLlm` turn and\n\t * before forwarding a sub-call to `opts.llmQuery`. No-ops unless both\n\t * `maxTotalCostUsd` and `getAccumulatedCostUsd` are supplied -- see\n\t * `RlmSessionOptions.maxTotalCostUsd`'s doc comment for why an unpaired\n\t * option is a no-op rather than a guess.\n\t */\n\tprivate checkCostBudget(): void {\n\t\tif (this.maxTotalCostUsd === undefined || !this.getAccumulatedCostUsd) return;\n\t\tconst accumulated = this.getAccumulatedCostUsd();\n\t\tif (accumulated < this.maxTotalCostUsd) return;\n\t\tconst error = new RlmBudgetExhaustedError(\n\t\t\t`RlmSession: cost budget exhausted (maxTotalCostUsd=$${this.maxTotalCostUsd}, accumulated=$${accumulated}) ` +\n\t\t\t\t\"-- killing the driver subprocess.\",\n\t\t);\n\t\tthis.killAndTerminate(error);\n\t\tthrow error;\n\t}\n\n\t/**\n\t * Races `promise` against the configured wall-clock step timeout\n\t * (Phase 28 Architecture section 3, R28-SBX.3) -- the real timeout\n\t * enforcement mechanism. On expiry, hard-kills the driver subprocess and\n\t * rejects with `RlmStepTimeoutError`, even if `promise` itself is stuck\n\t * awaiting something that `markTerminated`'s waiter-rejection can't reach\n\t * (e.g. a slow/hung `opts.llmQuery` callback, as opposed to a pending\n\t * `nextMessage()` wait) -- the explicit `reject` here is what actually\n\t * unblocks the caller in that case, not just a formality.\n\t */\n\tprivate withStepTimeout<T>(promise: Promise<T>): Promise<T> {\n\t\tif (!(this.stepTimeoutMs > 0)) return promise;\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tconst error = new RlmStepTimeoutError(\n\t\t\t\t\t`RlmSession: step exceeded the configured wall-clock timeout of ${this.stepTimeoutMs}ms -- killed.`,\n\t\t\t\t);\n\t\t\t\tthis.killAndTerminate(error);\n\t\t\t\treject(error);\n\t\t\t}, this.stepTimeoutMs);\n\t\t\ttimer.unref();\n\t\t\tpromise.then(\n\t\t\t\t(value) => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\tresolve(value);\n\t\t\t\t},\n\t\t\t\t(err) => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\treject(err);\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\t}\n\n\tprivate nextMessage(): Promise<DriverMessage> {\n\t\tconst queued = this.messageQueue.shift();\n\t\tif (queued) return Promise.resolve(queued);\n\t\tif (this.terminated) {\n\t\t\treturn Promise.reject(this.terminationError ?? new Error(\"RlmSession: driver process is no longer running\"));\n\t\t}\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.waiters.push({ resolve, reject });\n\t\t});\n\t}\n\n\tprivate send(message: Record<string, unknown>): void {\n\t\tif (this.terminated) {\n\t\t\tthrow this.terminationError ?? new Error(\"RlmSession: cannot send to a terminated driver process\");\n\t\t}\n\t\tthis.child.stdin?.write(`${JSON.stringify(message)}\\n`);\n\t}\n\n\t/**\n\t * Sends one `exec` message and handles any `llm_query_request` round-trips\n\t * until the matching `exec_result`, all under the wall-clock step timeout\n\t * (`withStepTimeout`, Phase 28 Architecture section 3).\n\t */\n\tprivate async runExec(code: string): Promise<DriverExecResult> {\n\t\tthis.send({ type: \"exec\", code });\n\t\treturn this.withStepTimeout(this.collectExecResult());\n\t}\n\n\t/** The message loop `runExec` races against the step timeout -- split out so `withStepTimeout` can wrap just this. */\n\tprivate async collectExecResult(): Promise<DriverExecResult> {\n\t\twhile (true) {\n\t\t\tconst message = await this.nextMessage();\n\t\t\tif (message.type === \"llm_query_request\") {\n\t\t\t\tconst id = message.id as string;\n\t\t\t\tif (!this.opts.llmQuery) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"RlmSession: REPL code called llm_query(...) but no `llmQuery` callback was provided in RlmSessionOptions.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// Budget pre-checks (Phase 28 Architecture section 6, R28-SBX.5) --\n\t\t\t\t// before this sub-call is dispatched, not after: either check can\n\t\t\t\t// throw (and kill the driver), in which case opts.llmQuery is never\n\t\t\t\t// called for this attempt at all.\n\t\t\t\tthis.checkSubCallBudget();\n\t\t\t\tthis.checkCostBudget();\n\t\t\t\tthis.subCallCount++;\n\t\t\t\tconst text = await this.opts.llmQuery(message.prompt as string);\n\t\t\t\tthis.send({ type: \"llm_query_response\", id, text });\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (message.type === \"exec_result\") {\n\t\t\t\treturn message as DriverExecResult;\n\t\t\t}\n\t\t\tthrow new Error(`RlmSession: unexpected message from driver: ${JSON.stringify(message)}`);\n\t\t}\n\t}\n\n\tprivate async seedContext(): Promise<void> {\n\t\t// JSON string escaping and Python double-quoted string literal escaping\n\t\t// agree on the escapes that matter here (\\\", \\\\, \\n, \\t, \\r, and\n\t\t// \\uXXXX), so JSON.stringify produces a valid Python string literal.\n\t\tawait this.runExec(`context = ${JSON.stringify(this.opts.prompt)}`);\n\t}\n\n\t/**\n\t * Runtime proof the OS-level sandbox is actually enforcing for *this*\n\t * process (Phase 28 Architecture section 1) -- sends `{\"type\":\n\t * \"self_test\"}` and requires the driver's `self_test_result` response to\n\t * report both a network connect attempt and an out-of-workdir file read\n\t * failing. Throws `RlmSandboxViolationError` (fail-closed, refusing to run\n\t * at all) if:\n\t * - the driver never responds (crash/hang -- guarded by the same\n\t * wall-clock timeout as a normal step, via `withStepTimeout`),\n\t * - the response isn't a well-formed `self_test_result`, or\n\t * - either check reports the sandbox did NOT block the attempt.\n\t *\n\t * Deliberately does not go through `runExec`/the guarded `exec()` wire\n\t * message: `repl_driver.py`'s `_run_self_test` handles `self_test`\n\t * on a route that bypasses its own Python-level guardrails (restricted\n\t * builtins, import allowlist, AST screen) entirely, because a self-test\n\t * *routed through* those guardrails would be a false positive -- e.g.\n\t * `import socket` failing on the guardrail import allowlist looks\n\t * identical to `import socket` failing because the OS sandbox denied the\n\t * connection, even with the OS sandbox completely disabled. See\n\t * `_run_self_test`'s docstring in repl_driver.py for the full reasoning.\n\t */\n\tprivate async runSelfTest(): Promise<void> {\n\t\tthis.send({ type: \"self_test\" });\n\t\tlet message: DriverMessage;\n\t\ttry {\n\t\t\tmessage = await this.withStepTimeout(this.nextMessage());\n\t\t} catch (err) {\n\t\t\tthrow new RlmSandboxViolationError(\n\t\t\t\t\"RlmSession: OS-level sandbox startup self-test did not complete \" +\n\t\t\t\t\t`(refusing to run -- fail-closed): ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t);\n\t\t}\n\t\tconst networkBlocked = message.type === \"self_test_result\" && message.networkBlocked === true;\n\t\tconst fileReadBlocked = message.type === \"self_test_result\" && message.fileReadBlocked === true;\n\t\tif (!networkBlocked || !fileReadBlocked) {\n\t\t\tconst error = new RlmSandboxViolationError(\n\t\t\t\t\"RlmSession: OS-level sandbox startup self-test failed -- the sandbox is not confining this \" +\n\t\t\t\t\t`process (networkBlocked=${networkBlocked}, fileReadBlocked=${fileReadBlocked}, ` +\n\t\t\t\t\t`response=${JSON.stringify(message)}). Refusing to run rather than executing unconfined ` +\n\t\t\t\t\t\"root-LLM-authored code (fail-closed).\",\n\t\t\t);\n\t\t\tthis.killAndTerminate(error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate resolveFinal(final: DriverFinalPayload): unknown {\n\t\tif (final.kind === \"value\") {\n\t\t\t// FINAL(answer) already sent `str(answer)` -- use as-is.\n\t\t\treturn final.value;\n\t\t}\n\t\t// FINAL_VAR(name) sent `repr(globals_dict[name])`. Parse it back into a\n\t\t// real JS value for JSON-safe types; fall back to the raw repr string\n\t\t// for anything else. See `pythonReprToValue`'s doc comment for why.\n\t\treturn pythonReprToValue(final.value);\n\t}\n\n\t/** One root-LLM turn: get code, exec it, append history, may resolve a final. */\n\tasync step(): Promise<RlmHistoryEntry> {\n\t\tawait this.ready;\n\t\t// Pre-dispatch cost budget check (Phase 28 Architecture section 6,\n\t\t// R28-SBX.5) -- before the root LLM is even asked for this step's code,\n\t\t// not just before a sub-call within it.\n\t\tthis.checkCostBudget();\n\t\tconst response = await this.opts.rootLlm(this.history.slice());\n\t\tconst code = extractPythonCode(response);\n\t\tconst result = await this.runExec(code);\n\t\tconst entry: RlmHistoryEntry = {\n\t\t\tstep: this.history.length + 1,\n\t\t\tcode,\n\t\t\t// Already capped+marked incrementally by the driver's\n\t\t\t// `_TruncatingStdout` (Phase 28 Architecture section 5) -- no\n\t\t\t// second Node-side truncation pass here (Phase 26's original\n\t\t\t// post-hoc `truncateStdout` call is exactly the OOM-via-print gap\n\t\t\t// this phase closes; re-applying it here on top of the driver's\n\t\t\t// own `[truncated N chars]` marker would risk mangling it).\n\t\t\ttruncatedStdout: result.stdout,\n\t\t\terror: result.error ?? null,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t};\n\t\tthis.history.push(entry);\n\t\tthis.lastFinal = result.final ?? null;\n\t\treturn entry;\n\t}\n\n\t/** Loops `step()` until a FINAL/FINAL_VAR, `maxIterations`, or an unrecoverable driver error. */\n\tasync run(): Promise<RlmResult> {\n\t\ttry {\n\t\t\twhile (this.history.length < this.maxIterations) {\n\t\t\t\tawait this.step();\n\t\t\t\tif (this.lastFinal) {\n\t\t\t\t\tconst final = this.lastFinal;\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkind: final.kind === \"var\" ? \"final_var\" : \"final\",\n\t\t\t\t\t\tvalue: this.resolveFinal(final),\n\t\t\t\t\t\tsteps: this.history.length,\n\t\t\t\t\t\thistory: this.history.slice(),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { kind: \"max_iterations\", steps: this.history.length, history: this.history.slice() };\n\t\t} catch (err) {\n\t\t\t// Typed stop reasons (Phase 28 Architecture section 6, R28-SBX.6):\n\t\t\t// a hard-killed step surfaces here as one of these internal error\n\t\t\t// classes; anything else (a genuine driver crash, a step\n\t\t\t// referencing llm_query with no callback configured, etc.) falls\n\t\t\t// through to the generic \"error\" kind, same as Phase 26.\n\t\t\tif (err instanceof RlmStepTimeoutError) {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"timeout\",\n\t\t\t\t\tvalue: err.message,\n\t\t\t\t\tsteps: this.history.length,\n\t\t\t\t\thistory: this.history.slice(),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (err instanceof RlmBudgetExhaustedError) {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"budget_exhausted\",\n\t\t\t\t\tvalue: err.message,\n\t\t\t\t\tsteps: this.history.length,\n\t\t\t\t\thistory: this.history.slice(),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (err instanceof RlmSandboxViolationError) {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"sandbox_violation\",\n\t\t\t\t\tvalue: err.message,\n\t\t\t\t\tsteps: this.history.length,\n\t\t\t\t\thistory: this.history.slice(),\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tkind: \"error\",\n\t\t\t\tvalue: err instanceof Error ? err.message : String(err),\n\t\t\t\tsteps: this.history.length,\n\t\t\t\thistory: this.history.slice(),\n\t\t\t};\n\t\t}\n\t}\n\n\t/** Kills the underlying python3 subprocess and removes its scratch workdir. Safe to call more than once. */\n\tdispose(): void {\n\t\tthis.markTerminated(new Error(\"RlmSession: disposed\"));\n\t\tthis.child.stdin?.end();\n\t\tthis.child.kill();\n\t\ttry {\n\t\t\trmSync(this.workdir, { recursive: true, force: true });\n\t\t} catch {\n\t\t\t// Best-effort cleanup only -- a leftover empty-ish tmp dir isn't\n\t\t\t// worth failing dispose() over.\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAqB,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG9C,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC;AAEtE,MAAM,sBAAsB,GAAG,EAAE,CAAC;AAClC,MAAM,6BAA6B,GAAG,IAAI,CAAC;AAE3C,4EAA4E;AAC5E,6EAA6E;AAC7E,wEAAwE;AACxE,4EAA4E;AAC5E,iCAAiC;AACjC,MAAM,uBAAuB,GAAG,MAAM,CAAC;AACvC;;;;GAIG;AACH,MAAM,wBAAwB,GAAG,MAAM,CAAC;AACxC,MAAM,qBAAqB,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;AAChD,MAAM,4BAA4B,GAAG,GAAG,CAAC;AAEzC;;;;GAIG;AACH,MAAM,mBAAoB,SAAQ,KAAK;CAAG;AAE1C;;;;;GAKG;AACH,MAAM,uBAAwB,SAAQ,KAAK;CAAG;AAE9C;;;;;;;;;;;;GAYG;AACH,MAAM,wBAAyB,SAAQ,KAAK;CAAG;AAE/C;;;;;;;;;;;;GAYG;AACH,SAAS,YAAY,CAAC,GAAW;IAChC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAClC,OAAO,QAAQ,CAAC,SAAS,GAAG,SAAS,EAAE,MAAM,CAAC,CAAC,IAAI,CAClD,CAAC,MAAM,EAAE,EAAE;YACV,MAAM,KAAK,GAAG,wBAAwB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACpD,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/C,CAAC,EACD,GAAG,EAAE,CAAC,IAAI,CACV,CAAC;IACH,CAAC;IACD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9B,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE;YACvF,IAAI,GAAG,EAAE,CAAC;gBACT,OAAO,CAAC,IAAI,CAAC,CAAC;gBACd,OAAO;YACR,CAAC;YACD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;YACrD,IAAI,CAAC,OAAO,EAAE,CAAC;gBACd,OAAO,CAAC,IAAI,CAAC,CAAC;gBACd,OAAO;YACR,CAAC;YACD,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;YAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC;AAuBD;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IACjD,MAAM,KAAK,GAAG,4CAA4C,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1E,IAAI,CAAC,KAAK;QAAE,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;IACnC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AACvC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,KAAa;IAC3D,IAAI,MAAM,CAAC,MAAM,IAAI,KAAK;QAAE,OAAO,MAAM,CAAC;IAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC;IACtC,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,gBAAgB,OAAO,SAAS,CAAC;AAClE,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC7C,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QAClC,MAAM,CAAC,SAAS,EAAE,CAAC;QACnB,OAAO,KAAK,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,MAAM,gBAAiB,SAAQ,KAAK;CAAG;AAEvC,8EAA8E;AAC9E,MAAM,YAAY;IACT,CAAC,GAAG,CAAC,CAAC;IACG,GAAG,CAAS;IAE7B,YAAY,GAAW;QACtB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IAChB,CAAC;IAEO,MAAM;QACb,OAAO,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAAE,IAAI,CAAC,CAAC,EAAE,CAAC;IAC1E,CAAC;IAED,SAAS;QACR,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,IAAI,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;YAChC,MAAM,IAAI,gBAAgB,CAAC,qCAAqC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3E,CAAC;IACF,CAAC;IAED,UAAU;QACT,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3B,IAAI,CAAC,KAAK,SAAS;YAAE,MAAM,IAAI,gBAAgB,CAAC,wBAAwB,CAAC,CAAC;QAC1E,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;QACtD,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACnD,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACnD,IAAI,CAAC,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC,cAAc,EAAE,CAAC;QAC5C,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACzC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;YACZ,OAAO,IAAI,CAAC;QACb,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACzC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;YACZ,OAAO,IAAI,CAAC;QACb,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1C,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;YACZ,OAAO,KAAK,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;IAC3B,CAAC;IAEO,WAAW;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,CAAC,CAAC,EAAE,CAAC;QACT,IAAI,GAAG,GAAG,EAAE,CAAC;QACb,OAAO,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC;YAC/D,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC5B,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;gBACjB,IAAI,CAAC,CAAC,EAAE,CAAC;gBACT,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAC7B,QAAQ,GAAG,EAAE,CAAC;oBACb,KAAK,GAAG;wBACP,GAAG,IAAI,IAAI,CAAC;wBACZ,MAAM;oBACP,KAAK,GAAG;wBACP,GAAG,IAAI,IAAI,CAAC;wBACZ,MAAM;oBACP,KAAK,GAAG;wBACP,GAAG,IAAI,IAAI,CAAC;wBACZ,MAAM;oBACP,KAAK,GAAG;wBACP,GAAG,IAAI,IAAI,CAAC;wBACZ,MAAM;oBACP,KAAK,GAAG;wBACP,GAAG,IAAI,IAAI,CAAC;wBACZ,MAAM;oBACP,KAAK,GAAG;wBACP,GAAG,IAAI,IAAI,CAAC;wBACZ,MAAM;oBACP,KAAK,GAAG;wBACP,GAAG,IAAI,IAAI,CAAC;wBACZ,MAAM;oBACP,KAAK,IAAI;wBACR,GAAG,IAAI,IAAI,CAAC;wBACZ,MAAM;oBACP,KAAK,GAAG;wBACP,GAAG,IAAI,GAAG,CAAC;wBACX,MAAM;oBACP,KAAK,GAAG;wBACP,GAAG,IAAI,GAAG,CAAC;wBACX,MAAM;oBACP,KAAK,GAAG,EAAE,CAAC;wBACV,GAAG,IAAI,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;wBACxF,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;wBACZ,MAAM;oBACP,CAAC;oBACD,KAAK,GAAG,EAAE,CAAC;wBACV,GAAG,IAAI,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;wBACxF,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;wBACZ,MAAM;oBACP,CAAC;oBACD,KAAK,GAAG,EAAE,CAAC;wBACV,GAAG,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;wBACzF,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;wBACZ,MAAM;oBACP,CAAC;oBACD;wBACC,GAAG,IAAI,GAAG,IAAI,EAAE,CAAC;gBACnB,CAAC;gBACD,IAAI,CAAC,CAAC,EAAE,CAAC;YACV,CAAC;iBAAM,CAAC;gBACP,GAAG,IAAI,EAAE,CAAC;gBACV,IAAI,CAAC,CAAC,EAAE,CAAC;YACV,CAAC;QACF,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK;YAAE,MAAM,IAAI,gBAAgB,CAAC,6BAA6B,CAAC,CAAC;QAC1F,IAAI,CAAC,CAAC,EAAE,CAAC;QACT,OAAO,GAAG,CAAC;IACZ,CAAC;IAEO,WAAW;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC;QACrB,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;YAAE,IAAI,CAAC,CAAC,EAAE,CAAC;QACnE,OAAO,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAAE,IAAI,CAAC,CAAC,EAAE,CAAC;QAC5E,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YAC9B,IAAI,CAAC,CAAC,EAAE,CAAC;YACT,OAAO,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAAE,IAAI,CAAC,CAAC,EAAE,CAAC;QAC7E,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YAC1D,IAAI,CAAC,CAAC,EAAE,CAAC;YACT,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;gBAAE,IAAI,CAAC,CAAC,EAAE,CAAC;YACnE,OAAO,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAAE,IAAI,CAAC,CAAC,EAAE,CAAC;QAC7E,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,4CAA4C,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YAChE,MAAM,IAAI,gBAAgB,CAAC,6BAA6B,KAAK,EAAE,CAAC,CAAC;QAClE,CAAC;QACD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;IACvB,CAAC;IAED,gEAAgE;IACxD,aAAa,CAAC,KAAa,EAAE,KAAa;QACjD,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,kBAAkB;QAC5B,MAAM,GAAG,GAAc,EAAE,CAAC;QAC1B,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC;YAChC,IAAI,CAAC,CAAC,EAAE,CAAC;YACT,OAAO,GAAG,CAAC;QACZ,CAAC;QACD,OAAO,IAAI,EAAE,CAAC;YACb,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;YAC5B,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBAC9B,IAAI,CAAC,CAAC,EAAE,CAAC;gBACT,IAAI,CAAC,MAAM,EAAE,CAAC;gBACd,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC;oBAChC,IAAI,CAAC,CAAC,EAAE,CAAC;oBACT,MAAM;gBACP,CAAC;gBACD,SAAS;YACV,CAAC;YACD,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC;gBAChC,IAAI,CAAC,CAAC,EAAE,CAAC;gBACT,MAAM;YACP,CAAC;YACD,MAAM,IAAI,gBAAgB,CAAC,oBAAoB,KAAK,QAAQ,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;QACvE,CAAC;QACD,OAAO,GAAG,CAAC;IACZ,CAAC;IAED,kGAAkG;IAC1F,cAAc;QACrB,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI;QACd,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YAC9B,IAAI,CAAC,CAAC,EAAE,CAAC;YACT,OAAO,EAAE,CAAC;QACX,CAAC;QACD,MAAM,eAAe,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QAC1C,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YAC9B,IAAI,CAAC,CAAC,EAAE,CAAC;YACT,MAAM,GAAG,GAA4B,EAAE,CAAC;YACxC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YACjD,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBACjC,IAAI,CAAC,CAAC,EAAE,CAAC;gBACT,IAAI,CAAC,MAAM,EAAE,CAAC;gBACd,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;oBAAE,MAAM;gBACpC,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;gBAC9B,IAAI,CAAC,MAAM,EAAE,CAAC;gBACd,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;oBAAE,MAAM,IAAI,gBAAgB,CAAC,mBAAmB,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;gBACtF,IAAI,CAAC,CAAC,EAAE,CAAC;gBACT,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;gBACrC,IAAI,CAAC,MAAM,EAAE,CAAC;YACf,CAAC;YACD,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;gBAAE,MAAM,IAAI,gBAAgB,CAAC,4BAA4B,CAAC,CAAC;YACvF,IAAI,CAAC,CAAC,EAAE,CAAC;YACT,OAAO,GAAG,CAAC;QACZ,CAAC;QACD,MAAM,GAAG,GAAc,CAAC,eAAe,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACjC,IAAI,CAAC,CAAC,EAAE,CAAC;YACT,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;gBAAE,MAAM;YACpC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;YAC5B,IAAI,CAAC,MAAM,EAAE,CAAC;QACf,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;YAAE,MAAM,IAAI,gBAAgB,CAAC,2BAA2B,CAAC,CAAC;QACtF,IAAI,CAAC,CAAC,EAAE,CAAC;QACT,OAAO,GAAG,CAAC;IACZ,CAAC;CACD;AAOD,MAAM,OAAO,UAAU;IACL,KAAK,CAAe;IACpB,OAAO,CAAS;IAChB,mBAAmB,CAAS;IAC5B,aAAa,CAAS;IACtB,aAAa,CAAS;IACtB,WAAW,CAAS;IACpB,iBAAiB,CAAS;IAC1B,WAAW,CAAqB;IAChC,eAAe,CAAqB;IACpC,qBAAqB,CAA6B;IAClD,OAAO,GAAsB,EAAE,CAAC;IAChC,KAAK,CAAgB;IAE9B,MAAM,GAAG,EAAE,CAAC;IACZ,YAAY,GAAoB,EAAE,CAAC;IACnC,OAAO,GAAoB,EAAE,CAAC;IAC9B,UAAU,GAAG,KAAK,CAAC;IACnB,gBAAgB,GAAiB,IAAI,CAAC;IACtC,SAAS,GAA8B,IAAI,CAAC;IAC5C,YAAY,GAAG,CAAC,CAAC;IACjB,YAAY,CAA6B;IACzC,eAAe,GAAG,KAAK,CAAC;IAEf,IAAI,CAAoB;IAEzC,YAAY,IAAuB;QAClC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,mBAAmB,IAAI,6BAA6B,CAAC;QACrF,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,sBAAsB,CAAC;QAClE,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,uBAAuB,CAAC;QACnE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,qBAAqB,CAAC;QAC7D,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,IAAI,4BAA4B,CAAC;QAChF,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;QACpC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC;QAC5C,IAAI,CAAC,qBAAqB,GAAG,IAAI,CAAC,qBAAqB,CAAC;QAExD,mEAAmE;QACnE,+DAA+D;QAC/D,oEAAoE;QACpE,gEAAgE;QAChE,4DAA4D;QAC5D,wBAAwB;QACxB,IAAI,CAAC,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC;QAE3D,gEAAgE;QAChE,sEAAsE;QACtE,0DAA0D;QAC1D,iEAAiE;QACjE,mEAAmE;QACnE,gEAAgE;QAChE,8DAA8D;QAC9D,8DAA8D;QAC9D,eAAe;QACf,IAAI,CAAC,KAAK,GAAG,cAAc,CAAC,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QAEhF,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;QAE5E,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YAC/C,mEAAmE;YACnE,+DAA+D;YAC/D,mEAAmE;YACnE,OAAO,CAAC,KAAK,CAAC,4BAA4B,KAAK,EAAE,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;YACxC,IAAI,CAAC,cAAc,CAAC,IAAI,KAAK,CAAC,2CAA2C,IAAI,YAAY,MAAM,GAAG,CAAC,CAAC,CAAC;QACtG,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YAChC,IAAI,CAAC,cAAc,CAAC,IAAI,KAAK,CAAC,+CAA+C,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC9F,CAAC,CAAC,CAAC;QAEH,sEAAsE;QACtE,sEAAsE;QACtE,sEAAsE;QACtE,IAAI,CAAC,eAAe,EAAE,CAAC;QAEvB,uEAAuE;QACvE,uEAAuE;QACvE,wEAAwE;QACxE,qEAAqE;QACrE,oDAAoD;QACpD,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC;QAEhF,sEAAsE;QACtE,kEAAkE;QAClE,uEAAuE;QACvE,wEAAwE;QACxE,uDAAuD;QACvD,qEAAqE;QACrE,oDAAoD;QACpD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QAC/D,qEAAqE;QACrE,uEAAuE;QACvE,qEAAqE;QACrE,wEAAwE;QACxE,0CAA0C;QAC1C,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC5B,CAAC;IAEO,aAAa,CAAC,KAAa;QAClC,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC;QACrB,IAAI,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7C,OAAO,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;YAChD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;YAClD,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;gBAAE,SAAS;YAC3B,IAAI,OAAsB,CAAC;YAC3B,IAAI,CAAC;gBACJ,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAkB,CAAC;YAC7C,CAAC;YAAC,MAAM,CAAC;gBACR,IAAI,CAAC,cAAc,CAAC,IAAI,KAAK,CAAC,+CAA+C,IAAI,EAAE,CAAC,CAAC,CAAC;gBACtF,SAAS;YACV,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACpC,IAAI,MAAM;gBAAE,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;;gBAC/B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtC,CAAC;IACF,CAAC;IAEO,cAAc,CAAC,KAAY;QAClC,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO;QAC5B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;QAC9B,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACjC,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;QAC/B,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QAClB,KAAK,MAAM,MAAM,IAAI,OAAO;YAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACpD,CAAC;IAED;;;;;;;;;OASG;IACK,gBAAgB,CAAC,KAAY;QACpC,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC3B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC5B,CAAC;IAED;;;;;;OAMG;IACK,eAAe;QACtB,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;YAAE,OAAO;QACpC,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;YAC9B,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;QACrB,CAAC,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC3B,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;IAC3B,CAAC;IAEO,KAAK,CAAC,OAAO;QACpB,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,eAAe;YAAE,OAAO;QACpD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;QAC3B,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC5B,IAAI,CAAC;YACJ,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,CAAC;YACzC,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,IAAI,IAAI,CAAC,WAAW;gBAAE,OAAO;YAC9D,IAAI,CAAC,gBAAgB,CACpB,IAAI,KAAK,CACR,qEAAqE,IAAI,CAAC,WAAW,SAAS;gBAC7F,aAAa,QAAQ,0EAA0E;gBAC/F,gDAAgD,CACjD,CACD,CAAC;QACH,CAAC;gBAAS,CAAC;YACV,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC;QAC9B,CAAC;IACF,CAAC;IAED;;;;;;;;;OASG;IACK,kBAAkB;QACzB,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS;YAAE,OAAO;QAC3C,IAAI,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,WAAW;YAAE,OAAO;QACjD,MAAM,KAAK,GAAG,IAAI,uBAAuB,CACxC,gEAAgE,IAAI,CAAC,WAAW,mBAAmB;YAClG,sBAAsB,IAAI,CAAC,YAAY,GAAG,CAAC,qCAAqC,CACjF,CAAC;QACF,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAC7B,MAAM,KAAK,CAAC;IACb,CAAC;IAED;;;;;;;OAOG;IACK,eAAe;QACtB,IAAI,IAAI,CAAC,eAAe,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,qBAAqB;YAAE,OAAO;QAC9E,MAAM,WAAW,GAAG,IAAI,CAAC,qBAAqB,EAAE,CAAC;QACjD,IAAI,WAAW,GAAG,IAAI,CAAC,eAAe;YAAE,OAAO;QAC/C,MAAM,KAAK,GAAG,IAAI,uBAAuB,CACxC,uDAAuD,IAAI,CAAC,eAAe,kBAAkB,WAAW,IAAI;YAC3G,mCAAmC,CACpC,CAAC;QACF,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAC7B,MAAM,KAAK,CAAC;IACb,CAAC;IAED;;;;;;;;;OASG;IACK,eAAe,CAAI,OAAmB,EAAE,SAAS,GAAG,IAAI,CAAC,aAAa;QAC7E,IAAI,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC;YAAE,OAAO,OAAO,CAAC;QACrC,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC7B,MAAM,KAAK,GAAG,IAAI,mBAAmB,CACpC,kEAAkE,SAAS,eAAe,CAC1F,CAAC;gBACF,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;gBAC7B,MAAM,CAAC,KAAK,CAAC,CAAC;YACf,CAAC,EAAE,SAAS,CAAC,CAAC;YACd,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,OAAO,CAAC,IAAI,CACX,CAAC,KAAK,EAAE,EAAE;gBACT,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,OAAO,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC,EACD,CAAC,GAAG,EAAE,EAAE;gBACP,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,MAAM,CAAC,GAAG,CAAC,CAAC;YACb,CAAC,CACD,CAAC;QACH,CAAC,CAAC,CAAC;IACJ,CAAC;IAEO,WAAW;QAClB,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QACzC,IAAI,MAAM;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC3C,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,IAAI,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC,CAAC;QAC9G,CAAC;QACD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACtC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;IACJ,CAAC;IAEO,IAAI,CAAC,OAAgC;QAC5C,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,gBAAgB,IAAI,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;QACpG,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,OAAO,CAAC,IAAY;QACjC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAClC,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,sHAAsH;IAC9G,KAAK,CAAC,iBAAiB;QAC9B,OAAO,IAAI,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC;YACzC,IAAI,OAAO,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;gBAC1C,MAAM,EAAE,GAAG,OAAO,CAAC,EAAY,CAAC;gBAChC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACzB,MAAM,IAAI,KAAK,CACd,2GAA2G,CAC3G,CAAC;gBACH,CAAC;gBACD,oEAAoE;gBACpE,kEAAkE;gBAClE,oEAAoE;gBACpE,kCAAkC;gBAClC,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBAC1B,IAAI,CAAC,eAAe,EAAE,CAAC;gBACvB,IAAI,CAAC,YAAY,EAAE,CAAC;gBACpB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAgB,CAAC,CAAC;gBAChE,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;gBACpD,SAAS;YACV,CAAC;YACD,IAAI,OAAO,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;gBACpC,OAAO,OAA2B,CAAC;YACpC,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,+CAA+C,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC3F,CAAC;IACF,CAAC;IAEO,KAAK,CAAC,WAAW;QACxB,wEAAwE;QACxE,iEAAiE;QACjE,qEAAqE;QACrE,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACrE,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACK,KAAK,CAAC,WAAW;QACxB,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;QACjC,IAAI,OAAsB,CAAC;QAC3B,IAAI,CAAC;YACJ,OAAO,GAAG,MAAM,IAAI,CAAC,eAAe,CACnC,IAAI,CAAC,WAAW,EAAE,EAClB,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,EAAE,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAAC,CACnF,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,IAAI,wBAAwB,CACjC,kEAAkE;gBACjE,qCAAqC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACxF,CAAC;QACH,CAAC;QACD,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,KAAK,kBAAkB,IAAI,OAAO,CAAC,cAAc,KAAK,IAAI,CAAC;QAC9F,MAAM,eAAe,GAAG,OAAO,CAAC,IAAI,KAAK,kBAAkB,IAAI,OAAO,CAAC,eAAe,KAAK,IAAI,CAAC;QAChG,IAAI,CAAC,cAAc,IAAI,CAAC,eAAe,EAAE,CAAC;YACzC,MAAM,KAAK,GAAG,IAAI,wBAAwB,CACzC,6FAA6F;gBAC5F,2BAA2B,cAAc,qBAAqB,eAAe,IAAI;gBACjF,YAAY,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,sDAAsD;gBACzF,uCAAuC,CACxC,CAAC;YACF,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;YAC7B,MAAM,KAAK,CAAC;QACb,CAAC;IACF,CAAC;IAEO,YAAY,CAAC,KAAyB;QAC7C,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC5B,yDAAyD;YACzD,OAAO,KAAK,CAAC,KAAK,CAAC;QACpB,CAAC;QACD,wEAAwE;QACxE,sEAAsE;QACtE,oEAAoE;QACpE,OAAO,iBAAiB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IAED,iFAAiF;IACjF,KAAK,CAAC,IAAI;QACT,MAAM,IAAI,CAAC,KAAK,CAAC;QACjB,mEAAmE;QACnE,wEAAwE;QACxE,wCAAwC;QACxC,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,KAAK,GAAoB;YAC9B,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;YAC7B,IAAI;YACJ,sDAAsD;YACtD,8DAA8D;YAC9D,6DAA6D;YAC7D,kEAAkE;YAClE,gEAAgE;YAChE,4DAA4D;YAC5D,eAAe,EAAE,MAAM,CAAC,MAAM;YAC9B,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,IAAI;YAC3B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACnC,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzB,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC;QACtC,OAAO,KAAK,CAAC;IACd,CAAC;IAED,iGAAiG;IACjG,KAAK,CAAC,GAAG;QACR,IAAI,CAAC;YACJ,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;gBACjD,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;gBAClB,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;oBACpB,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC;oBAC7B,OAAO;wBACN,IAAI,EAAE,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO;wBAClD,KAAK,EAAE,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;wBAC/B,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;wBAC1B,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;qBAC7B,CAAC;gBACH,CAAC;YACF,CAAC;YACD,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QAC9F,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,mEAAmE;YACnE,kEAAkE;YAClE,yDAAyD;YACzD,iEAAiE;YACjE,yDAAyD;YACzD,IAAI,GAAG,YAAY,mBAAmB,EAAE,CAAC;gBACxC,OAAO;oBACN,IAAI,EAAE,SAAS;oBACf,KAAK,EAAE,GAAG,CAAC,OAAO;oBAClB,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;oBAC1B,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;iBAC7B,CAAC;YACH,CAAC;YACD,IAAI,GAAG,YAAY,uBAAuB,EAAE,CAAC;gBAC5C,OAAO;oBACN,IAAI,EAAE,kBAAkB;oBACxB,KAAK,EAAE,GAAG,CAAC,OAAO;oBAClB,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;oBAC1B,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;iBAC7B,CAAC;YACH,CAAC;YACD,IAAI,GAAG,YAAY,wBAAwB,EAAE,CAAC;gBAC7C,OAAO;oBACN,IAAI,EAAE,mBAAmB;oBACzB,KAAK,EAAE,GAAG,CAAC,OAAO;oBAClB,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;oBAC1B,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;iBAC7B,CAAC;YACH,CAAC;YACD,OAAO;gBACN,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;gBACvD,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;gBAC1B,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;aAC7B,CAAC;QACH,CAAC;IACF,CAAC;IAED,4GAA4G;IAC5G,OAAO;QACN,IAAI,CAAC,cAAc,CAAC,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC,CAAC;QACvD,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC;QACxB,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAClB,IAAI,CAAC;YACJ,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,CAAC;QAAC,MAAM,CAAC;YACR,iEAAiE;YACjE,gCAAgC;QACjC,CAAC;IACF,CAAC;CACD","sourcesContent":["/**\n * `RlmSession` — the RLM root loop.\n *\n * root-LLM-produces-code -> REPL-executes -> truncated-stdout ->\n * history-append -> FINAL-check, per\n * .planning/phases/26-rlm-core-primitives/26-01-PLAN.md, Architecture\n * section 4. Talks to `../python/repl_driver.py` over the newline-delimited\n * JSON wire protocol documented in that file's module docstring (and\n * section 2-3 of the plan).\n */\n\nimport { type ChildProcess, execFile } from \"node:child_process\";\nimport { mkdtempSync, rmSync } from \"node:fs\";\nimport { readFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { spawnSandboxed } from \"./sandbox.js\";\nimport type { RlmHistoryEntry, RlmResult, RlmSessionOptions } from \"./types.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst DRIVER_PATH = join(__dirname, \"..\", \"python\", \"repl_driver.py\");\n\nconst DEFAULT_MAX_ITERATIONS = 24;\nconst DEFAULT_STDOUT_TRUNCATE_CHARS = 2000;\n\n// Resource-limit defaults (Phase 28, Architecture section 3). These are the\n// REAL enforcement mechanisms -- Node-side wall-clock + RSS polling, not any\n// Python-level RLIMIT/timer (see repl_driver.py's Linux-only RLIMIT_AS,\n// which is explicitly a backstop, not the primary mechanism, and isn't used\n// on macOS at all per the plan).\nconst DEFAULT_STEP_TIMEOUT_MS = 30_000;\n/**\n * The startup self-test spawns python3 and drives a 2 s socket probe, so it\n * is bounded by at least this much even when a caller sets a tiny step\n * budget -- otherwise a slow start would be reported as a sandbox violation.\n */\nconst MIN_SELF_TEST_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_RSS_BYTES = 256 * 1024 * 1024;\nconst DEFAULT_RSS_POLL_INTERVAL_MS = 250;\n\n/**\n * Thrown internally when a step's Node-side wall-clock timeout fires and the\n * driver subprocess is hard-killed. Caught in `run()` and mapped to\n * `RlmResult.kind === \"timeout\"` -- never surfaced to callers directly.\n */\nclass RlmStepTimeoutError extends Error {}\n\n/**\n * Thrown internally when a `maxSubCalls`/`maxTotalCostUsd` pre-check fails\n * and the driver subprocess is hard-killed as a result. Caught in `run()`\n * and mapped to `RlmResult.kind === \"budget_exhausted\"` -- never surfaced to\n * callers directly.\n */\nclass RlmBudgetExhaustedError extends Error {}\n\n/**\n * Thrown internally when the OS-level sandbox's runtime startup self-test\n * (Phase 28 Architecture section 1) doesn't confirm the sandbox is actually\n * enforcing for this process -- either the driver never answered, answered\n * with something other than a well-formed `self_test_result`, or reported\n * that a network connect or an out-of-workdir file read *succeeded*. Caught\n * in `run()` and mapped to `RlmResult.kind === \"sandbox_violation\"`. This is\n * distinct from the synchronous `SandboxUnavailableError` thrown by\n * `spawnSandboxed` itself (missing wrapper binary/profile/interpreter) --\n * that one fails closed before any process exists; this one fails closed\n * after a process exists but before it's ever trusted with real\n * root-LLM-authored code.\n */\nclass RlmSandboxViolationError extends Error {}\n\n/**\n * Reads the driver subprocess's current resident-set size in bytes, or\n * `null` if it can't be determined right now (process already exited,\n * `ps`/`/proc` unavailable, a transient read race, etc.). `null` always\n * means \"skip this poll\" -- never treated as \"assume 0 bytes\" or \"assume\n * over the limit\", since either guess could produce a wrong kill/no-kill\n * decision.\n *\n * Linux: reads `/proc/<pid>/status`'s `VmRSS` line directly (no subprocess\n * spawn needed). Every other platform (macOS in practice): shells out to\n * `ps -o rss= -p <pid>` (BSD `ps` reports RSS in KiB) since there's no\n * `/proc` to read. See Phase 28 Architecture section 3.\n */\nfunction readRssBytes(pid: number): Promise<number | null> {\n\tif (process.platform === \"linux\") {\n\t\treturn readFile(`/proc/${pid}/status`, \"utf8\").then(\n\t\t\t(status) => {\n\t\t\t\tconst match = /^VmRSS:\\s*(\\d+)\\s*kB$/m.exec(status);\n\t\t\t\treturn match ? Number(match[1]) * 1024 : null;\n\t\t\t},\n\t\t\t() => null,\n\t\t);\n\t}\n\treturn new Promise((resolve) => {\n\t\texecFile(\"ps\", [\"-o\", \"rss=\", \"-p\", String(pid)], { encoding: \"utf8\" }, (err, stdout) => {\n\t\t\tif (err) {\n\t\t\t\tresolve(null);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst trimmed = stdout.trim().split(\"\\n\")[0]?.trim();\n\t\t\tif (!trimmed) {\n\t\t\t\tresolve(null);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst kb = Number(trimmed);\n\t\t\tresolve(Number.isFinite(kb) ? kb * 1024 : null);\n\t\t});\n\t});\n}\n\n/** One newline-delimited-JSON message in either direction of the wire protocol. */\ninterface DriverMessage {\n\ttype: string;\n\t[key: string]: unknown;\n}\n\n/** The `exec_result.final` payload shape (see repl_driver.py). */\ninterface DriverFinalPayload {\n\tkind: \"value\" | \"var\";\n\t/** For \"value\": `str(answer)`. For \"var\": `repr(globals_dict[name])`. */\n\tvalue: string;\n\tname?: string;\n}\n\ninterface DriverExecResult extends DriverMessage {\n\ttype: \"exec_result\";\n\tstdout: string;\n\terror: string | null;\n\tfinal: DriverFinalPayload | null;\n}\n\n/**\n * Extracts Python source from a root-LLM response.\n *\n * Per R26-RLM.4, the response is expected to contain a ```repl or ```python\n * fenced block (possibly surrounded by prose explaining the plan). Falls\n * back to the whole (trimmed) response when no fence is present, so mocked\n * `rootLlm` callbacks in tests can return raw code directly.\n */\nexport function extractPythonCode(response: string): string {\n\tconst match = /```(?:repl|python)[ \\t]*\\r?\\n([\\s\\S]*?)```/.exec(response);\n\tif (!match) return response.trim();\n\treturn match[1].replace(/\\r?\\n$/, \"\");\n}\n\n/**\n * Truncates captured stdout to `limit` characters, appending an explicit\n * marker for how many characters were cut — matching the `[truncated N\n * chars]` format R28-SBX.4 (Phase 28) will enforce as a hard cap. This phase\n * only applies it as a plain formatting rule, not a safety mechanism.\n */\nexport function truncateStdout(stdout: string, limit: number): string {\n\tif (stdout.length <= limit) return stdout;\n\tconst omitted = stdout.length - limit;\n\treturn `${stdout.slice(0, limit)}\\n[truncated ${omitted} chars]`;\n}\n\n/**\n * Restricted Python-literal parser used to turn a `FINAL_VAR`-produced\n * `repr()` string back into a real JS value.\n *\n * Handles the JSON-safe subset the plan calls out: `None`/`True`/`False`,\n * int/float, single- or double-quoted strings (with the common backslash\n * escapes), lists, tuples (mapped to arrays — JS has no tuple type), and\n * dicts (mapped to plain objects; keys are coerced with `String(...)`).\n * Anything it can't parse (custom object reprs like `<Foo object at\n * 0x...>`, sets, NaN/Infinity, etc.) falls back to the raw repr string\n * rather than throwing, so a result is never lost — see the module-level\n * note in this file's neighboring test for why raw-string fallback beats\n * failing the whole run.\n */\nexport function pythonReprToValue(repr: string): unknown {\n\ttry {\n\t\tconst parser = new PyReprParser(repr);\n\t\tconst value = parser.parseValue();\n\t\tparser.expectEnd();\n\t\treturn value;\n\t} catch {\n\t\treturn repr;\n\t}\n}\n\nclass PyReprParseError extends Error {}\n\n/** Minimal recursive-descent parser for a subset of Python literal syntax. */\nclass PyReprParser {\n\tprivate i = 0;\n\tprivate readonly src: string;\n\n\tconstructor(src: string) {\n\t\tthis.src = src;\n\t}\n\n\tprivate skipWs(): void {\n\t\twhile (this.i < this.src.length && /\\s/.test(this.src[this.i])) this.i++;\n\t}\n\n\texpectEnd(): void {\n\t\tthis.skipWs();\n\t\tif (this.i !== this.src.length) {\n\t\t\tthrow new PyReprParseError(`unexpected trailing characters at ${this.i}`);\n\t\t}\n\t}\n\n\tparseValue(): unknown {\n\t\tthis.skipWs();\n\t\tconst c = this.src[this.i];\n\t\tif (c === undefined) throw new PyReprParseError(\"unexpected end of repr\");\n\t\tif (c === \"'\" || c === '\"') return this.parseString();\n\t\tif (c === \"[\") return this.parseSequence(\"[\", \"]\");\n\t\tif (c === \"(\") return this.parseSequence(\"(\", \")\");\n\t\tif (c === \"{\") return this.parseDictOrSet();\n\t\tif (this.src.startsWith(\"None\", this.i)) {\n\t\t\tthis.i += 4;\n\t\t\treturn null;\n\t\t}\n\t\tif (this.src.startsWith(\"True\", this.i)) {\n\t\t\tthis.i += 4;\n\t\t\treturn true;\n\t\t}\n\t\tif (this.src.startsWith(\"False\", this.i)) {\n\t\t\tthis.i += 5;\n\t\t\treturn false;\n\t\t}\n\t\treturn this.parseNumber();\n\t}\n\n\tprivate parseString(): string {\n\t\tconst quote = this.src[this.i];\n\t\tthis.i++;\n\t\tlet out = \"\";\n\t\twhile (this.i < this.src.length && this.src[this.i] !== quote) {\n\t\t\tconst ch = this.src[this.i];\n\t\t\tif (ch === \"\\\\\") {\n\t\t\t\tthis.i++;\n\t\t\t\tconst esc = this.src[this.i];\n\t\t\t\tswitch (esc) {\n\t\t\t\t\tcase \"n\":\n\t\t\t\t\t\tout += \"\\n\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"t\":\n\t\t\t\t\t\tout += \"\\t\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"r\":\n\t\t\t\t\t\tout += \"\\r\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"b\":\n\t\t\t\t\t\tout += \"\\b\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"f\":\n\t\t\t\t\t\tout += \"\\f\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"v\":\n\t\t\t\t\t\tout += \"\\v\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"0\":\n\t\t\t\t\t\tout += \"\\0\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"\\\\\":\n\t\t\t\t\t\tout += \"\\\\\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"'\":\n\t\t\t\t\t\tout += \"'\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '\"':\n\t\t\t\t\t\tout += '\"';\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"x\": {\n\t\t\t\t\t\tout += String.fromCharCode(Number.parseInt(this.src.slice(this.i + 1, this.i + 3), 16));\n\t\t\t\t\t\tthis.i += 2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"u\": {\n\t\t\t\t\t\tout += String.fromCharCode(Number.parseInt(this.src.slice(this.i + 1, this.i + 5), 16));\n\t\t\t\t\t\tthis.i += 4;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tcase \"U\": {\n\t\t\t\t\t\tout += String.fromCodePoint(Number.parseInt(this.src.slice(this.i + 1, this.i + 9), 16));\n\t\t\t\t\t\tthis.i += 8;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tout += esc ?? \"\";\n\t\t\t\t}\n\t\t\t\tthis.i++;\n\t\t\t} else {\n\t\t\t\tout += ch;\n\t\t\t\tthis.i++;\n\t\t\t}\n\t\t}\n\t\tif (this.src[this.i] !== quote) throw new PyReprParseError(\"unterminated string literal\");\n\t\tthis.i++;\n\t\treturn out;\n\t}\n\n\tprivate parseNumber(): number {\n\t\tconst start = this.i;\n\t\tif (this.src[this.i] === \"-\" || this.src[this.i] === \"+\") this.i++;\n\t\twhile (this.i < this.src.length && /[0-9]/.test(this.src[this.i])) this.i++;\n\t\tif (this.src[this.i] === \".\") {\n\t\t\tthis.i++;\n\t\t\twhile (this.i < this.src.length && /[0-9]/.test(this.src[this.i])) this.i++;\n\t\t}\n\t\tif (this.src[this.i] === \"e\" || this.src[this.i] === \"E\") {\n\t\t\tthis.i++;\n\t\t\tif (this.src[this.i] === \"-\" || this.src[this.i] === \"+\") this.i++;\n\t\t\twhile (this.i < this.src.length && /[0-9]/.test(this.src[this.i])) this.i++;\n\t\t}\n\t\tconst numStr = this.src.slice(start, this.i);\n\t\tif (!/^[+-]?[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?$/.test(numStr)) {\n\t\t\tthrow new PyReprParseError(`invalid number literal at ${start}`);\n\t\t}\n\t\treturn Number(numStr);\n\t}\n\n\t/** Parses `[...]` (list) or `(...)` (tuple) into a JS array. */\n\tprivate parseSequence(_open: string, close: string): unknown[] {\n\t\tthis.i++; // opening bracket\n\t\tconst out: unknown[] = [];\n\t\tthis.skipWs();\n\t\tif (this.src[this.i] === close) {\n\t\t\tthis.i++;\n\t\t\treturn out;\n\t\t}\n\t\twhile (true) {\n\t\t\tout.push(this.parseValue());\n\t\t\tthis.skipWs();\n\t\t\tif (this.src[this.i] === \",\") {\n\t\t\t\tthis.i++;\n\t\t\t\tthis.skipWs();\n\t\t\t\tif (this.src[this.i] === close) {\n\t\t\t\t\tthis.i++;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (this.src[this.i] === close) {\n\t\t\t\tthis.i++;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tthrow new PyReprParseError(`expected ',' or '${close}' at ${this.i}`);\n\t\t}\n\t\treturn out;\n\t}\n\n\t/** Parses `{...}` as a dict (if any `key: value` pair is found) or a set (mapped to an array). */\n\tprivate parseDictOrSet(): unknown {\n\t\tthis.i++; // {\n\t\tthis.skipWs();\n\t\tif (this.src[this.i] === \"}\") {\n\t\t\tthis.i++;\n\t\t\treturn {};\n\t\t}\n\t\tconst firstKeyOrValue = this.parseValue();\n\t\tthis.skipWs();\n\t\tif (this.src[this.i] === \":\") {\n\t\t\tthis.i++;\n\t\t\tconst out: Record<string, unknown> = {};\n\t\t\tout[String(firstKeyOrValue)] = this.parseValue();\n\t\t\tthis.skipWs();\n\t\t\twhile (this.src[this.i] === \",\") {\n\t\t\t\tthis.i++;\n\t\t\t\tthis.skipWs();\n\t\t\t\tif (this.src[this.i] === \"}\") break;\n\t\t\t\tconst key = this.parseValue();\n\t\t\t\tthis.skipWs();\n\t\t\t\tif (this.src[this.i] !== \":\") throw new PyReprParseError(`expected ':' at ${this.i}`);\n\t\t\t\tthis.i++;\n\t\t\t\tout[String(key)] = this.parseValue();\n\t\t\t\tthis.skipWs();\n\t\t\t}\n\t\t\tif (this.src[this.i] !== \"}\") throw new PyReprParseError(\"expected '}' to close dict\");\n\t\t\tthis.i++;\n\t\t\treturn out;\n\t\t}\n\t\tconst out: unknown[] = [firstKeyOrValue];\n\t\tthis.skipWs();\n\t\twhile (this.src[this.i] === \",\") {\n\t\t\tthis.i++;\n\t\t\tthis.skipWs();\n\t\t\tif (this.src[this.i] === \"}\") break;\n\t\t\tout.push(this.parseValue());\n\t\t\tthis.skipWs();\n\t\t}\n\t\tif (this.src[this.i] !== \"}\") throw new PyReprParseError(\"expected '}' to close set\");\n\t\tthis.i++;\n\t\treturn out;\n\t}\n}\n\ninterface PendingWaiter {\n\tresolve: (message: DriverMessage) => void;\n\treject: (error: Error) => void;\n}\n\nexport class RlmSession {\n\tprivate readonly child: ChildProcess;\n\tprivate readonly workdir: string;\n\tprivate readonly stdoutTruncateChars: number;\n\tprivate readonly maxIterations: number;\n\tprivate readonly stepTimeoutMs: number;\n\tprivate readonly maxRssBytes: number;\n\tprivate readonly rssPollIntervalMs: number;\n\tprivate readonly maxSubCalls: number | undefined;\n\tprivate readonly maxTotalCostUsd: number | undefined;\n\tprivate readonly getAccumulatedCostUsd: (() => number) | undefined;\n\tprivate readonly history: RlmHistoryEntry[] = [];\n\tprivate readonly ready: Promise<void>;\n\n\tprivate buffer = \"\";\n\tprivate messageQueue: DriverMessage[] = [];\n\tprivate waiters: PendingWaiter[] = [];\n\tprivate terminated = false;\n\tprivate terminationError: Error | null = null;\n\tprivate lastFinal: DriverFinalPayload | null = null;\n\tprivate subCallCount = 0;\n\tprivate rssPollTimer: NodeJS.Timeout | undefined;\n\tprivate rssPollInFlight = false;\n\n\tprivate readonly opts: RlmSessionOptions;\n\n\tconstructor(opts: RlmSessionOptions) {\n\t\tthis.opts = opts;\n\t\tthis.stdoutTruncateChars = opts.stdoutTruncateChars ?? DEFAULT_STDOUT_TRUNCATE_CHARS;\n\t\tthis.maxIterations = opts.maxIterations ?? DEFAULT_MAX_ITERATIONS;\n\t\tthis.stepTimeoutMs = opts.stepTimeoutMs ?? DEFAULT_STEP_TIMEOUT_MS;\n\t\tthis.maxRssBytes = opts.maxRssBytes ?? DEFAULT_MAX_RSS_BYTES;\n\t\tthis.rssPollIntervalMs = opts.rssPollIntervalMs ?? DEFAULT_RSS_POLL_INTERVAL_MS;\n\t\tthis.maxSubCalls = opts.maxSubCalls;\n\t\tthis.maxTotalCostUsd = opts.maxTotalCostUsd;\n\t\tthis.getAccumulatedCostUsd = opts.getAccumulatedCostUsd;\n\n\t\t// The session's scratch workdir -- the only place the OS-sandboxed\n\t\t// driver process may read *and* write freely (see sandbox.ts /\n\t\t// ../sandbox/macos.sb). Created fresh per session and cleaned up in\n\t\t// dispose(). Not caller-configurable (yet): Phase 28 scopes the\n\t\t// sandbox to this session-owned directory, not an arbitrary\n\t\t// caller-supplied path.\n\t\tthis.workdir = mkdtempSync(join(tmpdir(), \"rlm-session-\"));\n\n\t\t// spawnSandboxed wraps python3/repl_driver.py in the platform's\n\t\t// OS-level sandbox (sandbox-exec on macOS, unshare/bwrap on Linux) --\n\t\t// see sandbox.ts's module docstring for why this, and not\n\t\t// repl_driver.py's Python-level guardrails, is the real security\n\t\t// boundary. It throws SandboxUnavailableError synchronously (never\n\t\t// returns a fallback unwrapped command) if the sandbox can't be\n\t\t// established, which is exactly what should happen here: this\n\t\t// constructor must fail closed, not silently spawn the driver\n\t\t// unsandboxed.\n\t\tthis.child = spawnSandboxed({ driverPath: DRIVER_PATH, workdir: this.workdir });\n\n\t\tthis.child.stdout?.setEncoding(\"utf8\");\n\t\tthis.child.stdout?.on(\"data\", (chunk: string) => this.onStdoutChunk(chunk));\n\n\t\tthis.child.stderr?.setEncoding(\"utf8\");\n\t\tthis.child.stderr?.on(\"data\", (chunk: string) => {\n\t\t\t// Driver-side crashes (as opposed to in-REPL exceptions, which are\n\t\t\t// reported via exec_result.error) surface here. Don't throw on\n\t\t\t// stray stderr output alone -- just make it visible for debugging.\n\t\t\tconsole.error(`[rlm repl_driver stderr] ${chunk}`);\n\t\t});\n\n\t\tthis.child.once(\"exit\", (code, signal) => {\n\t\t\tthis.markTerminated(new Error(`RlmSession: python3 driver exited (code=${code}, signal=${signal})`));\n\t\t});\n\t\tthis.child.once(\"error\", (err) => {\n\t\t\tthis.markTerminated(new Error(`RlmSession: failed to spawn python3 driver: ${err.message}`));\n\t\t});\n\n\t\t// Node-side RSS polling (Phase 28 Architecture section 3) -- the real\n\t\t// memory enforcement mechanism, running for the whole lifetime of the\n\t\t// child process, independent of any single step's wall-clock timeout.\n\t\tthis.startRssPolling();\n\n\t\t// Tell the driver how aggressively to cap captured stdout *before* any\n\t\t// real step runs (see repl_driver.py's `configure` handling / Phase 28\n\t\t// Architecture section 5) -- fire-and-forget: writes to child.stdin are\n\t\t// delivered strictly in order, so this is guaranteed to be processed\n\t\t// before the `self_test`/`exec` messages sent next.\n\t\tthis.send({ type: \"configure\", stdoutTruncateChars: this.stdoutTruncateChars });\n\n\t\t// Runtime startup self-test (Phase 28 Architecture section 1) -- runs\n\t\t// before `context` is even seeded, let alone before any root-LLM-\n\t\t// authored `exec`. If the OS sandbox isn't actually enforcing for this\n\t\t// process, `runSelfTest()` throws and `seedContext()` never runs, so no\n\t\t// step ever executes against an unconfined driver. See\n\t\t// `RlmSandboxViolationError`'s doc comment for how this differs from\n\t\t// `spawnSandboxed`'s synchronous fail-closed throw.\n\t\tthis.ready = this.runSelfTest().then(() => this.seedContext());\n\t\t// Attach a no-op rejection handler so Node doesn't report this as an\n\t\t// unhandled rejection when the failure path is exercised without every\n\t\t// caller immediately awaiting `run()`/`step()` -- `await this.ready`\n\t\t// elsewhere still observes the same rejection (a promise notifies every\n\t\t// attached reaction, not just the first).\n\t\tthis.ready.catch(() => {});\n\t}\n\n\tprivate onStdoutChunk(chunk: string): void {\n\t\tthis.buffer += chunk;\n\t\tlet newlineIndex = this.buffer.indexOf(\"\\n\");\n\t\twhile (newlineIndex !== -1) {\n\t\t\tconst line = this.buffer.slice(0, newlineIndex);\n\t\t\tthis.buffer = this.buffer.slice(newlineIndex + 1);\n\t\t\tnewlineIndex = this.buffer.indexOf(\"\\n\");\n\t\t\tif (!line.trim()) continue;\n\t\t\tlet message: DriverMessage;\n\t\t\ttry {\n\t\t\t\tmessage = JSON.parse(line) as DriverMessage;\n\t\t\t} catch {\n\t\t\t\tthis.markTerminated(new Error(`RlmSession: driver emitted non-JSON output: ${line}`));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst waiter = this.waiters.shift();\n\t\t\tif (waiter) waiter.resolve(message);\n\t\t\telse this.messageQueue.push(message);\n\t\t}\n\t}\n\n\tprivate markTerminated(error: Error): void {\n\t\tif (this.terminated) return;\n\t\tthis.terminated = true;\n\t\tthis.terminationError = error;\n\t\tif (this.rssPollTimer) {\n\t\t\tclearInterval(this.rssPollTimer);\n\t\t\tthis.rssPollTimer = undefined;\n\t\t}\n\t\tconst waiters = this.waiters;\n\t\tthis.waiters = [];\n\t\tfor (const waiter of waiters) waiter.reject(error);\n\t}\n\n\t/**\n\t * Hard-kills the driver subprocess and marks the session terminated with\n\t * `error` in one step. Used by every resource-limit enforcement path (RSS\n\t * ceiling, wall-clock timeout, budget exhaustion) -- these are Node-side\n\t * hard kills, not attempts at graceful in-process recovery (Phase 28\n\t * Architecture sections 3 and 6). `markTerminated`'s first-write-wins\n\t * guard means whichever of these fires first supplies the message callers\n\t * actually see, even though the child's own `exit` handler will also fire\n\t * shortly after and try to (harmlessly, redundantly) mark terminated too.\n\t */\n\tprivate killAndTerminate(error: Error): void {\n\t\tthis.markTerminated(error);\n\t\tthis.child.kill(\"SIGKILL\");\n\t}\n\n\t/**\n\t * Starts the Node-side RSS poll loop (Phase 28 Architecture section 3) --\n\t * the real memory enforcement mechanism, not any Python-level\n\t * `RLIMIT_AS`. Runs for the child process's whole lifetime; `unref()`d so\n\t * it never by itself keeps the host Node process alive past everything\n\t * else finishing. A `maxRssBytes <= 0` disables polling entirely.\n\t */\n\tprivate startRssPolling(): void {\n\t\tif (!(this.maxRssBytes > 0)) return;\n\t\tconst timer = setInterval(() => {\n\t\t\tvoid this.pollRss();\n\t\t}, this.rssPollIntervalMs);\n\t\ttimer.unref();\n\t\tthis.rssPollTimer = timer;\n\t}\n\n\tprivate async pollRss(): Promise<void> {\n\t\tif (this.terminated || this.rssPollInFlight) return;\n\t\tconst pid = this.child.pid;\n\t\tif (!pid) return;\n\t\tthis.rssPollInFlight = true;\n\t\ttry {\n\t\t\tconst rssBytes = await readRssBytes(pid);\n\t\t\tif (rssBytes === null || rssBytes <= this.maxRssBytes) return;\n\t\t\tthis.killAndTerminate(\n\t\t\t\tnew Error(\n\t\t\t\t\t`RlmSession: python3 driver exceeded the configured RSS ceiling of ${this.maxRssBytes} bytes ` +\n\t\t\t\t\t\t`(observed ${rssBytes} bytes) -- killed. This is a resource-limit kill (Phase 28 Architecture ` +\n\t\t\t\t\t\t\"section 3), not the OS sandbox itself failing.\",\n\t\t\t\t),\n\t\t\t);\n\t\t} finally {\n\t\t\tthis.rssPollInFlight = false;\n\t\t}\n\t}\n\n\t/**\n\t * Pre-dispatch sub-call budget check (Phase 28 Architecture section 6,\n\t * R28-SBX.5): called *before* an `llm_query_request` is ever forwarded to\n\t * `opts.llmQuery`, so the (budget+1)th attempted sub-call never actually\n\t * dispatches -- the session is killed right here instead. Deliberately\n\t * NOT also checked before dispatching a whole step: a step that never\n\t * attempts another sub-call can still legitimately finish (e.g. call\n\t * FINAL immediately) even after the budget is otherwise exhausted, and\n\t * pre-emptively killing such a step here would be wrong.\n\t */\n\tprivate checkSubCallBudget(): void {\n\t\tif (this.maxSubCalls === undefined) return;\n\t\tif (this.subCallCount < this.maxSubCalls) return;\n\t\tconst error = new RlmBudgetExhaustedError(\n\t\t\t`RlmSession: llm_query sub-call budget exhausted (maxSubCalls=${this.maxSubCalls}) -- refusing to ` +\n\t\t\t\t`dispatch sub-call #${this.subCallCount + 1} and killing the driver subprocess.`,\n\t\t);\n\t\tthis.killAndTerminate(error);\n\t\tthrow error;\n\t}\n\n\t/**\n\t * Pre-dispatch cost budget check (Phase 28 Architecture section 6,\n\t * R28-SBX.5): called before dispatching a step's `rootLlm` turn and\n\t * before forwarding a sub-call to `opts.llmQuery`. No-ops unless both\n\t * `maxTotalCostUsd` and `getAccumulatedCostUsd` are supplied -- see\n\t * `RlmSessionOptions.maxTotalCostUsd`'s doc comment for why an unpaired\n\t * option is a no-op rather than a guess.\n\t */\n\tprivate checkCostBudget(): void {\n\t\tif (this.maxTotalCostUsd === undefined || !this.getAccumulatedCostUsd) return;\n\t\tconst accumulated = this.getAccumulatedCostUsd();\n\t\tif (accumulated < this.maxTotalCostUsd) return;\n\t\tconst error = new RlmBudgetExhaustedError(\n\t\t\t`RlmSession: cost budget exhausted (maxTotalCostUsd=$${this.maxTotalCostUsd}, accumulated=$${accumulated}) ` +\n\t\t\t\t\"-- killing the driver subprocess.\",\n\t\t);\n\t\tthis.killAndTerminate(error);\n\t\tthrow error;\n\t}\n\n\t/**\n\t * Races `promise` against the configured wall-clock step timeout\n\t * (Phase 28 Architecture section 3, R28-SBX.3) -- the real timeout\n\t * enforcement mechanism. On expiry, hard-kills the driver subprocess and\n\t * rejects with `RlmStepTimeoutError`, even if `promise` itself is stuck\n\t * awaiting something that `markTerminated`'s waiter-rejection can't reach\n\t * (e.g. a slow/hung `opts.llmQuery` callback, as opposed to a pending\n\t * `nextMessage()` wait) -- the explicit `reject` here is what actually\n\t * unblocks the caller in that case, not just a formality.\n\t */\n\tprivate withStepTimeout<T>(promise: Promise<T>, timeoutMs = this.stepTimeoutMs): Promise<T> {\n\t\tif (!(timeoutMs > 0)) return promise;\n\t\treturn new Promise<T>((resolve, reject) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tconst error = new RlmStepTimeoutError(\n\t\t\t\t\t`RlmSession: step exceeded the configured wall-clock timeout of ${timeoutMs}ms -- killed.`,\n\t\t\t\t);\n\t\t\t\tthis.killAndTerminate(error);\n\t\t\t\treject(error);\n\t\t\t}, timeoutMs);\n\t\t\ttimer.unref();\n\t\t\tpromise.then(\n\t\t\t\t(value) => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\tresolve(value);\n\t\t\t\t},\n\t\t\t\t(err) => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\treject(err);\n\t\t\t\t},\n\t\t\t);\n\t\t});\n\t}\n\n\tprivate nextMessage(): Promise<DriverMessage> {\n\t\tconst queued = this.messageQueue.shift();\n\t\tif (queued) return Promise.resolve(queued);\n\t\tif (this.terminated) {\n\t\t\treturn Promise.reject(this.terminationError ?? new Error(\"RlmSession: driver process is no longer running\"));\n\t\t}\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.waiters.push({ resolve, reject });\n\t\t});\n\t}\n\n\tprivate send(message: Record<string, unknown>): void {\n\t\tif (this.terminated) {\n\t\t\tthrow this.terminationError ?? new Error(\"RlmSession: cannot send to a terminated driver process\");\n\t\t}\n\t\tthis.child.stdin?.write(`${JSON.stringify(message)}\\n`);\n\t}\n\n\t/**\n\t * Sends one `exec` message and handles any `llm_query_request` round-trips\n\t * until the matching `exec_result`, all under the wall-clock step timeout\n\t * (`withStepTimeout`, Phase 28 Architecture section 3).\n\t */\n\tprivate async runExec(code: string): Promise<DriverExecResult> {\n\t\tthis.send({ type: \"exec\", code });\n\t\treturn this.withStepTimeout(this.collectExecResult());\n\t}\n\n\t/** The message loop `runExec` races against the step timeout -- split out so `withStepTimeout` can wrap just this. */\n\tprivate async collectExecResult(): Promise<DriverExecResult> {\n\t\twhile (true) {\n\t\t\tconst message = await this.nextMessage();\n\t\t\tif (message.type === \"llm_query_request\") {\n\t\t\t\tconst id = message.id as string;\n\t\t\t\tif (!this.opts.llmQuery) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\"RlmSession: REPL code called llm_query(...) but no `llmQuery` callback was provided in RlmSessionOptions.\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\t// Budget pre-checks (Phase 28 Architecture section 6, R28-SBX.5) --\n\t\t\t\t// before this sub-call is dispatched, not after: either check can\n\t\t\t\t// throw (and kill the driver), in which case opts.llmQuery is never\n\t\t\t\t// called for this attempt at all.\n\t\t\t\tthis.checkSubCallBudget();\n\t\t\t\tthis.checkCostBudget();\n\t\t\t\tthis.subCallCount++;\n\t\t\t\tconst text = await this.opts.llmQuery(message.prompt as string);\n\t\t\t\tthis.send({ type: \"llm_query_response\", id, text });\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (message.type === \"exec_result\") {\n\t\t\t\treturn message as DriverExecResult;\n\t\t\t}\n\t\t\tthrow new Error(`RlmSession: unexpected message from driver: ${JSON.stringify(message)}`);\n\t\t}\n\t}\n\n\tprivate async seedContext(): Promise<void> {\n\t\t// JSON string escaping and Python double-quoted string literal escaping\n\t\t// agree on the escapes that matter here (\\\", \\\\, \\n, \\t, \\r, and\n\t\t// \\uXXXX), so JSON.stringify produces a valid Python string literal.\n\t\tawait this.runExec(`context = ${JSON.stringify(this.opts.prompt)}`);\n\t}\n\n\t/**\n\t * Runtime proof the OS-level sandbox is actually enforcing for *this*\n\t * process (Phase 28 Architecture section 1) -- sends `{\"type\":\n\t * \"self_test\"}` and requires the driver's `self_test_result` response to\n\t * report both a network connect attempt and an out-of-workdir file read\n\t * failing. Throws `RlmSandboxViolationError` (fail-closed, refusing to run\n\t * at all) if:\n\t * - the driver never responds (crash/hang -- guarded by the step\n\t * wall-clock timeout, floored at `MIN_SELF_TEST_TIMEOUT_MS`),\n\t * - the response isn't a well-formed `self_test_result`, or\n\t * - either check reports the sandbox did NOT block the attempt.\n\t *\n\t * Deliberately does not go through `runExec`/the guarded `exec()` wire\n\t * message: `repl_driver.py`'s `_run_self_test` handles `self_test`\n\t * on a route that bypasses its own Python-level guardrails (restricted\n\t * builtins, import allowlist, AST screen) entirely, because a self-test\n\t * *routed through* those guardrails would be a false positive -- e.g.\n\t * `import socket` failing on the guardrail import allowlist looks\n\t * identical to `import socket` failing because the OS sandbox denied the\n\t * connection, even with the OS sandbox completely disabled. See\n\t * `_run_self_test`'s docstring in repl_driver.py for the full reasoning.\n\t */\n\tprivate async runSelfTest(): Promise<void> {\n\t\tthis.send({ type: \"self_test\" });\n\t\tlet message: DriverMessage;\n\t\ttry {\n\t\t\tmessage = await this.withStepTimeout(\n\t\t\t\tthis.nextMessage(),\n\t\t\t\tthis.stepTimeoutMs > 0 ? Math.max(this.stepTimeoutMs, MIN_SELF_TEST_TIMEOUT_MS) : 0,\n\t\t\t);\n\t\t} catch (err) {\n\t\t\tthrow new RlmSandboxViolationError(\n\t\t\t\t\"RlmSession: OS-level sandbox startup self-test did not complete \" +\n\t\t\t\t\t`(refusing to run -- fail-closed): ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t);\n\t\t}\n\t\tconst networkBlocked = message.type === \"self_test_result\" && message.networkBlocked === true;\n\t\tconst fileReadBlocked = message.type === \"self_test_result\" && message.fileReadBlocked === true;\n\t\tif (!networkBlocked || !fileReadBlocked) {\n\t\t\tconst error = new RlmSandboxViolationError(\n\t\t\t\t\"RlmSession: OS-level sandbox startup self-test failed -- the sandbox is not confining this \" +\n\t\t\t\t\t`process (networkBlocked=${networkBlocked}, fileReadBlocked=${fileReadBlocked}, ` +\n\t\t\t\t\t`response=${JSON.stringify(message)}). Refusing to run rather than executing unconfined ` +\n\t\t\t\t\t\"root-LLM-authored code (fail-closed).\",\n\t\t\t);\n\t\t\tthis.killAndTerminate(error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate resolveFinal(final: DriverFinalPayload): unknown {\n\t\tif (final.kind === \"value\") {\n\t\t\t// FINAL(answer) already sent `str(answer)` -- use as-is.\n\t\t\treturn final.value;\n\t\t}\n\t\t// FINAL_VAR(name) sent `repr(globals_dict[name])`. Parse it back into a\n\t\t// real JS value for JSON-safe types; fall back to the raw repr string\n\t\t// for anything else. See `pythonReprToValue`'s doc comment for why.\n\t\treturn pythonReprToValue(final.value);\n\t}\n\n\t/** One root-LLM turn: get code, exec it, append history, may resolve a final. */\n\tasync step(): Promise<RlmHistoryEntry> {\n\t\tawait this.ready;\n\t\t// Pre-dispatch cost budget check (Phase 28 Architecture section 6,\n\t\t// R28-SBX.5) -- before the root LLM is even asked for this step's code,\n\t\t// not just before a sub-call within it.\n\t\tthis.checkCostBudget();\n\t\tconst response = await this.opts.rootLlm(this.history.slice());\n\t\tconst code = extractPythonCode(response);\n\t\tconst result = await this.runExec(code);\n\t\tconst entry: RlmHistoryEntry = {\n\t\t\tstep: this.history.length + 1,\n\t\t\tcode,\n\t\t\t// Already capped+marked incrementally by the driver's\n\t\t\t// `_TruncatingStdout` (Phase 28 Architecture section 5) -- no\n\t\t\t// second Node-side truncation pass here (Phase 26's original\n\t\t\t// post-hoc `truncateStdout` call is exactly the OOM-via-print gap\n\t\t\t// this phase closes; re-applying it here on top of the driver's\n\t\t\t// own `[truncated N chars]` marker would risk mangling it).\n\t\t\ttruncatedStdout: result.stdout,\n\t\t\terror: result.error ?? null,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t};\n\t\tthis.history.push(entry);\n\t\tthis.lastFinal = result.final ?? null;\n\t\treturn entry;\n\t}\n\n\t/** Loops `step()` until a FINAL/FINAL_VAR, `maxIterations`, or an unrecoverable driver error. */\n\tasync run(): Promise<RlmResult> {\n\t\ttry {\n\t\t\twhile (this.history.length < this.maxIterations) {\n\t\t\t\tawait this.step();\n\t\t\t\tif (this.lastFinal) {\n\t\t\t\t\tconst final = this.lastFinal;\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkind: final.kind === \"var\" ? \"final_var\" : \"final\",\n\t\t\t\t\t\tvalue: this.resolveFinal(final),\n\t\t\t\t\t\tsteps: this.history.length,\n\t\t\t\t\t\thistory: this.history.slice(),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { kind: \"max_iterations\", steps: this.history.length, history: this.history.slice() };\n\t\t} catch (err) {\n\t\t\t// Typed stop reasons (Phase 28 Architecture section 6, R28-SBX.6):\n\t\t\t// a hard-killed step surfaces here as one of these internal error\n\t\t\t// classes; anything else (a genuine driver crash, a step\n\t\t\t// referencing llm_query with no callback configured, etc.) falls\n\t\t\t// through to the generic \"error\" kind, same as Phase 26.\n\t\t\tif (err instanceof RlmStepTimeoutError) {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"timeout\",\n\t\t\t\t\tvalue: err.message,\n\t\t\t\t\tsteps: this.history.length,\n\t\t\t\t\thistory: this.history.slice(),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (err instanceof RlmBudgetExhaustedError) {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"budget_exhausted\",\n\t\t\t\t\tvalue: err.message,\n\t\t\t\t\tsteps: this.history.length,\n\t\t\t\t\thistory: this.history.slice(),\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (err instanceof RlmSandboxViolationError) {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"sandbox_violation\",\n\t\t\t\t\tvalue: err.message,\n\t\t\t\t\tsteps: this.history.length,\n\t\t\t\t\thistory: this.history.slice(),\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tkind: \"error\",\n\t\t\t\tvalue: err instanceof Error ? err.message : String(err),\n\t\t\t\tsteps: this.history.length,\n\t\t\t\thistory: this.history.slice(),\n\t\t\t};\n\t\t}\n\t}\n\n\t/** Kills the underlying python3 subprocess and removes its scratch workdir. Safe to call more than once. */\n\tdispose(): void {\n\t\tthis.markTerminated(new Error(\"RlmSession: disposed\"));\n\t\tthis.child.stdin?.end();\n\t\tthis.child.kill();\n\t\ttry {\n\t\t\trmSync(this.workdir, { recursive: true, force: true });\n\t\t} catch {\n\t\t\t// Best-effort cleanup only -- a leftover empty-ish tmp dir isn't\n\t\t\t// worth failing dispose() over.\n\t\t}\n\t}\n}\n"]}
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@draht/rlm",
3
- "version": "2026.7.30",
3
+ "version": "2026.10.4-2",
4
4
  "description": "Recursive Language Models — root-LLM-produces-code / REPL-executes / truncated-stdout scaffold",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
8
  "files": [
9
9
  "dist",
10
+ "prompts",
10
11
  "python",
11
12
  "sandbox",
12
13
  "README.md"
@@ -23,9 +24,9 @@
23
24
  "clean": "rm -rf dist"
24
25
  },
25
26
  "dependencies": {
26
- "@draht/ai": "2026.7.30",
27
- "@draht/knowledge": "2026.7.30",
28
- "@draht/router": "2026.7.30"
27
+ "@draht/ai": "2026.10.4-2",
28
+ "@draht/knowledge": "2026.10.4-2",
29
+ "@draht/router": "2026.10.4-2"
29
30
  },
30
31
  "devDependencies": {
31
32
  "vitest": "^3.2.4",
@@ -0,0 +1,73 @@
1
+ # RLM Root Loop — Coder-Mid Tier
2
+
3
+ You are the root process of a Recursive Language Model (RLM). Your job is
4
+ to answer a question about a large piece of text called `context`, but
5
+ `context` is too big to paste into your own prompt — it lives only inside
6
+ a Python REPL that you control by writing code, one step at a time. Read
7
+ this whole prompt before writing any code.
8
+
9
+ ## What you have access to
10
+
11
+ - `context` — a Python `str` variable that already exists in the REPL.
12
+ You do not need to (and cannot) load or paste it; it's just there.
13
+ - Content type: {{context_type}}
14
+ - Total length: {{context_total_length}} characters
15
+ - If you slice it into chunks, these lengths are a reasonable starting
16
+ point: {{chunk_lengths}}
17
+ - `llm_query(prompt)` — a Python function you can call. It sends `prompt`
18
+ (a string you build, usually containing a chunk of `context` plus a
19
+ question about it) to a separate sub-LLM and returns its text answer as
20
+ a string. Use this when a piece of `context` needs to be read and
21
+ reasoned about by a language model rather than just sliced or
22
+ regex-matched.
23
+ - Budget: at most {{max_sub_call_budget}} calls total this session.
24
+ - Budget: each prompt you send should stay under roughly
25
+ {{sub_call_char_budget}} characters.
26
+ - `FINAL(answer)` — call this when you know the answer. It ends the
27
+ session and returns `str(answer)` as the result. Do not print the
28
+ answer instead of calling `FINAL` — printing is not how you finish.
29
+ - `FINAL_VAR(name)` — like `FINAL`, but for when your answer already lives
30
+ in a variable (e.g. a list or dict you built) and you don't want to
31
+ stringify it yourself. Pass the variable's name as a string, e.g.
32
+ `FINAL_VAR("results")`. It returns `repr(results)`.
33
+ - Standard library only: `re`, `json`, `math`, `itertools`, `collections`,
34
+ `statistics`. No file, network, or process access — don't try to import
35
+ anything else.
36
+
37
+ ## How each turn works, step by step
38
+
39
+ 1. You write one Python code block, fenced as ```repl (or ```python — both
40
+ work).
41
+ 2. It is executed in a REPL that stays alive across your whole session —
42
+ variables and functions you define in step 1 are still there in step 5.
43
+ 3. Whatever the code prints to stdout comes back to you, but truncated —
44
+ it is not free, so don't print large chunks of `context` just to "look
45
+ at" them; instead, use Python (slicing, `len()`, `re.search`) to check
46
+ or extract only what you need, and print short summaries.
47
+ 4. If your code raises an exception, you'll see the traceback next turn —
48
+ fix it and continue; your earlier variables are still intact unless the
49
+ exception happened while assigning them.
50
+ 5. Repeat until you can call `FINAL` or `FINAL_VAR`.
51
+
52
+ A reasonable approach: first, use plain Python (slicing, `len(context)`,
53
+ `re.search`, `context.find(...)`) to orient yourself and locate the
54
+ relevant region(s) of `context` — this costs nothing and doesn't use your
55
+ `llm_query` budget. Only reach for `llm_query` once you have a specific
56
+ chunk and a specific question that needs actual language understanding
57
+ (summarizing, extracting structured facts from prose, judging relevance).
58
+
59
+ ## Batching discipline — read this carefully
60
+
61
+ Do **not** call `llm_query` once per line, once per record, or once per
62
+ small item in `context`. If `context` contains, say, 2,000 log lines or
63
+ 500 short records, calling `llm_query` on each one individually will blow
64
+ through your {{max_sub_call_budget}}-call budget almost immediately and
65
+ produce a worse, slower answer than batching would.
66
+
67
+ Instead: group related lines/records/items into batches of roughly
68
+ 10,000–15,000 characters each, and send one `llm_query` call per batch
69
+ with a single question that applies to the whole batch (e.g. "extract all
70
+ X from the following records" rather than "does this one record contain
71
+ X?" repeated per record). If you need to combine per-batch answers, do
72
+ that combination in plain Python or with one final `llm_query` call over
73
+ the summaries — not with another per-item loop.
@@ -0,0 +1,45 @@
1
+ # RLM Root Loop — Frontier Tier
2
+
3
+ You are the root process of a Recursive Language Model (RLM). `context` is
4
+ too large to read directly, so you never see it in your own prompt or
5
+ history — you interact with it exclusively by writing Python that peeks,
6
+ chunks, searches, and recursively queries it inside a persistent REPL.
7
+
8
+ ## Environment
9
+
10
+ - `context`: a Python `str`, already bound as a global. Content type:
11
+ {{context_type}}. Total length: {{context_total_length}} characters.
12
+ - Suggested chunk boundaries (character offsets you can slice on, already
13
+ sized to the content): {{chunk_lengths}}.
14
+ - `llm_query(prompt: str) -> str` — recursively invokes a sub-LLM over
15
+ whatever string you pass it (a chunk, a chunk plus a question, a
16
+ synthesis of several prior answers, etc.). Budgeted: at most
17
+ {{max_sub_call_budget}} calls this session, each argument capped at
18
+ roughly {{sub_call_char_budget}} characters.
19
+ - `FINAL(answer)` — ends the session; `str(answer)` is returned.
20
+ - `FINAL_VAR(name)` — ends the session; `repr(globals()[name])` is returned.
21
+ - Stdlib only (`re`, `json`, `math`, `itertools`, `collections`,
22
+ `statistics`). No filesystem, network, or subprocess access.
23
+
24
+ ## Protocol
25
+
26
+ Each turn you emit one ```repl fenced Python block. It runs against globals
27
+ that persist turn to turn — variables, imports, and function definitions
28
+ survive. Captured stdout is truncated before it reaches your next turn's
29
+ history, so anything you need later belongs in a variable, not a `print`.
30
+
31
+ Plan the decomposition before executing: what you need to extract from
32
+ `context`, how (slicing, `re.search`, `json.loads`, a manual scan loop),
33
+ and where a sub-call genuinely earns its cost versus where plain Python
34
+ suffices. Call `FINAL`/`FINAL_VAR` the moment you can resolve the task —
35
+ don't keep exploring past sufficiency.
36
+
37
+ ## Batching discipline
38
+
39
+ `llm_query` calls cost real money and count against
40
+ {{max_sub_call_budget}}. Never issue one call per line, record, or small
41
+ item — that's the single most common way this scaffold burns its budget
42
+ for no benefit. Batch related content into ~10,000–15,000 character chunks
43
+ per call and ask one question that covers the whole batch; if a task
44
+ naturally decomposes into many similar sub-questions, batch the *items*
45
+ together into few calls rather than firing one call per item.
@@ -0,0 +1,88 @@
1
+ # RLM Root Loop — Small-Context Tier
2
+
3
+ Read this whole prompt before writing any code. You have a smaller working
4
+ memory than usual, so this prompt spells out every step in detail — follow
5
+ it literally rather than improvising a different approach.
6
+
7
+ ## The situation
8
+
9
+ There is a big piece of text you need to answer a question about. It is
10
+ called `context` and it is too big for you to ever see directly — it is
11
+ NOT in this prompt, and it will NOT be printed to you in full at any point.
12
+ Instead, `context` already exists as a Python string variable inside a
13
+ Python REPL (a program that runs the code you write and shows you the
14
+ output). You answer the question by writing small pieces of Python code,
15
+ one at a time, that look at small pieces of `context`.
16
+
17
+ Facts about `context`:
18
+ - Content type: {{context_type}}
19
+ - Total length: {{context_total_length}} characters
20
+ - If you need to split it into pieces, these piece sizes are a safe
21
+ starting point (in characters, in order): {{chunk_lengths}}
22
+
23
+ ## The four tools you have
24
+
25
+ 1. `llm_query(prompt)` — a Python function. Give it a string (usually: a
26
+ small piece of `context`, plus a plain-English question about that
27
+ piece), and it returns a string answer. Use this when you need a piece
28
+ of `context` actually read and understood, not just searched.
29
+ - You may call this at most {{max_sub_call_budget}} times in total.
30
+ Once you run out, you cannot call it again — plan carefully.
31
+ - Each `prompt` you send it should be at most roughly
32
+ {{sub_call_char_budget}} characters. If it's longer than that, cut it
33
+ down first.
34
+ 2. `FINAL(answer)` — call this exactly once, when you have the final
35
+ answer. It stops everything and returns `str(answer)`. Example:
36
+ `FINAL("the total is 42")`.
37
+ 3. `FINAL_VAR(name)` — an alternative to `FINAL` for when your answer is
38
+ already sitting in a variable, e.g. a list you built. Give it the
39
+ variable's name as a *string*: `FINAL_VAR("my_list")`, not
40
+ `FINAL_VAR(my_list)`.
41
+ 4. Small Python building blocks: `re` (pattern matching), `json` (parsing
42
+ JSON), `math`, `itertools`, `collections`, `statistics`. Nothing else is
43
+ available — no file access, no network, no other imports.
44
+
45
+ ## Exactly what to do, one step at a time
46
+
47
+ Step 1. Do not try to solve everything in one code block. Write one small
48
+ piece of code, look at what it prints, then decide the next small piece.
49
+
50
+ Step 2. Start by looking at `context` in small windows using plain
51
+ Python — this is free and does not use up your `llm_query` budget:
52
+ ```repl
53
+ print(len(context))
54
+ print(context[:1000])
55
+ ```
56
+ Look at the first ~1,000–2,000 characters, then jump to other spots with
57
+ slicing (`context[5000:7000]`) or search for a keyword with
58
+ `context.find("some keyword")` or `re.search(...)`. Keep each `print` short
59
+ (a few hundred to ~2,000 characters) — printing huge chunks wastes your
60
+ truncated-output budget without helping you.
61
+
62
+ Step 3. Once you've found the region(s) of `context` that actually matter,
63
+ only then consider `llm_query`. Do not call it just to "read" something you
64
+ could instead search or slice with plain Python.
65
+
66
+ Step 4. When you do call `llm_query`, follow the batching rule below.
67
+
68
+ Step 5. As soon as you can state the final answer, call `FINAL(...)` or
69
+ `FINAL_VAR(...)`. Do not keep looking for confirmation once you already
70
+ have enough to answer.
71
+
72
+ ## Batching rule — the most important rule in this prompt
73
+
74
+ If `context` contains many small items (lines, rows, short records, list
75
+ entries), do **not** call `llm_query` once per item. That means: if there
76
+ are 500 items, do NOT write a loop that calls `llm_query` 500 times. This
77
+ will use up your entire {{max_sub_call_budget}}-call budget on the first
78
+ few dozen items and leave you with no way to finish the task.
79
+
80
+ Instead, group items together into one chunk of about 10,000–15,000
81
+ characters (roughly a few hundred short lines, depending on how long each
82
+ one is), and send that whole chunk to `llm_query` in a single call with one
83
+ question that covers everything in it, for example: "Here are 200 log
84
+ lines. List every line that mentions a timeout." Repeat this — one call per
85
+ ~10,000–15,000-character chunk — until you've covered all of `context`,
86
+ then combine the per-chunk answers yourself in Python (string
87
+ concatenation, list building) or, if needed, with one more `llm_query` call
88
+ over the combined summaries.