@ponythewhite/base-context 1.0.15 → 1.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/base-context-runtime/pyproject.toml +1 -1
  3. package/dist/base-context-runtime/src/rlm/bash.py +19 -0
  4. package/dist/base-context-runtime/src/rlm/job_watch.py +156 -0
  5. package/dist/base-context-runtime/src/rlm/repl.md +28 -6
  6. package/dist/base-context-runtime/src/rlm/repl.py +6 -1
  7. package/dist/base-context-runtime/test/test_bash.py +4 -4
  8. package/dist/base-context-runtime/test/test_job_watch.py +67 -0
  9. package/dist/base-context-runtime/uv.lock +1 -1
  10. package/dist/build-info.json +1 -1
  11. package/dist/bundle/{bundled-modules-O67MEO74.js → bundled-modules-2DMTK7VK.js} +2 -2
  12. package/dist/bundle/{chunk-RQFC34FM.js → chunk-MSH6QRP5.js} +1601 -820
  13. package/dist/bundle/{chunk-DGO66I3L.js → chunk-T2OK7SRV.js} +3 -2
  14. package/dist/bundle/{cli-main-2RER6PBE.js → cli-main-WL2L5IEQ.js} +2 -2
  15. package/dist/bundle/cli.js +1 -1
  16. package/dist/bundle/{main-YERLD3T4.js → main-XPIEIEQJ.js} +2 -2
  17. package/dist/core/agent-session.d.ts +2 -0
  18. package/dist/core/agent-session.d.ts.map +1 -1
  19. package/dist/core/agent-session.js +155 -9
  20. package/dist/core/agent-session.js.map +1 -1
  21. package/dist/core/history-index-worker.d.ts.map +1 -1
  22. package/dist/core/history-index-worker.js +22 -1
  23. package/dist/core/history-index-worker.js.map +1 -1
  24. package/dist/core/history-index.d.ts +2 -0
  25. package/dist/core/history-index.d.ts.map +1 -1
  26. package/dist/core/history-index.js.map +1 -1
  27. package/dist/core/job-watch.d.ts +120 -0
  28. package/dist/core/job-watch.d.ts.map +1 -0
  29. package/dist/core/job-watch.js +648 -0
  30. package/dist/core/job-watch.js.map +1 -0
  31. package/dist/core/kernel/repl-manager.d.ts +5 -1
  32. package/dist/core/kernel/repl-manager.d.ts.map +1 -1
  33. package/dist/core/kernel/repl-manager.js +52 -0
  34. package/dist/core/kernel/repl-manager.js.map +1 -1
  35. package/dist/core/kernel/shared.d.ts +12 -0
  36. package/dist/core/kernel/shared.d.ts.map +1 -1
  37. package/dist/core/kernel/shared.js.map +1 -1
  38. package/dist/core/prompts/rlm.d.ts.map +1 -1
  39. package/dist/core/prompts/rlm.js +49 -39
  40. package/dist/core/prompts/rlm.js.map +1 -1
  41. package/dist/core/session-manager.d.ts +2 -0
  42. package/dist/core/session-manager.d.ts.map +1 -1
  43. package/dist/core/session-manager.js +9 -1
  44. package/dist/core/session-manager.js.map +1 -1
  45. package/dist/core/tools/prime-context.d.ts.map +1 -1
  46. package/dist/core/tools/prime-context.js +1 -1
  47. package/dist/core/tools/prime-context.js.map +1 -1
  48. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  49. package/dist/modes/interactive/interactive-mode.js +2 -1
  50. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  51. package/dist/skills/agent-message/SKILL.md +8 -2
  52. package/dist/skills/bounded-inspect/SKILL.md +78 -0
  53. package/dist/skills/bounded-inspect/pyproject.toml +16 -0
  54. package/dist/skills/bounded-inspect/src/bounded_inspect/__init__.py +313 -0
  55. package/dist/skills/bounded-inspect/src/bounded_inspect/__main__.py +3 -0
  56. package/dist/skills/job-watch/SKILL.md +43 -0
  57. package/dist/skills/job-watch/pyproject.toml +13 -0
  58. package/dist/skills/job-watch/src/job_watch/__init__.py +61 -0
  59. package/dist/skills/rlm-heartbeat/SKILL.md +10 -6
  60. package/dist/utils/file-lines.d.ts.map +1 -1
  61. package/dist/utils/file-lines.js +6 -2
  62. package/dist/utils/file-lines.js.map +1 -1
  63. package/docs/rlm-runtime.md +4 -1
  64. package/docs/skills.md +18 -26
  65. package/package.json +4 -4
  66. package/skills/agent-message/SKILL.md +8 -2
  67. package/skills/bounded-inspect/SKILL.md +78 -0
  68. package/skills/bounded-inspect/pyproject.toml +16 -0
  69. package/skills/bounded-inspect/src/bounded_inspect/__init__.py +313 -0
  70. package/skills/bounded-inspect/src/bounded_inspect/__main__.py +3 -0
  71. package/skills/job-watch/SKILL.md +43 -0
  72. package/skills/job-watch/pyproject.toml +13 -0
  73. package/skills/job-watch/src/job_watch/__init__.py +61 -0
  74. package/skills/rlm-heartbeat/SKILL.md +10 -6
@@ -177,10 +177,22 @@ export interface CapturedKernelLifecycle {
177
177
  /** Checks the original observation; it never probes or substitutes a newer snapshot. */
178
178
  isCurrent(): boolean;
179
179
  }
180
+ export interface JobWatchProbeRequest {
181
+ watchId: string;
182
+ generation: string;
183
+ resourceId: string;
184
+ jobId: string;
185
+ completionSource?: "handle" | "probe";
186
+ command: string | null;
187
+ timeoutMs: number;
188
+ }
180
189
  /** Public surface every kernel client exposes to the provisioner and session layer. */
