@code-yeongyu/senpi 2026.9.15 → 2026.9.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/dist/core/extensions/builtin/config-reload/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/config-reload/index.js +25 -8
- package/dist/core/extensions/builtin/config-reload/index.js.map +1 -1
- package/dist/core/extensions/builtin/config-reload/watch-engine.d.ts +4 -5
- package/dist/core/extensions/builtin/config-reload/watch-engine.d.ts.map +1 -1
- package/dist/core/extensions/builtin/config-reload/watch-engine.js +14 -18
- package/dist/core/extensions/builtin/config-reload/watch-engine.js.map +1 -1
- package/dist/core/extensions/builtin/config-reload/watch-event-source.d.ts.map +1 -1
- package/dist/core/extensions/builtin/config-reload/watch-event-source.js +32 -9
- package/dist/core/extensions/builtin/config-reload/watch-event-source.js.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/catalog-refresh.d.ts +23 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/catalog-refresh.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/catalog-refresh.js +48 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/catalog-refresh.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/environment.d.ts +13 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/environment.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/environment.js +24 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/environment.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/executable.d.ts +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/executable.d.ts.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/executable.js +7 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/executable.js.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/index.d.ts +2 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/index.js +11 -9
- package/dist/core/extensions/builtin/cursor-cli-oauth/index.js.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/models-probe.d.ts +18 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/models-probe.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/models-probe.js +61 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/models-probe.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/models.d.ts +4 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/models.d.ts.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/models.js +1 -44
- package/dist/core/extensions/builtin/cursor-cli-oauth/models.js.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.d.ts +22 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.d.ts.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.js +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.js.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/transport.d.ts.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/transport.js +2 -13
- package/dist/core/extensions/builtin/cursor-cli-oauth/transport.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-file-digest.d.ts +3 -0
- package/dist/core/extensions/builtin/terminal/monitor-file-digest.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-file-digest.js +25 -0
- package/dist/core/extensions/builtin/terminal/monitor-file-digest.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-file-watch.d.ts +9 -0
- package/dist/core/extensions/builtin/terminal/monitor-file-watch.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-file-watch.js +33 -0
- package/dist/core/extensions/builtin/terminal/monitor-file-watch.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-line-buffer.d.ts +6 -0
- package/dist/core/extensions/builtin/terminal/monitor-line-buffer.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-line-buffer.js +22 -0
- package/dist/core/extensions/builtin/terminal/monitor-line-buffer.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/monitor-registry.js +14 -35
- package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
- package/dist/core/extensions/kernel-tools-context.d.ts +13 -0
- package/dist/core/extensions/kernel-tools-context.d.ts.map +1 -0
- package/dist/core/extensions/kernel-tools-context.js +3 -0
- package/dist/core/extensions/kernel-tools-context.js.map +1 -0
- package/dist/core/extensions/runner.d.ts +19 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +103 -1
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +21 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +9 -17
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/session-manager.d.ts +3 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +14 -0
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +23 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +41 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +4 -3
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/read.d.ts +3 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +16 -2
- package/dist/core/tools/read.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +3 -3
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
- package/dist/modes/rpc/host-watchdog.js +5 -24
- package/dist/modes/rpc/host-watchdog.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +3 -8
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
- package/dist/modes/rpc/session-command-router.js +3 -0
- package/dist/modes/rpc/session-command-router.js.map +1 -1
- package/dist/modes/rpc/session-worker-client.d.ts +3 -0
- package/dist/modes/rpc/session-worker-client.d.ts.map +1 -1
- package/dist/modes/rpc/session-worker-client.js +17 -6
- package/dist/modes/rpc/session-worker-client.js.map +1 -1
- package/dist/modes/rpc/shutdown.d.ts +3 -0
- package/dist/modes/rpc/shutdown.d.ts.map +1 -0
- package/dist/modes/rpc/shutdown.js +17 -0
- package/dist/modes/rpc/shutdown.js.map +1 -0
- package/docs/extensions.md +3 -0
- package/docs/settings.md +12 -0
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +36 -0
- package/node_modules/@code-yeongyu/senpi-codemode/README.md +24 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/http-server.ts +7 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/kernel-tools-protocol.ts +85 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/protocol.ts +6 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/bridges/agent-bridge.ts +30 -11
- package/node_modules/@code-yeongyu/senpi-codemode/src/extension/session-manager.ts +11 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +10 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/jl/prelude.jl +46 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/jl/runner.jl +1 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/context-manager.ts +58 -10
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-contract.ts +12 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-context.d.ts +13 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-context.js +7 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-define.d.ts +10 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-define.js +12 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-errors.js +22 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-errors.ts +26 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-host.ts +134 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-naming.js +28 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-parse.d.ts +5 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-parse.js +255 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-pump.js +105 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-registry.d.ts +27 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-registry.js +127 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-schema.js +69 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-tools-types.ts +42 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/prelude.ts +2 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-core.js +26 -3
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-runtime.js +20 -8
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-slot.ts +6 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-startup.ts +9 -7
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/workpool.js +37 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/kernel-tools-unavailable.ts +5 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel.ts +13 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/prelude.py +57 -3
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/rb/prelude.rb +13 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/rb/workpool.rb +38 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-kernel.ts +13 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-queue.ts +9 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt-template.ts +4 -2
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-handler.ts +8 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/detached-cell-manager.ts +36 -71
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/image-resize.ts +52 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/image.ts +37 -72
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/managed-cell.ts +78 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +30 -5
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/run-eval-cell.ts +49 -30
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/terminal-snapshot-store.ts +35 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/tool-result-marshal.ts +23 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +2 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.js +51 -12
- package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/read.d.ts +3 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/read.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/read.js +18 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/tools/read.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/brace-scanner.d.ts +4 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/brace-scanner.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/brace-scanner.js +268 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/brace-scanner.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/header-protection.d.ts +18 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/header-protection.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/header-protection.js +69 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/header-protection.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/index.d.ts +27 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/index.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/index.js +118 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/index.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-context.d.ts +21 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-context.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-context.js +24 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-context.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-spans.d.ts +12 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-spans.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-spans.js +92 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/lexical-spans.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/types.d.ts +47 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/types.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/types.js +8 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/read-folders/types.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/segmented-read-view.d.ts +52 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/segmented-read-view.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/segmented-read-view.js +155 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/utils/segmented-read-view.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts +2 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/index.js +2 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/alibaba-token-plan.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/qwen-token-plan.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/empty-response-errors.d.ts +15 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/empty-response-errors.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/empty-response-errors.js +15 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/empty-response-errors.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +10 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.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-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { kernelToolError } from "./kernel-tools-errors.js";
|
|
2
|
+
|
|
3
|
+
const IDENT = /^[\p{ID_Start}$_][\p{ID_Continue}$\u200c\u200d]*$/u;
|
|
4
|
+
const RESERVED = new Set(
|
|
5
|
+
"await break case catch class const continue debugger default delete do else enum export extends false finally for function if implements import in instanceof interface let new null package private protected public return static super switch this throw true try typeof var void while with yield".split(
|
|
6
|
+
" ",
|
|
7
|
+
),
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
export function parseToolFunction(fn) {
|
|
11
|
+
if (typeof fn !== "function") fail();
|
|
12
|
+
const source = fn.toString();
|
|
13
|
+
if (source.includes("[native code]")) fail("tool() cannot wrap native functions");
|
|
14
|
+
return parseHead(source);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function parseHead(source) {
|
|
18
|
+
const pos = { i: 0, source };
|
|
19
|
+
skipTrivia(pos);
|
|
20
|
+
if (keyword(pos, "class")) fail();
|
|
21
|
+
let async = false;
|
|
22
|
+
if (keyword(pos, "async")) {
|
|
23
|
+
skipTrivia(pos);
|
|
24
|
+
if (!keyword(pos, "function")) fail();
|
|
25
|
+
async = true;
|
|
26
|
+
} else if (!keyword(pos, "function")) fail();
|
|
27
|
+
skipTrivia(pos);
|
|
28
|
+
if (peek(pos) === "*") fail();
|
|
29
|
+
const name = ident(pos);
|
|
30
|
+
if (!name) fail();
|
|
31
|
+
skipTrivia(pos);
|
|
32
|
+
if (peek(pos) !== "(") fail();
|
|
33
|
+
return { name, params: readParams(pos), async };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function readParams(pos) {
|
|
37
|
+
const parts = splitTopLevel(extractParenInner(pos));
|
|
38
|
+
if (parts.length === 1 && plainIdent(parts[0]) === "") return [];
|
|
39
|
+
return parts.map((part) => {
|
|
40
|
+
const name = plainIdent(part);
|
|
41
|
+
if (!name) fail("tool() requires simple identifier parameters");
|
|
42
|
+
return name;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function plainIdent(part) {
|
|
47
|
+
const pos = { i: 0, source: part };
|
|
48
|
+
skipTrivia(pos);
|
|
49
|
+
if (eof(pos)) return "";
|
|
50
|
+
const name = ident(pos);
|
|
51
|
+
if (!name) return null;
|
|
52
|
+
skipTrivia(pos);
|
|
53
|
+
return eof(pos) ? name : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function splitTopLevel(source) {
|
|
57
|
+
const parts = [];
|
|
58
|
+
let current = "";
|
|
59
|
+
const pos = { i: 0, source };
|
|
60
|
+
let paren = 0;
|
|
61
|
+
let square = 0;
|
|
62
|
+
let curly = 0;
|
|
63
|
+
while (!eof(pos)) {
|
|
64
|
+
const start = pos.i;
|
|
65
|
+
if (skipValue(pos)) {
|
|
66
|
+
current += source.slice(start, pos.i);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const ch = source[pos.i];
|
|
70
|
+
pos.i += 1;
|
|
71
|
+
if (ch === "(") paren += 1;
|
|
72
|
+
else if (ch === ")") paren -= 1;
|
|
73
|
+
else if (ch === "[") square += 1;
|
|
74
|
+
else if (ch === "]") square -= 1;
|
|
75
|
+
else if (ch === "{") curly += 1;
|
|
76
|
+
else if (ch === "}") curly -= 1;
|
|
77
|
+
if (ch === "," && paren === 0 && square === 0 && curly === 0) {
|
|
78
|
+
parts.push(current);
|
|
79
|
+
current = "";
|
|
80
|
+
} else current += ch;
|
|
81
|
+
}
|
|
82
|
+
parts.push(current);
|
|
83
|
+
return parts;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function extractParenInner(pos) {
|
|
87
|
+
const source = pos.source;
|
|
88
|
+
if (source[pos.i] !== "(") fail();
|
|
89
|
+
pos.i += 1;
|
|
90
|
+
const start = pos.i;
|
|
91
|
+
let paren = 1;
|
|
92
|
+
while (pos.i < source.length && paren > 0) {
|
|
93
|
+
const before = pos.i;
|
|
94
|
+
if (skipValue(pos)) continue;
|
|
95
|
+
if (pos.i !== before) continue;
|
|
96
|
+
const ch = source[pos.i];
|
|
97
|
+
pos.i += 1;
|
|
98
|
+
if (ch === "(") paren += 1;
|
|
99
|
+
else if (ch === ")") paren -= 1;
|
|
100
|
+
}
|
|
101
|
+
if (paren !== 0) fail();
|
|
102
|
+
return source.slice(start, pos.i - 1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function skipValue(pos) {
|
|
106
|
+
const ch = peek(pos);
|
|
107
|
+
if (ch === "'" || ch === '"') {
|
|
108
|
+
pos.i = skipString(pos.source, pos.i);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
if (ch === "`") {
|
|
112
|
+
pos.i = skipTemplate(pos.source, pos.i);
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
if (ch === "/" && pos.source[pos.i + 1] === "/") {
|
|
116
|
+
pos.i = skipLineComment(pos.source, pos.i);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
if (ch === "/" && pos.source[pos.i + 1] === "*") {
|
|
120
|
+
pos.i = skipBlockComment(pos.source, pos.i);
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function skipTrivia(pos) {
|
|
127
|
+
while (!eof(pos)) {
|
|
128
|
+
if (/\s/u.test(peek(pos))) {
|
|
129
|
+
pos.i += 1;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (peek(pos) === "/" && pos.source[pos.i + 1] === "/") {
|
|
133
|
+
pos.i = skipLineComment(pos.source, pos.i);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (peek(pos) === "/" && pos.source[pos.i + 1] === "*") {
|
|
137
|
+
pos.i = skipBlockComment(pos.source, pos.i);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function keyword(pos, word) {
|
|
145
|
+
if (!pos.source.startsWith(word, pos.i)) return false;
|
|
146
|
+
const next = pos.source[pos.i + word.length];
|
|
147
|
+
if (next !== undefined && isContinue(next)) return false;
|
|
148
|
+
pos.i += word.length;
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function ident(pos) {
|
|
153
|
+
if (eof(pos) || !isStart(peek(pos))) return null;
|
|
154
|
+
const start = pos.i;
|
|
155
|
+
pos.i += 1;
|
|
156
|
+
while (!eof(pos) && isContinue(peek(pos))) pos.i += 1;
|
|
157
|
+
const value = pos.source.slice(start, pos.i);
|
|
158
|
+
if (!IDENT.test(value) || RESERVED.has(value)) {
|
|
159
|
+
pos.i = start;
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function peek(pos) {
|
|
166
|
+
return pos.source[pos.i];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function eof(pos) {
|
|
170
|
+
return pos.i >= pos.source.length;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function fail(message = "tool() requires a named function") {
|
|
174
|
+
throw kernelToolError("invalid_tool_definition", message);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function isStart(ch) {
|
|
178
|
+
return /[\p{ID_Start}$_]/u.test(ch);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function isContinue(ch) {
|
|
182
|
+
return /[\p{ID_Continue}$\u200c\u200d]/u.test(ch);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function skipLineComment(source, i) {
|
|
186
|
+
i += 2;
|
|
187
|
+
while (i < source.length && source[i] !== "\n" && source[i] !== "\r" && source[i] !== "\u2028" && source[i] !== "\u2029") i += 1;
|
|
188
|
+
return i;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function skipBlockComment(source, i) {
|
|
192
|
+
const end = source.indexOf("*/", i + 2);
|
|
193
|
+
if (end < 0) fail();
|
|
194
|
+
return end + 2;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function skipString(source, i) {
|
|
198
|
+
const quote = source[i];
|
|
199
|
+
i += 1;
|
|
200
|
+
while (i < source.length) {
|
|
201
|
+
if (source[i] === "\\") {
|
|
202
|
+
i += 2;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (source[i] === quote) return i + 1;
|
|
206
|
+
i += 1;
|
|
207
|
+
}
|
|
208
|
+
fail();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function skipTemplate(source, i) {
|
|
212
|
+
i += 1;
|
|
213
|
+
while (i < source.length) {
|
|
214
|
+
if (source[i] === "\\") {
|
|
215
|
+
i += 2;
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (source[i] === "`") return i + 1;
|
|
219
|
+
if (source[i] === "$" && source[i + 1] === "{") {
|
|
220
|
+
i = skipTemplateExpr(source, i + 2);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
i += 1;
|
|
224
|
+
}
|
|
225
|
+
fail();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function skipTemplateExpr(source, i) {
|
|
229
|
+
let curly = 1;
|
|
230
|
+
while (i < source.length && curly > 0) {
|
|
231
|
+
if (source[i] === "'" || source[i] === '"') {
|
|
232
|
+
i = skipString(source, i);
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (source[i] === "`") {
|
|
236
|
+
i = skipTemplate(source, i);
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (source[i] === "/" && source[i + 1] === "/") {
|
|
240
|
+
i = skipLineComment(source, i);
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (source[i] === "/" && source[i + 1] === "*") {
|
|
244
|
+
i = skipBlockComment(source, i);
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (source[i] === "{") curly += 1;
|
|
248
|
+
else if (source[i] === "}") {
|
|
249
|
+
curly -= 1;
|
|
250
|
+
if (curly === 0) return i + 1;
|
|
251
|
+
}
|
|
252
|
+
i += 1;
|
|
253
|
+
}
|
|
254
|
+
fail();
|
|
255
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { kernelToolCallContext } from "./kernel-tools-context.js";
|
|
2
|
+
import { kernelToolError } from "./kernel-tools-errors.js";
|
|
3
|
+
|
|
4
|
+
export function createKernelToolPump({ getRuntime, emit, nestedInvokes }) {
|
|
5
|
+
async function describe(message) {
|
|
6
|
+
try {
|
|
7
|
+
const runtime = getRuntime();
|
|
8
|
+
if (!runtime?.kernelTools) throw kernelToolError("tools_unavailable", "JS runtime not initialized");
|
|
9
|
+
const results = runtime.kernelTools.describe(message.names).results;
|
|
10
|
+
emit({ type: "kernel-tool-describe-reply", requestId: message.requestId, ok: true, results });
|
|
11
|
+
} catch (error) {
|
|
12
|
+
emit({ type: "kernel-tool-describe-reply", requestId: message.requestId, ok: false, error: pumpError(error) });
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function invoke(message) {
|
|
17
|
+
const pendingTools = new Map();
|
|
18
|
+
const controller = new AbortController();
|
|
19
|
+
nestedInvokes.set(message.requestId, { pendingTools, controller });
|
|
20
|
+
try {
|
|
21
|
+
const runtime = getRuntime();
|
|
22
|
+
if (!runtime?.kernelTools) throw kernelToolError("tools_unavailable", "JS runtime not initialized");
|
|
23
|
+
const value = await kernelToolCallContext.run(
|
|
24
|
+
{ pendingTools, callId: message.call_id, generation: message.kernel_generation, signal: controller.signal },
|
|
25
|
+
() =>
|
|
26
|
+
runtime.kernelTools.invoke(
|
|
27
|
+
{
|
|
28
|
+
name: message.name,
|
|
29
|
+
kernel_generation: message.kernel_generation,
|
|
30
|
+
definition_revision: message.definition_revision,
|
|
31
|
+
args: message.args,
|
|
32
|
+
call_id: message.call_id,
|
|
33
|
+
},
|
|
34
|
+
controller.signal,
|
|
35
|
+
),
|
|
36
|
+
);
|
|
37
|
+
emit({ type: "kernel-tool-invoke-reply", requestId: message.requestId, ok: true, value });
|
|
38
|
+
} catch (error) {
|
|
39
|
+
emit({ type: "kernel-tool-invoke-reply", requestId: message.requestId, ok: false, error: pumpError(error) });
|
|
40
|
+
} finally {
|
|
41
|
+
nestedInvokes.delete(message.requestId);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
handle(message) {
|
|
47
|
+
if (message.type === "kernel-tool-describe") {
|
|
48
|
+
void describe(message);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
if (message.type === "kernel-tool-invoke") {
|
|
52
|
+
void invoke(message);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
if (message.type === "kernel-tool-cancel") {
|
|
56
|
+
const nested = nestedInvokes.get(message.requestId);
|
|
57
|
+
if (nested) abortNested(nested, kernelToolError("kernel_tool_stale", "Kernel tool call cancelled"));
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
},
|
|
62
|
+
abortAll(error) {
|
|
63
|
+
for (const nested of nestedInvokes.values()) abortNested(nested, error);
|
|
64
|
+
},
|
|
65
|
+
settleToolReply(message) {
|
|
66
|
+
for (const nested of nestedInvokes.values()) {
|
|
67
|
+
const pending = nested.pendingTools.get(message.callId);
|
|
68
|
+
if (!pending) continue;
|
|
69
|
+
nested.pendingTools.delete(message.callId);
|
|
70
|
+
if (message.ok) pending.resolve(message.value);
|
|
71
|
+
else pending.reject(errorFrom(message.error));
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function abortNested(nested, error) {
|
|
80
|
+
nested.controller.abort(error);
|
|
81
|
+
for (const [callId, pending] of nested.pendingTools) {
|
|
82
|
+
nested.pendingTools.delete(callId);
|
|
83
|
+
pending.reject(error);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function pumpError(error) {
|
|
88
|
+
if (error instanceof Error) {
|
|
89
|
+
return {
|
|
90
|
+
name: error.name,
|
|
91
|
+
message: error.message,
|
|
92
|
+
stack: error.stack,
|
|
93
|
+
...(typeof error.code === "string" ? { code: error.code } : {}),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return { message: String(error) };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function errorFrom(error) {
|
|
100
|
+
const result = new Error(error.message);
|
|
101
|
+
if (error.name) result.name = error.name;
|
|
102
|
+
if (error.stack) result.stack = error.stack;
|
|
103
|
+
if (typeof error.code === "string") result.code = error.code;
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export { createToolNamespace } from "./kernel-tools-define.js";
|
|
2
|
+
|
|
3
|
+
export type KernelToolRegistryOptions = {
|
|
4
|
+
readonly generation?: number;
|
|
5
|
+
readonly language?: "js" | "py" | "rb" | "jl";
|
|
6
|
+
readonly hostToolNames?: readonly string[] | (() => readonly string[]);
|
|
7
|
+
readonly foreignLanguageNames?: readonly string[] | (() => readonly string[]);
|
|
8
|
+
readonly reservedNames?: readonly string[];
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function createKernelToolRegistry(options?: KernelToolRegistryOptions): {
|
|
12
|
+
readonly generation: number;
|
|
13
|
+
define(fn: { readonly name: string }, metadata?: unknown): unknown;
|
|
14
|
+
describe(names: readonly string[]): unknown;
|
|
15
|
+
invoke(
|
|
16
|
+
request: {
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly kernel_generation: number;
|
|
19
|
+
readonly definition_revision: number;
|
|
20
|
+
readonly args: unknown;
|
|
21
|
+
readonly call_id: string;
|
|
22
|
+
},
|
|
23
|
+
signal?: AbortSignal,
|
|
24
|
+
): Promise<unknown>;
|
|
25
|
+
bumpGeneration(): number;
|
|
26
|
+
setCollisionNames(hostToolNames?: readonly string[], foreignLanguageNames?: readonly string[]): void;
|
|
27
|
+
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { createToolNamespace } from "./kernel-tools-define.js";
|
|
2
|
+
import { kernelToolError } from "./kernel-tools-errors.js";
|
|
3
|
+
import { kernelToolKey, MCP_TOOL_NAME_MAX_LENGTH, sanitizeNamePart } from "./kernel-tools-naming.js";
|
|
4
|
+
import { parseToolFunction } from "./kernel-tools-parse.js";
|
|
5
|
+
import { orderedArgs, resolveToolMetadata, validateInvokeArgs } from "./kernel-tools-schema.js";
|
|
6
|
+
|
|
7
|
+
export { createToolNamespace };
|
|
8
|
+
|
|
9
|
+
const DEFAULT_RESERVED = Object.freeze(["__agent__", "__output__", "__schema__"]);
|
|
10
|
+
|
|
11
|
+
function currentNames(source) {
|
|
12
|
+
return typeof source === "function" ? source() : (source ?? []);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function hasNameKey(source, key) {
|
|
16
|
+
return currentNames(source).some((name) => kernelToolKey(name) === key);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createKernelToolRegistry(options = {}) {
|
|
20
|
+
const language = options.language ?? "js";
|
|
21
|
+
let generation = options.generation ?? 1;
|
|
22
|
+
const reservedKeys = new Set((options.reservedNames ?? DEFAULT_RESERVED).map(kernelToolKey));
|
|
23
|
+
let hostSource = options.hostToolNames ?? [];
|
|
24
|
+
let foreignSource = options.foreignLanguageNames ?? [];
|
|
25
|
+
const entries = new Map();
|
|
26
|
+
|
|
27
|
+
function assertJs() {
|
|
28
|
+
if (language !== "js") throw kernelToolError("tools_unavailable", "Kernel tools are JavaScript-only");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function descriptorFor(entry) {
|
|
32
|
+
return {
|
|
33
|
+
name: entry.normalizedName,
|
|
34
|
+
description: entry.description,
|
|
35
|
+
input_schema: entry.input_schema,
|
|
36
|
+
language: "js",
|
|
37
|
+
kernel_generation: generation,
|
|
38
|
+
definition_revision: entry.revision,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
get generation() {
|
|
44
|
+
return generation;
|
|
45
|
+
},
|
|
46
|
+
define(fn, metadata) {
|
|
47
|
+
assertJs();
|
|
48
|
+
const parsed = parseToolFunction(fn);
|
|
49
|
+
const resolved = resolveToolMetadata(metadata, parsed.params);
|
|
50
|
+
if (sanitizeNamePart(parsed.name) !== parsed.name || parsed.name.length > MCP_TOOL_NAME_MAX_LENGTH) {
|
|
51
|
+
throw kernelToolError("invalid_tool_definition", "Kernel tool name must match MCP name grammar");
|
|
52
|
+
}
|
|
53
|
+
const normalizedName = parsed.name;
|
|
54
|
+
const key = kernelToolKey(parsed.name);
|
|
55
|
+
if (reservedKeys.has(key)) throw kernelToolError("reserved_tool_name", `Kernel tool name is reserved: ${parsed.name}`);
|
|
56
|
+
if (hasNameKey(hostSource, key) || hasNameKey(foreignSource, key)) {
|
|
57
|
+
throw kernelToolError("tool_name_collision", `Kernel tool name collides: ${parsed.name}`);
|
|
58
|
+
}
|
|
59
|
+
const existing = entries.get(key);
|
|
60
|
+
if (existing && existing.originalName !== parsed.name) {
|
|
61
|
+
throw kernelToolError("tool_name_collision", `Kernel tool name collides: ${parsed.name}`);
|
|
62
|
+
}
|
|
63
|
+
const entry = {
|
|
64
|
+
originalName: parsed.name,
|
|
65
|
+
normalizedName,
|
|
66
|
+
fn,
|
|
67
|
+
params: parsed.params,
|
|
68
|
+
description: resolved.description,
|
|
69
|
+
input_schema: resolved.input_schema,
|
|
70
|
+
revision: existing ? existing.revision + 1 : 1,
|
|
71
|
+
};
|
|
72
|
+
entries.set(key, entry);
|
|
73
|
+
return descriptorFor(entry);
|
|
74
|
+
},
|
|
75
|
+
describe(names) {
|
|
76
|
+
assertJs();
|
|
77
|
+
return {
|
|
78
|
+
results: names.map((name) => {
|
|
79
|
+
const entry = entries.get(kernelToolKey(name));
|
|
80
|
+
if (!entry) {
|
|
81
|
+
return {
|
|
82
|
+
name,
|
|
83
|
+
ok: false,
|
|
84
|
+
error: { code: "kernel_tool_missing", message: `Kernel tool is not defined: ${name}` },
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return { name, ok: true, descriptor: descriptorFor(entry) };
|
|
88
|
+
}),
|
|
89
|
+
};
|
|
90
|
+
},
|
|
91
|
+
async invoke(request, signal) {
|
|
92
|
+
assertJs();
|
|
93
|
+
if (signal?.aborted) {
|
|
94
|
+
throw signal.reason ?? kernelToolError("kernel_tool_failed", "Kernel tool call aborted");
|
|
95
|
+
}
|
|
96
|
+
if (request.kernel_generation !== generation) {
|
|
97
|
+
throw kernelToolError("kernel_tool_stale", "Kernel tool descriptor generation is stale");
|
|
98
|
+
}
|
|
99
|
+
const entry = entries.get(kernelToolKey(request.name));
|
|
100
|
+
if (!entry) throw kernelToolError("kernel_tool_missing", `Kernel tool is not defined: ${request.name}`);
|
|
101
|
+
if (entry.revision !== request.definition_revision) {
|
|
102
|
+
throw kernelToolError("kernel_tool_stale", "Kernel tool descriptor revision is stale");
|
|
103
|
+
}
|
|
104
|
+
validateInvokeArgs(entry.input_schema, request.args);
|
|
105
|
+
try {
|
|
106
|
+
const value = await entry.fn(...orderedArgs(entry.params, request.args));
|
|
107
|
+
const live = entries.get(kernelToolKey(request.name));
|
|
108
|
+
if (request.kernel_generation !== generation || live !== entry) {
|
|
109
|
+
throw kernelToolError("kernel_tool_stale", "Kernel tool descriptor is stale");
|
|
110
|
+
}
|
|
111
|
+
return value;
|
|
112
|
+
} catch (error) {
|
|
113
|
+
if (error instanceof Error && typeof error.code === "string") throw error;
|
|
114
|
+
throw kernelToolError("kernel_tool_failed", error instanceof Error ? error.message : String(error));
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
bumpGeneration() {
|
|
118
|
+
generation += 1;
|
|
119
|
+
entries.clear();
|
|
120
|
+
return generation;
|
|
121
|
+
},
|
|
122
|
+
setCollisionNames(hostToolNames, foreignLanguageNames) {
|
|
123
|
+
hostSource = hostToolNames ?? [];
|
|
124
|
+
foreignSource = foreignLanguageNames ?? [];
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { kernelToolError } from "./kernel-tools-errors.js";
|
|
2
|
+
|
|
3
|
+
export function defaultInputSchema(params) {
|
|
4
|
+
return {
|
|
5
|
+
type: "object",
|
|
6
|
+
properties: Object.fromEntries(params.map((name) => [name, {}])),
|
|
7
|
+
required: [...params],
|
|
8
|
+
additionalProperties: false,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function resolveToolMetadata(metadata, params) {
|
|
13
|
+
if (metadata === undefined) return { description: "", input_schema: defaultInputSchema(params) };
|
|
14
|
+
if (!isPlainJsonObject(metadata)) {
|
|
15
|
+
throw kernelToolError("invalid_tool_definition", "tool() metadata must be plain JSON");
|
|
16
|
+
}
|
|
17
|
+
const description = metadata.description === undefined ? "" : metadata.description;
|
|
18
|
+
if (typeof description !== "string") {
|
|
19
|
+
throw kernelToolError("invalid_tool_definition", "tool() description must be a string");
|
|
20
|
+
}
|
|
21
|
+
if (metadata.schema === undefined) return { description, input_schema: defaultInputSchema(params) };
|
|
22
|
+
const schema = metadata.schema;
|
|
23
|
+
if (!isPlainJsonObject(schema) || schema.type !== "object" || !isPlainJsonObject(schema.properties)) {
|
|
24
|
+
throw kernelToolError("invalid_tool_definition", "tool() metadata schema must be a JSON object schema");
|
|
25
|
+
}
|
|
26
|
+
const propertyKeys = Object.keys(schema.properties);
|
|
27
|
+
if (propertyKeys.length !== params.length || params.some((name) => !Object.hasOwn(schema.properties, name))) {
|
|
28
|
+
throw kernelToolError("invalid_tool_definition", "tool() metadata schema properties must match parameters");
|
|
29
|
+
}
|
|
30
|
+
return { description, input_schema: schema };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function validateInvokeArgs(schema, args) {
|
|
34
|
+
if (!isPlainJsonObject(args)) throw kernelToolError("invalid_tool_definition", "kernel tool args must be a JSON object");
|
|
35
|
+
const required = Array.isArray(schema.required) ? schema.required : [];
|
|
36
|
+
for (const key of required) {
|
|
37
|
+
if (!Object.hasOwn(args, key)) {
|
|
38
|
+
throw kernelToolError("invalid_tool_definition", `missing kernel tool argument: ${key}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (schema.additionalProperties === false) {
|
|
42
|
+
const allowed = new Set(Object.keys(schema.properties ?? {}));
|
|
43
|
+
for (const key of Object.keys(args)) {
|
|
44
|
+
if (!allowed.has(key)) {
|
|
45
|
+
throw kernelToolError("invalid_tool_definition", `unexpected kernel tool argument: ${key}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function isPlainJsonObject(value) {
|
|
52
|
+
return isJsonValue(value) && value !== null && typeof value === "object" && !Array.isArray(value);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function isJsonValue(value) {
|
|
56
|
+
if (value === null) return true;
|
|
57
|
+
const type = typeof value;
|
|
58
|
+
if (type === "string" || type === "boolean") return true;
|
|
59
|
+
if (type === "number") return Number.isFinite(value);
|
|
60
|
+
if (type !== "object") return false;
|
|
61
|
+
if (Array.isArray(value)) return value.every(isJsonValue);
|
|
62
|
+
const proto = Object.getPrototypeOf(value);
|
|
63
|
+
if (proto !== Object.prototype && proto !== null) return false;
|
|
64
|
+
return Object.values(value).every(isJsonValue);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function orderedArgs(params, args) {
|
|
68
|
+
return params.map((name) => args[name]);
|
|
69
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { KernelToolErrorCode } from "./kernel-tools-errors.ts";
|
|
2
|
+
|
|
3
|
+
export type { KernelToolErrorCode };
|
|
4
|
+
|
|
5
|
+
export type KernelToolDescriptor = {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly description: string;
|
|
8
|
+
readonly input_schema: unknown;
|
|
9
|
+
readonly language: "js";
|
|
10
|
+
readonly kernel_generation: number;
|
|
11
|
+
readonly definition_revision: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type KernelToolsInvokeRequest = {
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly kernel_generation: number;
|
|
17
|
+
readonly definition_revision: number;
|
|
18
|
+
readonly args: unknown;
|
|
19
|
+
readonly call_id: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type KernelToolsDescribeEntry =
|
|
23
|
+
| { readonly name: string; readonly ok: true; readonly descriptor: KernelToolDescriptor }
|
|
24
|
+
| {
|
|
25
|
+
readonly name: string;
|
|
26
|
+
readonly ok: false;
|
|
27
|
+
readonly error: { readonly code: KernelToolErrorCode; readonly message: string };
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type KernelToolsDescribeResult = {
|
|
31
|
+
readonly results: readonly KernelToolsDescribeEntry[];
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type KernelToolsCapability = {
|
|
35
|
+
describe(names: readonly string[]): Promise<KernelToolsDescribeResult>;
|
|
36
|
+
invoke(request: KernelToolsInvokeRequest, signal?: AbortSignal): Promise<unknown>;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const KERNEL_TOOLS_UNSUPPORTED = {
|
|
40
|
+
code: "tools_unavailable" as const,
|
|
41
|
+
message: "Kernel tools require a live JavaScript worker context",
|
|
42
|
+
};
|
|
@@ -7,9 +7,11 @@ export const JAVASCRIPT_KERNEL_PRELUDE = [
|
|
|
7
7
|
"read(path, options?): read UTF-8 text; plain paths use cwd and local:// uses the session local root.",
|
|
8
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; resolves to { text, images?, details?, hasError? }.",
|
|
10
|
+
"tool(fn, metadata?): register a named JavaScript function as a fenced kernel tool for in-process children.",
|
|
10
11
|
"completion(prompt, options?): request a host completion bridge call.",
|
|
11
12
|
"output(...ids, options?): retrieve task output through the reserved output bridge.",
|
|
12
13
|
"agent(prompt, options?): delegate work through the reserved agent bridge.",
|
|
14
|
+
"workpool(agent, name, options?): await a host-owned pool adapter with pool_id, push, close, inspect, and cancel; options only accept mode.",
|
|
13
15
|
"parallel(thunks): run async thunks through the configured bounded pool; preserves order and rethrows the lowest-index error after all settle.",
|
|
14
16
|
"pipeline(items, ...stages): map items through staged async transforms with a barrier between stages.",
|
|
15
17
|
].join("\n");
|