@code-yeongyu/senpi 2026.7.11 → 2026.7.13

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 (267) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/core/agent-session.d.ts +9 -0
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +56 -15
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts.map +1 -1
  7. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +76 -25
  8. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js.map +1 -1
  9. package/dist/core/resource-loader.d.ts +1 -0
  10. package/dist/core/resource-loader.d.ts.map +1 -1
  11. package/dist/core/resource-loader.js +20 -2
  12. package/dist/core/resource-loader.js.map +1 -1
  13. package/dist/modes/interactive/compaction-queue-transfer.d.ts +22 -0
  14. package/dist/modes/interactive/compaction-queue-transfer.d.ts.map +1 -0
  15. package/dist/modes/interactive/compaction-queue-transfer.js +82 -0
  16. package/dist/modes/interactive/compaction-queue-transfer.js.map +1 -0
  17. package/dist/modes/interactive/interactive-mode.d.ts +4 -0
  18. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  19. package/dist/modes/interactive/interactive-mode.js +83 -69
  20. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  21. package/node_modules/@babel/helper-string-parser/LICENSE +22 -0
  22. package/node_modules/@babel/helper-string-parser/README.md +19 -0
  23. package/node_modules/@babel/helper-string-parser/lib/index.js +295 -0
  24. package/node_modules/@babel/helper-string-parser/lib/index.js.map +1 -0
  25. package/node_modules/@babel/helper-string-parser/package.json +31 -0
  26. package/node_modules/@babel/helper-validator-identifier/LICENSE +22 -0
  27. package/node_modules/@babel/helper-validator-identifier/README.md +19 -0
  28. package/node_modules/@babel/helper-validator-identifier/lib/identifier.js +70 -0
  29. package/node_modules/@babel/helper-validator-identifier/lib/identifier.js.map +1 -0
  30. package/node_modules/@babel/helper-validator-identifier/lib/index.js +57 -0
  31. package/node_modules/@babel/helper-validator-identifier/lib/index.js.map +1 -0
  32. package/node_modules/@babel/helper-validator-identifier/lib/keyword.js +35 -0
  33. package/node_modules/@babel/helper-validator-identifier/lib/keyword.js.map +1 -0
  34. package/node_modules/@babel/helper-validator-identifier/package.json +31 -0
  35. package/node_modules/@babel/parser/LICENSE +19 -0
  36. package/node_modules/@babel/parser/README.md +19 -0
  37. package/node_modules/@babel/parser/bin/babel-parser.js +15 -0
  38. package/node_modules/@babel/parser/lib/index.js +14599 -0
  39. package/node_modules/@babel/parser/lib/index.js.map +1 -0
  40. package/node_modules/@babel/parser/package.json +50 -0
  41. package/node_modules/@babel/parser/typings/babel-parser.d.ts +262 -0
  42. package/node_modules/@babel/types/LICENSE +22 -0
  43. package/node_modules/@babel/types/README.md +19 -0
  44. package/node_modules/@babel/types/lib/asserts/assertNode.js +16 -0
  45. package/node_modules/@babel/types/lib/asserts/assertNode.js.map +1 -0
  46. package/node_modules/@babel/types/lib/asserts/generated/index.js +1251 -0
  47. package/node_modules/@babel/types/lib/asserts/generated/index.js.map +1 -0
  48. package/node_modules/@babel/types/lib/ast-types/generated/index.js +3 -0
  49. package/node_modules/@babel/types/lib/ast-types/generated/index.js.map +1 -0
  50. package/node_modules/@babel/types/lib/builders/flow/createFlowUnionType.js +18 -0
  51. package/node_modules/@babel/types/lib/builders/flow/createFlowUnionType.js.map +1 -0
  52. package/node_modules/@babel/types/lib/builders/flow/createTypeAnnotationBasedOnTypeof.js +31 -0
  53. package/node_modules/@babel/types/lib/builders/flow/createTypeAnnotationBasedOnTypeof.js.map +1 -0
  54. package/node_modules/@babel/types/lib/builders/generated/index.js +29 -0
  55. package/node_modules/@babel/types/lib/builders/generated/index.js.map +1 -0
  56. package/node_modules/@babel/types/lib/builders/generated/lowercase.js +2902 -0
  57. package/node_modules/@babel/types/lib/builders/generated/lowercase.js.map +1 -0
  58. package/node_modules/@babel/types/lib/builders/generated/uppercase.js +272 -0
  59. package/node_modules/@babel/types/lib/builders/generated/uppercase.js.map +1 -0
  60. package/node_modules/@babel/types/lib/builders/productions.js +12 -0
  61. package/node_modules/@babel/types/lib/builders/productions.js.map +1 -0
  62. package/node_modules/@babel/types/lib/builders/react/buildChildren.js +24 -0
  63. package/node_modules/@babel/types/lib/builders/react/buildChildren.js.map +1 -0
  64. package/node_modules/@babel/types/lib/builders/typescript/createTSUnionType.js +22 -0
  65. package/node_modules/@babel/types/lib/builders/typescript/createTSUnionType.js.map +1 -0
  66. package/node_modules/@babel/types/lib/builders/validateNode.js +21 -0
  67. package/node_modules/@babel/types/lib/builders/validateNode.js.map +1 -0
  68. package/node_modules/@babel/types/lib/clone/clone.js +12 -0
  69. package/node_modules/@babel/types/lib/clone/clone.js.map +1 -0
  70. package/node_modules/@babel/types/lib/clone/cloneDeep.js +12 -0
  71. package/node_modules/@babel/types/lib/clone/cloneDeep.js.map +1 -0
  72. package/node_modules/@babel/types/lib/clone/cloneDeepWithoutLoc.js +12 -0
  73. package/node_modules/@babel/types/lib/clone/cloneDeepWithoutLoc.js.map +1 -0
  74. package/node_modules/@babel/types/lib/clone/cloneNode.js +107 -0
  75. package/node_modules/@babel/types/lib/clone/cloneNode.js.map +1 -0
  76. package/node_modules/@babel/types/lib/clone/cloneWithoutLoc.js +12 -0
  77. package/node_modules/@babel/types/lib/clone/cloneWithoutLoc.js.map +1 -0
  78. package/node_modules/@babel/types/lib/comments/addComment.js +15 -0
  79. package/node_modules/@babel/types/lib/comments/addComment.js.map +1 -0
  80. package/node_modules/@babel/types/lib/comments/addComments.js +22 -0
  81. package/node_modules/@babel/types/lib/comments/addComments.js.map +1 -0
  82. package/node_modules/@babel/types/lib/comments/inheritInnerComments.js +12 -0
  83. package/node_modules/@babel/types/lib/comments/inheritInnerComments.js.map +1 -0
  84. package/node_modules/@babel/types/lib/comments/inheritLeadingComments.js +12 -0
  85. package/node_modules/@babel/types/lib/comments/inheritLeadingComments.js.map +1 -0
  86. package/node_modules/@babel/types/lib/comments/inheritTrailingComments.js +12 -0
  87. package/node_modules/@babel/types/lib/comments/inheritTrailingComments.js.map +1 -0
  88. package/node_modules/@babel/types/lib/comments/inheritsComments.js +17 -0
  89. package/node_modules/@babel/types/lib/comments/inheritsComments.js.map +1 -0
  90. package/node_modules/@babel/types/lib/comments/removeComments.js +15 -0
  91. package/node_modules/@babel/types/lib/comments/removeComments.js.map +1 -0
  92. package/node_modules/@babel/types/lib/constants/generated/index.js +60 -0
  93. package/node_modules/@babel/types/lib/constants/generated/index.js.map +1 -0
  94. package/node_modules/@babel/types/lib/constants/index.js +31 -0
  95. package/node_modules/@babel/types/lib/constants/index.js.map +1 -0
  96. package/node_modules/@babel/types/lib/converters/ensureBlock.js +14 -0
  97. package/node_modules/@babel/types/lib/converters/ensureBlock.js.map +1 -0
  98. package/node_modules/@babel/types/lib/converters/gatherSequenceExpressions.js +65 -0
  99. package/node_modules/@babel/types/lib/converters/gatherSequenceExpressions.js.map +1 -0
  100. package/node_modules/@babel/types/lib/converters/toBindingIdentifierName.js +14 -0
  101. package/node_modules/@babel/types/lib/converters/toBindingIdentifierName.js.map +1 -0
  102. package/node_modules/@babel/types/lib/converters/toBlock.js +29 -0
  103. package/node_modules/@babel/types/lib/converters/toBlock.js.map +1 -0
  104. package/node_modules/@babel/types/lib/converters/toComputedKey.js +14 -0
  105. package/node_modules/@babel/types/lib/converters/toComputedKey.js.map +1 -0
  106. package/node_modules/@babel/types/lib/converters/toExpression.js +28 -0
  107. package/node_modules/@babel/types/lib/converters/toExpression.js.map +1 -0
  108. package/node_modules/@babel/types/lib/converters/toIdentifier.js +25 -0
  109. package/node_modules/@babel/types/lib/converters/toIdentifier.js.map +1 -0
  110. package/node_modules/@babel/types/lib/converters/toKeyAlias.js +38 -0
  111. package/node_modules/@babel/types/lib/converters/toKeyAlias.js.map +1 -0
  112. package/node_modules/@babel/types/lib/converters/toSequenceExpression.js +19 -0
  113. package/node_modules/@babel/types/lib/converters/toSequenceExpression.js.map +1 -0
  114. package/node_modules/@babel/types/lib/converters/toStatement.js +39 -0
  115. package/node_modules/@babel/types/lib/converters/toStatement.js.map +1 -0
  116. package/node_modules/@babel/types/lib/converters/valueToNode.js +89 -0
  117. package/node_modules/@babel/types/lib/converters/valueToNode.js.map +1 -0
  118. package/node_modules/@babel/types/lib/definitions/core.js +1725 -0
  119. package/node_modules/@babel/types/lib/definitions/core.js.map +1 -0
  120. package/node_modules/@babel/types/lib/definitions/deprecated-aliases.js +11 -0
  121. package/node_modules/@babel/types/lib/definitions/deprecated-aliases.js.map +1 -0
  122. package/node_modules/@babel/types/lib/definitions/experimental.js +124 -0
  123. package/node_modules/@babel/types/lib/definitions/experimental.js.map +1 -0
  124. package/node_modules/@babel/types/lib/definitions/flow.js +495 -0
  125. package/node_modules/@babel/types/lib/definitions/flow.js.map +1 -0
  126. package/node_modules/@babel/types/lib/definitions/index.js +106 -0
  127. package/node_modules/@babel/types/lib/definitions/index.js.map +1 -0
  128. package/node_modules/@babel/types/lib/definitions/jsx.js +157 -0
  129. package/node_modules/@babel/types/lib/definitions/jsx.js.map +1 -0
  130. package/node_modules/@babel/types/lib/definitions/misc.js +31 -0
  131. package/node_modules/@babel/types/lib/definitions/misc.js.map +1 -0
  132. package/node_modules/@babel/types/lib/definitions/placeholders.js +27 -0
  133. package/node_modules/@babel/types/lib/definitions/placeholders.js.map +1 -0
  134. package/node_modules/@babel/types/lib/definitions/typescript.js +528 -0
  135. package/node_modules/@babel/types/lib/definitions/typescript.js.map +1 -0
  136. package/node_modules/@babel/types/lib/definitions/utils.js +296 -0
  137. package/node_modules/@babel/types/lib/definitions/utils.js.map +1 -0
  138. package/node_modules/@babel/types/lib/index-legacy.d.ts +2797 -0
  139. package/node_modules/@babel/types/lib/index.d.ts +3617 -0
  140. package/node_modules/@babel/types/lib/index.js +582 -0
  141. package/node_modules/@babel/types/lib/index.js.flow +2650 -0
  142. package/node_modules/@babel/types/lib/index.js.map +1 -0
  143. package/node_modules/@babel/types/lib/modifications/appendToMemberExpression.js +15 -0
  144. package/node_modules/@babel/types/lib/modifications/appendToMemberExpression.js.map +1 -0
  145. package/node_modules/@babel/types/lib/modifications/flow/removeTypeDuplicates.js +65 -0
  146. package/node_modules/@babel/types/lib/modifications/flow/removeTypeDuplicates.js.map +1 -0
  147. package/node_modules/@babel/types/lib/modifications/inherits.js +28 -0
  148. package/node_modules/@babel/types/lib/modifications/inherits.js.map +1 -0
  149. package/node_modules/@babel/types/lib/modifications/prependToMemberExpression.js +17 -0
  150. package/node_modules/@babel/types/lib/modifications/prependToMemberExpression.js.map +1 -0
  151. package/node_modules/@babel/types/lib/modifications/removeProperties.js +24 -0
  152. package/node_modules/@babel/types/lib/modifications/removeProperties.js.map +1 -0
  153. package/node_modules/@babel/types/lib/modifications/removePropertiesDeep.js +14 -0
  154. package/node_modules/@babel/types/lib/modifications/removePropertiesDeep.js.map +1 -0
  155. package/node_modules/@babel/types/lib/modifications/typescript/removeTypeDuplicates.js +66 -0
  156. package/node_modules/@babel/types/lib/modifications/typescript/removeTypeDuplicates.js.map +1 -0
  157. package/node_modules/@babel/types/lib/retrievers/getAssignmentIdentifiers.js +48 -0
  158. package/node_modules/@babel/types/lib/retrievers/getAssignmentIdentifiers.js.map +1 -0
  159. package/node_modules/@babel/types/lib/retrievers/getBindingIdentifiers.js +102 -0
  160. package/node_modules/@babel/types/lib/retrievers/getBindingIdentifiers.js.map +1 -0
  161. package/node_modules/@babel/types/lib/retrievers/getFunctionName.js +63 -0
  162. package/node_modules/@babel/types/lib/retrievers/getFunctionName.js.map +1 -0
  163. package/node_modules/@babel/types/lib/retrievers/getOuterBindingIdentifiers.js +13 -0
  164. package/node_modules/@babel/types/lib/retrievers/getOuterBindingIdentifiers.js.map +1 -0
  165. package/node_modules/@babel/types/lib/traverse/traverse.js +50 -0
  166. package/node_modules/@babel/types/lib/traverse/traverse.js.map +1 -0
  167. package/node_modules/@babel/types/lib/traverse/traverseFast.js +40 -0
  168. package/node_modules/@babel/types/lib/traverse/traverseFast.js.map +1 -0
  169. package/node_modules/@babel/types/lib/utils/deprecationWarning.js +44 -0
  170. package/node_modules/@babel/types/lib/utils/deprecationWarning.js.map +1 -0
  171. package/node_modules/@babel/types/lib/utils/inherit.js +13 -0
  172. package/node_modules/@babel/types/lib/utils/inherit.js.map +1 -0
  173. package/node_modules/@babel/types/lib/utils/react/cleanJSXElementLiteralChild.js +40 -0
  174. package/node_modules/@babel/types/lib/utils/react/cleanJSXElementLiteralChild.js.map +1 -0
  175. package/node_modules/@babel/types/lib/utils/shallowEqual.js +17 -0
  176. package/node_modules/@babel/types/lib/utils/shallowEqual.js.map +1 -0
  177. package/node_modules/@babel/types/lib/validators/buildMatchMemberExpression.js +13 -0
  178. package/node_modules/@babel/types/lib/validators/buildMatchMemberExpression.js.map +1 -0
  179. package/node_modules/@babel/types/lib/validators/generated/index.js +2797 -0
  180. package/node_modules/@babel/types/lib/validators/generated/index.js.map +1 -0
  181. package/node_modules/@babel/types/lib/validators/is.js +27 -0
  182. package/node_modules/@babel/types/lib/validators/is.js.map +1 -0
  183. package/node_modules/@babel/types/lib/validators/isBinding.js +27 -0
  184. package/node_modules/@babel/types/lib/validators/isBinding.js.map +1 -0
  185. package/node_modules/@babel/types/lib/validators/isBlockScoped.js +13 -0
  186. package/node_modules/@babel/types/lib/validators/isBlockScoped.js.map +1 -0
  187. package/node_modules/@babel/types/lib/validators/isImmutable.js +21 -0
  188. package/node_modules/@babel/types/lib/validators/isImmutable.js.map +1 -0
  189. package/node_modules/@babel/types/lib/validators/isLet.js +13 -0
  190. package/node_modules/@babel/types/lib/validators/isLet.js.map +1 -0
  191. package/node_modules/@babel/types/lib/validators/isNode.js +12 -0
  192. package/node_modules/@babel/types/lib/validators/isNode.js.map +1 -0
  193. package/node_modules/@babel/types/lib/validators/isNodesEquivalent.js +57 -0
  194. package/node_modules/@babel/types/lib/validators/isNodesEquivalent.js.map +1 -0
  195. package/node_modules/@babel/types/lib/validators/isPlaceholderType.js +15 -0
  196. package/node_modules/@babel/types/lib/validators/isPlaceholderType.js.map +1 -0
  197. package/node_modules/@babel/types/lib/validators/isReferenced.js +96 -0
  198. package/node_modules/@babel/types/lib/validators/isReferenced.js.map +1 -0
  199. package/node_modules/@babel/types/lib/validators/isScope.js +18 -0
  200. package/node_modules/@babel/types/lib/validators/isScope.js.map +1 -0
  201. package/node_modules/@babel/types/lib/validators/isSpecifierDefault.js +14 -0
  202. package/node_modules/@babel/types/lib/validators/isSpecifierDefault.js.map +1 -0
  203. package/node_modules/@babel/types/lib/validators/isType.js +17 -0
  204. package/node_modules/@babel/types/lib/validators/isType.js.map +1 -0
  205. package/node_modules/@babel/types/lib/validators/isValidES3Identifier.js +13 -0
  206. package/node_modules/@babel/types/lib/validators/isValidES3Identifier.js.map +1 -0
  207. package/node_modules/@babel/types/lib/validators/isValidIdentifier.js +18 -0
  208. package/node_modules/@babel/types/lib/validators/isValidIdentifier.js.map +1 -0
  209. package/node_modules/@babel/types/lib/validators/isVar.js +15 -0
  210. package/node_modules/@babel/types/lib/validators/isVar.js.map +1 -0
  211. package/node_modules/@babel/types/lib/validators/matchesPattern.js +44 -0
  212. package/node_modules/@babel/types/lib/validators/matchesPattern.js.map +1 -0
  213. package/node_modules/@babel/types/lib/validators/react/isCompatTag.js +11 -0
  214. package/node_modules/@babel/types/lib/validators/react/isCompatTag.js.map +1 -0
  215. package/node_modules/@babel/types/lib/validators/react/isReactComponent.js +11 -0
  216. package/node_modules/@babel/types/lib/validators/react/isReactComponent.js.map +1 -0
  217. package/node_modules/@babel/types/lib/validators/validate.js +42 -0
  218. package/node_modules/@babel/types/lib/validators/validate.js.map +1 -0
  219. package/node_modules/@babel/types/package.json +39 -0
  220. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +10 -2
  221. package/node_modules/@code-yeongyu/senpi-codemode/README.md +128 -78
  222. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -3
  223. package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/protocol.ts +15 -2
  224. package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/reserved.ts +9 -0
  225. package/node_modules/@code-yeongyu/senpi-codemode/src/bridges/agent-bridge.ts +197 -0
  226. package/node_modules/@code-yeongyu/senpi-codemode/src/bridges/output-bridge.ts +96 -0
  227. package/node_modules/@code-yeongyu/senpi-codemode/src/bridges/schema-injection.ts +3 -0
  228. package/node_modules/@code-yeongyu/senpi-codemode/src/completion/handler.ts +95 -26
  229. package/node_modules/@code-yeongyu/senpi-codemode/src/completion/tool-bridge.ts +11 -2
  230. package/node_modules/@code-yeongyu/senpi-codemode/src/config/settings.ts +96 -8
  231. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/session-manager.ts +24 -3
  232. package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +155 -21
  233. package/node_modules/@code-yeongyu/senpi-codemode/src/interpreters/detect.ts +1 -1
  234. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/jl/prelude.jl +252 -48
  235. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/jl/runner.jl +263 -83
  236. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/context-manager.ts +11 -5
  237. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/local-module-loader.ts +108 -0
  238. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/prelude.ts +8 -6
  239. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/rewrite-imports.ts +164 -0
  240. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-core.js +6 -1
  241. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-runtime.js +307 -41
  242. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel.ts +3 -2
  243. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py +792 -79
  244. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/rb/prelude.rb +234 -21
  245. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/rb/runner.rb +154 -51
  246. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-queue.ts +1 -0
  247. package/node_modules/@code-yeongyu/senpi-codemode/src/output/output-meta.ts +89 -0
  248. package/node_modules/@code-yeongyu/senpi-codemode/src/output/streaming-output.ts +296 -0
  249. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +107 -30
  250. package/node_modules/@code-yeongyu/senpi-codemode/src/timeouts/bridge-timeout.ts +2 -0
  251. package/node_modules/@code-yeongyu/senpi-codemode/src/timeouts/idle-timeout.ts +8 -19
  252. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-handler.ts +175 -113
  253. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool.ts +130 -120
  254. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/image.ts +274 -0
  255. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/json-tree.ts +247 -0
  256. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +641 -38
  257. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/status-events.ts +12 -0
  258. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +29 -1
  259. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  260. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js +16 -16
  261. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js.map +1 -1
  262. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js +6 -6
  263. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js.map +1 -1
  264. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  265. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  266. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  267. package/package.json +6 -6
