raga-cli 0.0.0-stage → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (478) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/LICENSE +21 -0
  3. package/README.md +139 -2
  4. package/bundled-skills/AGENTS.md +5 -0
  5. package/bundled-skills/README.md +25 -0
  6. package/bundled-skills/agents/comment-sicko.json +9 -0
  7. package/bundled-skills/agents/comment-sicko.md +28 -0
  8. package/bundled-skills/agents/model-roles.json +41 -0
  9. package/bundled-skills/agents/poteto-agent.json +9 -0
  10. package/bundled-skills/agents/poteto-agent.md +4 -0
  11. package/bundled-skills/agents/worker.json +8 -0
  12. package/bundled-skills/agents/worker.md +1 -0
  13. package/bundled-skills/bundle.json +1448 -0
  14. package/bundled-skills/dependencies.json +40 -0
  15. package/bundled-skills/docs/guide/01-setup.md +3 -0
  16. package/bundled-skills/docs/guide/02-peak.md +135 -0
  17. package/bundled-skills/docs/guide/03-understand.md +79 -0
  18. package/bundled-skills/docs/guide/04-design.md +133 -0
  19. package/bundled-skills/docs/guide/05-build-and-clean.md +83 -0
  20. package/bundled-skills/docs/guide/06-verify-and-ship.md +166 -0
  21. package/bundled-skills/docs/guide/07-overnight.md +118 -0
  22. package/bundled-skills/docs/guide/08-principles.md +72 -0
  23. package/bundled-skills/docs/guide/09-make-it-yours.md +96 -0
  24. package/bundled-skills/docs/guide/10-recipes-and-pitfalls.md +180 -0
  25. package/bundled-skills/docs/guide/README.md +38 -0
  26. package/bundled-skills/docs/guide/images/design.jpg +0 -0
  27. package/bundled-skills/docs/guide/images/overnight.jpg +0 -0
  28. package/bundled-skills/docs/guide/images/recipes.jpg +0 -0
  29. package/bundled-skills/docs/guide/images/router.jpg +0 -0
  30. package/bundled-skills/docs/guide/images/understanding.jpg +0 -0
  31. package/bundled-skills/docs/guide/images/verification.jpg +0 -0
  32. package/bundled-skills/licenses/commander +22 -0
  33. package/bundled-skills/licenses/cursor-team-kit +21 -0
  34. package/bundled-skills/licenses/no-mistakes +21 -0
  35. package/bundled-skills/licenses/pstack +21 -0
  36. package/bundled-skills/skills/peak/SKILL.md +184 -0
  37. package/bundled-skills/skills/peak/playbooks/authoring-a-skill.md +12 -0
  38. package/bundled-skills/skills/peak/playbooks/autonomous-run.md +13 -0
  39. package/bundled-skills/skills/peak/playbooks/autopilot-full.md +25 -0
  40. package/bundled-skills/skills/peak/playbooks/autopilot-stack.md +12 -0
  41. package/bundled-skills/skills/peak/playbooks/babysit.md +18 -0
  42. package/bundled-skills/skills/peak/playbooks/bug-fix.md +15 -0
  43. package/bundled-skills/skills/peak/playbooks/eval.md +25 -0
  44. package/bundled-skills/skills/peak/playbooks/feature.md +21 -0
  45. package/bundled-skills/skills/peak/playbooks/hillclimb.md +21 -0
  46. package/bundled-skills/skills/peak/playbooks/investigation.md +14 -0
  47. package/bundled-skills/skills/peak/playbooks/multi-phase-plan.md +155 -0
  48. package/bundled-skills/skills/peak/playbooks/opening-a-pr.md +34 -0
  49. package/bundled-skills/skills/peak/playbooks/orchestrate.md +112 -0
  50. package/bundled-skills/skills/peak/playbooks/pause-safely.md +10 -0
  51. package/bundled-skills/skills/peak/playbooks/perf-issue.md +25 -0
  52. package/bundled-skills/skills/peak/playbooks/prototype.md +14 -0
  53. package/bundled-skills/skills/peak/playbooks/refactoring.md +16 -0
  54. package/bundled-skills/skills/peak/playbooks/runtime-forensics.md +11 -0
  55. package/bundled-skills/skills/peak/playbooks/session-pickup.md +11 -0
  56. package/bundled-skills/skills/peak/playbooks/shipping.md +12 -0
  57. package/bundled-skills/skills/peak/playbooks/trace-forensics.md +14 -0
  58. package/bundled-skills/skills/peak/playbooks/visual-parity.md +11 -0
  59. package/bundled-skills/skills/peak/playbooks/worktree-cleanup.md +6 -0
  60. package/bundled-skills/skills/peak/references/architect/SKILL.md +81 -0
  61. package/bundled-skills/skills/peak/references/architect/references/design-red-flags.md +57 -0
  62. package/bundled-skills/skills/peak/references/architect/references/rationale-template.md +35 -0
  63. package/bundled-skills/skills/peak/references/architect/references/runner-prompt.md +20 -0
  64. package/bundled-skills/skills/peak/references/arena/SKILL.md +50 -0
  65. package/bundled-skills/skills/peak/references/automate-me/SKILL.md +106 -0
  66. package/bundled-skills/skills/peak/references/benchmark-checklist/SKILL.md +41 -0
  67. package/bundled-skills/skills/peak/references/blast-radius/SKILL.md +52 -0
  68. package/bundled-skills/skills/peak/references/bro/SKILL.md +9 -0
  69. package/bundled-skills/skills/peak/references/control-cli/SKILL.md +111 -0
  70. package/bundled-skills/skills/peak/references/control-ui/SKILL.md +111 -0
  71. package/bundled-skills/skills/peak/references/correct/SKILL.md +34 -0
  72. package/bundled-skills/skills/peak/references/create-formal-verification-skill/SKILL.md +43 -0
  73. package/bundled-skills/skills/peak/references/create-skill/SKILL.md +14 -0
  74. package/bundled-skills/skills/peak/references/create-verification-skill/SKILL.md +46 -0
  75. package/bundled-skills/skills/peak/references/create-verification-skill/references/feature-map-example/README.md +47 -0
  76. package/bundled-skills/skills/peak/references/create-verification-skill/references/feature-map-example/create-note.md +39 -0
  77. package/bundled-skills/skills/peak/references/create-verification-skill/references/feature-map-example/search.md +45 -0
  78. package/bundled-skills/skills/peak/references/deslop/SKILL.md +24 -0
  79. package/bundled-skills/skills/peak/references/figure-it-out/SKILL.md +54 -0
  80. package/bundled-skills/skills/peak/references/how/SKILL.md +38 -0
  81. package/bundled-skills/skills/peak/references/how/references/explainer-prompt.md +55 -0
  82. package/bundled-skills/skills/peak/references/how/references/explorer-prompt.md +35 -0
  83. package/bundled-skills/skills/peak/references/in-your-own-words/SKILL.md +9 -0
  84. package/bundled-skills/skills/peak/references/interrogate/SKILL.md +76 -0
  85. package/bundled-skills/skills/peak/references/interrogate/references/code-quality-review.md +47 -0
  86. package/bundled-skills/skills/peak/references/interrogate/references/lead-judgment.md +39 -0
  87. package/bundled-skills/skills/peak/references/interrogate/references/reviewer-prompt.md +62 -0
  88. package/bundled-skills/skills/peak/references/interrogate/references/rubric.md +77 -0
  89. package/bundled-skills/skills/peak/references/ketchup/SKILL.md +45 -0
  90. package/bundled-skills/skills/peak/references/maintain-formal-verification-skill/SKILL.md +60 -0
  91. package/bundled-skills/skills/peak/references/maintain-verification-skill/SKILL.md +41 -0
  92. package/bundled-skills/skills/peak/references/no-comments/SKILL.md +26 -0
  93. package/bundled-skills/skills/peak/references/no-mistakes/SKILL.md +20 -0
  94. package/bundled-skills/skills/peak/references/no-mistakes/upstream/SKILL.md +394 -0
  95. package/bundled-skills/skills/peak/references/peak-setup/SKILL.md +18 -0
  96. package/bundled-skills/skills/peak/references/poteto-help/SKILL.md +16 -0
  97. package/bundled-skills/skills/peak/references/poteto-help/references/prompting.md +51 -0
  98. package/bundled-skills/skills/peak/references/poteto-help/references/recipes.md +51 -0
  99. package/bundled-skills/skills/peak/references/principle-attack-the-premise/SKILL.md +23 -0
  100. package/bundled-skills/skills/peak/references/principle-boundary-discipline/SKILL.md +36 -0
  101. package/bundled-skills/skills/peak/references/principle-build-the-lever/SKILL.md +25 -0
  102. package/bundled-skills/skills/peak/references/principle-encode-lessons-in-structure/SKILL.md +13 -0
  103. package/bundled-skills/skills/peak/references/principle-exhaust-the-design-space/SKILL.md +11 -0
  104. package/bundled-skills/skills/peak/references/principle-experience-first/SKILL.md +21 -0
  105. package/bundled-skills/skills/peak/references/principle-explain-the-number/SKILL.md +19 -0
  106. package/bundled-skills/skills/peak/references/principle-fix-root-causes/SKILL.md +23 -0
  107. package/bundled-skills/skills/peak/references/principle-foundational-thinking/SKILL.md +23 -0
  108. package/bundled-skills/skills/peak/references/principle-guard-the-context-window/SKILL.md +14 -0
  109. package/bundled-skills/skills/peak/references/principle-laziness-protocol/SKILL.md +12 -0
  110. package/bundled-skills/skills/peak/references/principle-make-operations-idempotent/SKILL.md +24 -0
  111. package/bundled-skills/skills/peak/references/principle-migrate-callers-then-delete-legacy-apis/SKILL.md +16 -0
  112. package/bundled-skills/skills/peak/references/principle-minimize-reader-load/SKILL.md +20 -0
  113. package/bundled-skills/skills/peak/references/principle-model-the-domain/SKILL.md +26 -0
  114. package/bundled-skills/skills/peak/references/principle-never-block-on-the-human/SKILL.md +17 -0
  115. package/bundled-skills/skills/peak/references/principle-outcome-oriented-execution/SKILL.md +23 -0
  116. package/bundled-skills/skills/peak/references/principle-prove-it-works/SKILL.md +24 -0
  117. package/bundled-skills/skills/peak/references/principle-redesign-from-first-principles/SKILL.md +18 -0
  118. package/bundled-skills/skills/peak/references/principle-separate-before-serializing-shared-state/SKILL.md +18 -0
  119. package/bundled-skills/skills/peak/references/principle-sequence-verifiable-units/SKILL.md +19 -0
  120. package/bundled-skills/skills/peak/references/principle-subtract-before-you-add/SKILL.md +20 -0
  121. package/bundled-skills/skills/peak/references/principle-test-behavior-not-implementation/SKILL.md +22 -0
  122. package/bundled-skills/skills/peak/references/principle-type-system-discipline/SKILL.md +15 -0
  123. package/bundled-skills/skills/peak/references/raga-runtime/SKILL.md +37 -0
  124. package/bundled-skills/skills/peak/references/raga-runtime/managed-tools.mjs +34 -0
  125. package/bundled-skills/skills/peak/references/raga-runtime/raga_runtime.py +44 -0
  126. package/bundled-skills/skills/peak/references/recall/SKILL.md +37 -0
  127. package/bundled-skills/skills/peak/references/reflect/SKILL.md +63 -0
  128. package/bundled-skills/skills/peak/references/reflect/references/divergent-reviewer.md +38 -0
  129. package/bundled-skills/skills/peak/references/reflect/references/judgment-reviewer.md +37 -0
  130. package/bundled-skills/skills/peak/references/reflect/references/synthesizer.md +56 -0
  131. package/bundled-skills/skills/peak/references/reflect/references/tooling-reviewer.md +48 -0
  132. package/bundled-skills/skills/peak/references/reviewer-triage.md +142 -0
  133. package/bundled-skills/skills/peak/references/sharpen/SKILL.md +18 -0
  134. package/bundled-skills/skills/peak/references/show-me-your-work/SKILL.md +84 -0
  135. package/bundled-skills/skills/peak/references/show-me-your-work/references/decision-log-template.tsv +1 -0
  136. package/bundled-skills/skills/peak/references/show-me-your-work/scripts/log.sh +42 -0
  137. package/bundled-skills/skills/peak/references/statechart/SKILL.md +38 -0
  138. package/bundled-skills/skills/peak/references/statechart/scripts/chart-to-tla.mjs +236 -0
  139. package/bundled-skills/skills/peak/references/statechart/scripts/compare-charts.mjs +104 -0
  140. package/bundled-skills/skills/peak/references/statechart/scripts/lib.mjs +725 -0
  141. package/bundled-skills/skills/peak/references/statechart/scripts/model/OutputFolder.cfg +6 -0
  142. package/bundled-skills/skills/peak/references/statechart/scripts/model/OutputFolder.map.mjs +106 -0
  143. package/bundled-skills/skills/peak/references/statechart/scripts/model/OutputFolder.tla +176 -0
  144. package/bundled-skills/skills/peak/references/statechart/scripts/statechart.mjs +749 -0
  145. package/bundled-skills/skills/peak/references/statechart/scripts/test/statechart.test.mjs +2790 -0
  146. package/bundled-skills/skills/peak/references/statechart/scripts/test/sync.mmd +19 -0
  147. package/bundled-skills/skills/peak/references/statechart/scripts/test/upload.mmd +16 -0
  148. package/bundled-skills/skills/peak/references/statechart/scripts/tlc-to-chart.mjs +1347 -0
  149. package/bundled-skills/skills/peak/references/swarm/SKILL.md +43 -0
  150. package/bundled-skills/skills/peak/references/tdd/SKILL.md +44 -0
  151. package/bundled-skills/skills/peak/references/teach/SKILL.md +23 -0
  152. package/bundled-skills/skills/peak/references/technical-writing/SKILL.md +108 -0
  153. package/bundled-skills/skills/peak/references/typescript-best-practices/SKILL.md +33 -0
  154. package/bundled-skills/skills/peak/references/typescript-best-practices/references/patterns.md +328 -0
  155. package/bundled-skills/skills/peak/references/unslop/SKILL.md +64 -0
  156. package/bundled-skills/skills/peak/references/verify-this/SKILL.md +76 -0
  157. package/bundled-skills/skills/peak/references/why/SKILL.md +100 -0
  158. package/bundled-skills/skills/peak/references/why/references/epistemics.md +71 -0
  159. package/bundled-skills/skills/peak/references/why/references/investigator-prompt.md +84 -0
  160. package/bundled-skills/skills/peak/references/why/references/source-playbook.md +17 -0
  161. package/bundled-skills/skills/peak/references/why/references/sources/code-archaeology.md +51 -0
  162. package/bundled-skills/skills/peak/references/why/references/sources/databricks.md +70 -0
  163. package/bundled-skills/skills/peak/references/why/references/sources/datadog.md +97 -0
  164. package/bundled-skills/skills/peak/references/why/references/sources/incident-postmortem.md +5 -0
  165. package/bundled-skills/skills/peak/references/why/references/sources/linear.md +48 -0
  166. package/bundled-skills/skills/peak/references/why/references/sources/notion.md +55 -0
  167. package/bundled-skills/skills/peak/references/why/references/sources/sentry.md +66 -0
  168. package/bundled-skills/skills/peak/references/why/references/sources/slack.md +17 -0
  169. package/bundled-skills/skills/peak/references/why/references/synthesizer-prompt.md +129 -0
  170. package/bundled-skills/skills/peak/scripts/check-plan.mjs +205 -0
  171. package/bundled-skills/skills/peak/scripts/check-plan.test.ts +96 -0
  172. package/bundled-skills/skills/peak/scripts/orch/orch.test.ts +761 -0
  173. package/bundled-skills/skills/peak/scripts/orch/orch.ts +588 -0
  174. package/bundled-skills/skills/peak/scripts/orch/store.ts +1676 -0
  175. package/bundled-skills/skills/peak/scripts/vendor/commander/LICENSE +22 -0
  176. package/bundled-skills/skills/peak/scripts/vendor/commander/esm.mjs +16 -0
  177. package/bundled-skills/skills/peak/scripts/vendor/commander/index.js +24 -0
  178. package/bundled-skills/skills/peak/scripts/vendor/commander/lib/argument.js +149 -0
  179. package/bundled-skills/skills/peak/scripts/vendor/commander/lib/command.js +2778 -0
  180. package/bundled-skills/skills/peak/scripts/vendor/commander/lib/error.js +39 -0
  181. package/bundled-skills/skills/peak/scripts/vendor/commander/lib/help.js +747 -0
  182. package/bundled-skills/skills/peak/scripts/vendor/commander/lib/option.js +379 -0
  183. package/bundled-skills/skills/peak/scripts/vendor/commander/lib/suggestSimilar.js +101 -0
  184. package/bundled-skills/skills/peak/scripts/vendor/commander/package.json +1 -0
  185. package/bundled-skills/skills/peak/scripts/vendor/commander/typings/esm.d.mts +3 -0
  186. package/bundled-skills/skills/peak/scripts/vendor/commander/typings/index.d.ts +1113 -0
  187. package/bundled-skills/skills/peak/scripts/watch-pr/cli.test.ts +224 -0
  188. package/bundled-skills/skills/peak/scripts/watch-pr/cli.ts +223 -0
  189. package/bundled-skills/skills/peak/scripts/watch-pr/fakes.test-helper.ts +129 -0
  190. package/bundled-skills/skills/peak/scripts/watch-pr/github.test.ts +436 -0
  191. package/bundled-skills/skills/peak/scripts/watch-pr/github.ts +718 -0
  192. package/bundled-skills/skills/peak/scripts/watch-pr/policy.test.ts +420 -0
  193. package/bundled-skills/skills/peak/scripts/watch-pr/policy.ts +836 -0
  194. package/bundled-skills/skills/peak/scripts/watch-pr/render.ts +169 -0
  195. package/bundled-skills/skills/peak/scripts/watch-pr/tsconfig.json +13 -0
  196. package/bundled-skills/skills/peak/scripts/watch-pr/types.compile.ts +93 -0
  197. package/bundled-skills/skills/peak/scripts/watch-pr/types.ts +413 -0
  198. package/bundled-skills/skills/peak/scripts/watch-pr/watch-pr +4 -0
  199. package/bundled-skills/skills/peak/scripts/worktree-audit.sh +4 -0
  200. package/dist/bundle/chunks/anthropic-messages-BMDYDGHW.js +6 -0
  201. package/dist/bundle/chunks/anthropic.js +75 -0
  202. package/dist/bundle/chunks/azure-openai-responses-Q7EZTEHK.js +2 -0
  203. package/dist/bundle/chunks/bedrock-converse-stream.js +86 -0
  204. package/dist/bundle/chunks/chunk-3EGDWOKT.js +2 -0
  205. package/dist/bundle/chunks/chunk-3V45WARH.js +7 -0
  206. package/dist/bundle/chunks/chunk-5AGIVGSV.js +6 -0
  207. package/dist/bundle/chunks/chunk-6QGV3IWD.js +6 -0
  208. package/dist/bundle/chunks/chunk-7CBJINYJ.js +20 -0
  209. package/dist/bundle/chunks/chunk-7DDKXS2Q.js +2 -0
  210. package/dist/bundle/chunks/chunk-7ZC4UXSL.js +2 -0
  211. package/dist/bundle/chunks/chunk-AI2NMHE7.js +1567 -0
  212. package/dist/bundle/chunks/chunk-C4IJNE7U.js +2 -0
  213. package/dist/bundle/chunks/chunk-CF5GWZHW.js +31 -0
  214. package/dist/bundle/chunks/chunk-G54UBZTF.js +44 -0
  215. package/dist/bundle/chunks/chunk-H76KZMOI.js +2 -0
  216. package/dist/bundle/chunks/chunk-IOPE2TJ4.js +131 -0
  217. package/dist/bundle/chunks/chunk-IXDQLPKS.js +11 -0
  218. package/dist/bundle/chunks/chunk-J22MF7YW.js +64 -0
  219. package/dist/bundle/chunks/chunk-JLV25LHE.js +2 -0
  220. package/dist/bundle/chunks/chunk-JXWJOIH4.js +2 -0
  221. package/dist/bundle/chunks/chunk-KNY3PXQI.js +2 -0
  222. package/dist/bundle/chunks/chunk-L2ZPNLYD.js +2 -0
  223. package/dist/bundle/chunks/chunk-LPXNMA2W.js +2 -0
  224. package/dist/bundle/chunks/chunk-LVVZS2NO.js +12 -0
  225. package/dist/bundle/chunks/chunk-LX24DVQ5.js +2 -0
  226. package/dist/bundle/chunks/chunk-P2QCWBFM.js +9 -0
  227. package/dist/bundle/chunks/chunk-P7FPUEYA.js +2 -0
  228. package/dist/bundle/chunks/chunk-PGXDEOVV.js +2 -0
  229. package/dist/bundle/chunks/chunk-PJACZCIR.js +2 -0
  230. package/dist/bundle/chunks/chunk-PMRZO46Y.js +2 -0
  231. package/dist/bundle/chunks/chunk-PRRNXB7S.js +2 -0
  232. package/dist/bundle/chunks/chunk-Q4U25BSY.js +2 -0
  233. package/dist/bundle/chunks/chunk-RDZVAGPR.js +2 -0
  234. package/dist/bundle/chunks/chunk-RFPFZTK2.js +7 -0
  235. package/dist/bundle/chunks/chunk-RP2WKNT7.js +4 -0
  236. package/dist/bundle/chunks/chunk-TEPMHNKQ.js +2 -0
  237. package/dist/bundle/chunks/chunk-TMTOQ7H7.js +2 -0
  238. package/dist/bundle/chunks/chunk-UDT647VB.js +82 -0
  239. package/dist/bundle/chunks/chunk-UKVNB6C4.js +2 -0
  240. package/dist/bundle/chunks/chunk-USJZMAIR.js +2 -0
  241. package/dist/bundle/chunks/chunk-UTB4DTYQ.js +2 -0
  242. package/dist/bundle/chunks/chunk-WJGLPWUE.js +16 -0
  243. package/dist/bundle/chunks/chunk-YMZUNB25.js +2 -0
  244. package/dist/bundle/chunks/cli-4P6FVVLT.js +51 -0
  245. package/dist/bundle/chunks/cloudflare-workers-ai-system-one-S6CT2FXS.js +2 -0
  246. package/dist/bundle/chunks/codemode-worker.js +478 -0
  247. package/dist/bundle/chunks/easter-egg-3d-65VIC4G2.js +2 -0
  248. package/dist/bundle/chunks/execute-3H5WKMA3.js +505 -0
  249. package/dist/bundle/chunks/github-copilot.js +2 -0
  250. package/dist/bundle/chunks/google-generative-ai-35NZKG6S.js +2 -0
  251. package/dist/bundle/chunks/google-vertex-P33AHYYX.js +2 -0
  252. package/dist/bundle/chunks/https-proxy-agent-LVOO5TPL.js +13 -0
  253. package/dist/bundle/chunks/image-resize-worker.js +2 -0
  254. package/dist/bundle/chunks/jiti-loader-GL4HJFAD.js +21 -0
  255. package/dist/bundle/chunks/jiti-static-loader-2RCDI6B4.js +2 -0
  256. package/dist/bundle/chunks/kimi-coding.js +2 -0
  257. package/dist/bundle/chunks/lib-4AVRLBHM.js +9 -0
  258. package/dist/bundle/chunks/llama-cpp-classify-U4EH6TAM.js +22 -0
  259. package/dist/bundle/chunks/meta.js +2 -0
  260. package/dist/bundle/chunks/mistral-conversations-J62DPS2P.js +5 -0
  261. package/dist/bundle/chunks/multipart-parser-FMGQ7342.js +3 -0
  262. package/dist/bundle/chunks/node-AUOGOBCN.js +2 -0
  263. package/dist/bundle/chunks/node-KCMANYC2.js +16 -0
  264. package/dist/bundle/chunks/openai-chatgpt.js +75 -0
  265. package/dist/bundle/chunks/openai-codex-responses-4HXATL2M.js +10 -0
  266. package/dist/bundle/chunks/openai-codex.js +75 -0
  267. package/dist/bundle/chunks/openai-completions-75YYY35N.js +7 -0
  268. package/dist/bundle/chunks/openai-decisions-HEBEVGA2.js +5 -0
  269. package/dist/bundle/chunks/openai-responses-UWNAKTBV.js +4 -0
  270. package/dist/bundle/chunks/openrouter-images-HVOPXKHS.js +2 -0
  271. package/dist/bundle/chunks/openrouter.js +75 -0
  272. package/dist/bundle/chunks/pi-messages-DG7CALOV.js +8 -0
  273. package/dist/bundle/chunks/radius.js +75 -0
  274. package/dist/bundle/chunks/runtime-435R6IKY.js +2 -0
  275. package/dist/bundle/chunks/src-QKGKDFSU.js +4 -0
  276. package/dist/bundle/chunks/typesafe-system-one-24HEGHNN.js +2 -0
  277. package/dist/bundle/chunks/virtual-modules-U3JBUDP7.js +2 -0
  278. package/dist/bundle/chunks/xai.js +2 -0
  279. package/dist/bundle/cli-runtime.js +3 -0
  280. package/dist/bundle/cli.js +5 -0
  281. package/dist/bundle/index.js +2 -0
  282. package/dist/bundle/raga-runtime.js +3 -0
  283. package/dist/bundle/raga.js +5 -0
  284. package/dist/bundle/rpc-entry.js +3 -0
  285. package/dist/core/export-html/template.css +1085 -0
  286. package/dist/core/export-html/template.html +56 -0
  287. package/dist/core/export-html/template.js +2044 -0
  288. package/dist/core/export-html/vendor/highlight.min.js +1213 -0
  289. package/dist/core/export-html/vendor/marked-cjk-friendly.min.js +6 -0
  290. package/dist/core/export-html/vendor/marked.min.js +79 -0
  291. package/dist/modes/interactive/assets/clankolas.png +0 -0
  292. package/dist/modes/interactive/theme/dark.json +78 -0
  293. package/dist/modes/interactive/theme/light.json +78 -0
  294. package/docs/cli-integration.md +106 -0
  295. package/docs/cli.md +337 -0
  296. package/docs/codemode.md +230 -0
  297. package/docs/compaction.md +463 -0
  298. package/docs/configuration.md +62 -0
  299. package/docs/containerization.md +183 -0
  300. package/docs/custom-provider.md +227 -0
  301. package/docs/docs.json +220 -0
  302. package/docs/environment-variables.md +101 -0
  303. package/docs/extensions.md +400 -0
  304. package/docs/how-pi-works.md +49 -0
  305. package/docs/images/doom-extension.png +0 -0
  306. package/docs/images/exy.png +0 -0
  307. package/docs/images/interactive-mode.png +0 -0
  308. package/docs/images/tree-view.png +0 -0
  309. package/docs/index.md +39 -0
  310. package/docs/json.md +226 -0
  311. package/docs/keybindings.md +195 -0
  312. package/docs/llama-cpp.md +121 -0
  313. package/docs/mcp.md +272 -0
  314. package/docs/message-types.md +279 -0
  315. package/docs/models-configuration.md +196 -0
  316. package/docs/models.md +225 -0
  317. package/docs/nvidia-rtx-llama-cpp.md +96 -0
  318. package/docs/packages.md +129 -0
  319. package/docs/prompt-templates.md +59 -0
  320. package/docs/providers.md +247 -0
  321. package/docs/quickstart.md +136 -0
  322. package/docs/rpc-commands.md +860 -0
  323. package/docs/rpc-extension-ui.md +200 -0
  324. package/docs/rpc.md +193 -0
  325. package/docs/sdk.md +148 -0
  326. package/docs/security.md +99 -0
  327. package/docs/session-format.md +293 -0
  328. package/docs/sessions.md +66 -0
  329. package/docs/settings.md +184 -0
  330. package/docs/shell-aliases.md +93 -0
  331. package/docs/skills.md +93 -0
  332. package/docs/slash-commands.md +61 -0
  333. package/docs/terminal-setup.md +233 -0
  334. package/docs/termux.md +117 -0
  335. package/docs/themes.md +138 -0
  336. package/docs/tmux.md +55 -0
  337. package/docs/tui.md +195 -0
  338. package/docs/usage.md +94 -0
  339. package/docs/virtual-models.md +114 -0
  340. package/docs/windows.md +81 -0
  341. package/examples/README.md +36 -0
  342. package/examples/extensions/README.md +214 -0
  343. package/examples/extensions/auto-commit-on-exit.ts +49 -0
  344. package/examples/extensions/bash-spawn-hook.ts +30 -0
  345. package/examples/extensions/bookmark.ts +50 -0
  346. package/examples/extensions/border-status-editor.ts +150 -0
  347. package/examples/extensions/built-in-tool-renderer.ts +225 -0
  348. package/examples/extensions/claude-rules.ts +86 -0
  349. package/examples/extensions/commands.ts +72 -0
  350. package/examples/extensions/confirm-destructive.ts +59 -0
  351. package/examples/extensions/custom-compaction.ts +117 -0
  352. package/examples/extensions/custom-footer.ts +64 -0
  353. package/examples/extensions/custom-header.ts +73 -0
  354. package/examples/extensions/custom-provider-anthropic/index.ts +617 -0
  355. package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
  356. package/examples/extensions/custom-provider-anthropic/package.json +19 -0
  357. package/examples/extensions/custom-provider-gitlab-duo/index.ts +405 -0
  358. package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
  359. package/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -0
  360. package/examples/extensions/debug-provider.ts +90 -0
  361. package/examples/extensions/dirty-repo-guard.ts +56 -0
  362. package/examples/extensions/doom-overlay/README.md +46 -0
  363. package/examples/extensions/doom-overlay/doom/build/doom.js +21 -0
  364. package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
  365. package/examples/extensions/doom-overlay/doom/build.sh +152 -0
  366. package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -0
  367. package/examples/extensions/doom-overlay/doom-component.ts +132 -0
  368. package/examples/extensions/doom-overlay/doom-engine.ts +173 -0
  369. package/examples/extensions/doom-overlay/doom-keys.ts +104 -0
  370. package/examples/extensions/doom-overlay/index.ts +74 -0
  371. package/examples/extensions/doom-overlay/wad-finder.ts +55 -0
  372. package/examples/extensions/dynamic-resources/SKILL.md +8 -0
  373. package/examples/extensions/dynamic-resources/dynamic.json +81 -0
  374. package/examples/extensions/dynamic-resources/dynamic.md +5 -0
  375. package/examples/extensions/dynamic-resources/index.ts +15 -0
  376. package/examples/extensions/dynamic-tools.ts +74 -0
  377. package/examples/extensions/entry-renderer.ts +41 -0
  378. package/examples/extensions/event-bus.ts +43 -0
  379. package/examples/extensions/file-trigger.ts +41 -0
  380. package/examples/extensions/git-checkpoint.ts +53 -0
  381. package/examples/extensions/git-merge-and-resolve.ts +115 -0
  382. package/examples/extensions/github-issue-autocomplete.ts +185 -0
  383. package/examples/extensions/gondolin/index.ts +531 -0
  384. package/examples/extensions/gondolin/package-lock.json +175 -0
  385. package/examples/extensions/gondolin/package.json +19 -0
  386. package/examples/extensions/handoff.ts +190 -0
  387. package/examples/extensions/hello.ts +26 -0
  388. package/examples/extensions/hidden-thinking-label.ts +53 -0
  389. package/examples/extensions/inline-bash.ts +94 -0
  390. package/examples/extensions/input-transform-streaming.ts +39 -0
  391. package/examples/extensions/input-transform.ts +43 -0
  392. package/examples/extensions/interactive-shell.ts +196 -0
  393. package/examples/extensions/jev-router.ts +113 -0
  394. package/examples/extensions/mac-system-theme.ts +47 -0
  395. package/examples/extensions/message-renderer.ts +59 -0
  396. package/examples/extensions/minimal-mode.ts +353 -0
  397. package/examples/extensions/modal-editor.ts +85 -0
  398. package/examples/extensions/model-status.ts +31 -0
  399. package/examples/extensions/notify.ts +57 -0
  400. package/examples/extensions/overlay-qa-tests.ts +1450 -0
  401. package/examples/extensions/overlay-test.ts +153 -0
  402. package/examples/extensions/permission-gate.ts +34 -0
  403. package/examples/extensions/pirate.ts +47 -0
  404. package/examples/extensions/plan-mode/README.md +66 -0
  405. package/examples/extensions/plan-mode/index.ts +394 -0
  406. package/examples/extensions/plan-mode/utils.ts +168 -0
  407. package/examples/extensions/preset.ts +436 -0
  408. package/examples/extensions/project-trust.ts +64 -0
  409. package/examples/extensions/prompt-customizer.ts +49 -0
  410. package/examples/extensions/protected-paths.ts +30 -0
  411. package/examples/extensions/provider-payload.ts +18 -0
  412. package/examples/extensions/qna.ts +118 -0
  413. package/examples/extensions/question.ts +286 -0
  414. package/examples/extensions/questionnaire.ts +448 -0
  415. package/examples/extensions/rainbow-editor.ts +88 -0
  416. package/examples/extensions/reload-runtime.ts +37 -0
  417. package/examples/extensions/rpc-demo.ts +118 -0
  418. package/examples/extensions/sandbox/index.ts +326 -0
  419. package/examples/extensions/sandbox/package-lock.json +92 -0
  420. package/examples/extensions/sandbox/package.json +19 -0
  421. package/examples/extensions/send-user-message.ts +97 -0
  422. package/examples/extensions/session-name.ts +27 -0
  423. package/examples/extensions/shutdown-command.ts +63 -0
  424. package/examples/extensions/snake.ts +343 -0
  425. package/examples/extensions/space-invaders.ts +560 -0
  426. package/examples/extensions/ssh.ts +220 -0
  427. package/examples/extensions/status-line.ts +32 -0
  428. package/examples/extensions/structured-output.ts +65 -0
  429. package/examples/extensions/subagent/README.md +177 -0
  430. package/examples/extensions/subagent/agents/planner.md +37 -0
  431. package/examples/extensions/subagent/agents/reviewer.md +35 -0
  432. package/examples/extensions/subagent/agents/scout.md +50 -0
  433. package/examples/extensions/subagent/agents/worker.md +24 -0
  434. package/examples/extensions/subagent/agents.ts +157 -0
  435. package/examples/extensions/subagent/index.ts +1038 -0
  436. package/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
  437. package/examples/extensions/subagent/prompts/implement.md +10 -0
  438. package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
  439. package/examples/extensions/summarize.ts +199 -0
  440. package/examples/extensions/system-prompt-header.ts +17 -0
  441. package/examples/extensions/tic-tac-toe.ts +1008 -0
  442. package/examples/extensions/timed-confirm.ts +70 -0
  443. package/examples/extensions/titlebar-spinner.ts +58 -0
  444. package/examples/extensions/todo.ts +297 -0
  445. package/examples/extensions/tool-override.ts +144 -0
  446. package/examples/extensions/tools.ts +146 -0
  447. package/examples/extensions/trigger-compact.ts +50 -0
  448. package/examples/extensions/truncated-tool.ts +195 -0
  449. package/examples/extensions/widget-placement.ts +9 -0
  450. package/examples/extensions/with-deps/index.ts +32 -0
  451. package/examples/extensions/with-deps/package-lock.json +31 -0
  452. package/examples/extensions/with-deps/package.json +22 -0
  453. package/examples/extensions/working-indicator.ts +123 -0
  454. package/examples/extensions/working-message-test.ts +25 -0
  455. package/examples/rpc-client.ts +35 -0
  456. package/examples/rpc-extension-ui.ts +661 -0
  457. package/examples/sdk/01-minimal.ts +26 -0
  458. package/examples/sdk/02-custom-model.ts +49 -0
  459. package/examples/sdk/03-custom-prompt.ts +75 -0
  460. package/examples/sdk/04-skills.ts +55 -0
  461. package/examples/sdk/05-tools.ts +48 -0
  462. package/examples/sdk/06-extensions.ts +99 -0
  463. package/examples/sdk/07-context-files.ts +47 -0
  464. package/examples/sdk/08-prompt-templates.ts +51 -0
  465. package/examples/sdk/09-api-keys-and-oauth.ts +34 -0
  466. package/examples/sdk/10-settings.ts +53 -0
  467. package/examples/sdk/11-sessions.ts +52 -0
  468. package/examples/sdk/12-full-control.ts +74 -0
  469. package/examples/sdk/13-session-runtime.ts +67 -0
  470. package/examples/sdk/14-codemode-mcp.ts +61 -0
  471. package/examples/sdk/README.md +141 -0
  472. package/package.json +21 -5
  473. package/python/raga_rlm/__init__.py +336 -0
  474. package/python/raga_rlm/repl.py +920 -0
  475. package/schemas/keybindings.schema.json +393 -0
  476. package/schemas/models.schema.json +1211 -0
  477. package/schemas/settings.schema.json +815 -0
  478. package/schemas/theme.schema.json +365 -0
