@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.
- package/CHANGELOG.md +17 -0
- package/dist/core/agent-session.d.ts +9 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +56 -15
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +76 -25
- package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js.map +1 -1
- package/dist/core/resource-loader.d.ts +1 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +20 -2
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/modes/interactive/compaction-queue-transfer.d.ts +22 -0
- package/dist/modes/interactive/compaction-queue-transfer.d.ts.map +1 -0
- package/dist/modes/interactive/compaction-queue-transfer.js +82 -0
- package/dist/modes/interactive/compaction-queue-transfer.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +83 -69
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/node_modules/@babel/helper-string-parser/LICENSE +22 -0
- package/node_modules/@babel/helper-string-parser/README.md +19 -0
- package/node_modules/@babel/helper-string-parser/lib/index.js +295 -0
- package/node_modules/@babel/helper-string-parser/lib/index.js.map +1 -0
- package/node_modules/@babel/helper-string-parser/package.json +31 -0
- package/node_modules/@babel/helper-validator-identifier/LICENSE +22 -0
- package/node_modules/@babel/helper-validator-identifier/README.md +19 -0
- package/node_modules/@babel/helper-validator-identifier/lib/identifier.js +70 -0
- package/node_modules/@babel/helper-validator-identifier/lib/identifier.js.map +1 -0
- package/node_modules/@babel/helper-validator-identifier/lib/index.js +57 -0
- package/node_modules/@babel/helper-validator-identifier/lib/index.js.map +1 -0
- package/node_modules/@babel/helper-validator-identifier/lib/keyword.js +35 -0
- package/node_modules/@babel/helper-validator-identifier/lib/keyword.js.map +1 -0
- package/node_modules/@babel/helper-validator-identifier/package.json +31 -0
- package/node_modules/@babel/parser/LICENSE +19 -0
- package/node_modules/@babel/parser/README.md +19 -0
- package/node_modules/@babel/parser/bin/babel-parser.js +15 -0
- package/node_modules/@babel/parser/lib/index.js +14599 -0
- package/node_modules/@babel/parser/lib/index.js.map +1 -0
- package/node_modules/@babel/parser/package.json +50 -0
- package/node_modules/@babel/parser/typings/babel-parser.d.ts +262 -0
- package/node_modules/@babel/types/LICENSE +22 -0
- package/node_modules/@babel/types/README.md +19 -0
- package/node_modules/@babel/types/lib/asserts/assertNode.js +16 -0
- package/node_modules/@babel/types/lib/asserts/assertNode.js.map +1 -0
- package/node_modules/@babel/types/lib/asserts/generated/index.js +1251 -0
- package/node_modules/@babel/types/lib/asserts/generated/index.js.map +1 -0
- package/node_modules/@babel/types/lib/ast-types/generated/index.js +3 -0
- package/node_modules/@babel/types/lib/ast-types/generated/index.js.map +1 -0
- package/node_modules/@babel/types/lib/builders/flow/createFlowUnionType.js +18 -0
- package/node_modules/@babel/types/lib/builders/flow/createFlowUnionType.js.map +1 -0
- package/node_modules/@babel/types/lib/builders/flow/createTypeAnnotationBasedOnTypeof.js +31 -0
- package/node_modules/@babel/types/lib/builders/flow/createTypeAnnotationBasedOnTypeof.js.map +1 -0
- package/node_modules/@babel/types/lib/builders/generated/index.js +29 -0
- package/node_modules/@babel/types/lib/builders/generated/index.js.map +1 -0
- package/node_modules/@babel/types/lib/builders/generated/lowercase.js +2902 -0
- package/node_modules/@babel/types/lib/builders/generated/lowercase.js.map +1 -0
- package/node_modules/@babel/types/lib/builders/generated/uppercase.js +272 -0
- package/node_modules/@babel/types/lib/builders/generated/uppercase.js.map +1 -0
- package/node_modules/@babel/types/lib/builders/productions.js +12 -0
- package/node_modules/@babel/types/lib/builders/productions.js.map +1 -0
- package/node_modules/@babel/types/lib/builders/react/buildChildren.js +24 -0
- package/node_modules/@babel/types/lib/builders/react/buildChildren.js.map +1 -0
- package/node_modules/@babel/types/lib/builders/typescript/createTSUnionType.js +22 -0
- package/node_modules/@babel/types/lib/builders/typescript/createTSUnionType.js.map +1 -0
- package/node_modules/@babel/types/lib/builders/validateNode.js +21 -0
- package/node_modules/@babel/types/lib/builders/validateNode.js.map +1 -0
- package/node_modules/@babel/types/lib/clone/clone.js +12 -0
- package/node_modules/@babel/types/lib/clone/clone.js.map +1 -0
- package/node_modules/@babel/types/lib/clone/cloneDeep.js +12 -0
- package/node_modules/@babel/types/lib/clone/cloneDeep.js.map +1 -0
- package/node_modules/@babel/types/lib/clone/cloneDeepWithoutLoc.js +12 -0
- package/node_modules/@babel/types/lib/clone/cloneDeepWithoutLoc.js.map +1 -0
- package/node_modules/@babel/types/lib/clone/cloneNode.js +107 -0
- package/node_modules/@babel/types/lib/clone/cloneNode.js.map +1 -0
- package/node_modules/@babel/types/lib/clone/cloneWithoutLoc.js +12 -0
- package/node_modules/@babel/types/lib/clone/cloneWithoutLoc.js.map +1 -0
- package/node_modules/@babel/types/lib/comments/addComment.js +15 -0
- package/node_modules/@babel/types/lib/comments/addComment.js.map +1 -0
- package/node_modules/@babel/types/lib/comments/addComments.js +22 -0
- package/node_modules/@babel/types/lib/comments/addComments.js.map +1 -0
- package/node_modules/@babel/types/lib/comments/inheritInnerComments.js +12 -0
- package/node_modules/@babel/types/lib/comments/inheritInnerComments.js.map +1 -0
- package/node_modules/@babel/types/lib/comments/inheritLeadingComments.js +12 -0
- package/node_modules/@babel/types/lib/comments/inheritLeadingComments.js.map +1 -0
- package/node_modules/@babel/types/lib/comments/inheritTrailingComments.js +12 -0
- package/node_modules/@babel/types/lib/comments/inheritTrailingComments.js.map +1 -0
- package/node_modules/@babel/types/lib/comments/inheritsComments.js +17 -0
- package/node_modules/@babel/types/lib/comments/inheritsComments.js.map +1 -0
- package/node_modules/@babel/types/lib/comments/removeComments.js +15 -0
- package/node_modules/@babel/types/lib/comments/removeComments.js.map +1 -0
- package/node_modules/@babel/types/lib/constants/generated/index.js +60 -0
- package/node_modules/@babel/types/lib/constants/generated/index.js.map +1 -0
- package/node_modules/@babel/types/lib/constants/index.js +31 -0
- package/node_modules/@babel/types/lib/constants/index.js.map +1 -0
- package/node_modules/@babel/types/lib/converters/ensureBlock.js +14 -0
- package/node_modules/@babel/types/lib/converters/ensureBlock.js.map +1 -0
- package/node_modules/@babel/types/lib/converters/gatherSequenceExpressions.js +65 -0
- package/node_modules/@babel/types/lib/converters/gatherSequenceExpressions.js.map +1 -0
- package/node_modules/@babel/types/lib/converters/toBindingIdentifierName.js +14 -0
- package/node_modules/@babel/types/lib/converters/toBindingIdentifierName.js.map +1 -0
- package/node_modules/@babel/types/lib/converters/toBlock.js +29 -0
- package/node_modules/@babel/types/lib/converters/toBlock.js.map +1 -0
- package/node_modules/@babel/types/lib/converters/toComputedKey.js +14 -0
- package/node_modules/@babel/types/lib/converters/toComputedKey.js.map +1 -0
- package/node_modules/@babel/types/lib/converters/toExpression.js +28 -0
- package/node_modules/@babel/types/lib/converters/toExpression.js.map +1 -0
- package/node_modules/@babel/types/lib/converters/toIdentifier.js +25 -0
- package/node_modules/@babel/types/lib/converters/toIdentifier.js.map +1 -0
- package/node_modules/@babel/types/lib/converters/toKeyAlias.js +38 -0
- package/node_modules/@babel/types/lib/converters/toKeyAlias.js.map +1 -0
- package/node_modules/@babel/types/lib/converters/toSequenceExpression.js +19 -0
- package/node_modules/@babel/types/lib/converters/toSequenceExpression.js.map +1 -0
- package/node_modules/@babel/types/lib/converters/toStatement.js +39 -0
- package/node_modules/@babel/types/lib/converters/toStatement.js.map +1 -0
- package/node_modules/@babel/types/lib/converters/valueToNode.js +89 -0
- package/node_modules/@babel/types/lib/converters/valueToNode.js.map +1 -0
- package/node_modules/@babel/types/lib/definitions/core.js +1725 -0
- package/node_modules/@babel/types/lib/definitions/core.js.map +1 -0
- package/node_modules/@babel/types/lib/definitions/deprecated-aliases.js +11 -0
- package/node_modules/@babel/types/lib/definitions/deprecated-aliases.js.map +1 -0
- package/node_modules/@babel/types/lib/definitions/experimental.js +124 -0
- package/node_modules/@babel/types/lib/definitions/experimental.js.map +1 -0
- package/node_modules/@babel/types/lib/definitions/flow.js +495 -0
- package/node_modules/@babel/types/lib/definitions/flow.js.map +1 -0
- package/node_modules/@babel/types/lib/definitions/index.js +106 -0
- package/node_modules/@babel/types/lib/definitions/index.js.map +1 -0
- package/node_modules/@babel/types/lib/definitions/jsx.js +157 -0
- package/node_modules/@babel/types/lib/definitions/jsx.js.map +1 -0
- package/node_modules/@babel/types/lib/definitions/misc.js +31 -0
- package/node_modules/@babel/types/lib/definitions/misc.js.map +1 -0
- package/node_modules/@babel/types/lib/definitions/placeholders.js +27 -0
- package/node_modules/@babel/types/lib/definitions/placeholders.js.map +1 -0
- package/node_modules/@babel/types/lib/definitions/typescript.js +528 -0
- package/node_modules/@babel/types/lib/definitions/typescript.js.map +1 -0
- package/node_modules/@babel/types/lib/definitions/utils.js +296 -0
- package/node_modules/@babel/types/lib/definitions/utils.js.map +1 -0
- package/node_modules/@babel/types/lib/index-legacy.d.ts +2797 -0
- package/node_modules/@babel/types/lib/index.d.ts +3617 -0
- package/node_modules/@babel/types/lib/index.js +582 -0
- package/node_modules/@babel/types/lib/index.js.flow +2650 -0
- package/node_modules/@babel/types/lib/index.js.map +1 -0
- package/node_modules/@babel/types/lib/modifications/appendToMemberExpression.js +15 -0
- package/node_modules/@babel/types/lib/modifications/appendToMemberExpression.js.map +1 -0
- package/node_modules/@babel/types/lib/modifications/flow/removeTypeDuplicates.js +65 -0
- package/node_modules/@babel/types/lib/modifications/flow/removeTypeDuplicates.js.map +1 -0
- package/node_modules/@babel/types/lib/modifications/inherits.js +28 -0
- package/node_modules/@babel/types/lib/modifications/inherits.js.map +1 -0
- package/node_modules/@babel/types/lib/modifications/prependToMemberExpression.js +17 -0
- package/node_modules/@babel/types/lib/modifications/prependToMemberExpression.js.map +1 -0
- package/node_modules/@babel/types/lib/modifications/removeProperties.js +24 -0
- package/node_modules/@babel/types/lib/modifications/removeProperties.js.map +1 -0
- package/node_modules/@babel/types/lib/modifications/removePropertiesDeep.js +14 -0
- package/node_modules/@babel/types/lib/modifications/removePropertiesDeep.js.map +1 -0
- package/node_modules/@babel/types/lib/modifications/typescript/removeTypeDuplicates.js +66 -0
- package/node_modules/@babel/types/lib/modifications/typescript/removeTypeDuplicates.js.map +1 -0
- package/node_modules/@babel/types/lib/retrievers/getAssignmentIdentifiers.js +48 -0
- package/node_modules/@babel/types/lib/retrievers/getAssignmentIdentifiers.js.map +1 -0
- package/node_modules/@babel/types/lib/retrievers/getBindingIdentifiers.js +102 -0
- package/node_modules/@babel/types/lib/retrievers/getBindingIdentifiers.js.map +1 -0
- package/node_modules/@babel/types/lib/retrievers/getFunctionName.js +63 -0
- package/node_modules/@babel/types/lib/retrievers/getFunctionName.js.map +1 -0
- package/node_modules/@babel/types/lib/retrievers/getOuterBindingIdentifiers.js +13 -0
- package/node_modules/@babel/types/lib/retrievers/getOuterBindingIdentifiers.js.map +1 -0
- package/node_modules/@babel/types/lib/traverse/traverse.js +50 -0
- package/node_modules/@babel/types/lib/traverse/traverse.js.map +1 -0
- package/node_modules/@babel/types/lib/traverse/traverseFast.js +40 -0
- package/node_modules/@babel/types/lib/traverse/traverseFast.js.map +1 -0
- package/node_modules/@babel/types/lib/utils/deprecationWarning.js +44 -0
- package/node_modules/@babel/types/lib/utils/deprecationWarning.js.map +1 -0
- package/node_modules/@babel/types/lib/utils/inherit.js +13 -0
- package/node_modules/@babel/types/lib/utils/inherit.js.map +1 -0
- package/node_modules/@babel/types/lib/utils/react/cleanJSXElementLiteralChild.js +40 -0
- package/node_modules/@babel/types/lib/utils/react/cleanJSXElementLiteralChild.js.map +1 -0
- package/node_modules/@babel/types/lib/utils/shallowEqual.js +17 -0
- package/node_modules/@babel/types/lib/utils/shallowEqual.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/buildMatchMemberExpression.js +13 -0
- package/node_modules/@babel/types/lib/validators/buildMatchMemberExpression.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/generated/index.js +2797 -0
- package/node_modules/@babel/types/lib/validators/generated/index.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/is.js +27 -0
- package/node_modules/@babel/types/lib/validators/is.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/isBinding.js +27 -0
- package/node_modules/@babel/types/lib/validators/isBinding.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/isBlockScoped.js +13 -0
- package/node_modules/@babel/types/lib/validators/isBlockScoped.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/isImmutable.js +21 -0
- package/node_modules/@babel/types/lib/validators/isImmutable.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/isLet.js +13 -0
- package/node_modules/@babel/types/lib/validators/isLet.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/isNode.js +12 -0
- package/node_modules/@babel/types/lib/validators/isNode.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/isNodesEquivalent.js +57 -0
- package/node_modules/@babel/types/lib/validators/isNodesEquivalent.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/isPlaceholderType.js +15 -0
- package/node_modules/@babel/types/lib/validators/isPlaceholderType.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/isReferenced.js +96 -0
- package/node_modules/@babel/types/lib/validators/isReferenced.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/isScope.js +18 -0
- package/node_modules/@babel/types/lib/validators/isScope.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/isSpecifierDefault.js +14 -0
- package/node_modules/@babel/types/lib/validators/isSpecifierDefault.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/isType.js +17 -0
- package/node_modules/@babel/types/lib/validators/isType.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/isValidES3Identifier.js +13 -0
- package/node_modules/@babel/types/lib/validators/isValidES3Identifier.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/isValidIdentifier.js +18 -0
- package/node_modules/@babel/types/lib/validators/isValidIdentifier.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/isVar.js +15 -0
- package/node_modules/@babel/types/lib/validators/isVar.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/matchesPattern.js +44 -0
- package/node_modules/@babel/types/lib/validators/matchesPattern.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/react/isCompatTag.js +11 -0
- package/node_modules/@babel/types/lib/validators/react/isCompatTag.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/react/isReactComponent.js +11 -0
- package/node_modules/@babel/types/lib/validators/react/isReactComponent.js.map +1 -0
- package/node_modules/@babel/types/lib/validators/validate.js +42 -0
- package/node_modules/@babel/types/lib/validators/validate.js.map +1 -0
- package/node_modules/@babel/types/package.json +39 -0
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +10 -2
- package/node_modules/@code-yeongyu/senpi-codemode/README.md +128 -78
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -3
- package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/protocol.ts +15 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/reserved.ts +9 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/bridges/agent-bridge.ts +197 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/bridges/output-bridge.ts +96 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/bridges/schema-injection.ts +3 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/completion/handler.ts +95 -26
- package/node_modules/@code-yeongyu/senpi-codemode/src/completion/tool-bridge.ts +11 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/config/settings.ts +96 -8
- package/node_modules/@code-yeongyu/senpi-codemode/src/extension/session-manager.ts +24 -3
- package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +155 -21
- package/node_modules/@code-yeongyu/senpi-codemode/src/interpreters/detect.ts +1 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/jl/prelude.jl +252 -48
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/jl/runner.jl +263 -83
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/context-manager.ts +11 -5
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/local-module-loader.ts +108 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/prelude.ts +8 -6
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/rewrite-imports.ts +164 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-core.js +6 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-runtime.js +307 -41
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel.ts +3 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py +792 -79
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/rb/prelude.rb +234 -21
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/rb/runner.rb +154 -51
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-queue.ts +1 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/output/output-meta.ts +89 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/output/streaming-output.ts +296 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +107 -30
- package/node_modules/@code-yeongyu/senpi-codemode/src/timeouts/bridge-timeout.ts +2 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/timeouts/idle-timeout.ts +8 -19
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-handler.ts +175 -113
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool.ts +130 -120
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/image.ts +274 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/json-tree.ts +247 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +641 -38
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/status-events.ts +12 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +29 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js +16 -16
- package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js +6 -6
- package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +6 -6
|
@@ -1,30 +1,36 @@
|
|
|
1
|
-
|
|
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
|
|
13
|
-
if
|
|
18
|
+
for character in text
|
|
19
|
+
if character == '"'
|
|
14
20
|
Base.write(out, "\\\"")
|
|
15
|
-
elseif
|
|
21
|
+
elseif character == '\\'
|
|
16
22
|
Base.write(out, "\\\\")
|
|
17
|
-
elseif
|
|
23
|
+
elseif character == '\n'
|
|
18
24
|
Base.write(out, "\\n")
|
|
19
|
-
elseif
|
|
25
|
+
elseif character == '\r'
|
|
20
26
|
Base.write(out, "\\r")
|
|
21
|
-
elseif
|
|
27
|
+
elseif character == '\t'
|
|
22
28
|
Base.write(out, "\\t")
|
|
23
29
|
else
|
|
24
|
-
Base.write(out,
|
|
30
|
+
Base.write(out, character)
|
|
25
31
|
end
|
|
26
32
|
end
|
|
27
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
175
|
+
senpi_emit(Dict("type" => "text", "stream" => stream, "data" => data))
|
|
176
|
+
nothing
|
|
72
177
|
end
|
|
73
178
|
|
|
74
|
-
function
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
80
|
-
|
|
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
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
144
|
-
senpi_emit(Dict("type" => "
|
|
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
|
|
16
|
-
export {
|
|
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({
|
|
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
|
|
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,
|
|
8
|
-
"write(path, content): write UTF-8
|
|
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,
|
|
11
|
-
"
|
|
12
|
-
"
|
|
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");
|