@@ -1,30 +1,36 @@
1
- include("prelude.jl")
2
-
1
+ # allow: SIZE_OK — parser, stream capture, bridge calls, and the persistent execution loop share Main globals.
3
2
  using Sockets
4
3
 
4
+ const SENPI_ORIGINAL_STDOUT = stdout
5
+ const SENPI_ORIGINAL_STDIN = stdin
6
+ out_read, out_write = redirect_stdout()
7
+ err_read, err_write = redirect_stderr()
8
+ redirect_stdin(devnull)
9
+
10
+ include("prelude.jl")
11
+
5
12
  const senpi_connection = Dict{String, Any}()
13
+ const senpi_write_lock = ReentrantLock()
14
+ global senpi_current_cell = nothing
6
15
 
7
16
  function senpi_escape(text::AbstractString)
8
- # NOTE: prelude.jl defines Main-level `write`/`read` helpers for user cells,
9
- # which shadow Base within Main. Runner infrastructure must qualify Base calls
10
- # explicitly (Base.write/Base.read) or it crashes with a MethodError.
11
17
  out = IOBuffer()
12
- for c in text
13
- if c == '"'
18
+ for character in text
19
+ if character == '"'
14
20
  Base.write(out, "\\\"")
15
- elseif c == '\\'
21
+ elseif character == '\\'
16
22
  Base.write(out, "\\\\")