@@ -0,0 +1,920 @@
1
+ # Adapted from Prime Agent (MIT), github.com/PrimeIntellect-ai/prime-agent
2
+ """RLM kernel: a CPython REPL speaking newline-delimited JSON over stdio.
3
+
4
+ Entry point: `python -m raga_rlm.repl`. docs/RLM.md documents protocol 1.
5
+ Cells run with top-level await in one persistent `__main__` namespace on a
6
+ single asyncio event loop.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import ast
12
+ import asyncio
13
+ import codecs
14
+ import contextvars
15
+ import inspect
16
+ import io
17
+ import json
18
+ import linecache
19
+ import os
20
+ import pickle
21
+ import platform
22
+ import signal
23
+ import sys
24
+ import tempfile
25
+ import threading
26
+ import traceback
27
+ import types
28
+ import uuid
29
+ from typing import Any
30
+
31
+ try:
32
+ import resource
33
+ except ImportError: # Windows
34
+ resource = None # type: ignore[assignment]
35
+
36
+ import raga_rlm
37
+
38
+ PROTOCOL_VERSION = 1
39
+
40
+ DEFAULT_SNAPSHOT_MAX_BYTES = 256 * 1024 * 1024
41
+ DEFAULT_SNAPSHOT_MAX_VARIABLE_BYTES = 16 * 1024 * 1024
42
+ _SNAPSHOT_MAGIC = b"RAGA-RLM-SNAPSHOT-V1\n"
43
+
44
+ _STREAM_FRAME_TEXT_CAP = 64 * 1024
45
+ # Bounds a pathological repr or exception text in transit; the host truncates further.
46
+ _RESULT_TEXT_CAP = 1024 * 1024
47
+ _HOST_REQUEST_CAP = 16 * 1024 * 1024
48
+ # After stdin EOF a synchronous cell can block the queued shutdown forever.
49
+ _EOF_EXIT_GRACE_SECONDS = 10.0
50
+ # The host passes the `rlm.memoryLimitMb` setting; unset or 0 means no limit.
51
+ _MEMORY_LIMIT_ENV = "RAGA_RLM_MEMORY_LIMIT_MB"
52
+
53
+ _RUNTIME_FILES = {os.path.abspath(__file__), os.path.abspath(raga_rlm.__file__)}
54
+
55
+ _protocol_fd = -1
56
+ _host_stderr_fd = -1
57
+ _write_lock = threading.Lock()
58
+ _loop: asyncio.AbstractEventLoop | None = None
59
+ _serve_task: asyncio.Task[Any] | None = None
60
+
61
+ # Asyncio tasks copy the context at creation, so background tasks keep their cell id.
62
+ _current_cell: contextvars.ContextVar[str | None] = contextvars.ContextVar("_current_cell", default=None)
63
+ # Full stdout/stderr per running cell, for the `cells` record.
64
+ _cell_output: dict[str, dict[str, list[str]]] = {}
65
+ _cell_counter = 0
66
+ _pending_host: dict[str, asyncio.Future[dict[str, Any]]] = {}
67
+ _host_closed = False
68
+
69
+ # Interrupt bookkeeping shared by the reader thread and the loop thread.
70
+ _interrupt_lock = threading.Lock()
71
+ _active: dict[str, Any] = {"task": None, "rid": None, "interrupted": False}
72
+ _inflight: set[str] = set() # execute ids received and not yet finished
73
+ _pending_interrupts: dict[str, Any] = {"ids": set(), "any": False}
74
+ _sigint_target: str | None = None
75
+
76
+
77
+ def _send(event: dict[str, Any]) -> None:
78
+ """Write one protocol frame; the lock keeps frames whole."""
79
+ data = (json.dumps(event, separators=(",", ":")) + "\n").encode()
80
+ with _write_lock:
81
+ view = memoryview(data)
82
+ try:
83
+ while view:
84
+ view = view[os.write(_protocol_fd, view) :]
85
+ except OSError as err:
86
+ try:
87
+ os.write(_host_stderr_fd, f"raga_rlm.repl: dropped protocol frame: {err}\n".encode())
88
+ except OSError:
89
+ pass
90
+
91
+
92
+ def _protocol_error(message: str) -> None:
93
+ _send({"event": "error", "id": None, "ename": "ProtocolError", "evalue": message, "traceback": []})
94
+
95
+
96
+ # --- host requests -----------------------------------------------------------
97
+
98
+
99
+ async def host_request(data: dict[str, Any]) -> dict[str, Any]:
100
+ """Send one host_request and await the raw reply dict."""
101
+ if _loop is None:
102
+ raise RuntimeError("the RLM kernel is not serving")
103
+ if _host_closed:
104
+ raise RuntimeError("host connection closed; host_request cannot be answered")
105
+ # allow_nan=False: NaN would serialize as invalid JSON and break the host's parser.
106
+ if len(json.dumps(data, allow_nan=False)) > _HOST_REQUEST_CAP:
107
+ raise ValueError(f"host_request payload exceeds {_HOST_REQUEST_CAP} characters")
108
+ rid = uuid.uuid4().hex
109
+ future: asyncio.Future[dict[str, Any]] = _loop.create_future()
110
+ _pending_host[rid] = future
111
+ try:
112
+ _send({"event": "host_request", "id": rid, "cell": _current_cell.get(), "data": data})
113
+ return await future
114
+ finally:
115
+ _pending_host.pop(rid, None)
116
+
117
+
118
+ def _fail_pending_host_requests() -> None:
119
+ """No reply can arrive after EOF or shutdown; unblock every waiting cell."""
120
+ global _host_closed
121
+ _host_closed = True
122
+ for future in _pending_host.values():
123
+ if not future.done():
124
+ future.set_exception(RuntimeError("host connection closed; host_request cannot be answered"))
125
+
126
+
127
+ def _resolve_host_reply(rid: str, data: dict[str, Any]) -> None:
128
+ """Reader-thread half of the host bridge; replies to unknown ids are dropped."""
129
+ assert _loop is not None
130
+
131
+ def deliver() -> None:
132
+ future = _pending_host.get(rid)
133
+ if future is not None and not future.done():
134
+ future.set_result(data)
135
+
136
+ _loop.call_soon_threadsafe(deliver)
137
+
138
+
139
+ # --- output capture ----------------------------------------------------------
140
+
141
+
142
+ class _Pump:
143
+ """Ships the bytes of one captured fd pipe as `id: null` stream events."""
144
+
145
+ def __init__(self, read_fd: int, write_fd: int, stream: str) -> None:
146
+ self._read_fd = read_fd
147
+ # Private write end: a cell closing fd 1/2 cannot break drain tokens.
148
+ self._token_fd = os.dup(write_fd)
149
+ self._stream = stream
150
+ self._decoder = codecs.getincrementaldecoder("utf-8")("replace")
151
+ self._lock = threading.Lock()
152
+ self._watch: tuple[bytes, threading.Event] | None = None
153
+ self._buf = b""
154
+ self._thread = threading.Thread(target=self._run, daemon=True)
155
+ self._thread.start()
156
+
157
+ def drain(self) -> None:
158
+ """Block until every byte written to the fd so far has been shipped."""
159
+ token = b"\xff<drain:" + uuid.uuid4().hex.encode() + b">\xff"
160
+ seen = threading.Event()
161
+ with self._lock:
162
+ self._watch = (token, seen)
163
+ try:
164
+ os.write(self._token_fd, token)
165
+ while not seen.wait(0.1):
166
+ if not self._thread.is_alive():
167
+ return
168
+ except OSError:
169
+ return
170
+ finally:
171
+ with self._lock:
172
+ self._watch = None
173
+
174
+ def _run(self) -> None:
175
+ while True:
176
+ try:
177
+ chunk = os.read(self._read_fd, 65536)
178
+ except OSError:
179
+ break
180
+ if not chunk:
181
+ break
182
+ self._feed(chunk)
183
+
184
+ def _feed(self, chunk: bytes) -> None:
185
+ data = self._buf + chunk
186
+ self._buf = b""
187
+ with self._lock:
188
+ watch = self._watch
189
+ if watch is None:
190
+ self._emit(data)
191
+ return
192
+ token, seen = watch
193
+ while True:
194
+ i = data.find(token)
195
+ if i == -1:
196
+ break
197
+ self._emit(data[:i])
198
+ self._finish_decode()
199
+ seen.set()
200
+ data = data[i + len(token) :]
201
+ # Hold back a tail that may be the start of a token split across reads.
202
+ hold = 0
203
+ for k in range(min(len(data), len(token) - 1), 0, -1):
204
+ if data.endswith(token[:k]):
205
+ hold = k
206
+ break
207
+ if hold:
208
+ self._buf = data[len(data) - hold :]
209
+ data = data[: len(data) - hold]
210
+ self._emit(data)
211
+
212
+ def _emit(self, data: bytes) -> None:
213
+ if data:
214
+ text = self._decoder.decode(data)
215
+ if text:
216
+ _send({"event": self._stream, "id": None, "text": text})
217
+
218
+ def _finish_decode(self) -> None:
219
+ text = self._decoder.decode(b"", final=True)
220
+ self._decoder = codecs.getincrementaldecoder("utf-8")("replace")
221
+ if text:
222
+ _send({"event": self._stream, "id": None, "text": text})
223
+
224
+
225
+ class _RawBuffer(io.RawIOBase):
226
+ """`sys.stdout.buffer`: bytes go to the captured fd pipe (id null)."""
227
+
228
+ def __init__(self, fd: int) -> None:
229
+ self._fd = fd
230
+
231
+ def write(self, data: Any) -> int:
232
+ view = memoryview(data).cast("B")
233
+ total = len(view)
234
+ while view:
235
+ view = view[os.write(self._fd, view) :]
236
+ return total
237
+
238
+ def fileno(self) -> int:
239
+ return self._fd
240
+
241
+ def writable(self) -> bool:
242
+ return True
243
+
244
+
245
+ class _TaggedWriter(io.TextIOBase):
246
+ """`sys.stdout`/`sys.stderr` replacement that tags writes with the writer's cell id."""
247
+
248
+ def __init__(self, stream: str, fd: int) -> None:
249
+ self._stream = stream
250
+ self._fd = fd
251
+ self._frame_lock = threading.Lock()
252
+ self._buffer = _RawBuffer(fd)
253
+
254
+ def write(self, text: str) -> int:
255
+ if not isinstance(text, str):
256
+ raise TypeError(f"write() argument must be str, not {type(text).__name__}")
257
+ if text:
258
+ cell_id = _current_cell.get()
259
+ with self._frame_lock:
260
+ record = _cell_output.get(cell_id) if cell_id is not None else None
261
+ if record is not None:
262
+ record[self._stream].append(text)
263
+ for start in range(0, len(text), _STREAM_FRAME_TEXT_CAP):
264
+ chunk = text[start : start + _STREAM_FRAME_TEXT_CAP]
265
+ _send({"event": self._stream, "id": cell_id, "text": chunk})
266
+ return len(text)
267
+
268
+ def flush(self) -> None:
269
+ pass
270
+
271
+ def fileno(self) -> int:
272
+ return self._fd
273
+
274
+ def writable(self) -> bool:
275
+ return True
276
+
277
+ def isatty(self) -> bool:
278
+ return False
279
+
280
+ @property
281
+ def buffer(self) -> _RawBuffer:
282
+ return self._buffer
283
+
284
+ @property
285
+ def encoding(self) -> str:
286
+ return "utf-8"
287
+
288
+ @property
289
+ def errors(self) -> str:
290
+ return "replace"
291
+
292
+
293
+ _pump_out: _Pump
294
+ _pump_err: _Pump
295
+
296
+
297
+ def _drain_output() -> None:
298
+ for stream in (sys.stdout, sys.stderr):
299
+ try:
300
+ stream.flush()
301
+ except (OSError, ValueError, AttributeError):
302
+ pass
303
+ _pump_out.drain()
304
+ _pump_err.drain()
305
+
306
+
307
+ # --- interrupts --------------------------------------------------------------
308
+
309
+
310
+ def _consume_task_exception(task: asyncio.Task[Any]) -> None:
311
+ if not task.cancelled():
312
+ task.exception()
313
+
314
+
315
+ def _sigint_handler(signum: int, frame: types.FrameType | None) -> None:
316
+ task = _active["task"]
317
+ # No lock here (the main thread may hold it). The rid check drops a SIGINT
318
+ # that arrives after its cell finished, so it never hits a later cell.
319
+ if task is None or task.done() or _active["rid"] != _sigint_target:
320
+ return
321
+ _active["interrupted"] = True
322
+ running = asyncio.current_task(_loop) if _loop is not None else None
323
+ if running is task:
324
+ # The cell's own step (sync code or a blocking syscall): raise into it.
325
+ raise KeyboardInterrupt
326
+ # The loop is idle in select() or another task is mid-step: cancel the cell.
327
+ task.cancel()
328
+ if running is not None and running is not _serve_task:
329
+ # A background task blocked in sync code holds the only thread; unwind it.
330
+ running.add_done_callback(_consume_task_exception)
331
+ raise KeyboardInterrupt
332
+
333
+
334
+ def _request_interrupt(target: str | None) -> None:
335
+ """Reader thread: deliver an interrupt to the running cell or park it for a queued one."""
336
+ global _sigint_target
337
+ with _interrupt_lock:
338
+ rid = _active["rid"]
339
+ if rid is not None and (target is None or target == rid):
340
+ _sigint_target = rid
341
+ elif target is not None:
342
+ if target in _inflight:
343
+ _pending_interrupts["ids"].add(target)
344
+ return
345
+ else:
346
+ if _inflight:
347
+ _pending_interrupts["any"] = True
348
+ return
349
+ # Cells run on the main thread, so SIGINT must land there.
350
+ signal.pthread_kill(threading.main_thread().ident, signal.SIGINT)
351
+ if _loop is not None:
352
+ # Wake the selector so a cancel scheduled by the handler runs promptly.
353
+ _loop.call_soon_threadsafe(lambda: None)
354
+
355
+
356
+ def _finish_request(rid: str) -> None:
357
+ global _sigint_target
358
+ with _interrupt_lock:
359
+ if _sigint_target == rid:
360
+ _sigint_target = None
361
+ _inflight.discard(rid)
362
+ _pending_interrupts["ids"].discard(rid)
363
+ if not _inflight:
364
+ _pending_interrupts["any"] = False
365
+
366
+
367
+ # --- execution ---------------------------------------------------------------
368
+
369
+
370
+ def _safe_str(exc: BaseException) -> str:
371
+ try:
372
+ return str(exc)
373
+ except BaseException: # noqa: BLE001 - a broken __str__ must not kill the kernel
374
+ return "<exception str() failed>"
375
+
376
+
377
+ def _cap_text(text: str) -> str:
378
+ if len(text) > _RESULT_TEXT_CAP:
379
+ return text[:_RESULT_TEXT_CAP] + f"\n[... truncated at {_RESULT_TEXT_CAP} characters ...]"
380
+ return text
381
+
382
+
383
+ def _cell_stack(stack: traceback.StackSummary) -> traceback.StackSummary | None:
384
+ """Frames from the first cell frame on, without the kernel's own frames."""
385
+ start = next((i for i, f in enumerate(stack) if f.filename.startswith("<cell-")), None)
386
+ if start is None:
387
+ return None
388
+ frames = [f for f in stack[start:] if os.path.abspath(f.filename) not in _RUNTIME_FILES]
389
+ return traceback.StackSummary.from_list(frames)
390
+
391
+
392
+ def _error_event(cell_id: str, exc: BaseException) -> dict[str, Any]:
393
+ te = traceback.TracebackException.from_exception(exc)
394
+ stack = _cell_stack(te.stack)
395
+ if stack is None:
396
+ # No cell frame (a SyntaxError): exception-only keeps the source line and caret.
397
+ lines = traceback.format_exception_only(type(exc), exc)
398
+ else:
399
+ te.stack = stack
400
+ lines = list(te.format())
401
+ return {
402
+ "event": "error",
403
+ "id": cell_id,
404
+ "ename": type(exc).__name__,
405
+ "evalue": _cap_text(_safe_str(exc)),
406
+ "traceback": [_cap_text(line) for line in lines],
407
+ }
408
+
409
+
410
+ def _interrupt_event(cell_id: str, exc: BaseException) -> dict[str, Any]:
411
+ """Report a cell cancelled at an await as a KeyboardInterrupt."""
412
+ stack = _cell_stack(traceback.extract_tb(exc.__traceback__))
413
+ lines = []
414
+ if stack:
415
+ lines = ["Traceback (most recent call last):\n", *stack.format()]
416
+ lines.append("KeyboardInterrupt\n")
417
+ return {"event": "error", "id": cell_id, "ename": "KeyboardInterrupt", "evalue": "", "traceback": lines}
418
+
419
+
420
+ def _compile_cell(code: str, filename: str) -> tuple[list[types.CodeType], bool]:
421
+ """Compile a cell; a trailing expression compiles separately in eval mode."""
422
+ linecache.cache[filename] = (len(code), None, code.splitlines(keepends=True), filename)
423
+ tree = ast.parse(code, filename)
424
+ trailing: ast.Expression | None = None
425
+ if tree.body and isinstance(tree.body[-1], ast.Expr):
426
+ trailing = ast.Expression(tree.body.pop().value)
427
+ flags = ast.PyCF_ALLOW_TOP_LEVEL_AWAIT
428
+ codes: list[types.CodeType] = []
429
+ if tree.body:
430
+ codes.append(compile(tree, filename, "exec", flags=flags, dont_inherit=True))
431
+ if trailing is not None:
432
+ codes.append(compile(trailing, filename, "eval", flags=flags, dont_inherit=True))
433
+ return codes, trailing is not None
434
+
435
+
436
+ async def _run_codes(codes: list[types.CodeType], ns: dict[str, Any]) -> Any:
437
+ value: Any = None
438
+ for code_obj in codes:
439
+ value = eval(code_obj, ns) # noqa: S307 - running the model's cell is the kernel's job
440
+ if code_obj.co_flags & inspect.CO_COROUTINE:
441
+ value = await value
442
+ return value
443
+
444
+
445
+ async def _run_guarded(task: asyncio.Task[Any], rid: str) -> tuple[str, Any, dict[str, Any] | None]:
446
+ """Await a cell task; returns (status, value, error event or None)."""
447
+ with _interrupt_lock:
448
+ _active.update(task=task, rid=rid, interrupted=False)
449
+ if _pending_interrupts["any"] or rid in _pending_interrupts["ids"]:
450
+ _pending_interrupts["any"] = False
451
+ _pending_interrupts["ids"].discard(rid)
452
+ # Interrupt parked before the cell started: cancel before its first step.
453
+ _active["interrupted"] = True
454
+ task.cancel()
455
+ try:
456
+ return "ok", await task, None
457
+ except asyncio.CancelledError as exc:
458
+ if _active["interrupted"]:
459
+ return "error", None, _interrupt_event(rid, exc)
460
+ return "error", None, _error_event(rid, exc)
461
+ except BaseException as exc: # noqa: BLE001 - every cell failure becomes an error event
462
+ return "error", None, _error_event(rid, exc)
463
+ finally:
464
+ with _interrupt_lock:
465
+ _active.update(task=None, rid=None)
466
+
467
+
468
+ async def _handle_execute(req: dict[str, Any], ns: dict[str, Any]) -> None:
469
+ global _cell_counter
470
+ rid: str = req["id"]
471
+ code: str = req["code"]
472
+ _cell_counter += 1
473
+ filename = f"<cell-{_cell_counter}>"
474
+ output: dict[str, list[str]] = {"stdout": [], "stderr": []}
475
+ _cell_output[rid] = output
476
+ token = _current_cell.set(rid)
477
+ status, result_text, error = "ok", None, None
478
+ try:
479
+ try:
480
+ codes, has_trailing = _compile_cell(code, filename)
481
+ except BaseException as exc: # noqa: BLE001 - SyntaxError, RecursionError, ...
482
+ status, error = "error", _error_event(rid, exc)
483
+ else:
484
+ assert _loop is not None
485
+ status, value, error = await _run_guarded(_loop.create_task(_run_codes(codes, ns)), rid)
486
+ if status == "ok" and has_trailing and value is not None:
487
+ try:
488
+ ns["_"] = value
489
+ result_text = _cap_text(repr(value))
490
+ except BaseException as exc: # noqa: BLE001 - a broken __repr__ is a cell error
491
+ status, error = "error", _error_event(rid, exc)
492
+ _drain_output()
493
+ finally:
494
+ _current_cell.reset(token)
495
+ _cell_output.pop(rid, None)
496
+ _finish_request(rid)
497
+ if result_text is not None:
498
+ _send({"event": "result", "id": rid, "text": result_text})
499
+ if error is not None:
500
+ _send(error)
501
+ raga_rlm.record_cell(
502
+ {
503
+ "id": rid,
504
+ "code": code,
505
+ "stdout": "".join(output["stdout"]),
506
+ "stderr": "".join(output["stderr"]),
507
+ "result": result_text,
508
+ "error": "".join(error["traceback"]) if error else None,
509
+ }
510
+ )
511
+ done: dict[str, Any] = {"event": "done", "id": rid, "status": status}
512
+ rss_mb = _rss_mb()
513
+ if rss_mb is not None:
514
+ done["rss_mb"] = rss_mb
515
+ _send(done)
516
+
517
+
518
+ # --- memory ------------------------------------------------------------------
519
+
520
+
521
+ def _apply_memory_limit() -> None:
522
+ """Cap the data segment (heap and private mappings) at the host's limit.
523
+
524
+ On Linux an allocation past RLIMIT_DATA fails with MemoryError in the cell,
525
+ and the kernel keeps serving. macOS does not enforce RLIMIT_DATA, so there
526
+ is no hard limit there.
527
+ """
528
+ raw = os.environ.get(_MEMORY_LIMIT_ENV, "")
529
+ limit_mb = int(raw) if raw else 0
530
+ if limit_mb <= 0 or resource is None or not sys.platform.startswith("linux"):
531
+ return
532
+ limit = limit_mb * 1024 * 1024
533
+ _, hard = resource.getrlimit(resource.RLIMIT_DATA)
534
+ if hard != resource.RLIM_INFINITY:
535
+ limit = min(limit, hard)
536
+ resource.setrlimit(resource.RLIMIT_DATA, (limit, limit))
537
+
538
+
539
+ def _rss_mb() -> int | None:
540
+ """Current RSS in MiB on Linux; peak RSS elsewhere, where getrusage has no current value."""
541
+ try:
542
+ with open("/proc/self/statm", "rb") as fh:
543
+ pages = int(fh.read().split()[1])
544
+ return pages * os.sysconf("SC_PAGE_SIZE") // (1024 * 1024)
545
+ except (OSError, ValueError, IndexError):
546
+ pass
547
+ if resource is None:
548
+ return None
549
+ peak = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
550
+ # ru_maxrss is bytes on macOS and KiB on the other Unixes.
551
+ return peak // (1024 * 1024) if sys.platform == "darwin" else peak // 1024
552
+
553
+
554
+ # --- snapshot and restore ----------------------------------------------------
555
+
556
+
557
+ class _TooLarge(Exception):
558
+ pass
559
+
560
+
561
+ class _CappedWriter:
562
+ def __init__(self, sink: io.BytesIO, limit: int) -> None:
563
+ self._sink = sink
564
+ self._limit = limit
565
+ self._written = 0
566
+
567
+ def write(self, chunk: Any) -> int:
568
+ size = len(chunk)
569
+ if self._written + size > self._limit:
570
+ raise _TooLarge()
571
+ self._sink.write(chunk)
572
+ self._written += size
573
+ return size
574
+
575
+
576
+ def _user_names(ns: dict[str, Any]) -> list[str]:
577
+ return sorted(
578
+ name
579
+ for name, value in ns.items()
580
+ if isinstance(name, str)
581
+ and not name.startswith("_")
582
+ and name not in raga_rlm.BOOTSTRAP_NAMES
583
+ and not isinstance(value, types.ModuleType)
584
+ )
585
+
586
+
587
+ def _snapshot(ns: dict[str, Any], path: str, max_bytes: int, max_variable_bytes: int) -> dict[str, Any]:
588
+ try:
589
+ import dill
590
+ except ImportError:
591
+ return {"status": "error", "reason": "dill not installed"}
592
+ # Format: magic, then per name a 4-byte name length, the UTF-8 name, an
593
+ # 8-byte blob length, and the dill blob. One blob per name lets restore
594
+ # load each name on its own.
595
+ saved: list[str] = []
596
+ skipped: list[str] = []
597
+ directory = os.path.dirname(os.path.abspath(path))
598
+ os.makedirs(directory, exist_ok=True)
599
+ fd, tmp = tempfile.mkstemp(dir=directory, prefix=os.path.basename(path) + ".", suffix=".tmp")
600
+ try:
601
+ with os.fdopen(fd, "wb") as fh:
602
+ total = fh.write(_SNAPSHOT_MAGIC)
603
+ for name in _user_names(ns):
604
+ value = ns[name]
605
+ # A large buffer arrives in one write, after dill has copied it whole.
606
+ if _estimated_size(value) > max_variable_bytes:
607
+ skipped.append(name)
608
+ continue
609
+ try:
610
+ encoded = name.encode("utf-8")
611
+ buffer = io.BytesIO()
612
+ dill.dump(value, _CappedWriter(buffer, max_variable_bytes))
613
+ except Exception: # noqa: BLE001 - _TooLarge or unpicklable: skip this name only
614
+ skipped.append(name)
615
+ continue
616
+ blob = buffer.getvalue()
617
+ size = 12 + len(encoded) + len(blob)
618
+ if total + size > max_bytes:
619
+ skipped.append(name)
620
+ continue
621
+ fh.write(len(encoded).to_bytes(4, "little"))
622
+ fh.write(encoded)
623
+ fh.write(len(blob).to_bytes(8, "little"))
624
+ fh.write(blob)
625
+ total += size
626
+ saved.append(name)
627
+ os.replace(tmp, path)
628
+ except OSError as err:
629
+ try:
630
+ os.remove(tmp)
631
+ except OSError:
632
+ pass
633
+ return {"status": "error", "reason": f"snapshot write failed: {err}"}
634
+ return {"status": "ok", "saved": saved, "skipped": skipped, "bytes": total}
635
+
636
+
637
+ def _estimated_size(value: Any) -> int:
638
+ """A cheap lower bound on a value's pickle size, without pickling it; 0 when unknown."""
639
+ if isinstance(value, (str, bytes, bytearray)):
640
+ return len(value)
641
+ if isinstance(value, memoryview):
642
+ return value.nbytes
643
+ try:
644
+ # numpy arrays and similar buffers, without importing numpy.
645
+ nbytes = getattr(value, "nbytes", None)
646
+ except Exception: # noqa: BLE001 - a broken __getattr__ only loses the estimate
647
+ return 0
648
+ return nbytes if isinstance(nbytes, int) and not isinstance(nbytes, bool) else 0
649
+
650
+
651
+ def _index_records(fh: io.BufferedReader) -> dict[str, tuple[int, int]]:
652
+ """Map each name to its blob's (offset, length), reading only the record headers."""
653
+ if fh.read(len(_SNAPSHOT_MAGIC)) != _SNAPSHOT_MAGIC:
654
+ raise ValueError("not a raga RLM snapshot")
655
+ size = os.fstat(fh.fileno()).st_size
656
+ records: dict[str, tuple[int, int]] = {}
657
+ pos = len(_SNAPSHOT_MAGIC)
658
+ while pos < size:
659
+ header = fh.read(4)
660
+ if len(header) < 4:
661
+ raise ValueError("truncated snapshot record")
662
+ name_len = int.from_bytes(header, "little")
663
+ pos += 4
664
+ if pos + name_len + 8 > size:
665
+ raise ValueError("truncated snapshot record")
666
+ name = fh.read(name_len).decode("utf-8")
667
+ blob_len = int.from_bytes(fh.read(8), "little")
668
+ pos += name_len + 8
669
+ if pos + blob_len > size:
670
+ raise ValueError("truncated snapshot record")
671
+ records[name] = (pos, blob_len)
672
+ pos += blob_len
673
+ fh.seek(pos)
674
+ return records
675
+
676
+
677
+ def _snapshot_unpickler(dill: Any) -> type:
678
+ """dill pickles a pipe-backed file as its fd number; reopening that number
679
+ here would later close whatever owns the fd now (e.g. the loop's self-pipe)."""
680
+ create_filehandle = dill._dill._create_filehandle
681
+
682
+ def refuse_raw_fd(name: Any, *args: Any) -> Any:
683
+ if isinstance(name, int):
684
+ raise pickle.UnpicklingError(f"refusing to reopen raw file descriptor {name} from a snapshot")
685
+ return create_filehandle(name, *args)
686
+
687
+ class GuardedUnpickler(dill.Unpickler):
688
+ def find_class(self, module: str, name: str) -> Any:
689
+ target = super().find_class(module, name)
690
+ return refuse_raw_fd if target is create_filehandle else target
691
+
692
+ return GuardedUnpickler
693
+
694
+
695
+ def _restore(ns: dict[str, Any], path: str) -> dict[str, Any]:
696
+ if not os.path.exists(path):
697
+ return {"status": "ok", "restored": [], "failed": [], "reason": "snapshot not found"}
698
+ try:
699
+ import dill
700
+ except ImportError:
701
+ return {"status": "error", "reason": "dill not installed"}
702
+ unpickler = _snapshot_unpickler(dill)
703
+ restored: list[str] = []
704
+ failed: list[str] = []
705
+ try:
706
+ with open(path, "rb") as fh:
707
+ records = _index_records(fh)
708
+ # One blob in memory at a time.
709
+ for name, (offset, length) in records.items():
710
+ if name.startswith("_") or name in raga_rlm.BOOTSTRAP_NAMES:
711
+ continue
712
+ try:
713
+ fh.seek(offset)
714
+ ns[name] = unpickler(io.BytesIO(fh.read(length))).load()
715
+ except Exception: # noqa: BLE001 - one broken name must not abort the restore
716
+ failed.append(name)
717
+ continue
718
+ restored.append(name)
719
+ except (OSError, ValueError) as err:
720
+ # Only opening the file and indexing its headers raise here, before any name loads.
721
+ return {"status": "error", "reason": f"snapshot load failed: {err}"}
722
+ return {"status": "ok", "restored": sorted(restored), "failed": sorted(failed)}
723
+
724
+
725
+ def _answer(ns: dict[str, Any]) -> dict[str, Any]:
726
+ """`answer` from the namespace as {content: str, ready: bool}; the model may rebind it."""
727
+ value = ns.get("answer")
728
+ if not isinstance(value, dict):
729
+ return {"content": "", "ready": False}
730
+ content = value.get("content", "")
731
+ if not isinstance(content, str):
732
+ try:
733
+ content = json.dumps(content, allow_nan=False)
734
+ except (TypeError, ValueError):
735
+ content = _safe_str(content)
736
+ return {"content": _cap_text(content), "ready": bool(value.get("ready"))}
737
+
738
+
739
+ def _int_field(req: dict[str, Any], field: str, default: int) -> int:
740
+ value = req.get(field, default)
741
+ if isinstance(value, bool) or not isinstance(value, int) or value < 0:
742
+ raise ValueError(f"{field} must be a non-negative integer")
743
+ return value
744
+
745
+
746
+ # --- serving -----------------------------------------------------------------
747
+
748
+
749
+ async def _serve(queue: asyncio.Queue[dict[str, Any]], ns: dict[str, Any]) -> None:
750
+ while True:
751
+ req = await queue.get()
752
+ # A cell may have rebound SIGINT; the kernel must own it for every request.
753
+ signal.signal(signal.SIGINT, _sigint_handler)
754
+ rtype = req["type"]
755
+ rid = req.get("id")
756
+ if rtype == "shutdown":
757
+ if isinstance(rid, str):
758
+ _send({"event": "done", "id": rid, "status": "ok"})
759
+ return
760
+ try:
761
+ if rtype == "execute":
762
+ await _handle_execute(req, ns)
763
+ continue
764
+ if rtype == "snapshot":
765
+ reply = _snapshot(
766
+ ns,
767
+ req["path"],
768
+ _int_field(req, "max_bytes", DEFAULT_SNAPSHOT_MAX_BYTES),
769
+ _int_field(req, "max_variable_bytes", DEFAULT_SNAPSHOT_MAX_VARIABLE_BYTES),
770
+ )
771
+ elif rtype == "restore":
772
+ reply = _restore(ns, req["path"])
773
+ elif rtype == "set_context":
774
+ raga_rlm.set_context(req["task"], req.get("inputs", {}), req.get("data"))
775
+ reply = {"status": "ok"}
776
+ elif rtype == "get_answer":
777
+ reply = {"status": "ok", "answer": _answer(ns)}
778
+ else:
779
+ reply = {"status": "ok", "names": _user_names(ns)}
780
+ _send({"event": "done", "id": rid, **reply})
781
+ except BaseException as exc: # noqa: BLE001 - one broken request fails alone
782
+ _send({"event": "done", "id": rid, "status": "error", "reason": f"{type(exc).__name__}: {_safe_str(exc)}"})
783
+
784
+
785
+ _REQUIRED_FIELDS: dict[str, tuple[str, ...]] = {
786
+ "execute": ("id", "code"),
787
+ "snapshot": ("id", "path"),
788
+ "restore": ("id", "path"),
789
+ "list_names": ("id",),
790
+ "get_answer": ("id",),
791
+ "set_context": ("id", "task"),
792
+ "shutdown": (),
793
+ }
794
+
795
+
796
+ def _handle_line(raw: bytes, queue: asyncio.Queue[dict[str, Any]]) -> None:
797
+ assert _loop is not None
798
+ req = json.loads(raw)
799
+ if not isinstance(req, dict):
800
+ raise ValueError("request is not a JSON object")
801
+ rtype = req.get("type")
802
+ if rtype == "interrupt":
803
+ target = req.get("id")
804
+ if target is not None and not isinstance(target, str):
805
+ _protocol_error("interrupt id must be a string")
806
+ return
807
+ _request_interrupt(target)
808
+ return
809
+ if rtype == "host_reply":
810
+ # Bypasses the queue: the waiting cell is the running execute.
811
+ rid, data = req.get("id"), req.get("data")
812
+ if isinstance(rid, str) and isinstance(data, dict):
813
+ _resolve_host_reply(rid, data)
814
+ else:
815
+ _protocol_error("host_reply needs a string id and a dict data")
816
+ return
817
+ if not isinstance(rtype, str) or rtype not in _REQUIRED_FIELDS:
818
+ _protocol_error(f"unknown request type: {rtype!r}")
819
+ return
820
+ missing = [field for field in _REQUIRED_FIELDS[rtype] if not isinstance(req.get(field), str)]
821
+ if missing:
822
+ _protocol_error(f"{rtype} request needs string fields: {', '.join(missing)}")
823
+ return
824
+ if rtype == "shutdown" and "id" in req and not isinstance(req["id"], str):
825
+ _protocol_error("shutdown id must be a string")
826
+ return
827
+ if rtype == "execute":
828
+ with _interrupt_lock:
829
+ duplicate = req["id"] in _inflight
830
+ if not duplicate:
831
+ _inflight.add(req["id"])
832
+ if duplicate:
833
+ _protocol_error(f"duplicate in-flight execute id: {req['id']!r}")
834
+ return
835
+ if rtype == "shutdown":
836
+ # A cell waiting on a host reply would block the queued shutdown forever.
837
+ _loop.call_soon_threadsafe(_fail_pending_host_requests)
838
+ _loop.call_soon_threadsafe(queue.put_nowait, req)
839
+
840
+
841
+ def _read_requests(stdin_fd: int, queue: asyncio.Queue[dict[str, Any]]) -> None:
842
+ assert _loop is not None
843
+ with os.fdopen(stdin_fd, "rb") as stream:
844
+ for raw in stream:
845
+ raw = raw.strip()
846
+ if not raw:
847
+ continue
848
+ try:
849
+ _handle_line(raw, queue)
850
+ except BaseException as err: # noqa: BLE001 - hostile input must not kill the reader
851
+ _protocol_error(f"{type(err).__name__}: {_safe_str(err)}")
852
+ # stdin closed: same as shutdown.
853
+ _loop.call_soon_threadsafe(_fail_pending_host_requests)
854
+ _loop.call_soon_threadsafe(queue.put_nowait, {"type": "shutdown"})
855
+ threading.Timer(_EOF_EXIT_GRACE_SECONDS, os._exit, args=(0,)).start()
856
+
857
+
858
+ def _setup_fds() -> int:
859
+ """Reserve the original fd 1 for protocol frames; route fds 1 and 2 through pumped pipes."""
860
+ global _protocol_fd, _host_stderr_fd, _pump_out, _pump_err
861
+ _protocol_fd = os.dup(1)
862
+ os.set_inheritable(_protocol_fd, False)
863
+ _host_stderr_fd = os.dup(2)
864
+ os.set_inheritable(_host_stderr_fd, False)
865
+ out_r, out_w = os.pipe()
866
+ err_r, err_w = os.pipe()
867
+ os.dup2(out_w, 1)
868
+ os.dup2(err_w, 2)
869
+ os.close(out_w)
870
+ os.close(err_w)
871
+ sys.stdout = _TaggedWriter("stdout", os.dup(1))
872
+ sys.stderr = _TaggedWriter("stderr", os.dup(2))
873
+ stdin_fd = os.dup(0)
874
+ os.set_inheritable(stdin_fd, False)
875
+ devnull = os.open(os.devnull, os.O_RDONLY)
876
+ os.dup2(devnull, 0)
877
+ os.close(devnull)
878
+ sys.stdin = open(os.devnull) # noqa: SIM115 - input() sees EOF, never protocol frames
879
+ _pump_out = _Pump(out_r, 1, "stdout")
880
+ _pump_err = _Pump(err_r, 2, "stderr")
881
+ return stdin_fd
882
+
883
+
884
+ def main() -> None:
885
+ global _loop, _serve_task
886
+ _apply_memory_limit()
887
+ stdin_fd = _setup_fds()
888
+
889
+ # `python -m` runs this file as `__main__`; alias it so `raga_rlm.repl` is this module.
890
+ sys.modules["raga_rlm.repl"] = sys.modules[__name__]
891
+ # A real `__main__` module makes dill pickle cell functions and classes by value.
892
+ user_module = types.ModuleType("__main__")
893
+ user_module.__dict__["__builtins__"] = __builtins__
894
+ sys.modules["__main__"] = user_module
895
+ ns = user_module.__dict__
896
+ raga_rlm.bootstrap(ns)
897
+
898
+ _loop = asyncio.new_event_loop()
899
+ asyncio.set_event_loop(_loop)
900
+ queue: asyncio.Queue[dict[str, Any]] = asyncio.Queue()
901
+ signal.signal(signal.SIGINT, _sigint_handler)
902
+ threading.Thread(target=_read_requests, args=(stdin_fd, queue), daemon=True).start()
903
+
904
+ _send({"event": "ready", "protocol": PROTOCOL_VERSION, "python": platform.python_version()})
905
+
906
+ _serve_task = _loop.create_task(_serve(queue, ns))
907
+ # A KeyboardInterrupt raised into a cell or background task escapes
908
+ # run_until_complete; the cell already recorded it, so keep serving.
909
+ while not _serve_task.done():
910
+ try:
911
+ _loop.run_until_complete(_serve_task)
912
+ except KeyboardInterrupt:
913
+ continue
914
+ _drain_output()
915
+ # Hard exit: pending background tasks and user threads must not hold the process.
916
+ os._exit(0)
917
+
918
+
919
+ if __name__ == "__main__":
920
+ main()