181
190
  export interface KernelClient {
182
191
  readonly ownerSessionId: string | undefined;
183
192
  readonly isRunning: boolean;
193
+ /** Optional job_watch_probe_v1 capability. Never evaluates a notebook cell. */
194
+ jobWatchProbe?(request: JobWatchProbeRequest): Promise<Record<string, unknown>>;
195
+ cancelJobWatchProbe?(watchId: string): void;
184
196
  start(options?: KernelStartOptions): Promise<void>;
185
197
  execute(code: string, opts?: ExecuteOptions): Promise<ExecuteResult>;
186
198
  shutdown(opts?: KernelShutdownOptions): Promise<boolean>;
@@ -1 +1 @@
1
- {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/core/kernel/shared.ts"],"names":[],"mappings":"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","sourcesContent":["import { registerSessionResourceCleanup } from \"@ponythewhite/base-context-ai\";\nimport type { NativeRecoveryResponse } from \"../selective-recovery.js\";\nimport type { KernelBootstrapProgressHandler, KernelPythonSkill } from \"./bootstrap.js\";\nimport type { RestoreResult, SnapshotResult } from \"./state-snapshot.js\";\n\nexport const DEFAULT_MAX_OUTPUT_CHARS = 65536;\nexport const HOST_REQUEST_SHUTDOWN_TIMEOUT_MS = 5000;\nexport const KERNEL_SHUTDOWN_TIMEOUT_MS = 5000;\nexport const DEFAULT_SNAPSHOT_DEBOUNCE_MS = 1500;\nexport const SNAPSHOT_EXECUTION_TIMEOUT_MS = 5000;\nexport const KERNEL_ABORT_GRACE_MS = 1000;\nexport const KERNEL_BUSY_REUSE_WAIT_MS = 5000;\nexport const KERNEL_BUSY_INTERRUPT_INTERVAL_MS = 500;\nexport const MAX_LATE_SENT_AGENT_MESSAGE_HANDLERS = 256;\nconst KERNEL_BUSY_AFTER_INTERRUPT_MESSAGE =\n\t\"The Python kernel is still running the previously interrupted cell. Wait and try again, or kill the kernel to start fresh.\";\n\nexport class KernelBusyAfterInterruptError extends Error {\n\tconstructor() {\n\t\tsuper(KERNEL_BUSY_AFTER_INTERRUPT_MESSAGE);\n\t\tthis.name = \"KernelBusyAfterInterruptError\";\n\t}\n}\n\n/**\n * Handles one typed request from Python code running in the kernel.\n * The returned record is delivered verbatim to the Python caller.\n */\nexport interface HostRequestContext {\n\t/** Actual active tool/cell cancellation; never read from the Python payload. */\n\tsignal?: AbortSignal;\n\t/** A host-reserved share of this cell's aggregate recovery response budget. */\n\tnativeRecovery?: { maxBytes: number };\n}\n\nexport type HostRequestHandler = (\n\tpayload: Record<string, unknown>,\n\tcontext?: HostRequestContext,\n) => Promise<Record<string, unknown>>;\n\n/** Host request handlers keyed by request type (e.g. \"rlm.run\", \"goal.complete\"). */\nexport type HostRequestHandlers = Record<string, HostRequestHandler>;\n\n/** Where and how to persist the kernel's user namespace so it survives resume. */\nexport interface KernelSnapshotConfig {\n\t/** Absolute path for the dill payload. */\n\tpath: string;\n\t/** Absolute path for the JSON manifest written alongside the payload. */\n\tmanifestPath: string;\n\t/** Maximum aggregate snapshot size. Default 256 MiB. */\n\tmaxBytes?: number;\n\t/** Maximum serialized size of one variable. Default 16 MiB. */\n\tmaxVariableBytes?: number;\n\t/** Debounce window for the auto-snapshot after a successful execution. Default 1500 ms. */\n\tdebounceMs?: number;\n}\n\nexport interface KernelManagerOptions {\n\t/** Python interpreter with the kernel runtime available. Defaults to the auto-bootstrapped kernel. */\n\tpython?: string;\n\tcwd?: string;\n\tenv?: Record<string, string>;\n\tsessionId?: string;\n\thostHandlers?: HostRequestHandlers;\n\tpythonSkills?: readonly KernelPythonSkill[];\n\t/** Persist/revive the user namespace across kernel restarts and session resume. */\n\tsnapshot?: KernelSnapshotConfig;\n\t/** Runtime bootstrap re-run on a protocol-repaired kernel so live handles (rlm, bash, skills) exist again. */\n\tbootstrapCode?: string;\n\t/** File receiving the kernel process's stderr, rotated once at each spawn. */\n\tstderrLogPath?: string;\n}\n\nexport interface KernelStartOptions {\n\tonBootstrapProgress?: KernelBootstrapProgressHandler;\n\tsignal?: AbortSignal;\n}\n\nexport interface ExecuteOptions {\n\t/** Only a finalized ipython tool call can retain recovery output; not bootstrap/state cells. */\n\tnativeRecovery?: boolean;\n\t/** @internal Host-only closure captured by the admitted owned executor; never sent to Python. */\n\trunNativeRecovery?: <T>(read: () => Promise<T>) => Promise<T>;\n\t/** Aborting interrupts the kernel out-of-band. */\n\tsignal?: AbortSignal;\n\tonStream?: (chunk: string, name: \"stdout\" | \"stderr\") => void;\n\tonLateSentAgentMessage?: (message: KernelSentAgentMessage) => void;\n\t/** Cap stdout / stderr / result at this many characters. Default 65536. */\n\tmaxOutputChars?: number;\n\t/** Synthetic host cell (snapshot/restore/list); excluded from lastCellCode attribution. */\n\tinternal?: boolean;\n\t/** The protocol repair's own restore; exempt from waiting on the repair it belongs to. */\n\tprotocolRepair?: boolean;\n}\n\n/** MIME tag the `edit` skill emits diff payloads under. */\nexport const DIFF_DISPLAY_MIME = \"application/vnd.prime-agent.diff+json\";\n\n/** MIME tag the `attach-image` skill emits media payloads under. */\nexport const ATTACHMENT_DISPLAY_MIME = \"application/vnd.prime-agent.attachment+json\";\n\n/** MIME tag the `agent-message` skill emits after sending a message. */\nexport const AGENT_MESSAGE_DISPLAY_MIME = \"application/vnd.prime-agent.agent-message+json\";\n\n/**\n * Hard ceiling on a single attachment's base64 payload, a defensive guard\n * against a runaway direct display emit. The `attach-image` skill caps\n * its own images well under this (see `_MAX_IMAGE_BYTES`), so a skill-produced\n * attachment is never dropped here — only a non-skill emit can hit this.\n */\nexport const MAX_ATTACHMENT_DATA_CHARS = 10_000_000;\n\n/** One file edit, captured from a {@link DIFF_DISPLAY_MIME} display payload. */\nexport interface KernelDiffDisplay {\n\tpath: string;\n\toldStr: string;\n\tnewStr: string;\n\t/** 1-based line where `oldStr` begins in the file, for absolute line numbers. */\n\tstartLine?: number;\n}\n\n/** One media attachment, captured from an {@link ATTACHMENT_DISPLAY_MIME} display payload. */\nexport interface KernelAttachment {\n\tmimeType: string;\n\t/** base64-encoded bytes. */\n\tdata: string;\n\t/** Source path, surfaced to the TUI renderer. */\n\tpath?: string;\n}\n\nexport interface KernelSentAgentMessage {\n\tid: string;\n\tmessage: string;\n\tdeliveryStatus: \"delivered\" | \"queued\";\n\treceiverRole?: \"parent\" | \"sibling\" | \"child\";\n\ttarget: {\n\t\tactiveSessionId: string;\n\t\tsessionId: string;\n\t\tsessionName?: string;\n\t};\n}\n\nexport interface ExecuteResult {\n\t/** Whether the finalized textual output was captured without host truncation or interruption. */\n\toutputComplete?: boolean;\n\t/** Host-selected public data transferred once to the normal finalized ipython result. */\n\tnativeRecoveries?: NativeRecoveryResponse[];\n\tstdout: string;\n\tstderr: string;\n\t/** Text of the cell's trailing expression value, if the cell produced one. */\n\tresult?: string;\n\t/** Diffs emitted via display events, in order. */\n\tdiffs?: KernelDiffDisplay[];\n\t/** Media attachments emitted via display events, in order. */\n\tattachments?: KernelAttachment[];\n\t/** Agent messages sent from this cell, in order. */\n\tsentAgentMessages?: KernelSentAgentMessage[];\n\t/** Output that arrived without this cell's id (user threads, other cells' leftovers, raw fd writes). */\n\tbackgroundOutput?: string;\n\tstatus: \"ok\" | \"error\" | \"aborted\";\n\terror?: { ename: string; evalue: string; traceback: string[] };\n\tdurationMs: number;\n}\n\n/** Parse a {@link DIFF_DISPLAY_MIME} payload, tolerating malformed input. */\nexport function parseDiffDisplay(payload: unknown): KernelDiffDisplay | undefined {\n\tif (!isRecord(payload)) {\n\t\treturn undefined;\n\t}\n\tconst { path, old_str: oldStr, new_str: newStr, start_line: startLine } = payload;\n\tif (typeof path !== \"string\" || typeof oldStr !== \"string\" || typeof newStr !== \"string\") {\n\t\treturn undefined;\n\t}\n\treturn { path, oldStr, newStr, startLine: typeof startLine === \"number\" ? startLine : undefined };\n}\n\n/**\n * Parse an {@link ATTACHMENT_DISPLAY_MIME} payload. Malformed payloads are\n * tolerantly ignored (`undefined`); a well-formed payload exceeding\n * {@link MAX_ATTACHMENT_DATA_CHARS} is reported as `\"oversized\"` so the caller\n * can fail the cell loudly rather than silently dropping the image.\n */\nexport function parseAttachmentDisplay(payload: unknown): KernelAttachment | \"oversized\" | undefined {\n\tif (!isRecord(payload)) {\n\t\treturn undefined;\n\t}\n\tconst { mime_type: mimeType, data, path } = payload;\n\tif (typeof mimeType !== \"string\" || typeof data !== \"string\") {\n\t\treturn undefined;\n\t}\n\tif (data.length > MAX_ATTACHMENT_DATA_CHARS) {\n\t\treturn \"oversized\";\n\t}\n\treturn { mimeType, data, path: typeof path === \"string\" ? path : undefined };\n}\n\nexport function parseSentAgentMessage(payload: unknown): KernelSentAgentMessage | undefined {\n\tif (!isRecord(payload) || !isRecord(payload.target)) {\n\t\treturn undefined;\n\t}\n\tconst { id, message, deliveryStatus, receiverRole, target } = payload;\n\tconst { activeSessionId, sessionId, sessionName } = target;\n\tif (\n\t\ttypeof id !== \"string\" ||\n\t\ttypeof message !== \"string\" ||\n\t\t(deliveryStatus !== \"delivered\" && deliveryStatus !== \"queued\") ||\n\t\ttypeof activeSessionId !== \"string\" ||\n\t\ttypeof sessionId !== \"string\"\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tid,\n\t\tmessage,\n\t\tdeliveryStatus,\n\t\t...(receiverRole === \"parent\" || receiverRole === \"sibling\" || receiverRole === \"child\" ? { receiverRole } : {}),\n\t\ttarget: {\n\t\t\tactiveSessionId,\n\t\t\tsessionId,\n\t\t\t...(typeof sessionName === \"string\" ? { sessionName } : {}),\n\t\t},\n\t};\n}\n\nexport function createKernelStartupAbortError(): Error {\n\treturn new Error(\"Kernel startup aborted\");\n}\n\nexport function raceStartupWithAbort<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n\tif (!signal) {\n\t\treturn promise;\n\t}\n\tif (signal.aborted) {\n\t\treturn Promise.reject(createKernelStartupAbortError());\n\t}\n\treturn new Promise<T>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tconst cleanup = () => signal.removeEventListener(\"abort\", abort);\n\t\tconst abort = () => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treject(createKernelStartupAbortError());\n\t\t};\n\t\tsignal.addEventListener(\"abort\", abort, { once: true });\n\t\tpromise.then(\n\t\t\t(value) => {\n\t\t\t\tif (settled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(error: unknown) => {\n\t\t\t\tif (settled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\treject(error);\n\t\t\t},\n\t\t);\n\t});\n}\n\nexport interface Deferred<T> {\n\tpromise: Promise<T>;\n\tresolve: (value: T) => void;\n\treject: (error: Error) => void;\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nexport function createDeferred<T>(): Deferred<T> {\n\tlet resolve!: (value: T) => void;\n\tlet reject!: (error: Error) => void;\n\tconst promise = new Promise<T>((promiseResolve, promiseReject) => {\n\t\tresolve = promiseResolve;\n\t\treject = promiseReject;\n\t});\n\treturn { promise, resolve, reject };\n}\n\nexport interface KernelShutdownOptions {\n\tsnapshot?: boolean;\n\tdrainHostRequests?: boolean;\n}\n\n/** A bounded owned lifecycle observation, not a namespace, restore, or live-health assertion. */\nexport interface KernelLifecycleState {\n\treadonly source: \"repl-manager\" | \"ipython-provisioner\" | \"unobserved\";\n\treadonly owner: string | null;\n\treadonly generation: number | null;\n\treadonly state: \"idle\" | \"starting\" | \"running\" | \"shutdown\" | \"provisioning\" | \"disposed\" | \"unobserved\";\n}\n\nexport interface CapturedKernelLifecycle {\n\treadonly snapshot: Readonly<KernelLifecycleState>;\n\t/** Checks the original observation; it never probes or substitutes a newer snapshot. */\n\tisCurrent(): boolean;\n}\n\n/** Public surface every kernel client exposes to the provisioner and session layer. */\nexport interface KernelClient {\n\treadonly ownerSessionId: string | undefined;\n\treadonly isRunning: boolean;\n\tstart(options?: KernelStartOptions): Promise<void>;\n\texecute(code: string, opts?: ExecuteOptions): Promise<ExecuteResult>;\n\tshutdown(opts?: KernelShutdownOptions): Promise<boolean>;\n\trestart(): Promise<void>;\n\tkill(): Promise<void>;\n\tdisposeSync(): void;\n\tsnapshotState(): Promise<SnapshotResult | null>;\n\tpruneOversizedVariables(): Promise<SnapshotResult | null>;\n\trestoreState(): Promise<RestoreResult | null>;\n\tlistNamespaceNames(signal?: AbortSignal): Promise<string[] | null>;\n}\n\n// One registry serves every client kind; two parallel registries would\n// double-install process signal handlers.\nexport const liveKernels = new Set<KernelClient>();\nlet signalHandlersInstalled = false;\n\nregisterSessionResourceCleanup((sessionId) => {\n\tfor (const k of liveKernels) {\n\t\tif (!sessionId || k.ownerSessionId === sessionId) {\n\t\t\tvoid k.shutdown({ snapshot: true, drainHostRequests: true });\n\t\t}\n\t}\n});\n\nexport function installSignalHandlersOnce(): void {\n\tif (signalHandlersInstalled) return;\n\tsignalHandlersInstalled = true;\n\n\tconst asyncShutdown = async (): Promise<void> => {\n\t\t// These paths can await, so flush the namespace snapshot before tearing down.\n\t\tawait Promise.allSettled([...liveKernels].map((k) => k.shutdown({ snapshot: true })));\n\t};\n\n\t// `beforeExit` and signal handlers can await async cleanup. `exit`\n\t// can only do sync work (Node won't run pending microtasks past it),\n\t// so it falls back to `disposeSync()` which kills the child synchronously.\n\tprocess.on(\"beforeExit\", () => {\n\t\tvoid asyncShutdown();\n\t});\n\tprocess.on(\"SIGINT\", () => {\n\t\tvoid asyncShutdown().finally(() => process.exit(130));\n\t});\n\tprocess.on(\"SIGTERM\", () => {\n\t\tvoid asyncShutdown().finally(() => process.exit(143));\n\t});\n\tprocess.on(\"exit\", () => {\n\t\tfor (const k of liveKernels) k.disposeSync();\n\t});\n}\n"]}
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/core/kernel/shared.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,KAAK,EAAE,8BAA8B,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxF,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAEzE,eAAO,MAAM,wBAAwB,QAAQ,CAAC;AAC9C,eAAO,MAAM,gCAAgC,OAAO,CAAC;AACrD,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAC/C,eAAO,MAAM,4BAA4B,OAAO,CAAC;AACjD,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAClD,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAC1C,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAC9C,eAAO,MAAM,iCAAiC,MAAM,CAAC;AACrD,eAAO,MAAM,oCAAoC,MAAM,CAAC;AAIxD,qBAAa,6BAA8B,SAAQ,KAAK;IACvD,cAGC;CACD;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IAClC,gFAAgF;IAChF,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,+EAA+E;IAC/E,cAAc,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CACtC;AAED,MAAM,MAAM,kBAAkB,GAAG,CAChC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,OAAO,CAAC,EAAE,kBAAkB,KACxB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAEtC,qFAAqF;AACrF,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;AAErE,kFAAkF;AAClF,MAAM,WAAW,oBAAoB;IACpC,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2FAA2F;IAC3F,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACpC,sGAAsG;IACtG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,YAAY,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC5C,mFAAmF;IACnF,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,8GAA8G;IAC9G,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IAClC,mBAAmB,CAAC,EAAE,8BAA8B,CAAC;IACrD,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC9B,gGAAgG;IAChG,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iGAAiG;IACjG,iBAAiB,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAC9D,kDAAkD;IAClD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,QAAQ,KAAK,IAAI,CAAC;IAC9D,sBAAsB,CAAC,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,IAAI,CAAC;IACnE,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0FAA0F;IAC1F,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,2DAA2D;AAC3D,eAAO,MAAM,iBAAiB,0CAA0C,CAAC;AAEzE,oEAAoE;AACpE,eAAO,MAAM,uBAAuB,gDAAgD,CAAC;AAErF,wEAAwE;AACxE,eAAO,MAAM,0BAA0B,mDAAmD,CAAC;AAE3F;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,WAAa,CAAC;AAEpD,gFAAgF;AAChF,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,8FAA8F;AAC9F,MAAM,WAAW,gBAAgB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,sBAAsB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,WAAW,GAAG,QAAQ,CAAC;IACvC,YAAY,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;IAC9C,MAAM,EAAE;QACP,eAAe,EAAE,MAAM,CAAC;QACxB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACF;AAED,MAAM,WAAW,aAAa;IAC7B,iGAAiG;IACjG,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,yFAAyF;IACzF,gBAAgB,CAAC,EAAE,sBAAsB,EAAE,CAAC;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,KAAK,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC5B,8DAA8D;IAC9D,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACjC,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,sBAAsB,EAAE,CAAC;IAC7C,wGAAwG;IACxG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,SAAS,CAAC;IACnC,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC/D,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,iBAAiB,GAAG,SAAS,CAShF;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,GAAG,WAAW,GAAG,SAAS,CAYnG;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,sBAAsB,GAAG,SAAS,CA0B1F;AAED,wBAAgB,6BAA6B,IAAI,KAAK,CAErD;AAED,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,CAsCxG;AAED,MAAM,WAAW,QAAQ,CAAC,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACpB,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC5B,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAC/B;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEnD;AAED,wBAAgB,cAAc,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAQ/C;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,iGAAiG;AACjG,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,qBAAqB,GAAG,YAAY,CAAC;IACvE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,cAAc,GAAG,UAAU,GAAG,YAAY,CAAC;CAC1G;AAED,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,oBAAoB,CAAC,CAAC;IAClD,wFAAwF;IACxF,SAAS,IAAI,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;IACtC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,uFAAuF;AACvF,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,+EAA+E;IAC/E,aAAa,CAAC,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAChF,mBAAmB,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,KAAK,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACrE,QAAQ,CAAC,IAAI,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,WAAW,IAAI,IAAI,CAAC;IACpB,aAAa,IAAI,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAChD,uBAAuB,IAAI,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC1D,YAAY,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAC9C,kBAAkB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;CACnE;AAID,eAAO,MAAM,WAAW,mBAA0B,CAAC;AAWnD,wBAAgB,yBAAyB,IAAI,IAAI,CAwBhD","sourcesContent":["import { registerSessionResourceCleanup } from \"@ponythewhite/base-context-ai\";\nimport type { NativeRecoveryResponse } from \"../selective-recovery.js\";\nimport type { KernelBootstrapProgressHandler, KernelPythonSkill } from \"./bootstrap.js\";\nimport type { RestoreResult, SnapshotResult } from \"./state-snapshot.js\";\n\nexport const DEFAULT_MAX_OUTPUT_CHARS = 65536;\nexport const HOST_REQUEST_SHUTDOWN_TIMEOUT_MS = 5000;\nexport const KERNEL_SHUTDOWN_TIMEOUT_MS = 5000;\nexport const DEFAULT_SNAPSHOT_DEBOUNCE_MS = 1500;\nexport const SNAPSHOT_EXECUTION_TIMEOUT_MS = 5000;\nexport const KERNEL_ABORT_GRACE_MS = 1000;\nexport const KERNEL_BUSY_REUSE_WAIT_MS = 5000;\nexport const KERNEL_BUSY_INTERRUPT_INTERVAL_MS = 500;\nexport const MAX_LATE_SENT_AGENT_MESSAGE_HANDLERS = 256;\nconst KERNEL_BUSY_AFTER_INTERRUPT_MESSAGE =\n\t\"The Python kernel is still running the previously interrupted cell. Wait and try again, or kill the kernel to start fresh.\";\n\nexport class KernelBusyAfterInterruptError extends Error {\n\tconstructor() {\n\t\tsuper(KERNEL_BUSY_AFTER_INTERRUPT_MESSAGE);\n\t\tthis.name = \"KernelBusyAfterInterruptError\";\n\t}\n}\n\n/**\n * Handles one typed request from Python code running in the kernel.\n * The returned record is delivered verbatim to the Python caller.\n */\nexport interface HostRequestContext {\n\t/** Actual active tool/cell cancellation; never read from the Python payload. */\n\tsignal?: AbortSignal;\n\t/** A host-reserved share of this cell's aggregate recovery response budget. */\n\tnativeRecovery?: { maxBytes: number };\n}\n\nexport type HostRequestHandler = (\n\tpayload: Record<string, unknown>,\n\tcontext?: HostRequestContext,\n) => Promise<Record<string, unknown>>;\n\n/** Host request handlers keyed by request type (e.g. \"rlm.run\", \"goal.complete\"). */\nexport type HostRequestHandlers = Record<string, HostRequestHandler>;\n\n/** Where and how to persist the kernel's user namespace so it survives resume. */\nexport interface KernelSnapshotConfig {\n\t/** Absolute path for the dill payload. */\n\tpath: string;\n\t/** Absolute path for the JSON manifest written alongside the payload. */\n\tmanifestPath: string;\n\t/** Maximum aggregate snapshot size. Default 256 MiB. */\n\tmaxBytes?: number;\n\t/** Maximum serialized size of one variable. Default 16 MiB. */\n\tmaxVariableBytes?: number;\n\t/** Debounce window for the auto-snapshot after a successful execution. Default 1500 ms. */\n\tdebounceMs?: number;\n}\n\nexport interface KernelManagerOptions {\n\t/** Python interpreter with the kernel runtime available. Defaults to the auto-bootstrapped kernel. */\n\tpython?: string;\n\tcwd?: string;\n\tenv?: Record<string, string>;\n\tsessionId?: string;\n\thostHandlers?: HostRequestHandlers;\n\tpythonSkills?: readonly KernelPythonSkill[];\n\t/** Persist/revive the user namespace across kernel restarts and session resume. */\n\tsnapshot?: KernelSnapshotConfig;\n\t/** Runtime bootstrap re-run on a protocol-repaired kernel so live handles (rlm, bash, skills) exist again. */\n\tbootstrapCode?: string;\n\t/** File receiving the kernel process's stderr, rotated once at each spawn. */\n\tstderrLogPath?: string;\n}\n\nexport interface KernelStartOptions {\n\tonBootstrapProgress?: KernelBootstrapProgressHandler;\n\tsignal?: AbortSignal;\n}\n\nexport interface ExecuteOptions {\n\t/** Only a finalized ipython tool call can retain recovery output; not bootstrap/state cells. */\n\tnativeRecovery?: boolean;\n\t/** @internal Host-only closure captured by the admitted owned executor; never sent to Python. */\n\trunNativeRecovery?: <T>(read: () => Promise<T>) => Promise<T>;\n\t/** Aborting interrupts the kernel out-of-band. */\n\tsignal?: AbortSignal;\n\tonStream?: (chunk: string, name: \"stdout\" | \"stderr\") => void;\n\tonLateSentAgentMessage?: (message: KernelSentAgentMessage) => void;\n\t/** Cap stdout / stderr / result at this many characters. Default 65536. */\n\tmaxOutputChars?: number;\n\t/** Synthetic host cell (snapshot/restore/list); excluded from lastCellCode attribution. */\n\tinternal?: boolean;\n\t/** The protocol repair's own restore; exempt from waiting on the repair it belongs to. */\n\tprotocolRepair?: boolean;\n}\n\n/** MIME tag the `edit` skill emits diff payloads under. */\nexport const DIFF_DISPLAY_MIME = \"application/vnd.prime-agent.diff+json\";\n\n/** MIME tag the `attach-image` skill emits media payloads under. */\nexport const ATTACHMENT_DISPLAY_MIME = \"application/vnd.prime-agent.attachment+json\";\n\n/** MIME tag the `agent-message` skill emits after sending a message. */\nexport const AGENT_MESSAGE_DISPLAY_MIME = \"application/vnd.prime-agent.agent-message+json\";\n\n/**\n * Hard ceiling on a single attachment's base64 payload, a defensive guard\n * against a runaway direct display emit. The `attach-image` skill caps\n * its own images well under this (see `_MAX_IMAGE_BYTES`), so a skill-produced\n * attachment is never dropped here — only a non-skill emit can hit this.\n */\nexport const MAX_ATTACHMENT_DATA_CHARS = 10_000_000;\n\n/** One file edit, captured from a {@link DIFF_DISPLAY_MIME} display payload. */\nexport interface KernelDiffDisplay {\n\tpath: string;\n\toldStr: string;\n\tnewStr: string;\n\t/** 1-based line where `oldStr` begins in the file, for absolute line numbers. */\n\tstartLine?: number;\n}\n\n/** One media attachment, captured from an {@link ATTACHMENT_DISPLAY_MIME} display payload. */\nexport interface KernelAttachment {\n\tmimeType: string;\n\t/** base64-encoded bytes. */\n\tdata: string;\n\t/** Source path, surfaced to the TUI renderer. */\n\tpath?: string;\n}\n\nexport interface KernelSentAgentMessage {\n\tid: string;\n\tmessage: string;\n\tdeliveryStatus: \"delivered\" | \"queued\";\n\treceiverRole?: \"parent\" | \"sibling\" | \"child\";\n\ttarget: {\n\t\tactiveSessionId: string;\n\t\tsessionId: string;\n\t\tsessionName?: string;\n\t};\n}\n\nexport interface ExecuteResult {\n\t/** Whether the finalized textual output was captured without host truncation or interruption. */\n\toutputComplete?: boolean;\n\t/** Host-selected public data transferred once to the normal finalized ipython result. */\n\tnativeRecoveries?: NativeRecoveryResponse[];\n\tstdout: string;\n\tstderr: string;\n\t/** Text of the cell's trailing expression value, if the cell produced one. */\n\tresult?: string;\n\t/** Diffs emitted via display events, in order. */\n\tdiffs?: KernelDiffDisplay[];\n\t/** Media attachments emitted via display events, in order. */\n\tattachments?: KernelAttachment[];\n\t/** Agent messages sent from this cell, in order. */\n\tsentAgentMessages?: KernelSentAgentMessage[];\n\t/** Output that arrived without this cell's id (user threads, other cells' leftovers, raw fd writes). */\n\tbackgroundOutput?: string;\n\tstatus: \"ok\" | \"error\" | \"aborted\";\n\terror?: { ename: string; evalue: string; traceback: string[] };\n\tdurationMs: number;\n}\n\n/** Parse a {@link DIFF_DISPLAY_MIME} payload, tolerating malformed input. */\nexport function parseDiffDisplay(payload: unknown): KernelDiffDisplay | undefined {\n\tif (!isRecord(payload)) {\n\t\treturn undefined;\n\t}\n\tconst { path, old_str: oldStr, new_str: newStr, start_line: startLine } = payload;\n\tif (typeof path !== \"string\" || typeof oldStr !== \"string\" || typeof newStr !== \"string\") {\n\t\treturn undefined;\n\t}\n\treturn { path, oldStr, newStr, startLine: typeof startLine === \"number\" ? startLine : undefined };\n}\n\n/**\n * Parse an {@link ATTACHMENT_DISPLAY_MIME} payload. Malformed payloads are\n * tolerantly ignored (`undefined`); a well-formed payload exceeding\n * {@link MAX_ATTACHMENT_DATA_CHARS} is reported as `\"oversized\"` so the caller\n * can fail the cell loudly rather than silently dropping the image.\n */\nexport function parseAttachmentDisplay(payload: unknown): KernelAttachment | \"oversized\" | undefined {\n\tif (!isRecord(payload)) {\n\t\treturn undefined;\n\t}\n\tconst { mime_type: mimeType, data, path } = payload;\n\tif (typeof mimeType !== \"string\" || typeof data !== \"string\") {\n\t\treturn undefined;\n\t}\n\tif (data.length > MAX_ATTACHMENT_DATA_CHARS) {\n\t\treturn \"oversized\";\n\t}\n\treturn { mimeType, data, path: typeof path === \"string\" ? path : undefined };\n}\n\nexport function parseSentAgentMessage(payload: unknown): KernelSentAgentMessage | undefined {\n\tif (!isRecord(payload) || !isRecord(payload.target)) {\n\t\treturn undefined;\n\t}\n\tconst { id, message, deliveryStatus, receiverRole, target } = payload;\n\tconst { activeSessionId, sessionId, sessionName } = target;\n\tif (\n\t\ttypeof id !== \"string\" ||\n\t\ttypeof message !== \"string\" ||\n\t\t(deliveryStatus !== \"delivered\" && deliveryStatus !== \"queued\") ||\n\t\ttypeof activeSessionId !== \"string\" ||\n\t\ttypeof sessionId !== \"string\"\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tid,\n\t\tmessage,\n\t\tdeliveryStatus,\n\t\t...(receiverRole === \"parent\" || receiverRole === \"sibling\" || receiverRole === \"child\" ? { receiverRole } : {}),\n\t\ttarget: {\n\t\t\tactiveSessionId,\n\t\t\tsessionId,\n\t\t\t...(typeof sessionName === \"string\" ? { sessionName } : {}),\n\t\t},\n\t};\n}\n\nexport function createKernelStartupAbortError(): Error {\n\treturn new Error(\"Kernel startup aborted\");\n}\n\nexport function raceStartupWithAbort<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n\tif (!signal) {\n\t\treturn promise;\n\t}\n\tif (signal.aborted) {\n\t\treturn Promise.reject(createKernelStartupAbortError());\n\t}\n\treturn new Promise<T>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tconst cleanup = () => signal.removeEventListener(\"abort\", abort);\n\t\tconst abort = () => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treject(createKernelStartupAbortError());\n\t\t};\n\t\tsignal.addEventListener(\"abort\", abort, { once: true });\n\t\tpromise.then(\n\t\t\t(value) => {\n\t\t\t\tif (settled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(error: unknown) => {\n\t\t\t\tif (settled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\treject(error);\n\t\t\t},\n\t\t);\n\t});\n}\n\nexport interface Deferred<T> {\n\tpromise: Promise<T>;\n\tresolve: (value: T) => void;\n\treject: (error: Error) => void;\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nexport function createDeferred<T>(): Deferred<T> {\n\tlet resolve!: (value: T) => void;\n\tlet reject!: (error: Error) => void;\n\tconst promise = new Promise<T>((promiseResolve, promiseReject) => {\n\t\tresolve = promiseResolve;\n\t\treject = promiseReject;\n\t});\n\treturn { promise, resolve, reject };\n}\n\nexport interface KernelShutdownOptions {\n\tsnapshot?: boolean;\n\tdrainHostRequests?: boolean;\n}\n\n/** A bounded owned lifecycle observation, not a namespace, restore, or live-health assertion. */\nexport interface KernelLifecycleState {\n\treadonly source: \"repl-manager\" | \"ipython-provisioner\" | \"unobserved\";\n\treadonly owner: string | null;\n\treadonly generation: number | null;\n\treadonly state: \"idle\" | \"starting\" | \"running\" | \"shutdown\" | \"provisioning\" | \"disposed\" | \"unobserved\";\n}\n\nexport interface CapturedKernelLifecycle {\n\treadonly snapshot: Readonly<KernelLifecycleState>;\n\t/** Checks the original observation; it never probes or substitutes a newer snapshot. */\n\tisCurrent(): boolean;\n}\n\nexport interface JobWatchProbeRequest {\n\twatchId: string;\n\tgeneration: string;\n\tresourceId: string;\n\tjobId: string;\n\tcompletionSource?: \"handle\" | \"probe\";\n\tcommand: string | null;\n\ttimeoutMs: number;\n}\n\n/** Public surface every kernel client exposes to the provisioner and session layer. */\nexport interface KernelClient {\n\treadonly ownerSessionId: string | undefined;\n\treadonly isRunning: boolean;\n\t/** Optional job_watch_probe_v1 capability. Never evaluates a notebook cell. */\n\tjobWatchProbe?(request: JobWatchProbeRequest): Promise<Record<string, unknown>>;\n\tcancelJobWatchProbe?(watchId: string): void;\n\tstart(options?: KernelStartOptions): Promise<void>;\n\texecute(code: string, opts?: ExecuteOptions): Promise<ExecuteResult>;\n\tshutdown(opts?: KernelShutdownOptions): Promise<boolean>;\n\trestart(): Promise<void>;\n\tkill(): Promise<void>;\n\tdisposeSync(): void;\n\tsnapshotState(): Promise<SnapshotResult | null>;\n\tpruneOversizedVariables(): Promise<SnapshotResult | null>;\n\trestoreState(): Promise<RestoreResult | null>;\n\tlistNamespaceNames(signal?: AbortSignal): Promise<string[] | null>;\n}\n\n// One registry serves every client kind; two parallel registries would\n// double-install process signal handlers.\nexport const liveKernels = new Set<KernelClient>();\nlet signalHandlersInstalled = false;\n\nregisterSessionResourceCleanup((sessionId) => {\n\tfor (const k of liveKernels) {\n\t\tif (!sessionId || k.ownerSessionId === sessionId) {\n\t\t\tvoid k.shutdown({ snapshot: true, drainHostRequests: true });\n\t\t}\n\t}\n});\n\nexport function installSignalHandlersOnce(): void {\n\tif (signalHandlersInstalled) return;\n\tsignalHandlersInstalled = true;\n\n\tconst asyncShutdown = async (): Promise<void> => {\n\t\t// These paths can await, so flush the namespace snapshot before tearing down.\n\t\tawait Promise.allSettled([...liveKernels].map((k) => k.shutdown({ snapshot: true })));\n\t};\n\n\t// `beforeExit` and signal handlers can await async cleanup. `exit`\n\t// can only do sync work (Node won't run pending microtasks past it),\n\t// so it falls back to `disposeSync()` which kills the child synchronously.\n\tprocess.on(\"beforeExit\", () => {\n\t\tvoid asyncShutdown();\n\t});\n\tprocess.on(\"SIGINT\", () => {\n\t\tvoid asyncShutdown().finally(() => process.exit(130));\n\t});\n\tprocess.on(\"SIGTERM\", () => {\n\t\tvoid asyncShutdown().finally(() => process.exit(143));\n\t});\n\tprocess.on(\"exit\", () => {\n\t\tfor (const k of liveKernels) k.disposeSync();\n\t});\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/core/kernel/shared.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAK/E,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAC9C,MAAM,CAAC,MAAM,gCAAgC,GAAG,IAAI,CAAC;AACrD,MAAM,CAAC,MAAM,0BAA0B,GAAG,IAAI,CAAC;AAC/C,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC;AACjD,MAAM,CAAC,MAAM,6BAA6B,GAAG,IAAI,CAAC;AAClD,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAC1C,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC;AAC9C,MAAM,CAAC,MAAM,iCAAiC,GAAG,GAAG,CAAC;AACrD,MAAM,CAAC,MAAM,oCAAoC,GAAG,GAAG,CAAC;AACxD,MAAM,mCAAmC,GACxC,4HAA4H,CAAC;AAE9H,MAAM,OAAO,6BAA8B,SAAQ,KAAK;IACvD,cAAc;QACb,KAAK,CAAC,mCAAmC,CAAC,CAAC;QAC3C,IAAI,CAAC,IAAI,GAAG,+BAA+B,CAAC;IAAA,CAC5C;CACD;AAyED,2DAA2D;AAC3D,MAAM,CAAC,MAAM,iBAAiB,GAAG,uCAAuC,CAAC;AAEzE,oEAAoE;AACpE,MAAM,CAAC,MAAM,uBAAuB,GAAG,6CAA6C,CAAC;AAErF,wEAAwE;AACxE,MAAM,CAAC,MAAM,0BAA0B,GAAG,gDAAgD,CAAC;AAE3F;;;;;GAKG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,UAAU,CAAC;AAsDpD,6EAA6E;AAC7E,MAAM,UAAU,gBAAgB,CAAC,OAAgB,EAAiC;IACjF,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC;IAClF,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC1F,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;AAAA,CAClG;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAgB,EAA8C;IACpG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IACpD,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9D,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,GAAG,yBAAyB,EAAE,CAAC;QAC7C,OAAO,WAAW,CAAC;IACpB,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;AAAA,CAC7E;AAED,MAAM,UAAU,qBAAqB,CAAC,OAAgB,EAAsC;IAC3F,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACrD,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACtE,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IAC3D,IACC,OAAO,EAAE,KAAK,QAAQ;QACtB,OAAO,OAAO,KAAK,QAAQ;QAC3B,CAAC,cAAc,KAAK,WAAW,IAAI,cAAc,KAAK,QAAQ,CAAC;QAC/D,OAAO,eAAe,KAAK,QAAQ;QACnC,OAAO,SAAS,KAAK,QAAQ,EAC5B,CAAC;QACF,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,OAAO;QACN,EAAE;QACF,OAAO;QACP,cAAc;QACd,GAAG,CAAC,YAAY,KAAK,QAAQ,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChH,MAAM,EAAE;YACP,eAAe;YACf,SAAS;YACT,GAAG,CAAC,OAAO,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC3D;KACD,CAAC;AAAA,CACF;AAED,MAAM,UAAU,6BAA6B,GAAU;IACtD,OAAO,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;AAAA,CAC3C;AAED,MAAM,UAAU,oBAAoB,CAAI,OAAmB,EAAE,MAA+B,EAAc;IACzG,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,OAAO,OAAO,CAAC;IAChB,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,OAAO,CAAC,MAAM,CAAC,6BAA6B,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;QAC1C,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACjE,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC;YACnB,IAAI,OAAO,EAAE,CAAC;gBACb,OAAO;YACR,CAAC;YACD,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,EAAE,CAAC;YACV,MAAM,CAAC,6BAA6B,EAAE,CAAC,CAAC;QAAA,CACxC,CAAC;QACF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACxD,OAAO,CAAC,IAAI,CACX,CAAC,KAAK,EAAE,EAAE,CAAC;YACV,IAAI,OAAO,EAAE,CAAC;gBACb,OAAO;YACR,CAAC;YACD,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,EAAE,CAAC;YACV,OAAO,CAAC,KAAK,CAAC,CAAC;QAAA,CACf,EACD,CAAC,KAAc,EAAE,EAAE,CAAC;YACnB,IAAI,OAAO,EAAE,CAAC;gBACb,OAAO;YACR,CAAC;YACD,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,EAAE,CAAC;YACV,MAAM,CAAC,KAAK,CAAC,CAAC;QAAA,CACd,CACD,CAAC;IAAA,CACF,CAAC,CAAC;AAAA,CACH;AAQD,MAAM,UAAU,QAAQ,CAAC,KAAc,EAAoC;IAC1E,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAAA,CAC5E;AAED,MAAM,UAAU,YAAY,CAAC,KAAc,EAAU;IACpD,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAAA,CAC9D;AAED,MAAM,UAAU,cAAc,GAAmB;IAChD,IAAI,OAA4B,CAAC;IACjC,IAAI,MAA+B,CAAC;IACpC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAI,CAAC,cAAc,EAAE,aAAa,EAAE,EAAE,CAAC;QACjE,OAAO,GAAG,cAAc,CAAC;QACzB,MAAM,GAAG,aAAa,CAAC;IAAA,CACvB,CAAC,CAAC;IACH,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AAAA,CACpC;AAqCD,uEAAuE;AACvE,0CAA0C;AAC1C,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAgB,CAAC;AACnD,IAAI,uBAAuB,GAAG,KAAK,CAAC;AAEpC,8BAA8B,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC;IAC7C,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC7B,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;YAClD,KAAK,CAAC,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9D,CAAC;IACF,CAAC;AAAA,CACD,CAAC,CAAC;AAEH,MAAM,UAAU,yBAAyB,GAAS;IACjD,IAAI,uBAAuB;QAAE,OAAO;IACpC,uBAAuB,GAAG,IAAI,CAAC;IAE/B,MAAM,aAAa,GAAG,KAAK,IAAmB,EAAE,CAAC;QAChD,8EAA8E;QAC9E,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAAA,CACtF,CAAC;IAEF,mEAAmE;IACnE,qEAAqE;IACrE,2EAA2E;IAC3E,OAAO,CAAC,EAAE,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC;QAC9B,KAAK,aAAa,EAAE,CAAC;IAAA,CACrB,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC;QAC1B,KAAK,aAAa,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAAA,CACtD,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC;QAC3B,KAAK,aAAa,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAAA,CACtD,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC;QACxB,KAAK,MAAM,CAAC,IAAI,WAAW;YAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAAA,CAC7C,CAAC,CAAC;AAAA,CACH","sourcesContent":["import { registerSessionResourceCleanup } from \"@ponythewhite/base-context-ai\";\nimport type { NativeRecoveryResponse } from \"../selective-recovery.js\";\nimport type { KernelBootstrapProgressHandler, KernelPythonSkill } from \"./bootstrap.js\";\nimport type { RestoreResult, SnapshotResult } from \"./state-snapshot.js\";\n\nexport const DEFAULT_MAX_OUTPUT_CHARS = 65536;\nexport const HOST_REQUEST_SHUTDOWN_TIMEOUT_MS = 5000;\nexport const KERNEL_SHUTDOWN_TIMEOUT_MS = 5000;\nexport const DEFAULT_SNAPSHOT_DEBOUNCE_MS = 1500;\nexport const SNAPSHOT_EXECUTION_TIMEOUT_MS = 5000;\nexport const KERNEL_ABORT_GRACE_MS = 1000;\nexport const KERNEL_BUSY_REUSE_WAIT_MS = 5000;\nexport const KERNEL_BUSY_INTERRUPT_INTERVAL_MS = 500;\nexport const MAX_LATE_SENT_AGENT_MESSAGE_HANDLERS = 256;\nconst KERNEL_BUSY_AFTER_INTERRUPT_MESSAGE =\n\t\"The Python kernel is still running the previously interrupted cell. Wait and try again, or kill the kernel to start fresh.\";\n\nexport class KernelBusyAfterInterruptError extends Error {\n\tconstructor() {\n\t\tsuper(KERNEL_BUSY_AFTER_INTERRUPT_MESSAGE);\n\t\tthis.name = \"KernelBusyAfterInterruptError\";\n\t}\n}\n\n/**\n * Handles one typed request from Python code running in the kernel.\n * The returned record is delivered verbatim to the Python caller.\n */\nexport interface HostRequestContext {\n\t/** Actual active tool/cell cancellation; never read from the Python payload. */\n\tsignal?: AbortSignal;\n\t/** A host-reserved share of this cell's aggregate recovery response budget. */\n\tnativeRecovery?: { maxBytes: number };\n}\n\nexport type HostRequestHandler = (\n\tpayload: Record<string, unknown>,\n\tcontext?: HostRequestContext,\n) => Promise<Record<string, unknown>>;\n\n/** Host request handlers keyed by request type (e.g. \"rlm.run\", \"goal.complete\"). */\nexport type HostRequestHandlers = Record<string, HostRequestHandler>;\n\n/** Where and how to persist the kernel's user namespace so it survives resume. */\nexport interface KernelSnapshotConfig {\n\t/** Absolute path for the dill payload. */\n\tpath: string;\n\t/** Absolute path for the JSON manifest written alongside the payload. */\n\tmanifestPath: string;\n\t/** Maximum aggregate snapshot size. Default 256 MiB. */\n\tmaxBytes?: number;\n\t/** Maximum serialized size of one variable. Default 16 MiB. */\n\tmaxVariableBytes?: number;\n\t/** Debounce window for the auto-snapshot after a successful execution. Default 1500 ms. */\n\tdebounceMs?: number;\n}\n\nexport interface KernelManagerOptions {\n\t/** Python interpreter with the kernel runtime available. Defaults to the auto-bootstrapped kernel. */\n\tpython?: string;\n\tcwd?: string;\n\tenv?: Record<string, string>;\n\tsessionId?: string;\n\thostHandlers?: HostRequestHandlers;\n\tpythonSkills?: readonly KernelPythonSkill[];\n\t/** Persist/revive the user namespace across kernel restarts and session resume. */\n\tsnapshot?: KernelSnapshotConfig;\n\t/** Runtime bootstrap re-run on a protocol-repaired kernel so live handles (rlm, bash, skills) exist again. */\n\tbootstrapCode?: string;\n\t/** File receiving the kernel process's stderr, rotated once at each spawn. */\n\tstderrLogPath?: string;\n}\n\nexport interface KernelStartOptions {\n\tonBootstrapProgress?: KernelBootstrapProgressHandler;\n\tsignal?: AbortSignal;\n}\n\nexport interface ExecuteOptions {\n\t/** Only a finalized ipython tool call can retain recovery output; not bootstrap/state cells. */\n\tnativeRecovery?: boolean;\n\t/** @internal Host-only closure captured by the admitted owned executor; never sent to Python. */\n\trunNativeRecovery?: <T>(read: () => Promise<T>) => Promise<T>;\n\t/** Aborting interrupts the kernel out-of-band. */\n\tsignal?: AbortSignal;\n\tonStream?: (chunk: string, name: \"stdout\" | \"stderr\") => void;\n\tonLateSentAgentMessage?: (message: KernelSentAgentMessage) => void;\n\t/** Cap stdout / stderr / result at this many characters. Default 65536. */\n\tmaxOutputChars?: number;\n\t/** Synthetic host cell (snapshot/restore/list); excluded from lastCellCode attribution. */\n\tinternal?: boolean;\n\t/** The protocol repair's own restore; exempt from waiting on the repair it belongs to. */\n\tprotocolRepair?: boolean;\n}\n\n/** MIME tag the `edit` skill emits diff payloads under. */\nexport const DIFF_DISPLAY_MIME = \"application/vnd.prime-agent.diff+json\";\n\n/** MIME tag the `attach-image` skill emits media payloads under. */\nexport const ATTACHMENT_DISPLAY_MIME = \"application/vnd.prime-agent.attachment+json\";\n\n/** MIME tag the `agent-message` skill emits after sending a message. */\nexport const AGENT_MESSAGE_DISPLAY_MIME = \"application/vnd.prime-agent.agent-message+json\";\n\n/**\n * Hard ceiling on a single attachment's base64 payload, a defensive guard\n * against a runaway direct display emit. The `attach-image` skill caps\n * its own images well under this (see `_MAX_IMAGE_BYTES`), so a skill-produced\n * attachment is never dropped here — only a non-skill emit can hit this.\n */\nexport const MAX_ATTACHMENT_DATA_CHARS = 10_000_000;\n\n/** One file edit, captured from a {@link DIFF_DISPLAY_MIME} display payload. */\nexport interface KernelDiffDisplay {\n\tpath: string;\n\toldStr: string;\n\tnewStr: string;\n\t/** 1-based line where `oldStr` begins in the file, for absolute line numbers. */\n\tstartLine?: number;\n}\n\n/** One media attachment, captured from an {@link ATTACHMENT_DISPLAY_MIME} display payload. */\nexport interface KernelAttachment {\n\tmimeType: string;\n\t/** base64-encoded bytes. */\n\tdata: string;\n\t/** Source path, surfaced to the TUI renderer. */\n\tpath?: string;\n}\n\nexport interface KernelSentAgentMessage {\n\tid: string;\n\tmessage: string;\n\tdeliveryStatus: \"delivered\" | \"queued\";\n\treceiverRole?: \"parent\" | \"sibling\" | \"child\";\n\ttarget: {\n\t\tactiveSessionId: string;\n\t\tsessionId: string;\n\t\tsessionName?: string;\n\t};\n}\n\nexport interface ExecuteResult {\n\t/** Whether the finalized textual output was captured without host truncation or interruption. */\n\toutputComplete?: boolean;\n\t/** Host-selected public data transferred once to the normal finalized ipython result. */\n\tnativeRecoveries?: NativeRecoveryResponse[];\n\tstdout: string;\n\tstderr: string;\n\t/** Text of the cell's trailing expression value, if the cell produced one. */\n\tresult?: string;\n\t/** Diffs emitted via display events, in order. */\n\tdiffs?: KernelDiffDisplay[];\n\t/** Media attachments emitted via display events, in order. */\n\tattachments?: KernelAttachment[];\n\t/** Agent messages sent from this cell, in order. */\n\tsentAgentMessages?: KernelSentAgentMessage[];\n\t/** Output that arrived without this cell's id (user threads, other cells' leftovers, raw fd writes). */\n\tbackgroundOutput?: string;\n\tstatus: \"ok\" | \"error\" | \"aborted\";\n\terror?: { ename: string; evalue: string; traceback: string[] };\n\tdurationMs: number;\n}\n\n/** Parse a {@link DIFF_DISPLAY_MIME} payload, tolerating malformed input. */\nexport function parseDiffDisplay(payload: unknown): KernelDiffDisplay | undefined {\n\tif (!isRecord(payload)) {\n\t\treturn undefined;\n\t}\n\tconst { path, old_str: oldStr, new_str: newStr, start_line: startLine } = payload;\n\tif (typeof path !== \"string\" || typeof oldStr !== \"string\" || typeof newStr !== \"string\") {\n\t\treturn undefined;\n\t}\n\treturn { path, oldStr, newStr, startLine: typeof startLine === \"number\" ? startLine : undefined };\n}\n\n/**\n * Parse an {@link ATTACHMENT_DISPLAY_MIME} payload. Malformed payloads are\n * tolerantly ignored (`undefined`); a well-formed payload exceeding\n * {@link MAX_ATTACHMENT_DATA_CHARS} is reported as `\"oversized\"` so the caller\n * can fail the cell loudly rather than silently dropping the image.\n */\nexport function parseAttachmentDisplay(payload: unknown): KernelAttachment | \"oversized\" | undefined {\n\tif (!isRecord(payload)) {\n\t\treturn undefined;\n\t}\n\tconst { mime_type: mimeType, data, path } = payload;\n\tif (typeof mimeType !== \"string\" || typeof data !== \"string\") {\n\t\treturn undefined;\n\t}\n\tif (data.length > MAX_ATTACHMENT_DATA_CHARS) {\n\t\treturn \"oversized\";\n\t}\n\treturn { mimeType, data, path: typeof path === \"string\" ? path : undefined };\n}\n\nexport function parseSentAgentMessage(payload: unknown): KernelSentAgentMessage | undefined {\n\tif (!isRecord(payload) || !isRecord(payload.target)) {\n\t\treturn undefined;\n\t}\n\tconst { id, message, deliveryStatus, receiverRole, target } = payload;\n\tconst { activeSessionId, sessionId, sessionName } = target;\n\tif (\n\t\ttypeof id !== \"string\" ||\n\t\ttypeof message !== \"string\" ||\n\t\t(deliveryStatus !== \"delivered\" && deliveryStatus !== \"queued\") ||\n\t\ttypeof activeSessionId !== \"string\" ||\n\t\ttypeof sessionId !== \"string\"\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tid,\n\t\tmessage,\n\t\tdeliveryStatus,\n\t\t...(receiverRole === \"parent\" || receiverRole === \"sibling\" || receiverRole === \"child\" ? { receiverRole } : {}),\n\t\ttarget: {\n\t\t\tactiveSessionId,\n\t\t\tsessionId,\n\t\t\t...(typeof sessionName === \"string\" ? { sessionName } : {}),\n\t\t},\n\t};\n}\n\nexport function createKernelStartupAbortError(): Error {\n\treturn new Error(\"Kernel startup aborted\");\n}\n\nexport function raceStartupWithAbort<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n\tif (!signal) {\n\t\treturn promise;\n\t}\n\tif (signal.aborted) {\n\t\treturn Promise.reject(createKernelStartupAbortError());\n\t}\n\treturn new Promise<T>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tconst cleanup = () => signal.removeEventListener(\"abort\", abort);\n\t\tconst abort = () => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treject(createKernelStartupAbortError());\n\t\t};\n\t\tsignal.addEventListener(\"abort\", abort, { once: true });\n\t\tpromise.then(\n\t\t\t(value) => {\n\t\t\t\tif (settled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(error: unknown) => {\n\t\t\t\tif (settled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\treject(error);\n\t\t\t},\n\t\t);\n\t});\n}\n\nexport interface Deferred<T> {\n\tpromise: Promise<T>;\n\tresolve: (value: T) => void;\n\treject: (error: Error) => void;\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nexport function createDeferred<T>(): Deferred<T> {\n\tlet resolve!: (value: T) => void;\n\tlet reject!: (error: Error) => void;\n\tconst promise = new Promise<T>((promiseResolve, promiseReject) => {\n\t\tresolve = promiseResolve;\n\t\treject = promiseReject;\n\t});\n\treturn { promise, resolve, reject };\n}\n\nexport interface KernelShutdownOptions {\n\tsnapshot?: boolean;\n\tdrainHostRequests?: boolean;\n}\n\n/** A bounded owned lifecycle observation, not a namespace, restore, or live-health assertion. */\nexport interface KernelLifecycleState {\n\treadonly source: \"repl-manager\" | \"ipython-provisioner\" | \"unobserved\";\n\treadonly owner: string | null;\n\treadonly generation: number | null;\n\treadonly state: \"idle\" | \"starting\" | \"running\" | \"shutdown\" | \"provisioning\" | \"disposed\" | \"unobserved\";\n}\n\nexport interface CapturedKernelLifecycle {\n\treadonly snapshot: Readonly<KernelLifecycleState>;\n\t/** Checks the original observation; it never probes or substitutes a newer snapshot. */\n\tisCurrent(): boolean;\n}\n\n/** Public surface every kernel client exposes to the provisioner and session layer. */\nexport interface KernelClient {\n\treadonly ownerSessionId: string | undefined;\n\treadonly isRunning: boolean;\n\tstart(options?: KernelStartOptions): Promise<void>;\n\texecute(code: string, opts?: ExecuteOptions): Promise<ExecuteResult>;\n\tshutdown(opts?: KernelShutdownOptions): Promise<boolean>;\n\trestart(): Promise<void>;\n\tkill(): Promise<void>;\n\tdisposeSync(): void;\n\tsnapshotState(): Promise<SnapshotResult | null>;\n\tpruneOversizedVariables(): Promise<SnapshotResult | null>;\n\trestoreState(): Promise<RestoreResult | null>;\n\tlistNamespaceNames(signal?: AbortSignal): Promise<string[] | null>;\n}\n\n// One registry serves every client kind; two parallel registries would\n// double-install process signal handlers.\nexport const liveKernels = new Set<KernelClient>();\nlet signalHandlersInstalled = false;\n\nregisterSessionResourceCleanup((sessionId) => {\n\tfor (const k of liveKernels) {\n\t\tif (!sessionId || k.ownerSessionId === sessionId) {\n\t\t\tvoid k.shutdown({ snapshot: true, drainHostRequests: true });\n\t\t}\n\t}\n});\n\nexport function installSignalHandlersOnce(): void {\n\tif (signalHandlersInstalled) return;\n\tsignalHandlersInstalled = true;\n\n\tconst asyncShutdown = async (): Promise<void> => {\n\t\t// These paths can await, so flush the namespace snapshot before tearing down.\n\t\tawait Promise.allSettled([...liveKernels].map((k) => k.shutdown({ snapshot: true })));\n\t};\n\n\t// `beforeExit` and signal handlers can await async cleanup. `exit`\n\t// can only do sync work (Node won't run pending microtasks past it),\n\t// so it falls back to `disposeSync()` which kills the child synchronously.\n\tprocess.on(\"beforeExit\", () => {\n\t\tvoid asyncShutdown();\n\t});\n\tprocess.on(\"SIGINT\", () => {\n\t\tvoid asyncShutdown().finally(() => process.exit(130));\n\t});\n\tprocess.on(\"SIGTERM\", () => {\n\t\tvoid asyncShutdown().finally(() => process.exit(143));\n\t});\n\tprocess.on(\"exit\", () => {\n\t\tfor (const k of liveKernels) k.disposeSync();\n\t});\n}\n"]}
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/core/kernel/shared.ts"],"names":[],"mappings":"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","sourcesContent":["import { registerSessionResourceCleanup } from \"@ponythewhite/base-context-ai\";\nimport type { NativeRecoveryResponse } from \"../selective-recovery.js\";\nimport type { KernelBootstrapProgressHandler, KernelPythonSkill } from \"./bootstrap.js\";\nimport type { RestoreResult, SnapshotResult } from \"./state-snapshot.js\";\n\nexport const DEFAULT_MAX_OUTPUT_CHARS = 65536;\nexport const HOST_REQUEST_SHUTDOWN_TIMEOUT_MS = 5000;\nexport const KERNEL_SHUTDOWN_TIMEOUT_MS = 5000;\nexport const DEFAULT_SNAPSHOT_DEBOUNCE_MS = 1500;\nexport const SNAPSHOT_EXECUTION_TIMEOUT_MS = 5000;\nexport const KERNEL_ABORT_GRACE_MS = 1000;\nexport const KERNEL_BUSY_REUSE_WAIT_MS = 5000;\nexport const KERNEL_BUSY_INTERRUPT_INTERVAL_MS = 500;\nexport const MAX_LATE_SENT_AGENT_MESSAGE_HANDLERS = 256;\nconst KERNEL_BUSY_AFTER_INTERRUPT_MESSAGE =\n\t\"The Python kernel is still running the previously interrupted cell. Wait and try again, or kill the kernel to start fresh.\";\n\nexport class KernelBusyAfterInterruptError extends Error {\n\tconstructor() {\n\t\tsuper(KERNEL_BUSY_AFTER_INTERRUPT_MESSAGE);\n\t\tthis.name = \"KernelBusyAfterInterruptError\";\n\t}\n}\n\n/**\n * Handles one typed request from Python code running in the kernel.\n * The returned record is delivered verbatim to the Python caller.\n */\nexport interface HostRequestContext {\n\t/** Actual active tool/cell cancellation; never read from the Python payload. */\n\tsignal?: AbortSignal;\n\t/** A host-reserved share of this cell's aggregate recovery response budget. */\n\tnativeRecovery?: { maxBytes: number };\n}\n\nexport type HostRequestHandler = (\n\tpayload: Record<string, unknown>,\n\tcontext?: HostRequestContext,\n) => Promise<Record<string, unknown>>;\n\n/** Host request handlers keyed by request type (e.g. \"rlm.run\", \"goal.complete\"). */\nexport type HostRequestHandlers = Record<string, HostRequestHandler>;\n\n/** Where and how to persist the kernel's user namespace so it survives resume. */\nexport interface KernelSnapshotConfig {\n\t/** Absolute path for the dill payload. */\n\tpath: string;\n\t/** Absolute path for the JSON manifest written alongside the payload. */\n\tmanifestPath: string;\n\t/** Maximum aggregate snapshot size. Default 256 MiB. */\n\tmaxBytes?: number;\n\t/** Maximum serialized size of one variable. Default 16 MiB. */\n\tmaxVariableBytes?: number;\n\t/** Debounce window for the auto-snapshot after a successful execution. Default 1500 ms. */\n\tdebounceMs?: number;\n}\n\nexport interface KernelManagerOptions {\n\t/** Python interpreter with the kernel runtime available. Defaults to the auto-bootstrapped kernel. */\n\tpython?: string;\n\tcwd?: string;\n\tenv?: Record<string, string>;\n\tsessionId?: string;\n\thostHandlers?: HostRequestHandlers;\n\tpythonSkills?: readonly KernelPythonSkill[];\n\t/** Persist/revive the user namespace across kernel restarts and session resume. */\n\tsnapshot?: KernelSnapshotConfig;\n\t/** Runtime bootstrap re-run on a protocol-repaired kernel so live handles (rlm, bash, skills) exist again. */\n\tbootstrapCode?: string;\n\t/** File receiving the kernel process's stderr, rotated once at each spawn. */\n\tstderrLogPath?: string;\n}\n\nexport interface KernelStartOptions {\n\tonBootstrapProgress?: KernelBootstrapProgressHandler;\n\tsignal?: AbortSignal;\n}\n\nexport interface ExecuteOptions {\n\t/** Only a finalized ipython tool call can retain recovery output; not bootstrap/state cells. */\n\tnativeRecovery?: boolean;\n\t/** @internal Host-only closure captured by the admitted owned executor; never sent to Python. */\n\trunNativeRecovery?: <T>(read: () => Promise<T>) => Promise<T>;\n\t/** Aborting interrupts the kernel out-of-band. */\n\tsignal?: AbortSignal;\n\tonStream?: (chunk: string, name: \"stdout\" | \"stderr\") => void;\n\tonLateSentAgentMessage?: (message: KernelSentAgentMessage) => void;\n\t/** Cap stdout / stderr / result at this many characters. Default 65536. */\n\tmaxOutputChars?: number;\n\t/** Synthetic host cell (snapshot/restore/list); excluded from lastCellCode attribution. */\n\tinternal?: boolean;\n\t/** The protocol repair's own restore; exempt from waiting on the repair it belongs to. */\n\tprotocolRepair?: boolean;\n}\n\n/** MIME tag the `edit` skill emits diff payloads under. */\nexport const DIFF_DISPLAY_MIME = \"application/vnd.prime-agent.diff+json\";\n\n/** MIME tag the `attach-image` skill emits media payloads under. */\nexport const ATTACHMENT_DISPLAY_MIME = \"application/vnd.prime-agent.attachment+json\";\n\n/** MIME tag the `agent-message` skill emits after sending a message. */\nexport const AGENT_MESSAGE_DISPLAY_MIME = \"application/vnd.prime-agent.agent-message+json\";\n\n/**\n * Hard ceiling on a single attachment's base64 payload, a defensive guard\n * against a runaway direct display emit. The `attach-image` skill caps\n * its own images well under this (see `_MAX_IMAGE_BYTES`), so a skill-produced\n * attachment is never dropped here — only a non-skill emit can hit this.\n */\nexport const MAX_ATTACHMENT_DATA_CHARS = 10_000_000;\n\n/** One file edit, captured from a {@link DIFF_DISPLAY_MIME} display payload. */\nexport interface KernelDiffDisplay {\n\tpath: string;\n\toldStr: string;\n\tnewStr: string;\n\t/** 1-based line where `oldStr` begins in the file, for absolute line numbers. */\n\tstartLine?: number;\n}\n\n/** One media attachment, captured from an {@link ATTACHMENT_DISPLAY_MIME} display payload. */\nexport interface KernelAttachment {\n\tmimeType: string;\n\t/** base64-encoded bytes. */\n\tdata: string;\n\t/** Source path, surfaced to the TUI renderer. */\n\tpath?: string;\n}\n\nexport interface KernelSentAgentMessage {\n\tid: string;\n\tmessage: string;\n\tdeliveryStatus: \"delivered\" | \"queued\";\n\treceiverRole?: \"parent\" | \"sibling\" | \"child\";\n\ttarget: {\n\t\tactiveSessionId: string;\n\t\tsessionId: string;\n\t\tsessionName?: string;\n\t};\n}\n\nexport interface ExecuteResult {\n\t/** Whether the finalized textual output was captured without host truncation or interruption. */\n\toutputComplete?: boolean;\n\t/** Host-selected public data transferred once to the normal finalized ipython result. */\n\tnativeRecoveries?: NativeRecoveryResponse[];\n\tstdout: string;\n\tstderr: string;\n\t/** Text of the cell's trailing expression value, if the cell produced one. */\n\tresult?: string;\n\t/** Diffs emitted via display events, in order. */\n\tdiffs?: KernelDiffDisplay[];\n\t/** Media attachments emitted via display events, in order. */\n\tattachments?: KernelAttachment[];\n\t/** Agent messages sent from this cell, in order. */\n\tsentAgentMessages?: KernelSentAgentMessage[];\n\t/** Output that arrived without this cell's id (user threads, other cells' leftovers, raw fd writes). */\n\tbackgroundOutput?: string;\n\tstatus: \"ok\" | \"error\" | \"aborted\";\n\terror?: { ename: string; evalue: string; traceback: string[] };\n\tdurationMs: number;\n}\n\n/** Parse a {@link DIFF_DISPLAY_MIME} payload, tolerating malformed input. */\nexport function parseDiffDisplay(payload: unknown): KernelDiffDisplay | undefined {\n\tif (!isRecord(payload)) {\n\t\treturn undefined;\n\t}\n\tconst { path, old_str: oldStr, new_str: newStr, start_line: startLine } = payload;\n\tif (typeof path !== \"string\" || typeof oldStr !== \"string\" || typeof newStr !== \"string\") {\n\t\treturn undefined;\n\t}\n\treturn { path, oldStr, newStr, startLine: typeof startLine === \"number\" ? startLine : undefined };\n}\n\n/**\n * Parse an {@link ATTACHMENT_DISPLAY_MIME} payload. Malformed payloads are\n * tolerantly ignored (`undefined`); a well-formed payload exceeding\n * {@link MAX_ATTACHMENT_DATA_CHARS} is reported as `\"oversized\"` so the caller\n * can fail the cell loudly rather than silently dropping the image.\n */\nexport function parseAttachmentDisplay(payload: unknown): KernelAttachment | \"oversized\" | undefined {\n\tif (!isRecord(payload)) {\n\t\treturn undefined;\n\t}\n\tconst { mime_type: mimeType, data, path } = payload;\n\tif (typeof mimeType !== \"string\" || typeof data !== \"string\") {\n\t\treturn undefined;\n\t}\n\tif (data.length > MAX_ATTACHMENT_DATA_CHARS) {\n\t\treturn \"oversized\";\n\t}\n\treturn { mimeType, data, path: typeof path === \"string\" ? path : undefined };\n}\n\nexport function parseSentAgentMessage(payload: unknown): KernelSentAgentMessage | undefined {\n\tif (!isRecord(payload) || !isRecord(payload.target)) {\n\t\treturn undefined;\n\t}\n\tconst { id, message, deliveryStatus, receiverRole, target } = payload;\n\tconst { activeSessionId, sessionId, sessionName } = target;\n\tif (\n\t\ttypeof id !== \"string\" ||\n\t\ttypeof message !== \"string\" ||\n\t\t(deliveryStatus !== \"delivered\" && deliveryStatus !== \"queued\") ||\n\t\ttypeof activeSessionId !== \"string\" ||\n\t\ttypeof sessionId !== \"string\"\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tid,\n\t\tmessage,\n\t\tdeliveryStatus,\n\t\t...(receiverRole === \"parent\" || receiverRole === \"sibling\" || receiverRole === \"child\" ? { receiverRole } : {}),\n\t\ttarget: {\n\t\t\tactiveSessionId,\n\t\t\tsessionId,\n\t\t\t...(typeof sessionName === \"string\" ? { sessionName } : {}),\n\t\t},\n\t};\n}\n\nexport function createKernelStartupAbortError(): Error {\n\treturn new Error(\"Kernel startup aborted\");\n}\n\nexport function raceStartupWithAbort<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n\tif (!signal) {\n\t\treturn promise;\n\t}\n\tif (signal.aborted) {\n\t\treturn Promise.reject(createKernelStartupAbortError());\n\t}\n\treturn new Promise<T>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tconst cleanup = () => signal.removeEventListener(\"abort\", abort);\n\t\tconst abort = () => {\n\t\t\tif (settled) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\treject(createKernelStartupAbortError());\n\t\t};\n\t\tsignal.addEventListener(\"abort\", abort, { once: true });\n\t\tpromise.then(\n\t\t\t(value) => {\n\t\t\t\tif (settled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(error: unknown) => {\n\t\t\t\tif (settled) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettled = true;\n\t\t\t\tcleanup();\n\t\t\t\treject(error);\n\t\t\t},\n\t\t);\n\t});\n}\n\nexport interface Deferred<T> {\n\tpromise: Promise<T>;\n\tresolve: (value: T) => void;\n\treject: (error: Error) => void;\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function errorMessage(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nexport function createDeferred<T>(): Deferred<T> {\n\tlet resolve!: (value: T) => void;\n\tlet reject!: (error: Error) => void;\n\tconst promise = new Promise<T>((promiseResolve, promiseReject) => {\n\t\tresolve = promiseResolve;\n\t\treject = promiseReject;\n\t});\n\treturn { promise, resolve, reject };\n}\n\nexport interface KernelShutdownOptions {\n\tsnapshot?: boolean;\n\tdrainHostRequests?: boolean;\n}\n\n/** A bounded owned lifecycle observation, not a namespace, restore, or live-health assertion. */\nexport interface KernelLifecycleState {\n\treadonly source: \"repl-manager\" | \"ipython-provisioner\" | \"unobserved\";\n\treadonly owner: string | null;\n\treadonly generation: number | null;\n\treadonly state: \"idle\" | \"starting\" | \"running\" | \"shutdown\" | \"provisioning\" | \"disposed\" | \"unobserved\";\n}\n\nexport interface CapturedKernelLifecycle {\n\treadonly snapshot: Readonly<KernelLifecycleState>;\n\t/** Checks the original observation; it never probes or substitutes a newer snapshot. */\n\tisCurrent(): boolean;\n}\n\nexport interface JobWatchProbeRequest {\n\twatchId: string;\n\tgeneration: string;\n\tresourceId: string;\n\tjobId: string;\n\tcompletionSource?: \"handle\" | \"probe\";\n\tcommand: string | null;\n\ttimeoutMs: number;\n}\n\n/** Public surface every kernel client exposes to the provisioner and session layer. */\nexport interface KernelClient {\n\treadonly ownerSessionId: string | undefined;\n\treadonly isRunning: boolean;\n\t/** Optional job_watch_probe_v1 capability. Never evaluates a notebook cell. */\n\tjobWatchProbe?(request: JobWatchProbeRequest): Promise<Record<string, unknown>>;\n\tcancelJobWatchProbe?(watchId: string): void;\n\tstart(options?: KernelStartOptions): Promise<void>;\n\texecute(code: string, opts?: ExecuteOptions): Promise<ExecuteResult>;\n\tshutdown(opts?: KernelShutdownOptions): Promise<boolean>;\n\trestart(): Promise<void>;\n\tkill(): Promise<void>;\n\tdisposeSync(): void;\n\tsnapshotState(): Promise<SnapshotResult | null>;\n\tpruneOversizedVariables(): Promise<SnapshotResult | null>;\n\trestoreState(): Promise<RestoreResult | null>;\n\tlistNamespaceNames(signal?: AbortSignal): Promise<string[] | null>;\n}\n\n// One registry serves every client kind; two parallel registries would\n// double-install process signal handlers.\nexport const liveKernels = new Set<KernelClient>();\nlet signalHandlersInstalled = false;\n\nregisterSessionResourceCleanup((sessionId) => {\n\tfor (const k of liveKernels) {\n\t\tif (!sessionId || k.ownerSessionId === sessionId) {\n\t\t\tvoid k.shutdown({ snapshot: true, drainHostRequests: true });\n\t\t}\n\t}\n});\n\nexport function installSignalHandlersOnce(): void {\n\tif (signalHandlersInstalled) return;\n\tsignalHandlersInstalled = true;\n\n\tconst asyncShutdown = async (): Promise<void> => {\n\t\t// These paths can await, so flush the namespace snapshot before tearing down.\n\t\tawait Promise.allSettled([...liveKernels].map((k) => k.shutdown({ snapshot: true })));\n\t};\n\n\t// `beforeExit` and signal handlers can await async cleanup. `exit`\n\t// can only do sync work (Node won't run pending microtasks past it),\n\t// so it falls back to `disposeSync()` which kills the child synchronously.\n\tprocess.on(\"beforeExit\", () => {\n\t\tvoid asyncShutdown();\n\t});\n\tprocess.on(\"SIGINT\", () => {\n\t\tvoid asyncShutdown().finally(() => process.exit(130));\n\t});\n\tprocess.on(\"SIGTERM\", () => {\n\t\tvoid asyncShutdown().finally(() => process.exit(143));\n\t});\n\tprocess.on(\"exit\", () => {\n\t\tfor (const k of liveKernels) k.disposeSync();\n\t});\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"rlm.d.ts","sourceRoot":"","sources":["../../../src/core/prompts/rlm.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AA4CD,MAAM,WAAW,yBAAyB;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,yBAAyB,GAAG,MAAM,GAAG,SAAS,CAe9F;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CA6GhE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACpC,OAAO,GAAE;IAAE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CAAO,GACrG,MAAM,CAuBR","sourcesContent":["import { DEFAULT_RLM_EXTRA_IMPORT_LABELS } from \"../kernel/bootstrap.js\";\n\nexport interface RlmPromptOptions {\n\tcwd: string;\n\tskillsDir?: string;\n\tinstalledSkills?: string[];\n\tmessagesPath: string;\n\tallowRecursion?: boolean;\n\tdepth?: number;\n\tparentAgent?: string;\n\tactiveTools?: string[];\n}\n\nconst LONG_RUNNING_WORK_PROMPT = [\n\t\"For slow or independently completing work, use a nonblocking control loop: start the work, record its handle or output location, then end your turn. Read the result on a later turn or when a reply arrives.\",\n\t\"When delegation is available and useful, assign independent substantive tasks to separate workers. Start independent workers without waiting for each one sequentially, and let them run in parallel.\",\n\t\"Do not keep the turn open by polling with `time.sleep()` or shell `sleep`, and do not replace polling with a long blocking `await`. Await only the short operation needed to start work or inspect a result that is already available; otherwise end the turn.\",\n].join(\"\\n\");\n\nconst USER_PROGRESS_PROMPT =\n\t\"As the user-facing root agent, when work follows a plan, uses many subagents, or spans multiple turns, proactively give regular concise progress updates so the user does not have to ask. State the current plan, what has completed, any blockers, the proposed fixes, and the next actions. Lead with user-visible outcomes rather than internal process or gate names. Mention internal details only when they explain a blocker or decision. Send an update at meaningful milestones and before ending a turn while work is still running. Do not repeat unchanged status or interrupt short work with unnecessary updates.\";\n\nconst SIMPLIFIED_TECHNICAL_ENGLISH_PROMPT = [\n\t\"Use simplified technical English by default for user-facing prose.\",\n\t\"Prefer short sentences, common words, and concrete verbs. State one main action or fact per sentence when practical. Use lists for steps or conditions.\",\n\t\"Keep necessary technical terms, names, commands, code, paths, and exact quoted text unchanged. State uncertainty directly.\",\n\t\"Treat this as clarity guidance, not a claim of formal ASD-STE100 compliance. Preserve a user-requested format, tone, terminology, and necessary precision.\",\n].join(\"\\n\");\n\nconst REPL_CONTROL_PROMPT = [\n\t\"The `ipython` tool is a persistent Python REPL — the agent's long-lived control environment for reasoning, context management, state, tool orchestration, and recursive subcalls. Top-level `await` works directly. Use it to keep intermediate variables, inspect and transform outputs, and write small helper functions. Compaction removes individual variables whose serialized form exceeds 16 MiB; keep large source data on disk and reload it when needed.\",\n\t\"\",\n\t\"Reuse a named Python function for repeated multi-step work; keep one-off operations inline. Pass changing inputs explicitly and read current data each call. Save cross-task helpers as editable .py files with short project-skill instructions, without packaging them. After editing loaded code, execute its updated definition or invoke the saved file afresh.\",\n\t\"\",\n\t\"Python is the orchestration language: use Python for loops, conditionals, parsing, and state. Use `bash()` to invoke programs, not to write shell programs — no shell loops or heredocs; do those in Python.\",\n\t\"\",\n\t\"Do not assume the REPL is the native runtime of the external thing being investigated. A repository, package, service, dataset, paper, website, benchmark, or API may have its own environment and normal interface. Evaluate external systems through their own interface, then use the REPL to coordinate the process and analyze what comes back.\",\n\t\"\",\n\t\"`bash(command)` starts a shell command in the background and returns a handle immediately: `h = bash('npm test')`. Use `h.pid` / `h.running` for liveness, `h.tail(n)` / `h.output()` for combined stdout+stderr so far, `h.poll()` for a non-blocking result, `h.kill()` to terminate (SIGTERM, escalating to SIGKILL; on Windows kill() uses taskkill /T and detached or reparented descendants may survive), and `await h` (or `await bash('cmd')`) for the completed result with exit_code, output, and duration. Prefer bash() for long-running commands so the turn keeps working. Run shell commands with `bash()`, not `subprocess`/`os.system`: subprocess calls block the kernel, show the user nothing while they run, and spawn processes the harness cannot see or stop.\",\n\t\"\",\n\t\"Important: do not install dependencies into the kernel just to make an external project import or run there. If a project import, test, script, CLI, or dependency check is needed, run it through that project's own environment and normal command interface. For example, in a Python repo use its documented commands, `uv run ...`, `.venv/bin/python ...`, or the active project interpreter from the repo root. Treat failures from that native environment as the relevant result.\",\n\t\"\",\n\t\"Use Python for reading, searching, and editing files — it gives you reusable variables you can slice, filter, and act on without re-reading. Always assign read/search results to named variables so you can revisit them later.\",\n\t\"\",\n\t\"Each `bash()` call is its own process, so shell state does not persist between calls; use `os.chdir(...)` for the working directory and `os.environ[...]` for environment variables — both persist in the REPL and apply to later `bash()` calls.\",\n\t\"\",\n\t\"Python state in the kernel persists across cells: named variables, helper functions, classes, imports, notes, parsed outputs, and helper data structures all remain available in every later turn. Tool calls are themselves Python `await` expressions, so their return values can be bound to variables and composed into program logic just like any other call.\",\n\t\"\",\n\t\"Continual harness state is available as `rlm.harness` and `rlm.get_harness_state()`. CRUD calls are local to this Base Context session by default: `rlm.harness.create_memory(...)`, `rlm.harness.update_memory(...)`, `rlm.harness.delete_memory(...)`, `rlm.harness.create_skill(...)`, `rlm.harness.update_skill(...)`, `rlm.harness.delete_skill(...)`, `rlm.harness.create_subagent(...)`, `rlm.harness.update_subagent(...)`, `rlm.harness.delete_subagent(...)`, `rlm.harness.create_prompt_note(...)`, `rlm.harness.update_prompt_note(...)`, `rlm.harness.delete_prompt_note(...)`, plus `rlm.harness.record_refinement(...)` and `rlm.harness.overview()`. Use `global_=True` only for stable cross-session lessons; Python reserves `global`, so literal `global=True` is invalid syntax.\",\n\t\"\",\n\t\"Terminology: continual harness names the persisted prompt, memory, skill, and subagent layer; RLM names the runtime, Python REPL kernel, and native call interface exposed to the model.\",\n\t\"\",\n\t\"RLM-native call contract: installed Python skills are pre-imported modules. Read the matching SKILL.md and call its documented function, such as `await <skill_import>.<function>(...)`; when a CLI exists, use `<skill_import> ...` from shell. Continual harness skill entries are Python REPL skills with an explicit Python `reference` and `arguments` contract. Spawn a reusable delegation spec with `await rlm('sub-task')`; admission returns a child handle immediately. Results arrive only through an available messaging capability or files, never as an `rlm()` return value. Do not invent non-native wrappers such as `call_skill(...)` or `run_subagent(...)`.\",\n].join(\"\\n\");\n\nexport interface ChildAgentDoctrineOptions {\n\tdepth?: number;\n\tparentAgent?: string;\n\tinstalledSkills?: string[];\n\tactiveTools?: string[];\n}\n\nexport function buildChildAgentDoctrine(options: ChildAgentDoctrineOptions): string | undefined {\n\tconst depth = options.depth ?? 0;\n\tconst hasIpython = options.activeTools === undefined || options.activeTools.includes(\"ipython\");\n\tconst hasAgentMessage = options.installedSkills?.includes(\"agent_message\") ?? false;\n\tif (depth <= 0) return undefined;\n\n\tconst lines = [\n\t\t`You are a child agent spawned by ${options.parentAgent ?? \"your parent agent\"}. Task prompts are labeled \\`[task from parent]\\`.`,\n\t];\n\tif (hasAgentMessage && hasIpython) {\n\t\tlines.push(\n\t\t\t'When a task calls for an answer, reply explicitly with `await agent_message.send(message, receiver_role=\"parent\")`. Not every message or task needs a reply; continue cleanup after sending and go idle normally.',\n\t\t);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nexport function buildRlmPrompt(options: RlmPromptOptions): string {\n\tconst { cwd, skillsDir, messagesPath } = options;\n\tconst installedSkills = options.installedSkills ?? [];\n\tconst hasAgentMessage = installedSkills.includes(\"agent_message\");\n\tconst hasAgentObserve = installedSkills.includes(\"agent_observe\");\n\tconst allowRecursion = options.allowRecursion ?? true;\n\tconst depth = options.depth ?? 0;\n\tconst activeTools = options.activeTools ?? [];\n\tconst hasIpython = options.activeTools === undefined ? true : activeTools.includes(\"ipython\");\n\tconst canRunShellSkills = hasIpython || activeTools.includes(\"bash\");\n\tconst parts = [\n\t\t\"You are a general purpose agent that uses code to solve tasks.\",\n\t\t\"You solve tasks by breaking down problems into sub-tasks, writing and executing code, observing results, and iterating one step at a time.\",\n\t\t\"When you are done, stop calling tools and state your final answer.\",\n\t\t\"\",\n\t\tLONG_RUNNING_WORK_PROMPT,\n\t\t\"\",\n\t\t...(depth === 0 ? [USER_PROGRESS_PROMPT, \"\"] : []),\n\t\tSIMPLIFIED_TECHNICAL_ENGLISH_PROMPT,\n\t\t\"\",\n\t\t`Recursive agent depth: ${depth}`,\n\t\t`Pre-installed Python packages: ${DEFAULT_RLM_EXTRA_IMPORT_LABELS.join(\", \")}.`,\n\t\t\"Install additional packages with `uv pip install <pkg>` (this is a uv-managed venv with no pip module).\",\n\t];\n\n\tconst childDoctrine = buildChildAgentDoctrine(options);\n\tif (childDoctrine) {\n\t\tparts.push(\"\", childDoctrine);\n\t}\n\n\tconst skillLines: string[] = [];\n\tif (skillsDir) {\n\t\tskillLines.push(`Local skills live under ${skillsDir}. Read their SKILL.md files when helpful.`);\n\t}\n\tif (installedSkills.length > 0) {\n\t\tconst installed = installedSkills.map((skill) => `\\`${skill}\\``).join(\", \");\n\t\tif (hasIpython) {\n\t\t\tskillLines.push(`Installed Python skill modules (pre-imported): ${installed}.`);\n\t\t\tskillLines.push(\n\t\t\t\t\"Read each skill's SKILL.md for its API. Inspect a module with `help(<skill>)` or `dir(<skill>)`, then inspect a documented callable with `inspect.signature(<skill>.<function>)`.\",\n\t\t\t);\n\t\t} else if (canRunShellSkills) {\n\t\t\tskillLines.push(`Installed skills available as shell commands: ${installed}.`);\n\t\t}\n\t\tif (canRunShellSkills) {\n\t\t\tskillLines.push(\n\t\t\t\t\"Each skill is also available as a shell command by the same name: `<skill> ...`. Discover its CLI usage with `<skill> --help`.\",\n\t\t\t);\n\t\t}\n\t\tif (hasIpython && installedSkills.includes(\"edit\")) {\n\t\t\tskillLines.push(\n\t\t\t\t\"For targeted existing-file edits, prefer the pre-imported async `edit` skill from the REPL: `old = '''...'''; new = '''...'''; await edit(path=\\\"pkg/file.py\\\", old_str=old, new_str=new)`. Use exact old/new strings; if the text contains triple double quotes, use triple single-quoted variables or build `old`/`new` from inspected file slices.\",\n\t\t\t);\n\t\t}\n\t}\n\tif (skillLines.length > 0) {\n\t\tparts.push(\"\", ...skillLines);\n\t}\n\tif (hasAgentMessage) {\n\t\tparts.push(\n\t\t\t\"Agent messaging is restricted to your parent, siblings, and direct children; roots are siblings, and deeper communication relays through the intermediate child.\",\n\t\t);\n\t}\n\tif (hasAgentObserve) {\n\t\tparts.push(\n\t\t\t\"Agent observation is restricted to your parent, siblings, and direct children; roots are siblings, and deeper inspection relays through the intermediate child.\",\n\t\t);\n\t}\n\n\tif (allowRecursion && hasIpython) {\n\t\tparts.push(\n\t\t\t\"\",\n\t\t\t\"A callable `rlm` is already in your global namespace. `await rlm('sub-task')` spawns a child and returns immediately after task admission with `rlm_child_id`, `name`, `session_dir`, and `model`; it never waits for or returns the child's answer.\",\n\t\t\t\"Choose a stable child name with `await rlm('sub-task', name='api-reviewer')`; names must be unique among siblings. If omitted, the host generates a readable unique name.\",\n\t\t\t\"A child inherits your model. If a different model is explicitly requested, use `await rlm.find_models(...)` and an exact returned selector. An unavailable requested model fails spawn; decide whether to retry or omit `model`. Children also inherit your thinking level; the `thinking` option overrides it with any level the resolved child model supports, and an unsupported level fails spawn.\",\n\t\t);\n\t\tif (hasAgentMessage) {\n\t\t\tparts.push(\n\t\t\t\t\"Children reply explicitly with `await agent_message.send(message, receiver_role='parent')` when an answer is needed. Replies and follow-ups arrive as ordinary agent messages; not every task requires a reply.\",\n\t\t\t\t\"Use `await agent_message.list_agents()` to discover family and `await rlm.list_subagents()` to recover direct child handles. Use `agent_message.send(..., receiver_role='child', receiver_name=child.name)` for follow-ups.\",\n\t\t\t);\n\t\t} else {\n\t\t\tparts.push(\"Use `await rlm.list_subagents()` to recover direct child handles after admission.\");\n\t\t}\n\t\tif (hasAgentObserve) {\n\t\t\tparts.push(\n\t\t\t\t\"Use `agent_observe` to inspect a child's rollout. Observation is restricted to your parent, siblings, and direct children; relay through the intermediate child for deeper descendants.\",\n\t\t\t);\n\t\t} else {\n\t\t\tparts.push(\"Inspect files a child wrote when you need to collect its work without an observation capability.\");\n\t\t}\n\t\tparts.push(\n\t\t\t\"Spawn independent children in separate calls and end your turn instead of awaiting completion. Multiple replies may arrive over multiple turns. Delete a direct child explicitly with `await rlm.delete_subagent(child)` when it is no longer needed.\",\n\t\t);\n\t}\n\n\tif (hasIpython) {\n\t\tparts.push(\"\", REPL_CONTROL_PROMPT);\n\t\tif (installedSkills.includes(\"refine\")) {\n\t\t\tparts.push(\n\t\t\t\t\"\",\n\t\t\t\t\"Treat continual harness refinement as a small, evidence-backed update after observing a repeated failure or reusable tactic: diagnose the issue, update the smallest relevant continual harness component, validate on the next action, then record the outcome. Use `await refine.run()` to turn repeated delegation patterns into reusable subagent specs, repeated procedures into skills, durable facts/preferences into memories, and narrow behavioral policies into prompt addendums. It returns immediately and runs when the current turn ends, so continue working normally after calling it. Do not rewrite the whole continual harness when a focused memory, skill, prompt note, or subagent spec is enough.\",\n\t\t\t);\n\t\t}\n\t}\n\n\t// Runtime coordinates follow the fixed harness guidance without moving user/project instructions.\n\tparts.push(\"\", `Working directory: ${cwd}`, `Conversation log: ${messagesPath}`);\n\treturn parts.join(\"\\n\");\n}\n\n/**\n * Supplemental sub-agent delegation guidance, appended after the base RLM\n * prompt (see system-prompt.ts). The recursion block covers the mechanics\n * (`rlm(...)` admission and handle management); this block adds the\n * when and why in the same When -> Why -> menu order Claude Code's Agent tool\n * uses. The subagent-spec menu itself renders just after this, inside the\n * harness-state block.\n */\nexport function buildSubagentGuidance(\n\toptions: { includeRefineExamples?: boolean; hasAgentMessage?: boolean; hasAgentObserve?: boolean } = {},\n): string {\n\tconst lines = [\n\t\t\"# Delegating to sub-agents\",\n\t\t\"\",\n\t\t\"Spawn independent, self-contained work with `handle = await rlm('task', name='worker')`. This returns at admission, not completion; keep the handle to stop or inspect the child later.\",\n\t];\n\tif (options.hasAgentMessage) {\n\t\tlines.push(\n\t\t\t\"Ask for an explicit reply when needed. A child replies with `await agent_message.send(message, receiver_role='parent')`; parent follow-ups use `receiver_role='child'` plus the child's name or id. Not every message needs a reply.\",\n\t\t);\n\t}\n\tlines.push(\"Use `await rlm.list_subagents()` after kernel restart or compaction.\");\n\tif (options.hasAgentObserve) {\n\t\tlines.push(\"Use `agent_observe` for bounded transcript inspection.\");\n\t}\n\tlines.push(\n\t\t\"Have children write files and read those files for fan-in.\",\n\t\t\"Delegate parallel context-heavy research or independent implementation; do a single known lookup, edit, or command inline.\",\n\t);\n\tif (options.includeRefineExamples ?? true) {\n\t\tlines.push(\"Persist genuinely reusable delegation patterns with `await refine.run()`.\");\n\t}\n\treturn lines.join(\"\\n\");\n}\n"]}
1
+ {"version":3,"file":"rlm.d.ts","sourceRoot":"","sources":["../../../src/core/prompts/rlm.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAoDD,MAAM,WAAW,yBAAyB;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,yBAAyB,GAAG,MAAM,GAAG,SAAS,CAe9F;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CA6GhE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACpC,OAAO,GAAE;IAAE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CAAO,GACrG,MAAM,CAyBR","sourcesContent":["import { DEFAULT_RLM_EXTRA_IMPORT_LABELS } from \"../kernel/bootstrap.js\";\n\nexport interface RlmPromptOptions {\n\tcwd: string;\n\tskillsDir?: string;\n\tinstalledSkills?: string[];\n\tmessagesPath: string;\n\tallowRecursion?: boolean;\n\tdepth?: number;\n\tparentAgent?: string;\n\tactiveTools?: string[];\n}\n\nfunction buildLongRunningWorkPrompt(hasJobWatch: boolean): string {\n\treturn [\n\t\thasJobWatch\n\t\t\t? \"Use `job_watch` for repeated job checks after an authorized launch. Consume prepared evidence; call `park` and end the turn only when no independent work remains. Parking holds goal and autonomous continuation, not user input. Preserve every required report. Tiny one-off commands stay inline.\"\n\t\t\t: \"For slow or independently completing work, use a nonblocking control loop: start the work, record its handle or output location, then end your turn. Read the result on a later turn or when a reply arrives.\",\n\t\t\"When delegation is available and useful, assign independent substantive tasks to separate workers. Start independent workers without waiting for each one sequentially, and let them run in parallel.\",\n\t\t\"Do not keep the turn open by polling with `time.sleep()` or shell `sleep`, and do not replace polling with a long blocking `await`. Await only the short operation needed to start work or inspect a result that is already available; otherwise end the turn.\",\n\t].join(\"\\n\");\n}\n\nconst USER_PROGRESS_PROMPT =\n\t\"As the user-facing root agent, when work follows a plan, uses many subagents, or spans multiple turns, proactively give regular concise progress updates so the user does not have to ask. State the current plan, what has completed, any blockers, the proposed fixes, and the next actions. Lead with user-visible outcomes rather than internal process or gate names. Mention internal details only when they explain a blocker or decision. Send an update at meaningful milestones and before ending a turn while work is still running. Do not repeat unchanged status or interrupt short work with unnecessary updates.\";\n\nconst SIMPLIFIED_TECHNICAL_ENGLISH_PROMPT = [\n\t\"Use simplified technical English by default for user-facing prose.\",\n\t\"Prefer short sentences, common words, and concrete verbs. State one main action or fact per sentence when practical. Use lists for steps or conditions.\",\n\t\"Keep necessary technical terms, names, commands, code, paths, and exact quoted text unchanged. State uncertainty directly.\",\n\t\"Treat this as clarity guidance, not a claim of formal ASD-STE100 compliance. Preserve a user-requested format, tone, terminology, and necessary precision.\",\n].join(\"\\n\");\n\nfunction buildReplControlPrompt(hasBoundedInspect: boolean): string {\n\treturn [\n\t\t\"The `ipython` tool is a persistent Python REPL — the agent's long-lived control environment for reasoning, context management, state, tool orchestration, and recursive subcalls. Top-level `await` works directly. Use it to keep intermediate variables, inspect and transform outputs, and write small helper functions. Compaction removes individual variables whose serialized form exceeds 16 MiB; keep large source data on disk and reload it when needed.\",\n\t\t\"\",\n\t\t\"Reuse a named Python function for repeated multi-step work; keep one-off operations inline. Once a stable operation has been useful twice, save a small editable project .py helper without packaging it. Pass current commit, configuration, paths, resource identity, and limits explicitly instead of capturing stale globals; read current data each call. After editing loaded code, execute its updated definition or invoke the saved file afresh. Repetition never authorizes another deployment or experiment.\",\n\t\t\"\",\n\t\t\"Python is the orchestration language: use Python for loops, conditionals, parsing, and state. Use `bash()` to invoke programs, not to write shell programs — no shell loops or heredocs; do those in Python.\",\n\t\t\"\",\n\t\t\"Do not assume the REPL is the native runtime of the external thing being investigated. A repository, package, service, dataset, paper, website, benchmark, or API may have its own environment and normal interface. Evaluate external systems through their own interface, then use the REPL to coordinate the process and analyze what comes back.\",\n\t\t\"\",\n\t\t\"`bash(command)` starts a shell command in the background and returns a handle immediately: `h = bash('npm test')`. Use `h.pid` / `h.running` for liveness, `h.tail(n)` / `h.output()` for combined stdout+stderr so far, `h.poll()` for a non-blocking result, `h.kill()` to terminate (SIGTERM, escalating to SIGKILL; on Windows kill() uses taskkill /T and detached or reparented descendants may survive), and `await h` (or `await bash('cmd')`) for the completed result with exit_code, output, and duration. Prefer bash() for long-running commands so the turn keeps working. Run shell commands with `bash()`, not `subprocess`/`os.system`: subprocess calls block the kernel, show the user nothing while they run, and spawn processes the harness cannot see or stop.\",\n\t\t\"\",\n\t\t\"Important: do not install dependencies into the kernel just to make an external project import or run there. If a project import, test, script, CLI, or dependency check is needed, run it through that project's own environment and normal command interface. For example, in a Python repo use its documented commands, `uv run ...`, `.venv/bin/python ...`, or the active project interpreter from the repo root. Treat failures from that native environment as the relevant result.\",\n\t\t\"\",\n\t\thasBoundedInspect\n\t\t\t? \"Use Python for reading, searching, and editing files; prefer `bounded_inspect` for potentially large file evidence. Select fields/windows and emit once under one aggregate byte budget. Keep small one-off reads inline. Always assign read/search results to named variables so you can revisit them later.\"\n\t\t\t: \"Use Python for reading, searching, and editing files — it gives you reusable variables you can slice, filter, and act on without re-reading. Always assign read/search results to named variables so you can revisit them later.\",\n\t\t\"\",\n\t\t\"Each `bash()` call is its own process, so shell state does not persist between calls; use `os.chdir(...)` for the working directory and `os.environ[...]` for environment variables — both persist in the REPL and apply to later `bash()` calls.\",\n\t\t\"\",\n\t\t\"Python state in the kernel persists across cells: named variables, helper functions, classes, imports, notes, parsed outputs, and helper data structures all remain available in every later turn. Tool calls are themselves Python `await` expressions, so their return values can be bound to variables and composed into program logic just like any other call.\",\n\t\t\"\",\n\t\t\"Continual harness state is available as `rlm.harness` and `rlm.get_harness_state()`. CRUD calls are local to this Base Context session by default: `rlm.harness.create_memory(...)`, `rlm.harness.update_memory(...)`, `rlm.harness.delete_memory(...)`, `rlm.harness.create_skill(...)`, `rlm.harness.update_skill(...)`, `rlm.harness.delete_skill(...)`, `rlm.harness.create_subagent(...)`, `rlm.harness.update_subagent(...)`, `rlm.harness.delete_subagent(...)`, `rlm.harness.create_prompt_note(...)`, `rlm.harness.update_prompt_note(...)`, `rlm.harness.delete_prompt_note(...)`, plus `rlm.harness.record_refinement(...)` and `rlm.harness.overview()`. Use `global_=True` only for stable cross-session lessons; Python reserves `global`, so literal `global=True` is invalid syntax.\",\n\t\t\"\",\n\t\t\"Terminology: continual harness names the persisted prompt, memory, skill, and subagent layer; RLM names the runtime, Python REPL kernel, and native call interface exposed to the model.\",\n\t\t\"\",\n\t\t\"RLM-native call contract: installed Python skills are pre-imported modules. Use the matching SKILL.md and call its documented function, such as `await <skill_import>.<function>(...)`; when a CLI exists, use `<skill_import> ...` from shell. Continual harness skill entries are Python REPL skills with an explicit Python `reference` and `arguments` contract. Spawn a reusable delegation spec with `await rlm('sub-task')`; admission returns a child handle immediately. Results arrive only through an available messaging capability or files, never as an `rlm()` return value. Do not invent non-native wrappers such as `call_skill(...)` or `run_subagent(...)`.\",\n\t].join(\"\\n\");\n}\n\nexport interface ChildAgentDoctrineOptions {\n\tdepth?: number;\n\tparentAgent?: string;\n\tinstalledSkills?: string[];\n\tactiveTools?: string[];\n}\n\nexport function buildChildAgentDoctrine(options: ChildAgentDoctrineOptions): string | undefined {\n\tconst depth = options.depth ?? 0;\n\tconst hasIpython = options.activeTools === undefined || options.activeTools.includes(\"ipython\");\n\tconst hasAgentMessage = options.installedSkills?.includes(\"agent_message\") ?? false;\n\tif (depth <= 0) return undefined;\n\n\tconst lines = [\n\t\t`You are a child agent spawned by ${options.parentAgent ?? \"your parent agent\"}. Task prompts are labeled \\`[task from parent]\\`.`,\n\t];\n\tif (hasAgentMessage && hasIpython) {\n\t\tlines.push(\n\t\t\t'When a task calls for an answer, use `await agent_message.send(message, receiver_role=\"parent\")` for short coordination and `await agent_message.send_result(summary, findings, receiver_role=\"parent\")` for substantial findings or final reports. Include the outcome and important caveats. Not every message needs a reply; finish cleanup and go idle normally.',\n\t\t);\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nexport function buildRlmPrompt(options: RlmPromptOptions): string {\n\tconst { cwd, skillsDir, messagesPath } = options;\n\tconst installedSkills = options.installedSkills ?? [];\n\tconst hasAgentMessage = installedSkills.includes(\"agent_message\");\n\tconst hasAgentObserve = installedSkills.includes(\"agent_observe\");\n\tconst allowRecursion = options.allowRecursion ?? true;\n\tconst depth = options.depth ?? 0;\n\tconst activeTools = options.activeTools ?? [];\n\tconst hasIpython = options.activeTools === undefined ? true : activeTools.includes(\"ipython\");\n\tconst canRunShellSkills = hasIpython || activeTools.includes(\"bash\");\n\tconst parts = [\n\t\t\"You are a general purpose agent that uses code to solve tasks.\",\n\t\t\"You solve tasks by breaking down problems into sub-tasks, writing and executing code, observing results, and iterating one step at a time.\",\n\t\t\"When you are done, stop calling tools and state your final answer.\",\n\t\t\"\",\n\t\tbuildLongRunningWorkPrompt(hasIpython && installedSkills.includes(\"job_watch\")),\n\t\t\"\",\n\t\t...(depth === 0 ? [USER_PROGRESS_PROMPT, \"\"] : []),\n\t\tSIMPLIFIED_TECHNICAL_ENGLISH_PROMPT,\n\t\t\"\",\n\t\t`Recursive agent depth: ${depth}`,\n\t\t`Pre-installed Python packages: ${DEFAULT_RLM_EXTRA_IMPORT_LABELS.join(\", \")}.`,\n\t\t\"Install additional packages with `uv pip install <pkg>` (this is a uv-managed venv with no pip module).\",\n\t];\n\n\tconst childDoctrine = buildChildAgentDoctrine(options);\n\tif (childDoctrine) {\n\t\tparts.push(\"\", childDoctrine);\n\t}\n\n\tconst skillLines: string[] = [];\n\tif (skillsDir) {\n\t\tskillLines.push(`Local skills live under ${skillsDir}. Read their SKILL.md files when helpful.`);\n\t}\n\tif (installedSkills.length > 0) {\n\t\tconst installed = installedSkills.map((skill) => `\\`${skill}\\``).join(\", \");\n\t\tif (hasIpython) {\n\t\t\tskillLines.push(`Installed Python skill modules (pre-imported): ${installed}.`);\n\t\t\tskillLines.push(\n\t\t\t\t\"A complete selected SKILL.md body already in context needs no duplicate read; recover it later if needed. Inspect a module with `help(<skill>)` or `dir(<skill>)`, then inspect a documented callable with `inspect.signature(<skill>.<function>)`.\",\n\t\t\t);\n\t\t} else if (canRunShellSkills) {\n\t\t\tskillLines.push(`Installed Python skill packages: ${installed}.`);\n\t\t}\n\t\tif (canRunShellSkills) {\n\t\t\tskillLines.push(\n\t\t\t\t\"Only use a shell command when the skill documents a CLI: `<skill> ...`. Discover its usage with `<skill> --help`; not every Python skill has a CLI.\",\n\t\t\t);\n\t\t}\n\t\tif (hasIpython && installedSkills.includes(\"edit\")) {\n\t\t\tskillLines.push(\n\t\t\t\t\"For targeted existing-file edits, prefer the pre-imported async `edit` skill from the REPL: `old = '''...'''; new = '''...'''; await edit(path=\\\"pkg/file.py\\\", old_str=old, new_str=new)`. Use exact old/new strings; if the text contains triple double quotes, use triple single-quoted variables or build `old`/`new` from inspected file slices.\",\n\t\t\t);\n\t\t}\n\t}\n\tif (skillLines.length > 0) {\n\t\tparts.push(\"\", ...skillLines);\n\t}\n\tif (hasAgentMessage) {\n\t\tparts.push(\n\t\t\t\"Agent messaging is restricted to your parent, siblings, and direct children; roots are siblings, and deeper communication relays through the intermediate child.\",\n\t\t);\n\t}\n\tif (hasAgentObserve) {\n\t\tparts.push(\n\t\t\t\"Agent observation is restricted to your parent, siblings, and direct children; roots are siblings, and deeper inspection relays through the intermediate child.\",\n\t\t);\n\t}\n\n\tif (allowRecursion && hasIpython) {\n\t\tparts.push(\n\t\t\t\"\",\n\t\t\t\"A callable `rlm` is already in your global namespace. `await rlm('sub-task')` spawns a child and returns immediately after task admission with `rlm_child_id`, `name`, `session_dir`, and `model`; it never waits for or returns the child's answer.\",\n\t\t\t\"Choose a stable child name with `await rlm('sub-task', name='api-reviewer')`; names must be unique among siblings. If omitted, the host generates a readable unique name.\",\n\t\t\t\"A child inherits your model. If a different model is explicitly requested, use `await rlm.find_models(...)` and an exact returned selector. An unavailable requested model fails spawn; decide whether to retry or omit `model`. Children also inherit your thinking level; the `thinking` option overrides it with any level the resolved child model supports, and an unsupported level fails spawn.\",\n\t\t);\n\t\tif (hasAgentMessage) {\n\t\t\tparts.push(\n\t\t\t\t\"Replies and follow-ups arrive as ordinary agent messages or result capsules; request an explicit answer only when needed.\",\n\t\t\t\t\"Use `await agent_message.list_agents()` to discover family and `await rlm.list_subagents()` to recover direct child handles. Use `agent_message.send(..., receiver_role='child', receiver_name=child.name)` for follow-ups.\",\n\t\t\t);\n\t\t} else {\n\t\t\tparts.push(\"Use `await rlm.list_subagents()` to recover direct child handles after admission.\");\n\t\t}\n\t\tif (hasAgentObserve) {\n\t\t\tparts.push(\n\t\t\t\t\"Use `agent_observe` to inspect a child's rollout. Observation is restricted to your parent, siblings, and direct children; relay through the intermediate child for deeper descendants.\",\n\t\t\t);\n\t\t} else {\n\t\t\tparts.push(\"Inspect files a child wrote when you need to collect its work without an observation capability.\");\n\t\t}\n\t\tparts.push(\n\t\t\t\"Spawn independent children in separate calls and end your turn instead of awaiting completion. Multiple replies may arrive over multiple turns. Delete a direct child explicitly with `await rlm.delete_subagent(child)` when it is no longer needed.\",\n\t\t);\n\t}\n\n\tif (hasIpython) {\n\t\tparts.push(\"\", buildReplControlPrompt(installedSkills.includes(\"bounded_inspect\")));\n\t\tif (installedSkills.includes(\"refine\")) {\n\t\t\tparts.push(\n\t\t\t\t\"\",\n\t\t\t\t\"Treat continual harness refinement as a small, evidence-backed update after observing a repeated failure or reusable tactic: diagnose the issue, update the smallest relevant continual harness component, validate on the next action, then record the outcome. Use `await refine.run()` to turn repeated delegation patterns into reusable subagent specs, repeated procedures into skills, durable facts/preferences into memories, and narrow behavioral policies into prompt addendums. It returns immediately and runs when the current turn ends, so continue working normally after calling it. Do not rewrite the whole continual harness when a focused memory, skill, prompt note, or subagent spec is enough.\",\n\t\t\t);\n\t\t}\n\t}\n\n\t// Runtime coordinates follow the fixed harness guidance without moving user/project instructions.\n\tparts.push(\"\", `Working directory: ${cwd}`, `Conversation log: ${messagesPath}`);\n\treturn parts.join(\"\\n\");\n}\n\n/**\n * Supplemental sub-agent delegation guidance, appended after the base RLM\n * prompt (see system-prompt.ts). The recursion block covers the mechanics\n * (`rlm(...)` admission and handle management); this block adds the\n * when and why in the same When -> Why -> menu order Claude Code's Agent tool\n * uses. The subagent-spec menu itself renders just after this, inside the\n * harness-state block.\n */\nexport function buildSubagentGuidance(\n\toptions: { includeRefineExamples?: boolean; hasAgentMessage?: boolean; hasAgentObserve?: boolean } = {},\n): string {\n\tconst lines = [\n\t\t\"# Delegating to sub-agents\",\n\t\t\"\",\n\t\t\"Spawn independent, self-contained work with `handle = await rlm('task', name='worker')`. This returns at admission, not completion; keep the handle to stop or inspect the child later.\",\n\t];\n\tif (options.hasAgentMessage) {\n\t\tlines.push(\n\t\t\t\"Use `agent_message.send` for short steering, blockers, and decisions. For substantial findings or final reports, a child replies with `await agent_message.send_result(summary, findings, receiver_role='parent')`; parent follow-ups use `receiver_role='child'` plus the child's name or id. Keep the outcome, important caveats, and evidence references in the summary. Avoid routine acknowledgements: messaging an idle child can start another turn.\",\n\t\t);\n\t}\n\tlines.push(\"Use `await rlm.list_subagents()` after kernel restart or compaction.\");\n\tif (options.hasAgentObserve) {\n\t\tlines.push(\"Use `agent_observe` for bounded transcript inspection.\");\n\t}\n\tlines.push(\n\t\toptions.hasAgentMessage\n\t\t\t? \"Skim result capsules first and recover details only when needed. Child results are evidence, not verified truth; newer reports do not silently supersede earlier evidence.\"\n\t\t\t: \"Have children write files and read those files for fan-in.\",\n\t\t\"Delegate parallel context-heavy research or independent implementation; do a single known lookup, edit, or command inline.\",\n\t);\n\tif (options.includeRefineExamples ?? true) {\n\t\tlines.push(\"Persist genuinely reusable delegation patterns with `await refine.run()`.\");\n\t}\n\treturn lines.join(\"\\n\");\n}\n"]}
@@ -1,9 +1,13 @@
1
1
  import { DEFAULT_RLM_EXTRA_IMPORT_LABELS } from "../kernel/bootstrap.js";
2
- const LONG_RUNNING_WORK_PROMPT = [
3
- "For slow or independently completing work, use a nonblocking control loop: start the work, record its handle or output location, then end your turn. Read the result on a later turn or when a reply arrives.",
4
- "When delegation is available and useful, assign independent substantive tasks to separate workers. Start independent workers without waiting for each one sequentially, and let them run in parallel.",
5
- "Do not keep the turn open by polling with `time.sleep()` or shell `sleep`, and do not replace polling with a long blocking `await`. Await only the short operation needed to start work or inspect a result that is already available; otherwise end the turn.",
6
- ].join("\n");
2
+ function buildLongRunningWorkPrompt(hasJobWatch) {
3
+ return [
4
+ hasJobWatch
5
+ ? "Use `job_watch` for repeated job checks after an authorized launch. Consume prepared evidence; call `park` and end the turn only when no independent work remains. Parking holds goal and autonomous continuation, not user input. Preserve every required report. Tiny one-off commands stay inline."
6
+ : "For slow or independently completing work, use a nonblocking control loop: start the work, record its handle or output location, then end your turn. Read the result on a later turn or when a reply arrives.",
7
+ "When delegation is available and useful, assign independent substantive tasks to separate workers. Start independent workers without waiting for each one sequentially, and let them run in parallel.",
8
+ "Do not keep the turn open by polling with `time.sleep()` or shell `sleep`, and do not replace polling with a long blocking `await`. Await only the short operation needed to start work or inspect a result that is already available; otherwise end the turn.",
9
+ ].join("\n");
10
+ }
7
11
  const USER_PROGRESS_PROMPT = "As the user-facing root agent, when work follows a plan, uses many subagents, or spans multiple turns, proactively give regular concise progress updates so the user does not have to ask. State the current plan, what has completed, any blockers, the proposed fixes, and the next actions. Lead with user-visible outcomes rather than internal process or gate names. Mention internal details only when they explain a blocker or decision. Send an update at meaningful milestones and before ending a turn while work is still running. Do not repeat unchanged status or interrupt short work with unnecessary updates.";
8
12
  const SIMPLIFIED_TECHNICAL_ENGLISH_PROMPT = [
9
13
  "Use simplified technical English by default for user-facing prose.",
@@ -11,31 +15,35 @@ const SIMPLIFIED_TECHNICAL_ENGLISH_PROMPT = [
11
15
  "Keep necessary technical terms, names, commands, code, paths, and exact quoted text unchanged. State uncertainty directly.",
12
16
  "Treat this as clarity guidance, not a claim of formal ASD-STE100 compliance. Preserve a user-requested format, tone, terminology, and necessary precision.",
13
17
  ].join("\n");
14
- const REPL_CONTROL_PROMPT = [
15
- "The `ipython` tool is a persistent Python REPL — the agent's long-lived control environment for reasoning, context management, state, tool orchestration, and recursive subcalls. Top-level `await` works directly. Use it to keep intermediate variables, inspect and transform outputs, and write small helper functions. Compaction removes individual variables whose serialized form exceeds 16 MiB; keep large source data on disk and reload it when needed.",
16
- "",
17
- "Reuse a named Python function for repeated multi-step work; keep one-off operations inline. Pass changing inputs explicitly and read current data each call. Save cross-task helpers as editable .py files with short project-skill instructions, without packaging them. After editing loaded code, execute its updated definition or invoke the saved file afresh.",
18
- "",
19
- "Python is the orchestration language: use Python for loops, conditionals, parsing, and state. Use `bash()` to invoke programs, not to write shell programs — no shell loops or heredocs; do those in Python.",
20
- "",
21
- "Do not assume the REPL is the native runtime of the external thing being investigated. A repository, package, service, dataset, paper, website, benchmark, or API may have its own environment and normal interface. Evaluate external systems through their own interface, then use the REPL to coordinate the process and analyze what comes back.",
22
- "",
23
- "`bash(command)` starts a shell command in the background and returns a handle immediately: `h = bash('npm test')`. Use `h.pid` / `h.running` for liveness, `h.tail(n)` / `h.output()` for combined stdout+stderr so far, `h.poll()` for a non-blocking result, `h.kill()` to terminate (SIGTERM, escalating to SIGKILL; on Windows kill() uses taskkill /T and detached or reparented descendants may survive), and `await h` (or `await bash('cmd')`) for the completed result with exit_code, output, and duration. Prefer bash() for long-running commands so the turn keeps working. Run shell commands with `bash()`, not `subprocess`/`os.system`: subprocess calls block the kernel, show the user nothing while they run, and spawn processes the harness cannot see or stop.",
24
- "",
25
- "Important: do not install dependencies into the kernel just to make an external project import or run there. If a project import, test, script, CLI, or dependency check is needed, run it through that project's own environment and normal command interface. For example, in a Python repo use its documented commands, `uv run ...`, `.venv/bin/python ...`, or the active project interpreter from the repo root. Treat failures from that native environment as the relevant result.",
26
- "",
27
- "Use Python for reading, searching, and editing files — it gives you reusable variables you can slice, filter, and act on without re-reading. Always assign read/search results to named variables so you can revisit them later.",
28
- "",
29
- "Each `bash()` call is its own process, so shell state does not persist between calls; use `os.chdir(...)` for the working directory and `os.environ[...]` for environment variables — both persist in the REPL and apply to later `bash()` calls.",
30
- "",
31
- "Python state in the kernel persists across cells: named variables, helper functions, classes, imports, notes, parsed outputs, and helper data structures all remain available in every later turn. Tool calls are themselves Python `await` expressions, so their return values can be bound to variables and composed into program logic just like any other call.",
32
- "",
33
- "Continual harness state is available as `rlm.harness` and `rlm.get_harness_state()`. CRUD calls are local to this Base Context session by default: `rlm.harness.create_memory(...)`, `rlm.harness.update_memory(...)`, `rlm.harness.delete_memory(...)`, `rlm.harness.create_skill(...)`, `rlm.harness.update_skill(...)`, `rlm.harness.delete_skill(...)`, `rlm.harness.create_subagent(...)`, `rlm.harness.update_subagent(...)`, `rlm.harness.delete_subagent(...)`, `rlm.harness.create_prompt_note(...)`, `rlm.harness.update_prompt_note(...)`, `rlm.harness.delete_prompt_note(...)`, plus `rlm.harness.record_refinement(...)` and `rlm.harness.overview()`. Use `global_=True` only for stable cross-session lessons; Python reserves `global`, so literal `global=True` is invalid syntax.",
34
- "",
35
- "Terminology: continual harness names the persisted prompt, memory, skill, and subagent layer; RLM names the runtime, Python REPL kernel, and native call interface exposed to the model.",
36
- "",
37
- "RLM-native call contract: installed Python skills are pre-imported modules. Read the matching SKILL.md and call its documented function, such as `await <skill_import>.<function>(...)`; when a CLI exists, use `<skill_import> ...` from shell. Continual harness skill entries are Python REPL skills with an explicit Python `reference` and `arguments` contract. Spawn a reusable delegation spec with `await rlm('sub-task')`; admission returns a child handle immediately. Results arrive only through an available messaging capability or files, never as an `rlm()` return value. Do not invent non-native wrappers such as `call_skill(...)` or `run_subagent(...)`.",
38
- ].join("\n");
18
+ function buildReplControlPrompt(hasBoundedInspect) {
19
+ return [
20
+ "The `ipython` tool is a persistent Python REPL — the agent's long-lived control environment for reasoning, context management, state, tool orchestration, and recursive subcalls. Top-level `await` works directly. Use it to keep intermediate variables, inspect and transform outputs, and write small helper functions. Compaction removes individual variables whose serialized form exceeds 16 MiB; keep large source data on disk and reload it when needed.",
21
+ "",
22
+ "Reuse a named Python function for repeated multi-step work; keep one-off operations inline. Once a stable operation has been useful twice, save a small editable project .py helper without packaging it. Pass current commit, configuration, paths, resource identity, and limits explicitly instead of capturing stale globals; read current data each call. After editing loaded code, execute its updated definition or invoke the saved file afresh. Repetition never authorizes another deployment or experiment.",
23
+ "",
24
+ "Python is the orchestration language: use Python for loops, conditionals, parsing, and state. Use `bash()` to invoke programs, not to write shell programs — no shell loops or heredocs; do those in Python.",
25
+ "",
26
+ "Do not assume the REPL is the native runtime of the external thing being investigated. A repository, package, service, dataset, paper, website, benchmark, or API may have its own environment and normal interface. Evaluate external systems through their own interface, then use the REPL to coordinate the process and analyze what comes back.",
27
+ "",
28
+ "`bash(command)` starts a shell command in the background and returns a handle immediately: `h = bash('npm test')`. Use `h.pid` / `h.running` for liveness, `h.tail(n)` / `h.output()` for combined stdout+stderr so far, `h.poll()` for a non-blocking result, `h.kill()` to terminate (SIGTERM, escalating to SIGKILL; on Windows kill() uses taskkill /T and detached or reparented descendants may survive), and `await h` (or `await bash('cmd')`) for the completed result with exit_code, output, and duration. Prefer bash() for long-running commands so the turn keeps working. Run shell commands with `bash()`, not `subprocess`/`os.system`: subprocess calls block the kernel, show the user nothing while they run, and spawn processes the harness cannot see or stop.",
29
+ "",
30
+ "Important: do not install dependencies into the kernel just to make an external project import or run there. If a project import, test, script, CLI, or dependency check is needed, run it through that project's own environment and normal command interface. For example, in a Python repo use its documented commands, `uv run ...`, `.venv/bin/python ...`, or the active project interpreter from the repo root. Treat failures from that native environment as the relevant result.",
31
+ "",
32
+ hasBoundedInspect
33
+ ? "Use Python for reading, searching, and editing files; prefer `bounded_inspect` for potentially large file evidence. Select fields/windows and emit once under one aggregate byte budget. Keep small one-off reads inline. Always assign read/search results to named variables so you can revisit them later."
34
+ : "Use Python for reading, searching, and editing files — it gives you reusable variables you can slice, filter, and act on without re-reading. Always assign read/search results to named variables so you can revisit them later.",
35
+ "",
36
+ "Each `bash()` call is its own process, so shell state does not persist between calls; use `os.chdir(...)` for the working directory and `os.environ[...]` for environment variables — both persist in the REPL and apply to later `bash()` calls.",
37
+ "",
38
+ "Python state in the kernel persists across cells: named variables, helper functions, classes, imports, notes, parsed outputs, and helper data structures all remain available in every later turn. Tool calls are themselves Python `await` expressions, so their return values can be bound to variables and composed into program logic just like any other call.",
39
+ "",
40
+ "Continual harness state is available as `rlm.harness` and `rlm.get_harness_state()`. CRUD calls are local to this Base Context session by default: `rlm.harness.create_memory(...)`, `rlm.harness.update_memory(...)`, `rlm.harness.delete_memory(...)`, `rlm.harness.create_skill(...)`, `rlm.harness.update_skill(...)`, `rlm.harness.delete_skill(...)`, `rlm.harness.create_subagent(...)`, `rlm.harness.update_subagent(...)`, `rlm.harness.delete_subagent(...)`, `rlm.harness.create_prompt_note(...)`, `rlm.harness.update_prompt_note(...)`, `rlm.harness.delete_prompt_note(...)`, plus `rlm.harness.record_refinement(...)` and `rlm.harness.overview()`. Use `global_=True` only for stable cross-session lessons; Python reserves `global`, so literal `global=True` is invalid syntax.",
41
+ "",
42
+ "Terminology: continual harness names the persisted prompt, memory, skill, and subagent layer; RLM names the runtime, Python REPL kernel, and native call interface exposed to the model.",
43
+ "",
44
+ "RLM-native call contract: installed Python skills are pre-imported modules. Use the matching SKILL.md and call its documented function, such as `await <skill_import>.<function>(...)`; when a CLI exists, use `<skill_import> ...` from shell. Continual harness skill entries are Python REPL skills with an explicit Python `reference` and `arguments` contract. Spawn a reusable delegation spec with `await rlm('sub-task')`; admission returns a child handle immediately. Results arrive only through an available messaging capability or files, never as an `rlm()` return value. Do not invent non-native wrappers such as `call_skill(...)` or `run_subagent(...)`.",
45
+ ].join("\n");
46
+ }
39
47
  export function buildChildAgentDoctrine(options) {
40
48
  const depth = options.depth ?? 0;
41
49
  const hasIpython = options.activeTools === undefined || options.activeTools.includes("ipython");
@@ -46,7 +54,7 @@ export function buildChildAgentDoctrine(options) {
46
54
  `You are a child agent spawned by ${options.parentAgent ?? "your parent agent"}. Task prompts are labeled \`[task from parent]\`.`,
47
55
  ];
48
56
  if (hasAgentMessage && hasIpython) {
49
- lines.push('When a task calls for an answer, reply explicitly with `await agent_message.send(message, receiver_role="parent")`. Not every message or task needs a reply; continue cleanup after sending and go idle normally.');
57
+ lines.push('When a task calls for an answer, use `await agent_message.send(message, receiver_role="parent")` for short coordination and `await agent_message.send_result(summary, findings, receiver_role="parent")` for substantial findings or final reports. Include the outcome and important caveats. Not every message needs a reply; finish cleanup and go idle normally.');
50
58
  }
51
59
  return lines.join("\n");
52
60
  }
@@ -65,7 +73,7 @@ export function buildRlmPrompt(options) {
65
73
  "You solve tasks by breaking down problems into sub-tasks, writing and executing code, observing results, and iterating one step at a time.",
66
74
  "When you are done, stop calling tools and state your final answer.",
67
75
  "",
68
- LONG_RUNNING_WORK_PROMPT,
76
+ buildLongRunningWorkPrompt(hasIpython && installedSkills.includes("job_watch")),
69
77
  "",
70
78
  ...(depth === 0 ? [USER_PROGRESS_PROMPT, ""] : []),
71
79
  SIMPLIFIED_TECHNICAL_ENGLISH_PROMPT,
@@ -86,13 +94,13 @@ export function buildRlmPrompt(options) {
86
94
  const installed = installedSkills.map((skill) => `\`${skill}\``).join(", ");
87
95
  if (hasIpython) {
88
96
  skillLines.push(`Installed Python skill modules (pre-imported): ${installed}.`);
89
- skillLines.push("Read each skill's SKILL.md for its API. Inspect a module with `help(<skill>)` or `dir(<skill>)`, then inspect a documented callable with `inspect.signature(<skill>.<function>)`.");
97
+ skillLines.push("A complete selected SKILL.md body already in context needs no duplicate read; recover it later if needed. Inspect a module with `help(<skill>)` or `dir(<skill>)`, then inspect a documented callable with `inspect.signature(<skill>.<function>)`.");
90
98
  }
91
99
  else if (canRunShellSkills) {
92
- skillLines.push(`Installed skills available as shell commands: ${installed}.`);
100
+ skillLines.push(`Installed Python skill packages: ${installed}.`);
93
101
  }
94
102
  if (canRunShellSkills) {
95
- skillLines.push("Each skill is also available as a shell command by the same name: `<skill> ...`. Discover its CLI usage with `<skill> --help`.");
103
+ skillLines.push("Only use a shell command when the skill documents a CLI: `<skill> ...`. Discover its usage with `<skill> --help`; not every Python skill has a CLI.");
96
104
  }
97
105
  if (hasIpython && installedSkills.includes("edit")) {
98
106
  skillLines.push("For targeted existing-file edits, prefer the pre-imported async `edit` skill from the REPL: `old = '''...'''; new = '''...'''; await edit(path=\"pkg/file.py\", old_str=old, new_str=new)`. Use exact old/new strings; if the text contains triple double quotes, use triple single-quoted variables or build `old`/`new` from inspected file slices.");
@@ -110,7 +118,7 @@ export function buildRlmPrompt(options) {
110
118
  if (allowRecursion && hasIpython) {
111
119
  parts.push("", "A callable `rlm` is already in your global namespace. `await rlm('sub-task')` spawns a child and returns immediately after task admission with `rlm_child_id`, `name`, `session_dir`, and `model`; it never waits for or returns the child's answer.", "Choose a stable child name with `await rlm('sub-task', name='api-reviewer')`; names must be unique among siblings. If omitted, the host generates a readable unique name.", "A child inherits your model. If a different model is explicitly requested, use `await rlm.find_models(...)` and an exact returned selector. An unavailable requested model fails spawn; decide whether to retry or omit `model`. Children also inherit your thinking level; the `thinking` option overrides it with any level the resolved child model supports, and an unsupported level fails spawn.");
112
120
  if (hasAgentMessage) {
113
- parts.push("Children reply explicitly with `await agent_message.send(message, receiver_role='parent')` when an answer is needed. Replies and follow-ups arrive as ordinary agent messages; not every task requires a reply.", "Use `await agent_message.list_agents()` to discover family and `await rlm.list_subagents()` to recover direct child handles. Use `agent_message.send(..., receiver_role='child', receiver_name=child.name)` for follow-ups.");
121
+ parts.push("Replies and follow-ups arrive as ordinary agent messages or result capsules; request an explicit answer only when needed.", "Use `await agent_message.list_agents()` to discover family and `await rlm.list_subagents()` to recover direct child handles. Use `agent_message.send(..., receiver_role='child', receiver_name=child.name)` for follow-ups.");
114
122
  }
115
123
  else {
116
124
  parts.push("Use `await rlm.list_subagents()` to recover direct child handles after admission.");
@@ -124,7 +132,7 @@ export function buildRlmPrompt(options) {
124
132
  parts.push("Spawn independent children in separate calls and end your turn instead of awaiting completion. Multiple replies may arrive over multiple turns. Delete a direct child explicitly with `await rlm.delete_subagent(child)` when it is no longer needed.");
125
133
  }
126
134
  if (hasIpython) {
127
- parts.push("", REPL_CONTROL_PROMPT);
135
+ parts.push("", buildReplControlPrompt(installedSkills.includes("bounded_inspect")));
128
136
  if (installedSkills.includes("refine")) {
129
137
  parts.push("", "Treat continual harness refinement as a small, evidence-backed update after observing a repeated failure or reusable tactic: diagnose the issue, update the smallest relevant continual harness component, validate on the next action, then record the outcome. Use `await refine.run()` to turn repeated delegation patterns into reusable subagent specs, repeated procedures into skills, durable facts/preferences into memories, and narrow behavioral policies into prompt addendums. It returns immediately and runs when the current turn ends, so continue working normally after calling it. Do not rewrite the whole continual harness when a focused memory, skill, prompt note, or subagent spec is enough.");
130
138
  }
@@ -148,13 +156,15 @@ export function buildSubagentGuidance(options = {}) {
148
156
  "Spawn independent, self-contained work with `handle = await rlm('task', name='worker')`. This returns at admission, not completion; keep the handle to stop or inspect the child later.",
149
157
  ];
150
158
  if (options.hasAgentMessage) {
151
- lines.push("Ask for an explicit reply when needed. A child replies with `await agent_message.send(message, receiver_role='parent')`; parent follow-ups use `receiver_role='child'` plus the child's name or id. Not every message needs a reply.");
159
+ lines.push("Use `agent_message.send` for short steering, blockers, and decisions. For substantial findings or final reports, a child replies with `await agent_message.send_result(summary, findings, receiver_role='parent')`; parent follow-ups use `receiver_role='child'` plus the child's name or id. Keep the outcome, important caveats, and evidence references in the summary. Avoid routine acknowledgements: messaging an idle child can start another turn.");
152
160
  }
153
161
  lines.push("Use `await rlm.list_subagents()` after kernel restart or compaction.");
154
162
  if (options.hasAgentObserve) {
155
163
  lines.push("Use `agent_observe` for bounded transcript inspection.");
156
164
  }
157
- lines.push("Have children write files and read those files for fan-in.", "Delegate parallel context-heavy research or independent implementation; do a single known lookup, edit, or command inline.");
165
+ lines.push(options.hasAgentMessage
166
+ ? "Skim result capsules first and recover details only when needed. Child results are evidence, not verified truth; newer reports do not silently supersede earlier evidence."
167
+ : "Have children write files and read those files for fan-in.", "Delegate parallel context-heavy research or independent implementation; do a single known lookup, edit, or command inline.");
158
168
  if (options.includeRefineExamples ?? true) {
159
169
  lines.push("Persist genuinely reusable delegation patterns with `await refine.run()`.");
160
170
  }