17
- elseif c == '\n'
23
+ elseif character == '\n'
18
24
  Base.write(out, "\\n")
19
- elseif c == '\r'
25
+ elseif character == '\r'
20
26
  Base.write(out, "\\r")
21
- elseif c == '\t'
27
+ elseif character == '\t'
22
28
  Base.write(out, "\\t")
23
29
  else
24
- Base.write(out, c)
30
+ Base.write(out, character)
25
31
  end
26
32
  end
27
- return String(take!(out))
33
+ String(take!(out))
28
34
  end
29
35
 
30
36
  function senpi_json(value)
@@ -37,62 +43,192 @@ function senpi_json(value)
37
43
  elseif value isa AbstractString
38
44
  return "\"" * senpi_escape(value) * "\""
39
45
  elseif value isa AbstractDict
40
- pairs = ["\"" * senpi_escape(string(k)) * "\":" * senpi_json(v) for (k, v) in value]
41
- return "{" * join(pairs, ",") * "}"
46
+ return "{" * join(["\"" * senpi_escape(string(key)) * "\":" * senpi_json(item) for (key, item) in value], ",") * "}"
42
47
  elseif value isa AbstractVector || value isa Tuple
43
- return "[" * join([senpi_json(v) for v in value], ",") * "]"
44
- else
45
- return "\"" * senpi_escape(string(value)) * "\""
48
+ return "[" * join([senpi_json(item) for item in value], ",") * "]"
46
49
  end
