@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
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { kernelToolCallContext } from "./kernel-tools-context.js";
|
|
2
|
+
import { kernelToolError } from "./kernel-tools-errors.js";
|
|
3
|
+
import { createKernelToolPump } from "./kernel-tools-pump.js";
|
|
1
4
|
import { JsWorkerRuntime } from "./worker-runtime.js";
|
|
2
5
|
|
|
3
6
|
// Mirrors INTERRUPT_ACK_OP and CHILD_LIFECYCLE_OP in src/bridge/reserved.ts (this worker file cannot import TypeScript).
|
|
@@ -21,6 +24,12 @@ export function createWorkerCore(transport, options) {
|
|
|
21
24
|
let runtime = null;
|
|
22
25
|
let activeCell = null;
|
|
23
26
|
const pendingTools = new Map();
|
|
27
|
+
const nestedInvokes = new Map();
|
|
28
|
+
const kernelTools = createKernelToolPump({
|
|
29
|
+
getRuntime: () => runtime,
|
|
30
|
+
emit: (message) => transport.send(message),
|
|
31
|
+
nestedInvokes,
|
|
32
|
+
});
|
|
24
33
|
|
|
25
34
|
function emit(message) {
|
|
26
35
|
transport.send(message);
|
|
@@ -47,9 +56,12 @@ export function createWorkerCore(transport, options) {
|
|
|
47
56
|
}
|
|
48
57
|
|
|
49
58
|
async function callTool(toolName, args) {
|
|
50
|
-
|
|
59
|
+
const nested = kernelToolCallContext.getStore();
|
|
60
|
+
if (!nested && activeCell?.interruption) throw activeCell.interruption;
|
|
61
|
+
if (nested?.signal.aborted) throw nested.signal.reason;
|
|
62
|
+
const bag = nested?.pendingTools ?? pendingTools;
|
|
51
63
|
const callId = `js-${crypto.randomUUID()}`;
|
|
52
|
-
const promise = new Promise((resolve, reject) =>
|
|
64
|
+
const promise = new Promise((resolve, reject) => bag.set(callId, { resolve, reject }));
|
|
53
65
|
emit({ type: "tool-call", callId, toolName, args });
|
|
54
66
|
return await promise;
|
|
55
67
|
}
|
|
@@ -63,10 +75,16 @@ export function createWorkerCore(transport, options) {
|
|
|
63
75
|
pendingTools.delete(callId);
|
|
64
76
|
pending.reject(interruption);
|
|
65
77
|
}
|
|
78
|
+
kernelTools.abortAll(kernelToolError("kernel_tool_stale", interruption.message));
|
|
66
79
|
runtime.interrupt();
|
|
67
80
|
}
|
|
68
81
|
|
|
69
82
|
function onMessage(message) {
|
|
83
|
+
if (kernelTools.handle(message)) return;
|
|
84
|
+
if (message.type === "kernel-tools-names") {
|
|
85
|
+
runtime?.kernelTools.setCollisionNames(message.hostToolNames ?? [], message.foreignLanguageNames ?? []);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
70
88
|
if (message.type === "init") {
|
|
71
89
|
applySessionEnvironment(message.sessionEnv);
|
|
72
90
|
runtime = new JsWorkerRuntime({
|
|
@@ -74,6 +92,9 @@ export function createWorkerCore(transport, options) {
|
|
|
74
92
|
parallelPoolWidth: options.parallelPoolWidth,
|
|
75
93
|
localRoots: message.connection.localRoots,
|
|
76
94
|
artifactsDir: message.connection.artifactsDir,
|
|
95
|
+
kernelGeneration: message.kernelGeneration ?? 1,
|
|
96
|
+
hostToolNames: message.hostToolNames ?? [],
|
|
97
|
+
foreignLanguageNames: message.foreignLanguageNames ?? [],
|
|
77
98
|
onChildEvent: (event) => emit({ type: "status", event: { op: CHILD_LIFECYCLE_OP, ...event } }),
|
|
78
99
|
});
|
|
79
100
|
emit({ type: "ready" });
|
|
@@ -84,6 +105,7 @@ export function createWorkerCore(transport, options) {
|
|
|
84
105
|
return;
|
|
85
106
|
}
|
|
86
107
|
if (message.type === "tool-reply") {
|
|
108
|
+
if (kernelTools.settleToolReply(message)) return;
|
|
87
109
|
const pending = pendingTools.get(message.callId);
|
|
88
110
|
if (!pending) return;
|
|
89
111
|
pendingTools.delete(message.callId);
|
|
@@ -144,7 +166,7 @@ function cellInterruptedError(reason) {
|
|
|
144
166
|
|
|
145
167
|
function bridgeError(error) {
|
|
146
168
|
if (error instanceof Error) {
|
|
147
|
-
return { name: error.name, message: error.message, stack: error.stack };
|
|
169
|
+
return { name: error.name, message: error.message, stack: error.stack, ...(typeof error.code === "string" ? { code: error.code } : {}) };
|
|
148
170
|
}
|
|
149
171
|
return { message: String(error) };
|
|
150
172
|
}
|
|
@@ -153,5 +175,6 @@ function errorFromBridge(error) {
|
|
|
153
175
|
const result = new Error(error.message);
|
|
154
176
|
if (error.name) result.name = error.name;
|
|
155
177
|
if (error.stack) result.stack = error.stack;
|
|
178
|
+
if (typeof error.code === "string") result.code = error.code;
|
|
156
179
|
return result;
|
|
157
180
|
}
|
|
@@ -6,6 +6,10 @@ import { encodeDisplayImage, resolveDisplayOps } from "./display-image.js";
|
|
|
6
6
|
import { terminateProcessTrees } from "./process-tree.js";
|
|
7
7
|
import { awaitMaybePromise, indirectEval, wrapUserCode } from "./worker-indirect-eval.js";
|
|
8
8
|
import { installShellCapture } from "./worker-shell-capture.js";
|
|
9
|
+
import { createWorkpool } from "./workpool.js";
|
|
10
|
+
import { inKernelToolInvoke } from "./kernel-tools-context.js";
|
|
11
|
+
import { kernelToolError } from "./kernel-tools-errors.js";
|
|
12
|
+
import { createKernelToolRegistry, createToolNamespace } from "./kernel-tools-registry.js";
|
|
9
13
|
|
|
10
14
|
const PREPARED_CELL_PREFIX = "/*senpi:prepared-cell*/";
|
|
11
15
|
// How long a child gets to honour SIGTERM before SIGKILL. Short, because the
|
|
@@ -22,6 +26,7 @@ export class JsWorkerRuntime {
|
|
|
22
26
|
#pendingDisplays = [];
|
|
23
27
|
#children = new Set();
|
|
24
28
|
#onChildEvent;
|
|
29
|
+
#tools;
|
|
25
30
|
|
|
26
31
|
constructor(options) {
|
|
27
32
|
this.#cwd = options.cwd;
|
|
@@ -29,9 +34,18 @@ export class JsWorkerRuntime {
|
|
|
29
34
|
this.#onChildEvent = typeof options.onChildEvent === "function" ? options.onChildEvent : null;
|
|
30
35
|
this.#localRoots = { ...(options.localRoots ?? {}) };
|
|
31
36
|
if (options.artifactsDir && !this.#localRoots.local) this.#localRoots.local = join(options.artifactsDir, "local");
|
|
37
|
+
this.#tools = createKernelToolRegistry({
|
|
38
|
+
generation: options.kernelGeneration ?? 1,
|
|
39
|
+
hostToolNames: options.hostToolNames ?? [],
|
|
40
|
+
foreignLanguageNames: options.foreignLanguageNames ?? [],
|
|
41
|
+
});
|
|
32
42
|
this.#installGlobals();
|
|
33
43
|
}
|
|
34
44
|
|
|
45
|
+
get kernelTools() {
|
|
46
|
+
return this.#tools;
|
|
47
|
+
}
|
|
48
|
+
|
|
35
49
|
async run(code, cellId, hooks) {
|
|
36
50
|
this.#hooks = hooks;
|
|
37
51
|
try {
|
|
@@ -107,17 +121,13 @@ export class JsWorkerRuntime {
|
|
|
107
121
|
globalThis.output = async (...args) => await this.#output(args);
|
|
108
122
|
globalThis.tool_schema = async name => await this.#toolSchema(name);
|
|
109
123
|
globalThis.agent = async (prompt, options, ...rest) => await this.#agent(prompt, options, rest);
|
|
124
|
+
globalThis.workpool = (agent, name, options) => createWorkpool((toolName, args) => this.#callTool(toolName, args), agent, name, options);
|
|
110
125
|
globalThis.parallel = async thunks => await this.#parallel(thunks);
|
|
111
126
|
globalThis.pipeline = async (items, ...stages) => await this.#pipeline(items, stages);
|
|
112
127
|
globalThis.completion = async (prompt, opts) => await this.#callTool("completion", { prompt, opts });
|
|
113
|
-
globalThis.tool =
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
get: (_target, prop) => {
|
|
117
|
-
if (typeof prop !== "string") return undefined;
|
|
118
|
-
return async args => await this.#callTool(prop, args ?? {});
|
|
119
|
-
},
|
|
120
|
-
},
|
|
128
|
+
globalThis.tool = createToolNamespace(
|
|
129
|
+
(fn, metadata) => this.#tools.define(fn, metadata),
|
|
130
|
+
async (name, args) => await this.#callTool(name, args),
|
|
121
131
|
);
|
|
122
132
|
globalThis.tools = globalThis.tool;
|
|
123
133
|
const originalLog = console.log.bind(console);
|
|
@@ -273,6 +283,7 @@ export class JsWorkerRuntime {
|
|
|
273
283
|
}
|
|
274
284
|
|
|
275
285
|
async #agent(prompt, options, rest) {
|
|
286
|
+
if (inKernelToolInvoke()) throw kernelToolError("kernel_tool_recursion", "kernel tools may not invoke agent()");
|
|
276
287
|
const parsed = optionsArg({
|
|
277
288
|
name: "agent",
|
|
278
289
|
value: options,
|
|
@@ -302,6 +313,7 @@ export class JsWorkerRuntime {
|
|
|
302
313
|
output: text,
|
|
303
314
|
handle: details.handle ?? `agent://${id}`,
|
|
304
315
|
id,
|
|
316
|
+
run_epoch: details.run_epoch,
|
|
305
317
|
agent: details.agent ?? callArgs.agent ?? null,
|
|
306
318
|
};
|
|
307
319
|
if (Object.hasOwn(callArgs, "schema")) node.data = output;
|
|
@@ -39,18 +39,23 @@ export class WorkerSlot {
|
|
|
39
39
|
return this.#startupAbort !== null;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
get generation(): number {
|
|
43
|
+
return this.#generation;
|
|
44
|
+
}
|
|
45
|
+
|
|
42
46
|
postMessage(message: HostToKernelMessage): void {
|
|
43
47
|
this.#worker?.postMessage(message);
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
async ensureReady(): Promise<void> {
|
|
47
51
|
if (!this.#ready) {
|
|
48
|
-
const generation = ++this.#generation;
|
|
52
|
+
const generation = this.#generation === 0 ? ++this.#generation : this.#generation;
|
|
49
53
|
const controller = new AbortController();
|
|
50
54
|
this.#startupAbort = controller;
|
|
51
55
|
const ready = startWorkerWithInlineFallback(
|
|
52
56
|
{
|
|
53
57
|
options: this.#options,
|
|
58
|
+
kernelGeneration: generation,
|
|
54
59
|
publish: (worker) => this.#publish(worker, generation),
|
|
55
60
|
isCurrent: (worker) => this.#isCurrent(worker, generation),
|
|
56
61
|
retire: (worker) => {
|
|
@@ -2,6 +2,7 @@ import { dirname, join } from "node:path";
|
|
|
2
2
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
3
3
|
import { type CodemodeRuntimeAssetEnvironment, requireCodemodeRuntimeAsset } from "../shared/runtime-asset.ts";
|
|
4
4
|
import { createInlineWorker, type WorkerLike } from "./inline-worker.ts";
|
|
5
|
+
import { resolveKernelToolNameSource } from "./kernel-contract.ts";
|
|
5
6
|
import { type JavaScriptKernelOptions, localBridgeConnection } from "./local-module-loader.ts";
|
|
6
7
|
import { spawnNodeWorker, WorkerStartupCancelledError, waitForReady } from "./worker-host.ts";
|
|
7
8
|
|
|
@@ -16,6 +17,7 @@ export function resolveJsWorkerEntryUrl(options: JavaScriptWorkerEntryUrlOptions
|
|
|
16
17
|
|
|
17
18
|
export interface WorkerStartupHooks {
|
|
18
19
|
readonly options: JavaScriptKernelOptions;
|
|
20
|
+
readonly kernelGeneration: number;
|
|
19
21
|
/** Wires the worker into the kernel; throws `WorkerStartupCancelledError` once the generation is stale. */
|
|
20
22
|
publish(worker: WorkerLike): void;
|
|
21
23
|
isCurrent(worker: WorkerLike): boolean;
|
|
@@ -27,7 +29,7 @@ export async function startWorkerWithInlineFallback(hooks: WorkerStartupHooks, s
|
|
|
27
29
|
let worker = spawnWorker(hooks.options);
|
|
28
30
|
hooks.publish(worker);
|
|
29
31
|
try {
|
|
30
|
-
await initializeWorker(worker, hooks
|
|
32
|
+
await initializeWorker(worker, hooks, signal);
|
|
31
33
|
return;
|
|
32
34
|
} catch (error) {
|
|
33
35
|
if (!hooks.isCurrent(worker) || error instanceof WorkerStartupCancelledError) {
|
|
@@ -41,7 +43,7 @@ export async function startWorkerWithInlineFallback(hooks: WorkerStartupHooks, s
|
|
|
41
43
|
if (!hooks.canFallBackInline()) throw new WorkerStartupCancelledError();
|
|
42
44
|
worker = createInlineWorker(hooks.options.cwd, hooks.options.parallelPoolWidth);
|
|
43
45
|
hooks.publish(worker);
|
|
44
|
-
await initializeWorker(worker, hooks
|
|
46
|
+
await initializeWorker(worker, hooks, signal);
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
function spawnWorker(options: JavaScriptKernelOptions): WorkerLike {
|
|
@@ -54,16 +56,16 @@ function spawnWorker(options: JavaScriptKernelOptions): WorkerLike {
|
|
|
54
56
|
}
|
|
55
57
|
}
|
|
56
58
|
|
|
57
|
-
async function initializeWorker(
|
|
58
|
-
worker: WorkerLike,
|
|
59
|
-
options: JavaScriptKernelOptions,
|
|
60
|
-
signal: AbortSignal,
|
|
61
|
-
): Promise<void> {
|
|
59
|
+
async function initializeWorker(worker: WorkerLike, hooks: WorkerStartupHooks, signal: AbortSignal): Promise<void> {
|
|
62
60
|
const ready = waitForReady(worker, signal);
|
|
61
|
+
const options = hooks.options;
|
|
63
62
|
worker.postMessage({
|
|
64
63
|
type: "init",
|
|
65
64
|
sessionId: options.sessionId,
|
|
66
65
|
connection: localBridgeConnection(options),
|
|
66
|
+
kernelGeneration: hooks.kernelGeneration,
|
|
67
|
+
hostToolNames: resolveKernelToolNameSource(options.hostToolNames),
|
|
68
|
+
foreignLanguageNames: resolveKernelToolNameSource(options.foreignLanguageNames),
|
|
67
69
|
...(options.sessionEnv === undefined ? {} : { sessionEnv: options.sessionEnv }),
|
|
68
70
|
});
|
|
69
71
|
await ready;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { inKernelToolInvoke } from "./kernel-tools-context.js";
|
|
2
|
+
import { kernelToolError } from "./kernel-tools-errors.js";
|
|
3
|
+
|
|
4
|
+
// Only host-tool sugar: no admission, worker, or queue state belongs to the kernel.
|
|
5
|
+
export async function createWorkpool(callTool, agent, name, options = {}) {
|
|
6
|
+
if (inKernelToolInvoke()) throw kernelToolError("kernel_tool_recursion", "kernel tools may not invoke workpool()");
|
|
7
|
+
if (options === null || typeof options !== "object" || Array.isArray(options) || Object.keys(options).some(key => key !== "mode")) {
|
|
8
|
+
throw new TypeError("workpool() options only accept mode");
|
|
9
|
+
}
|
|
10
|
+
const call = async args => {
|
|
11
|
+
try {
|
|
12
|
+
return await callTool("workpool", args);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
if (error?.code === "unknown_tool" || error?.code === "inactive_tool") {
|
|
15
|
+
throw workpoolError("workpool_unavailable", "No active host workpool tool", error);
|
|
16
|
+
}
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const result = await call({ op: "create", agent, name, ...options });
|
|
21
|
+
if (result.details?.error) throw workpoolError(result.details.error.code, result.details.error.message);
|
|
22
|
+
const pool_id = result.details?.pool_id;
|
|
23
|
+
if (result.hasError || typeof pool_id !== "string" || !/^wp_[0-9a-f]{32}$/.test(pool_id)) {
|
|
24
|
+
throw workpoolError("workpool_unavailable", "Host did not return a workpool identity");
|
|
25
|
+
}
|
|
26
|
+
return Object.freeze({
|
|
27
|
+
pool_id,
|
|
28
|
+
push: items => call({ op: "push", pool_id, items }),
|
|
29
|
+
close: () => call({ op: "close", pool_id }),
|
|
30
|
+
inspect: () => call({ op: "inspect", pool_id }),
|
|
31
|
+
cancel: () => call({ op: "cancel", pool_id }),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function workpoolError(code, message, cause) {
|
|
36
|
+
return Object.assign(new Error(message, { cause }), { code });
|
|
37
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { KernelInterruptHandle } from "../../tool/types.ts";
|
|
2
|
+
import { rejectKernelToolsUnavailable } from "../kernel-tools-unavailable.ts";
|
|
2
3
|
import type { PendingRun, PythonKernelRunOptions, PythonKernelStartOptions, ResultMessage } from "./kernel-contract.ts";
|
|
3
4
|
import { failedPythonResult, PythonKernelTransport } from "./transport.ts";
|
|
4
5
|
|
|
@@ -31,6 +32,18 @@ export class PythonKernel {
|
|
|
31
32
|
return kernel;
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
listKernelToolNames(): readonly string[] {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describeKernelTools(_names: readonly string[]): Promise<never> {
|
|
40
|
+
return rejectKernelToolsUnavailable();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
invokeKernelTool(_request: unknown, _signal?: AbortSignal): Promise<never> {
|
|
44
|
+
return rejectKernelToolsUnavailable();
|
|
45
|
+
}
|
|
46
|
+
|
|
34
47
|
run(input: PythonKernelRunOptions): Promise<ResultMessage> {
|
|
35
48
|
if (this.#failure) return Promise.reject(this.#failure);
|
|
36
49
|
if (this.#closed) return Promise.reject(new Error("Python kernel is closed"));
|
|
@@ -24,7 +24,7 @@ from collections.abc import Iterable
|
|
|
24
24
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
25
25
|
from pathlib import Path
|
|
26
26
|
from threading import Lock, Thread
|
|
27
|
-
from typing import Any, Callable
|
|
27
|
+
from typing import Any, Callable, Union
|
|
28
28
|
from urllib.parse import unquote
|
|
29
29
|
|
|
30
30
|
SESSION_ID = ""
|
|
@@ -43,7 +43,11 @@ TIMEOUT_RESUME_OP = "timeout-resume"
|
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
class PreludeRuntimeError(RuntimeError):
|
|
46
|
-
"""Host bridge or magic execution failed."""
|
|
46
|
+
"""Host bridge or magic execution failed, retaining its machine code."""
|
|
47
|
+
|
|
48
|
+
def __init__(self, message: str, code: str | None = None) -> None:
|
|
49
|
+
super().__init__(message)
|
|
50
|
+
self.code = code
|
|
47
51
|
|
|
48
52
|
|
|
49
53
|
class PreludeValueError(ValueError):
|
|
@@ -370,7 +374,7 @@ def bridge_post(path: str, payload: dict[str, Any]) -> Any:
|
|
|
370
374
|
return body.get("value")
|
|
371
375
|
error = body.get("error") if isinstance(body, dict) else body
|
|
372
376
|
if isinstance(error, dict):
|
|
373
|
-
raise PreludeRuntimeError(str(error.get("message", error)))
|
|
377
|
+
raise PreludeRuntimeError(str(error.get("message", error)), error.get("code"))
|
|
374
378
|
raise PreludeRuntimeError(str(error))
|
|
375
379
|
|
|
376
380
|
|
|
@@ -416,6 +420,54 @@ class ToolProxy:
|
|
|
416
420
|
|
|
417
421
|
tool = ToolProxy()
|
|
418
422
|
|
|
423
|
+
JsonValue = Union[str, int, float, bool, None, list["JsonValue"], dict[str, "JsonValue"]]
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def _workpool_call(args: dict[str, JsonValue]) -> dict[str, JsonValue]:
|
|
427
|
+
try:
|
|
428
|
+
return tool.workpool(args)
|
|
429
|
+
except PreludeRuntimeError as error:
|
|
430
|
+
if error.code in ("unknown_tool", "inactive_tool"):
|
|
431
|
+
raise PreludeRuntimeError("No active host workpool tool", "workpool_unavailable") from error
|
|
432
|
+
raise
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
class Workpool:
|
|
436
|
+
"""An opaque host identity, not a worker queue."""
|
|
437
|
+
|
|
438
|
+
__slots__ = ("pool_id",)
|
|
439
|
+
|
|
440
|
+
def __init__(self, pool_id: str) -> None:
|
|
441
|
+
self.pool_id = pool_id
|
|
442
|
+
|
|
443
|
+
def push(self, items: list[JsonValue]) -> dict[str, JsonValue]:
|
|
444
|
+
return _workpool_call({"op": "push", "pool_id": self.pool_id, "items": items})
|
|
445
|
+
|
|
446
|
+
def close(self) -> dict[str, JsonValue]:
|
|
447
|
+
return _workpool_call({"op": "close", "pool_id": self.pool_id})
|
|
448
|
+
|
|
449
|
+
def inspect(self) -> dict[str, JsonValue]:
|
|
450
|
+
return _workpool_call({"op": "inspect", "pool_id": self.pool_id})
|
|
451
|
+
|
|
452
|
+
def cancel(self) -> dict[str, JsonValue]:
|
|
453
|
+
return _workpool_call({"op": "cancel", "pool_id": self.pool_id})
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def workpool(agent: dict[str, JsonValue], name: str, *, mode: str | None = None) -> Workpool:
|
|
457
|
+
args: dict[str, JsonValue] = {"op": "create", "agent": agent, "name": name}
|
|
458
|
+
if mode is not None:
|
|
459
|
+
args["mode"] = mode
|
|
460
|
+
result = _workpool_call(args)
|
|
461
|
+
details = result.get("details")
|
|
462
|
+
if isinstance(details, dict):
|
|
463
|
+
error = details.get("error")
|
|
464
|
+
if isinstance(error, dict):
|
|
465
|
+
raise PreludeRuntimeError(str(error["message"]), str(error["code"]))
|
|
466
|
+
pool_id = details.get("pool_id")
|
|
467
|
+
if not result.get("hasError") and isinstance(pool_id, str) and re.fullmatch(r"wp_[0-9a-f]{32}", pool_id):
|
|
468
|
+
return Workpool(pool_id)
|
|
469
|
+
raise PreludeRuntimeError("Host did not return a workpool identity", "workpool_unavailable")
|
|
470
|
+
|
|
419
471
|
|
|
420
472
|
def completion(
|
|
421
473
|
prompt: str,
|
|
@@ -522,6 +574,7 @@ def agent(
|
|
|
522
574
|
"output": text_value,
|
|
523
575
|
"handle": handle_value,
|
|
524
576
|
"id": agent_id,
|
|
577
|
+
"run_epoch": response_record.get("run_epoch"),
|
|
525
578
|
"agent": response_record.get("agent", agent),
|
|
526
579
|
}
|
|
527
580
|
if schema is not None:
|
|
@@ -917,6 +970,7 @@ USER_NS.update(
|
|
|
917
970
|
"tool": tool,
|
|
918
971
|
"completion": completion,
|
|
919
972
|
"agent": agent,
|
|
973
|
+
"workpool": workpool,
|
|
920
974
|
"output": output,
|
|
921
975
|
"tool_schema": tool_schema,
|
|
922
976
|
"__senpi_magic": _magic,
|
|
@@ -140,13 +140,22 @@ def __senpi_bridge_request(path, payload)
|
|
|
140
140
|
return body["value"] if body.is_a?(Hash) && body["ok"] == true
|
|
141
141
|
|
|
142
142
|
error = body.is_a?(Hash) ? body["error"] : body
|
|
143
|
-
raise(error.is_a?(Hash) ? error["message"].to_s : error.to_s)
|
|
143
|
+
raise SenpiBridgeError.new(error.is_a?(Hash) ? error["message"].to_s : error.to_s, error.is_a?(Hash) ? error["code"] : nil)
|
|
144
144
|
end
|
|
145
145
|
|
|
146
146
|
def __senpi_call_tool(name, args)
|
|
147
147
|
__senpi_bridge_request("/call", { "callId" => "rb-#{Process.pid}-#{rand(1_000_000)}", "toolName" => name, "args" => args })
|
|
148
148
|
end
|
|
149
149
|
|
|
150
|
+
class SenpiBridgeError < StandardError
|
|
151
|
+
attr_reader :code
|
|
152
|
+
|
|
153
|
+
def initialize(message, code = nil)
|
|
154
|
+
super(message)
|
|
155
|
+
@code = code
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
150
159
|
class SenpiToolCallable
|
|
151
160
|
def initialize(name)
|
|
152
161
|
@name = name
|
|
@@ -177,6 +186,8 @@ def tool
|
|
|
177
186
|
$__senpi_tool_proxy ||= SenpiToolProxy.new
|
|
178
187
|
end
|
|
179
188
|
|
|
189
|
+
require_relative "workpool"
|
|
190
|
+
|
|
180
191
|
def completion(prompt, model: "default", system: nil, schema: nil, **kwargs)
|
|
181
192
|
options = { "model" => model }.merge(kwargs.transform_keys(&:to_s))
|
|
182
193
|
options["system"] = system unless system.nil?
|
|
@@ -213,7 +224,7 @@ def agent(prompt, agent: "task", model: nil, label: nil, schema: nil, isolated:
|
|
|
213
224
|
text_value = record.fetch("text", response)
|
|
214
225
|
result = schema.nil? ? text_value : record.key?("data") ? record["data"] : JSON.parse(text_value.to_s)
|
|
215
226
|
return result unless handle
|
|
216
|
-
{ "text" => text_value, "output" => text_value, "handle" => record["handle"] || (record["id"] && "agent://#{record["id"]}"), "id" => record["id"], "agent" => record.fetch("agent", agent) }.tap do |node|
|
|
227
|
+
{ "text" => text_value, "output" => text_value, "handle" => record["handle"] || (record["id"] && "agent://#{record["id"]}"), "id" => record["id"], "run_epoch" => record["run_epoch"], "agent" => record.fetch("agent", agent) }.tap do |node|
|
|
217
228
|
node["data"] = result unless schema.nil?
|
|
218
229
|
end
|
|
219
230
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Only an opaque host identity is retained; all work remains engine-owned.
|
|
2
|
+
def __senpi_workpool_call(args)
|
|
3
|
+
tool.workpool(args)
|
|
4
|
+
rescue SenpiBridgeError => error
|
|
5
|
+
raise SenpiBridgeError.new("No active host workpool tool", "workpool_unavailable") if ["unknown_tool", "inactive_tool"].include?(error.code)
|
|
6
|
+
raise
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class SenpiWorkpool
|
|
10
|
+
attr_reader :pool_id
|
|
11
|
+
|
|
12
|
+
def initialize(pool_id)
|
|
13
|
+
@pool_id = pool_id
|
|
14
|
+
freeze
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def push(items)
|
|
18
|
+
__senpi_workpool_call({ "op" => "push", "pool_id" => @pool_id, "items" => items })
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
[:close, :inspect, :cancel].each do |op|
|
|
22
|
+
define_method(op) { __senpi_workpool_call({ "op" => op.to_s, "pool_id" => @pool_id }) }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def workpool(agent, name, mode: nil)
|
|
27
|
+
args = { "op" => "create", "agent" => agent, "name" => name }
|
|
28
|
+
args["mode"] = mode unless mode.nil?
|
|
29
|
+
result = __senpi_workpool_call(args)
|
|
30
|
+
details = result["details"]
|
|
31
|
+
if details.is_a?(Hash)
|
|
32
|
+
error = details["error"]
|
|
33
|
+
raise SenpiBridgeError.new(error["message"], error["code"]) if error.is_a?(Hash)
|
|
34
|
+
pool_id = details["pool_id"]
|
|
35
|
+
return SenpiWorkpool.new(pool_id) if !result["hasError"] && pool_id.is_a?(String) && /\Awp_[0-9a-f]{32}\z/.match?(pool_id)
|
|
36
|
+
end
|
|
37
|
+
raise SenpiBridgeError.new("Host did not return a workpool identity", "workpool_unavailable")
|
|
38
|
+
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { HostToKernelMessage, KernelToHostMessage } from "../../bridge/protocol.ts";
|
|
2
2
|
import { decodeBridgeFrame, encodeBridgeFrame, isKernelToHostMessage } from "../../bridge/protocol.ts";
|
|
3
3
|
import type { KernelInterruptHandle } from "../../tool/types.ts";
|
|
4
|
+
import { rejectKernelToolsUnavailable } from "../kernel-tools-unavailable.ts";
|
|
4
5
|
import { applySessionEnvironment } from "../session-env.ts";
|
|
5
6
|
import type { KernelResult, KernelRunInput, SubprocessKernelOptions, ToolCallMessage } from "./subprocess-contract.ts";
|
|
6
7
|
import { type SubprocessLike, SubprocessProcess, type SubprocessSpawn, spawnSubprocess } from "./subprocess-process.ts";
|
|
@@ -69,6 +70,18 @@ export class SubprocessKernel {
|
|
|
69
70
|
return { stateRetained: Promise.resolve(false) };
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
listKernelToolNames(): readonly string[] {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
describeKernelTools(_names: readonly string[]): Promise<never> {
|
|
78
|
+
return rejectKernelToolsUnavailable();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
invokeKernelTool(_request: unknown, _signal?: AbortSignal): Promise<never> {
|
|
82
|
+
return rejectKernelToolsUnavailable();
|
|
83
|
+
}
|
|
84
|
+
|
|
72
85
|
nextToolCall(): Promise<ToolCallMessage> {
|
|
73
86
|
return this.runs.nextToolCall();
|
|
74
87
|
}
|
|
@@ -2,6 +2,9 @@ import type { KernelToHostMessage } from "../../bridge/protocol.ts";
|
|
|
2
2
|
import type { KernelResult, KernelRunInput, ToolCallMessage } from "./subprocess-contract.ts";
|
|
3
3
|
import { createPendingRun, failureResult, type PendingRun, settlePendingRun } from "./subprocess-run.ts";
|
|
4
4
|
|
|
5
|
+
// Same bound as the JS kernel's pull-API fallback queue (context-manager.ts); clears happen at run teardown.
|
|
6
|
+
const MAX_PENDING_TOOL_CALLS = 256;
|
|
7
|
+
|
|
5
8
|
export class SubprocessRunQueue {
|
|
6
9
|
readonly #queue: PendingRun[] = [];
|
|
7
10
|
readonly #pendingCalls: ToolCallMessage[] = [];
|
|
@@ -59,7 +62,10 @@ export class SubprocessRunQueue {
|
|
|
59
62
|
pushToolCall(message: ToolCallMessage): void {
|
|
60
63
|
const waiter = this.#callWaiters.shift();
|
|
61
64
|
if (waiter) waiter(message);
|
|
62
|
-
else
|
|
65
|
+
else {
|
|
66
|
+
this.#pendingCalls.push(message);
|
|
67
|
+
if (this.#pendingCalls.length > MAX_PENDING_TOOL_CALLS) this.#pendingCalls.shift();
|
|
68
|
+
}
|
|
63
69
|
}
|
|
64
70
|
|
|
65
71
|
handleMessage(
|
|
@@ -90,6 +96,8 @@ export class SubprocessRunQueue {
|
|
|
90
96
|
case "ready":
|
|
91
97
|
case "init-failed":
|
|
92
98
|
case "closed":
|
|
99
|
+
case "kernel-tool-describe-reply":
|
|
100
|
+
case "kernel-tool-invoke-reply":
|
|
93
101
|
onMessage?.(message);
|
|
94
102
|
return false;
|
|
95
103
|
default: {
|
|
@@ -57,8 +57,10 @@ tool_schema(name?) → dict
|
|
|
57
57
|
completion(prompt, model?="default", system?=None, schema?=None) → str | dict
|
|
58
58
|
Oneshot, stateless. \`model\`: \`"smol"\` fast | \`"default"\` session | \`"slow"\` most capable. \`schema\` (JSON-Schema) → parsed structured output.
|
|
59
59
|
{{#if spawns}}agent(prompt, agent?="{{spawnDefaultAgent}}", model?=None, label?=None, schema?=None, handle?=False) → str | dict
|
|
60
|
-
Run a subagent → final output. \`agent\` picks a discovered agent. \`schema\` as in completion(). \`handle\` → workflow node { text, output, handle: \`agent://<id>\`, id, agent } (parsed under \`data\` with \`schema\`).
|
|
61
|
-
{{/if}}
|
|
60
|
+
Run a subagent → final output. \`agent\` picks a discovered agent. \`schema\` as in completion(). \`handle\` → workflow node { text, output, handle: \`agent://<id>\`, id, run_epoch, agent } (parsed under \`data\` with \`schema\`).
|
|
61
|
+
{{/if}}workpool(agent, name, mode?) → { pool_id, push(items), close(), inspect(), cancel() }
|
|
62
|
+
Thin host-tool adapter; agent is a plain-data spec. The host owns scheduling and keyed yields; explicitly close for automatic aggregate delivery. Requires the host workpool tool; omitted mode uses its default.
|
|
63
|
+
parallel(thunks) → list
|
|
62
64
|
Thunks through a bounded pool (as wide as a \`task\` batch), input order kept; a throwing thunk propagates.
|
|
63
65
|
pipeline(items, ...stages) → list
|
|
64
66
|
Map items through one-arg stages with a barrier between stages; each stage receives the previous stage's result.
|
|
@@ -90,6 +90,8 @@ export class CellHandler {
|
|
|
90
90
|
case "init-failed":
|
|
91
91
|
case "result":
|
|
92
92
|
case "closed":
|
|
93
|
+
case "kernel-tool-describe-reply":
|
|
94
|
+
case "kernel-tool-invoke-reply":
|
|
93
95
|
return;
|
|
94
96
|
default:
|
|
95
97
|
throw new TypeError(`Unhandled kernel message: ${String(message)}`);
|
|
@@ -223,7 +225,12 @@ export class CellHandler {
|
|
|
223
225
|
type: "tool-reply",
|
|
224
226
|
callId: message.callId,
|
|
225
227
|
ok: false,
|
|
226
|
-
error: {
|
|
228
|
+
error: {
|
|
229
|
+
message: text,
|
|
230
|
+
...(error instanceof Error && "code" in error && typeof error.code === "string"
|
|
231
|
+
? { code: error.code }
|
|
232
|
+
: {}),
|
|
233
|
+
},
|
|
227
234
|
});
|
|
228
235
|
}
|
|
229
236
|
this.#resultBuilder.emitUpdate(false);
|