50
+ "\"" * senpi_escape(string(value)) * "\""
47
51
  end
48
52
 
49
- function senpi_parse_string(line::AbstractString, field::AbstractString)
50
- m = match(Regex("\"" * field * "\"\\s*:\\s*\"((?:\\\\.|[^\"])*)\""), line)
51
- m === nothing && return nothing
52
- return replace(m.captures[1], "\\n" => "\n", "\\\"" => "\"", "\\\\" => "\\")
53
+ function senpi_json_parse(input::AbstractString)
54
+ characters = collect(input)
55
+ cursor = Ref(1)
56
+ length_value = length(characters)
57
+ function skip_space()
58
+ while cursor[] <= length_value && isspace(characters[cursor[]])
59
+ cursor[] += 1
60
+ end
61
+ end
62
+ function parse_string()
63
+ characters[cursor[]] == '"' || error("Expected JSON string")
64
+ cursor[] += 1
65
+ out = IOBuffer()
66
+ while cursor[] <= length_value
67
+ character = characters[cursor[]]
68
+ cursor[] += 1
69
+ character == '"' && return String(take!(out))
70
+ if character != '\\'
71
+ Base.write(out, character)
72
+ continue
73
+ end
74
+ cursor[] <= length_value || error("Unexpected JSON string escape")
75
+ escaped = characters[cursor[]]
76
+ cursor[] += 1
77
+ if escaped == 'u'
78
+ cursor[] + 3 <= length_value || error("Incomplete JSON unicode escape")
79
+ code = parse(Int, String(characters[cursor[]:cursor[] + 3]); base=16)
80
+ Base.write(out, Char(code))
81
+ cursor[] += 4
82
+ else
83
+ mapped = escaped == 'n' ? '\n' : escaped == 'r' ? '\r' : escaped == 't' ? '\t' : escaped == 'b' ? '\b' : escaped == 'f' ? '\f' : escaped
84
+ Base.write(out, mapped)
85
+ end
86
+ end
87
+ error("Unterminated JSON string")
88
+ end
89
+ function parse_value()
90
+ skip_space()
91
+ cursor[] <= length_value || error("Unexpected JSON end")
92
+ character = characters[cursor[]]
93
+ if character == '"'
94
+ return parse_string()
95
+ elseif character == '{'
96
+ cursor[] += 1
97
+ object_result = Dict{String, Any}()
98
+ skip_space()
99
+ if cursor[] <= length_value && characters[cursor[]] == '}'
100
+ cursor[] += 1
101
+ return object_result
102
+ end
103
+ while true
104
+ skip_space()
105
+ key = parse_string()
106
+ skip_space()
107
+ cursor[] <= length_value && characters[cursor[]] == ':' || error("Expected JSON object colon")
108
+ cursor[] += 1
109
+ object_result[key] = parse_value()
110
+ skip_space()
111
+ cursor[] <= length_value || error("Unexpected JSON object end")
112
+ characters[cursor[]] == '}' && (cursor[] += 1; return object_result)
113
+ characters[cursor[]] == ',' || error("Expected JSON object separator")
114
+ cursor[] += 1
115
+ end
116
+ elseif character == '['
117
+ cursor[] += 1
118
+ array_result = Any[]
119
+ skip_space()
120
+ if cursor[] <= length_value && characters[cursor[]] == ']'
121
+ cursor[] += 1
122
+ return array_result
123
+ end
124
+ while true
125
+ push!(array_result, parse_value())
126
+ skip_space()
127
+ cursor[] <= length_value || error("Unexpected JSON array end")
128
+ characters[cursor[]] == ']' && (cursor[] += 1; return array_result)
129
+ characters[cursor[]] == ',' || error("Expected JSON array separator")
130
+ cursor[] += 1
131
+ end
132
+ elseif startswith(String(characters[cursor[]:end]), "true")
133
+ cursor[] += 4
134
+ return true
135
+ elseif startswith(String(characters[cursor[]:end]), "false")
136
+ cursor[] += 5
137
+ return false
138
+ elseif startswith(String(characters[cursor[]:end]), "null")
139
+ cursor[] += 4
140
+ return nothing
141
+ end
142
+ start = cursor[]
143
+ while cursor[] <= length_value && characters[cursor[]] in ['-', '+', '.', 'e', 'E', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
144
+ cursor[] += 1
145
+ end
146
+ number = String(characters[start:cursor[] - 1])
147
+ integer = tryparse(Int, number)
148
+ integer === nothing || return integer
149
+ decimal = tryparse(Float64, number)
150
+ decimal === nothing && error("Invalid JSON value")
151
+ decimal
152
+ end
153
+ parsed_result = parse_value()
154
+ skip_space()
155
+ cursor[] > length_value || error("Trailing JSON data")
156
+ parsed_result
53
157
  end
54
158
 
55
- function senpi_parse_bool(line::AbstractString, field::AbstractString)
56
- m = match(Regex("\"" * field * "\"\\s*:\\s*(true|false)"), line)
57
- m === nothing && return nothing
58
- return m.captures[1] == "true"
159
+ function senpi_emit(frame)
160
+ lock(senpi_write_lock) do
161
+ println(SENPI_ORIGINAL_STDOUT, senpi_json(frame))
162
+ flush(SENPI_ORIGINAL_STDOUT)
163
+ end
164
+ nothing
59
165
  end
60
166
 
61
- function senpi_parse_json_value(line::AbstractString)
62
- value = senpi_parse_string(line, "value")
63
- value !== nothing && return value
64
- m = match(r""""value"\s*:\s*(true|false)""", line)
65
- m !== nothing && return m.captures[1] == "true"
66
- m = match(r""""value"\s*:\s*(-?\d+(?:\.\d+)?)""", line)
67
- if m !== nothing
68
- text = m.captures[1]
69
- return occursin(".", text) ? parse(Float64, text) : parse(Int, text)
167
+ function senpi_emit_stream(stream::String, bytes::Vector{UInt8})
168
+ senpi_current_cell === nothing && return nothing
169
+ isempty(bytes) && return nothing
170
+ data = try
171
+ String(copy(bytes))
172
+ catch
173
+ repr(bytes)
70
174
  end
71
- return nothing
175
+ senpi_emit(Dict("type" => "text", "stream" => stream, "data" => data))
176
+ nothing
72
177
  end
73
178
 
74
- function senpi_parse_error_message(line::AbstractString)
75
- message = senpi_parse_string(line, "message")
76
- return message === nothing ? line : message
179
+ function senpi_drain_stream(io, stream::String)
180
+ while true
181
+ bytes = readavailable(io)
182
+ if !isempty(bytes)
183
+ senpi_emit_stream(stream, bytes)
184
+ elseif eof(io)
185
+ return nothing
186
+ else
187
+ yield()
188
+ sleep(0.001)
189
+ end
190
+ end
77
191
  end
78
192
 
79
- function senpi_emit(frame)
80
- println(senpi_json(frame))
81
- flush(stdout)
82
- end
193
+ @async senpi_drain_stream(out_read, "stdout")
194
+ @async senpi_drain_stream(err_read, "stderr")
83
195
 
84
- function senpi_call_tool(name::String, args)
85
- if !haskey(senpi_connection, "port") || !haskey(senpi_connection, "token")
86
- error("Julia tool bridge is not initialized")
196
+ function senpi_http_body(response::AbstractString)
197
+ parts = split(response, "\r\n\r\n"; limit=2)
198
+ length(parts) == 2 || return response
199
+ headers, body = parts
200
+ occursin("transfer-encoding: chunked", lowercase(headers)) || return body
201
+ bytes = collect(codeunits(body))
202
+ output = UInt8[]
203
+ cursor = 1
204
+ while cursor <= length(bytes)
205
+ header_end = nothing
206
+ for index in cursor:length(bytes) - 1
207
+ if bytes[index] == 0x0d && bytes[index + 1] == 0x0a
208
+ header_end = index
209
+ break
210
+ end
211
+ end
212
+ header_end === nothing && error("Invalid chunked bridge response")
213
+ chunk_size = parse(Int, split(String(bytes[cursor:header_end - 1]), ";"; limit=2)[1]; base=16)
214
+ cursor = header_end + 2
215
+ chunk_size == 0 && break
216
+ cursor + chunk_size - 1 <= length(bytes) || error("Truncated chunked bridge response")
217
+ append!(output, bytes[cursor:cursor + chunk_size - 1])
218
+ cursor += chunk_size + 2
87
219
  end
88
- port = senpi_connection["port"]
89
- token = senpi_connection["token"]
90
- call_id = string(time_ns())
91
- body = senpi_json(Dict("callId" => call_id, "toolName" => name, "args" => args))
220
+ String(output)
221
+ end
222
+
223
+ function senpi_bridge_request(path::String, payload)
224
+ port = get(senpi_connection, "port", nothing)
225
+ token = get(senpi_connection, "token", nothing)
226
+ port isa Integer && token isa AbstractString || error("Julia tool bridge is not initialized")
227
+ body = senpi_json(payload)
92
228
  socket = connect(ip"127.0.0.1", port)
93
229
  try
94
230
  request = join([
95
- "POST /call HTTP/1.1",
231
+ "POST " * path * " HTTP/1.1",
96
232
  "Host: 127.0.0.1",
97
233
  "Authorization: Bearer " * string(token),
98
234
  "Content-Type: application/json",
@@ -104,44 +240,88 @@ function senpi_call_tool(name::String, args)
104
240
  Base.write(socket, request)
105
241
  flush(socket)
106
242
  response = Base.read(socket, String)
107
- parts = split(response, "\r\n\r\n"; limit=2)
108
- response_body = length(parts) == 2 ? parts[2] : response
109
- if senpi_parse_bool(response_body, "ok") == true
110
- return senpi_parse_json_value(response_body)
111
- end
112
- error(senpi_parse_error_message(response_body))
243
+ parsed = senpi_json_parse(senpi_http_body(response))
244
+ parsed isa AbstractDict || error("Bridge returned invalid JSON")
245
+ get(parsed, "ok", false) === true && return get(parsed, "value", nothing)
246
+ failure = get(parsed, "error", parsed)
247
+ error(failure isa AbstractDict ? string(get(failure, "message", failure)) : string(failure))
113
248
  finally
114
249
  close(socket)
115
250
  end
116
251
  end
117
252
 
118
- while !eof(stdin)
119
- line = readline(stdin)
120
- type = senpi_parse_string(line, "type")
121
- if type == "init"
122
- port_match = match(r""""port"\s*:\s*(\d+)""", line)
123
- token = senpi_parse_string(line, "token")
124
- if port_match !== nothing && token !== nothing
125
- senpi_connection["port"] = parse(Int, port_match.captures[1])
126
- senpi_connection["token"] = token
253
+ function senpi_call_tool(name::String, arguments)
254
+ senpi_bridge_request("/call", Dict("callId" => "jl-" * string(time_ns()), "toolName" => name, "args" => arguments))
255
+ end
256
+
257
+ function senpi_completion(prompt::String, options)
258
+ senpi_bridge_request("/completion", Dict("prompt" => prompt, "opts" => options))
259
+ end
260
+
261
+ function senpi_error(error)
262
+ message = sprint(showerror, error)
263
+ Dict("name" => string(typeof(error)), "message" => message)
264
+ end
265
+
266
+ function senpi_should_display_result(parsed)
267
+ if parsed isa Expr && parsed.head === :block && !isempty(parsed.args)
268
+ last = parsed.args[end]
269
+ if last isa Expr && last.head in [Symbol("="), :function, :struct, :using, :import, :const, :global, :local, :macro]
270
+ return false
127
271
  end
128
- senpi_emit(Dict("type" => "ready"))
129
- elseif type == "run"
130
- cell_id = senpi_parse_string(line, "cellId")
131
- code = senpi_parse_string(line, "code")
132
- started = time()
133
- try
134
- value = Core.eval(Main, Meta.parse(code))
135
- frame = Dict("type" => "result", "cellId" => cell_id, "ok" => true, "durationMs" => round(Int, (time() - started) * 1000))
136
- if value !== nothing
137
- frame["valueRepr"] = senpi_json(value)
138
- end
139
- senpi_emit(frame)
140
- catch err
141
- senpi_emit(Dict("type" => "result", "cellId" => cell_id, "ok" => false, "error" => Dict("message" => string(err)), "durationMs" => round(Int, (time() - started) * 1000)))
272
+ end
273
+ true
274
+ end
275
+
276
+ function senpi_set_connection(value)
277
+ value isa AbstractDict || error("missing bridge connection")
278
+ empty!(senpi_connection)
279
+ for (key, item) in value
280
+ senpi_connection[string(key)] = item
281
+ end
282
+ end
283
+
284
+ function senpi_run_cell(message)
285
+ cell_id = string(get(message, "cellId", ""))
286
+ code = string(get(message, "code", ""))
287
+ started = time()
288
+ global senpi_current_cell = cell_id
289
+ try
290
+ parsed = Meta.parse("begin\n" * code * "\nend")
291
+ if parsed isa Expr && parsed.head === :error
292
+ error(string(parsed.args[1]))
293
+ end
294
+ value = Core.eval(Main, parsed)
295
+ flush(stdout)
296
+ flush(stderr)
297
+ yield()
298
+ frame = Dict{String, Any}("type" => "result", "cellId" => cell_id, "ok" => true, "durationMs" => round(Int, (time() - started) * 1000))
299
+ value !== nothing && senpi_should_display_result(parsed) && (frame["valueRepr"] = senpi_json(value))
300
+ senpi_emit(frame)
301
+ catch error
302
+ senpi_emit(Dict("type" => "result", "cellId" => cell_id, "ok" => false, "error" => senpi_error(error), "durationMs" => round(Int, (time() - started) * 1000)))
303
+ finally
304
+ global senpi_current_cell = nothing
305
+ end
306
+ end
307
+
308
+ while !eof(SENPI_ORIGINAL_STDIN)
309
+ line = readline(SENPI_ORIGINAL_STDIN)
310
+ isempty(line) && continue
311
+ try
312
+ message = senpi_json_parse(line)
313
+ message isa AbstractDict || error("Bridge frame must be an object")
314
+ kind = get(message, "type", nothing)
315
+ if kind == "init"
316
+ senpi_set_connection(get(message, "connection", nothing))
317
+ senpi_emit(Dict("type" => "ready"))
318
+ elseif kind == "run"
319
+ senpi_run_cell(message)
320
+ elseif kind == "close"
321
+ senpi_emit(Dict("type" => "closed"))
322
+ break
142
323
  end
143
- elseif type == "close"
144
- senpi_emit(Dict("type" => "closed"))
145
- exit(0)
324
+ catch error
325
+ senpi_emit(Dict("type" => "init-failed", "error" => senpi_error(error)))
146
326
  end
147
327
  end
@@ -3,20 +3,21 @@ import { createInlineWorker, type WorkerLike } from "./inline-worker.ts";
3
3
  import {
4
4
  assertJavaScriptKernelOpen,
5
5
  type JavaScriptKernelMode,
6
- type JavaScriptKernelOptions,
7
6
  type JavaScriptRunInput,
8
7
  type LifecycleState,
9
8
  type ResultMessage,
10
9
  type ToolCallMessage,
11
10
  } from "./kernel-contract.ts";
11
+ import { type JavaScriptKernelOptions, LocalModuleLoader, localBridgeConnection } from "./local-module-loader.ts";
12
12
  import { JavaScriptRunQueue, type PendingJavaScriptRun, stoppedResult } from "./run-queue.ts";
13
13
  import { bridgeError, spawnNodeWorker, WorkerStartupCancelledError, waitForReady } from "./worker-host.ts";
14
14
 
15
- export type { JavaScriptKernelMode, JavaScriptKernelOptions, JavaScriptRunInput } from "./kernel-contract.ts";
16
- export { JavaScriptKernelClosedError } from "./kernel-contract.ts";
15
+ export { JavaScriptKernelClosedError, type JavaScriptKernelMode, type JavaScriptRunInput } from "./kernel-contract.ts";
16
+ export type { JavaScriptKernelOptions } from "./local-module-loader.ts";
17
17
 
18
18
  export class JavaScriptKernel {
19
19
  readonly #options: JavaScriptKernelOptions;
20
+ readonly #moduleLoader: LocalModuleLoader;
20
21
  #worker: WorkerLike | null = null;
21
22
  #mode: JavaScriptKernelMode = "worker";
22
23
  #lifecycle: LifecycleState = "open";
@@ -33,6 +34,7 @@ export class JavaScriptKernel {
33
34
 
34
35
  constructor(options: JavaScriptKernelOptions) {
35
36
  this.#options = options;
37
+ this.#moduleLoader = new LocalModuleLoader(options);
36
38
  }
37
39
 
38
40
  get mode(): JavaScriptKernelMode {
@@ -178,7 +180,11 @@ export class JavaScriptKernel {
178
180
 
179
181
  async #initializeWorker(worker: WorkerLike, signal: AbortSignal): Promise<void> {
180
182
  const ready = waitForReady(worker, signal);
181
- worker.postMessage({ type: "init", sessionId: this.#options.sessionId, connection: { port: 1, token: "local" } });
183
+ worker.postMessage({
184
+ type: "init",
185
+ sessionId: this.#options.sessionId,
186
+ connection: localBridgeConnection(this.#options),
187
+ });
182
188
  await ready;
183
189
  }
184
190
 
@@ -196,7 +202,7 @@ export class JavaScriptKernel {
196
202
  this.#worker.postMessage({
197
203
  type: "run",
198
204
  cellId: next.input.cellId,
199
- code: next.input.code,
205
+ code: this.#moduleLoader.prepareCell(next.input.code),
200
206
  timeoutMs: next.input.timeoutMs,
201
207
  });
202
208
  }
@@ -0,0 +1,108 @@
1
+ import { join, resolve, sep } from "node:path";
2
+ import { pathToFileURL } from "node:url";
3
+ import type { BridgeConnectionConfig } from "../../bridge/protocol.ts";
4
+ import {
5
+ RESERVED_AGENT_TOOL,
6
+ RESERVED_OUTPUT_TOOL,
7
+ TIMEOUT_PAUSE_OP,
8
+ TIMEOUT_RESUME_OP,
9
+ } from "../../bridge/reserved.ts";
10
+ import type { JavaScriptKernelOptions as BaseJavaScriptKernelOptions } from "./kernel-contract.ts";
11
+ import { rewriteImports } from "./rewrite-imports.ts";
12
+
13
+ const PREPARED_CELL_PREFIX = "/*senpi:prepared-cell*/";
14
+
15
+ export interface LocalModuleLoaderOptions {
16
+ readonly cwd: string;
17
+ readonly localRoots?: Readonly<Record<string, string>>;
18
+ readonly artifactsDir?: string;
19
+ }
20
+
21
+ export type JavaScriptKernelOptions = BaseJavaScriptKernelOptions & LocalModuleLoaderOptions;
22
+
23
+ export function localBridgeConnection(options: LocalModuleLoaderOptions): BridgeConnectionConfig {
24
+ return {
25
+ port: 1,
26
+ token: "local",
27
+ ...(options.localRoots ? { localRoots: { ...options.localRoots } } : {}),
28
+ ...(options.artifactsDir ? { artifactsDir: options.artifactsDir } : {}),
29
+ };
30
+ }
31
+
32
+ type RuntimeModuleContext = {
33
+ readonly cwdUrl: string;
34
+ readonly localRootUrls: Readonly<Record<string, string>>;
35
+ readonly reservedAgentTool: string;
36
+ readonly reservedOutputTool: string;
37
+ readonly timeoutPauseOp: string;
38
+ readonly timeoutResumeOp: string;
39
+ };
40
+
41
+ function directoryUrl(directory: string): string {
42
+ return pathToFileURL(`${resolve(directory)}${sep}`).href;
43
+ }
44
+
45
+ function runtimeContext(options: LocalModuleLoaderOptions): RuntimeModuleContext {
46
+ const roots: Record<string, string> = {};
47
+ for (const [scheme, root] of Object.entries(options.localRoots ?? {})) {
48
+ roots[scheme.toLowerCase()] = directoryUrl(root);
49
+ }
50
+ if (options.artifactsDir && roots["local"] === undefined) {
51
+ roots["local"] = directoryUrl(join(options.artifactsDir, "local"));
52
+ }
53
+ return {
54
+ cwdUrl: directoryUrl(options.cwd),
55
+ localRootUrls: roots,
56
+ reservedAgentTool: RESERVED_AGENT_TOOL,
57
+ reservedOutputTool: RESERVED_OUTPUT_TOOL,
58
+ timeoutPauseOp: TIMEOUT_PAUSE_OP,
59
+ timeoutResumeOp: TIMEOUT_RESUME_OP,
60
+ };
61
+ }
62
+
63
+ function loaderPrelude(context: RuntimeModuleContext): string {
64
+ const serialized = JSON.stringify(context);
65
+ return [
66
+ `globalThis.__senpi_module_context__ = ${serialized};`,
67
+ "globalThis.__senpi_reserved_agent_tool__ = globalThis.__senpi_module_context__.reservedAgentTool;",
68
+ "globalThis.__senpi_reserved_output_tool__ = globalThis.__senpi_module_context__.reservedOutputTool;",
69
+ "globalThis.__senpi_timeout_pause_op__ = globalThis.__senpi_module_context__.timeoutPauseOp;",
70
+ "globalThis.__senpi_timeout_resume_op__ = globalThis.__senpi_module_context__.timeoutResumeOp;",
71
+ "globalThis.__senpi_import__ = async (source, options) => {",
72
+ " const context = globalThis.__senpi_module_context__;",
73
+ " const specifier = String(source);",
74
+ " const match = /^([a-z][a-z0-9+.-]*):\\/\\/(.*)$/i.exec(specifier);",
75
+ " let target = specifier;",
76
+ " if (match) {",
77
+ " const scheme = match[1].toLowerCase();",
78
+ " const root = context.localRootUrls[scheme];",
79
+ " if (!root) throw new Error('Unsupported module protocol: ' + specifier);",
80
+ " let relative;",
81
+ " try { relative = decodeURIComponent(match[2].replaceAll('\\\\', '/')); }",
82
+ " catch { throw new Error('Invalid module URL encoding: ' + specifier); }",
83
+ " if (relative.startsWith('/') || relative.split('/').includes('..')) {",
84
+ " throw new Error('Module path escapes ' + scheme + ':// root: ' + specifier);",
85
+ " }",
86
+ " target = new URL(relative, root).href;",
87
+ " } else if (specifier.startsWith('./') || specifier.startsWith('../') || specifier === '.' || specifier === '..') {",
88
+ " target = new URL(specifier, context.cwdUrl).href;",
89
+ " } else if (specifier.startsWith('/') || /^[A-Za-z]:[\\\\/]/.test(specifier)) {",
90
+ " const urlModule = await import('node:url');",
91
+ " target = urlModule.pathToFileURL(specifier).href;",
92
+ " }",
93
+ " return options === undefined ? import(target) : import(target, options);",
94
+ "};",
95
+ ].join("\n");
96
+ }
97
+
98
+ export class LocalModuleLoader {
99
+ readonly #prelude: string;
100
+
101
+ constructor(options: LocalModuleLoaderOptions) {
102
+ this.#prelude = loaderPrelude(runtimeContext(options));
103
+ }
104
+
105
+ prepareCell(code: string): string {
106
+ return `${PREPARED_CELL_PREFIX}${JSON.stringify({ prelude: this.#prelude, code: rewriteImports(code) })}`;
107
+ }
108
+ }
@@ -1,13 +1,15 @@
1
1
  export const JAVASCRIPT_KERNEL_PRELUDE = [
2
2
  "print(...values): write stdout text.",
3
- "display(value): emit JSON or image display output.",
3
+ "display(value): emit JSON, image, or markdown display output.",
4
4
  "log(message): emit a progress log line.",
5
5
  "phase(title): emit a progress phase.",
6
6
  "env(key?, value?): read, set, or list environment values.",
7
- "read(path, offset?, limit?): read UTF-8 text with optional 1-indexed line slicing.",
8
- "write(path, content): write UTF-8 text and return the resolved path.",
7
+ "read(path, options?): read UTF-8 text; plain paths use cwd and local:// uses the session local root.",
8
+ "write(path, content): write UTF-8 or binary data and return the resolved path.",
9
9
  "tool.<name>(args): request a host tool call through the bridge.",
10
- "completion(prompt, opts?): request a host completion bridge call.",
11
- "parallel(thunks): run async thunks through the configured bounded pool.",
12
- "pipeline(items, ...stages): map items through staged async transforms.",
10
+ "completion(prompt, options?): request a host completion bridge call.",
11
+ "output(...ids, options?): retrieve task output through the reserved output bridge.",
12
+ "agent(prompt, options?): delegate work through the reserved agent bridge.",
13
+ "parallel(thunks): run async thunks through the configured bounded pool; preserves order and rethrows the lowest-index error after all settle.",
14
+ "pipeline(items, ...stages): map items through staged async transforms with a barrier between stages.",
13
15
  ].join("\n");