@code-yeongyu/senpi 2026.9.15 → 2026.9.16-2
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 +73 -0
- package/README.md +1 -1
- package/dist/core/agent-session.d.ts +28 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +111 -13
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +4 -2
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/stream-watchdog.d.ts +56 -2
- package/dist/core/compaction/stream-watchdog.d.ts.map +1 -1
- package/dist/core/compaction/stream-watchdog.js +93 -15
- package/dist/core/compaction/stream-watchdog.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.d.ts +6 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js +17 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.d.ts +4 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.js +31 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.d.ts +2 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.js +16 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry-wiring.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry-wiring.js +19 -8
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry-wiring.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js +2 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts +13 -1
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +66 -2
- package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.js +16 -10
- package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculative-summary.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculative-summary.js +4 -2
- package/dist/core/extensions/builtin/compaction/speculative-summary.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculative.d.ts +8 -1
- package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculative.js +31 -7
- package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/transient-failure.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/transient-failure.js +6 -3
- package/dist/core/extensions/builtin/compaction/transient-failure.js.map +1 -1
- 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/keybindings.d.ts +5 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +1 -0
- package/dist/core/keybindings.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/retry-fallback/settings.d.ts +3 -0
- package/dist/core/retry-fallback/settings.d.ts.map +1 -1
- package/dist/core/retry-fallback/settings.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +30 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +92 -5
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-resident-store.d.ts +33 -7
- package/dist/core/session-resident-store.d.ts.map +1 -1
- package/dist/core/session-resident-store.js +163 -14
- package/dist/core/session-resident-store.js.map +1 -1
- package/dist/core/session-write-reservation.d.ts +6 -0
- package/dist/core/session-write-reservation.d.ts.map +1 -1
- package/dist/core/session-write-reservation.js +19 -0
- package/dist/core/session-write-reservation.js.map +1 -1
- package/dist/core/settings-manager.d.ts +35 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +62 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +2 -1
- package/dist/core/slash-commands.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/components/assistant-render-descriptors.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-render-descriptors.js +12 -5
- package/dist/modes/interactive/components/assistant-render-descriptors.js.map +1 -1
- package/dist/modes/interactive/components/extension-input.d.ts +2 -0
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-input.js +5 -0
- package/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +19 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +69 -17
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/tips/catalog/session-tips.js +1 -1
- package/dist/modes/interactive/tips/catalog/session-tips.js.map +1 -1
- package/dist/modes/interactive/working-status.d.ts +3 -2
- package/dist/modes/interactive/working-status.d.ts.map +1 -1
- package/dist/modes/interactive/working-status.js +17 -4
- package/dist/modes/interactive/working-status.js.map +1 -1
- package/dist/modes/print-mode.d.ts +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +5 -1
- package/dist/modes/print-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 +5 -2
- package/docs/keybindings.md +1 -0
- package/docs/session-format.md +1 -1
- package/docs/sessions.md +3 -3
- package/docs/settings.md +31 -1
- package/docs/usage.md +1 -1
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +50 -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 +31 -3
- 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 +52 -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/agent-loop.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +30 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts +5 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.js +2 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.js.map +1 -1
- 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 +4 -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 +3 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/stream-throughput-watchdog.d.ts +101 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/stream-throughput-watchdog.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/stream-throughput-watchdog.js +178 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/stream-throughput-watchdog.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts +9 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/types.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 +24 -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 +92 -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
|
@@ -12,6 +12,56 @@
|
|
|
12
12
|
|
|
13
13
|
### Removed
|
|
14
14
|
|
|
15
|
+
## [2026.9.16-2] - 2026-09-16
|
|
16
|
+
|
|
17
|
+
### Breaking Changes
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
|
|
25
|
+
- The JavaScript kernel-tool capability now reaches host tools called from inside a running eval cell. A cell that registers `tool(fn)` can hand those functions to its in-process children through `task`/`agent`/`workpool`, because `ExtensionContext.kernelTools` resolves for the duration of each host tool call the cell makes. It was undefined at the dispatch point — the worker's message loop ran outside the `kernelToolsStorage` scope that only covered the awaited run chain — so every grant was refused with `tools_unavailable` and the capability shipped in the previous release could not be used ([#1754](https://github.com/code-yeongyu/senpi/issues/1754)).
|
|
26
|
+
|
|
27
|
+
### Removed
|
|
28
|
+
|
|
29
|
+
## [2026.9.16] - 2026-09-16
|
|
30
|
+
|
|
31
|
+
### Breaking Changes
|
|
32
|
+
|
|
33
|
+
- Background `agent(..., handle=true)` now requires the host task tool to return structured `details.task_id` (`st_` plus lowercase hex) and an integer `details.run_epoch >= 0`. The result gains a `run_epoch` field next to `id` and `handle`, in every kernel language. A host that returns an error, or only a prose task id in its text, raises `invalid_task_handle`; the old regex scrape of the text is gone. Extra producer fields in `details` are accepted. Foreground text and JSON results are unchanged ([#1646](https://github.com/code-yeongyu/senpi/issues/1646)).
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- Added `workpool(agent, name, mode?)` to the JS, Python, Ruby and Julia preludes as a thin adapter over the host `workpool` tool. `agent` is a plain-data spec with one of `category` or `subagent_type` plus `prompt` and optional `model`; `mode` is `fresh` or `keep_alive` and is forwarded only when given. The adapter exposes `pool_id`, `push(items)`, `close()`, `inspect()` and `cancel()`, each returning the same `{text, details, images?, hasError?}` envelope as a direct tool call, and holds no worker, queue or admission state, so a kernel reset drops only the variable and the pool survives. A missing or inactive host tool raises `workpool_unavailable`; a host error at creation is raised instead of returning a broken adapter. Kernel tools may not call `workpool()` (`kernel_tool_recursion`) ([#1646](https://github.com/code-yeongyu/senpi/issues/1646)).
|
|
38
|
+
- Added JavaScript kernel tools. `tool(fn, metadata?)` registers a named `function` or `async function` declaration as a fenced tool for in-process children while `tool.<name>(args)` host calls keep working. The parser reads only the declaration head: anonymous functions, classes, generators, native functions and non-identifier parameters are rejected with `invalid_tool_definition`; unicode identifiers are kept as written. Names must already fit the MCP grammar (`[A-Za-z0-9_-]`, at most 64 characters) and are checked against reserved bridge names, live host tools (including tools attached after the worker started) and tools registered by the session's Python, Ruby or Julia kernels, raising `reserved_tool_name` or `tool_name_collision`. Each descriptor carries `name`, a JSON input schema, `kernel_generation` and `definition_revision`; a stale generation or revision is refused with `kernel_tool_stale`, redefining a function bumps its revision, and a kernel reset clears every registered tool and bumps the generation. `agent()` accepts `tools: string[]` to hand a child those tool names. Kernel-tool requests against Python, Ruby or Julia kernels fail with `tools_unavailable` ([#1647](https://github.com/code-yeongyu/senpi/issues/1647)).
|
|
39
|
+
- The JS worker answers kernel-tool `describe` and `invoke` requests on a separate pump from the top-level run queue, so a parent cell can stay pending on `agent()` while an in-process child calls one of the parent's registered functions. Interrupting the parent settles every nested invoke, and a worker reset, crash or close rejects the pending ones with `kernel_tool_stale` ([#1647](https://github.com/code-yeongyu/senpi/issues/1647)).
|
|
40
|
+
- `CodemodeExtensionAPI.kernelTools` exposes the parent cell's kernel-tool `describe`/`invoke` capability to extensions while a JavaScript eval is live; `KernelToolDescriptor`, `KernelToolsCapability`, `KernelToolsDescribeResult`, `KernelToolsInvokeRequest` and `KERNEL_TOOLS_UNSUPPORTED` are exported. Kernels may implement `EvalKernel.listKernelToolNames()` so the session manager can detect cross-language name collisions ([#1647](https://github.com/code-yeongyu/senpi/issues/1647)).
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- The eval prompt documents `workpool()` and the `run_epoch` field on background `agent()` handles, and the JS prelude describes `tool(fn, metadata?)` and `workpool()` ([#1646](https://github.com/code-yeongyu/senpi/issues/1646), [#1647](https://github.com/code-yeongyu/senpi/issues/1647)).
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
### Removed
|
|
49
|
+
|
|
50
|
+
## [2026.9.15-2] - 2026-09-15
|
|
51
|
+
|
|
52
|
+
### Breaking Changes
|
|
53
|
+
|
|
54
|
+
### Added
|
|
55
|
+
|
|
56
|
+
### Changed
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
- Detached eval result cards no longer arm the 1 Hz repaint ticker (they render static with frozen elapsed time), and a live ticker whose row stops rendering now stops itself after 60 idle ticks and rearms on the next render, so transcript rebuilds and session switches cannot accumulate intervals on idle sessions ([#1696](https://github.com/code-yeongyu/senpi/issues/1696)).
|
|
61
|
+
- Bounded three unbounded retentions that grew long-lived session heaps without limit: settled eval cells now leave the live registry into a 32-entry terminal snapshot LRU, the JS kernel's unconsumed tool-call queue is capped at 256 and cleared on interrupt/reset/close/crash (mirroring the subprocess kernel), and per-cell display buffers cap at 8 images / 24 MB / 64 JSON outputs with elision notes ([#1695](https://github.com/code-yeongyu/senpi/issues/1695)).
|
|
62
|
+
|
|
63
|
+
### Removed
|
|
64
|
+
|
|
15
65
|
## [2026.9.15] - 2026-09-15
|
|
16
66
|
|
|
17
67
|
### Breaking Changes
|
|
@@ -144,6 +144,7 @@ options object and asynchronous helpers are `await`-able.
|
|
|
144
144
|
| `tool_schema(name?)` | Returns a tool's parameter schema without calling it; omit `name` to list tool names. |
|
|
145
145
|
| `completion(prompt, model?, system?, schema?)` | Requests a one-shot host completion; `schema` asks the host to parse structured output. |
|
|
146
146
|
| `agent(prompt, ...)` | Delegates to the configured active `taskTools.task` tool. Supports background handles and structured JSON results. |
|
|
147
|
+
| `workpool(agent, name, mode?)` | Creates a thin adapter over the normal host `workpool` tool; exposes `pool_id`, `push(items)`, `close()`, `inspect()`, and `cancel()`. JS awaits creation and operations. |
|
|
147
148
|
| `output(ids, format?, offset?, limit?)` | Delegates transcript retrieval to the configured active `taskTools.output` tool. |
|
|
148
149
|
| `parallel(thunks)` | Runs thunks through the configured bounded pool while preserving input order. |
|
|
149
150
|
| `pipeline(items, ...stages)` | Applies stages left to right with a barrier between stages. |
|
|
@@ -164,6 +165,29 @@ updates, and transcripts remain owned by that engine.
|
|
|
164
165
|
`isolated`, `apply`, and `merge` are accepted for compatibility but emit a
|
|
165
166
|
warning because this task-engine integration has no isolation model.
|
|
166
167
|
|
|
168
|
+
Background `agent()` handles retain `id` and `agent://<id>` and include `run_epoch`.
|
|
169
|
+
The host must return structured `details.task_id` (`st_` plus lowercase hex) and
|
|
170
|
+
an integer `details.run_epoch >= 0`. Missing or malformed details raise
|
|
171
|
+
`invalid_task_handle`; prose IDs are never used. Foreground text/JSON is unchanged.
|
|
172
|
+
|
|
173
|
+
`workpool` takes exactly one of `{category, prompt, model?}` or
|
|
174
|
+
`{subagent_type, prompt, model?}` as its plain-data agent spec. Mode is `fresh`
|
|
175
|
+
or `keep_alive`: pass `{mode: "fresh"}` in JS, `mode="fresh"` in Python/Julia,
|
|
176
|
+
or `mode: "fresh"` in Ruby. Omission is forwarded unchanged to the engine;
|
|
177
|
+
hosts without an approved default still require an explicit mode. Custom tool
|
|
178
|
+
names are not enabled by this adapter.
|
|
179
|
+
|
|
180
|
+
`push` forwards `[{key, input}]` and returns the host receipt without waiting
|
|
181
|
+
for admission. Operations return the same `{text, details, images?, hasError?}`
|
|
182
|
+
envelope as direct tool calls. Creation refuses host errors instead of returning
|
|
183
|
+
a broken adapter; a missing host raises `workpool_unavailable`. The host owns
|
|
184
|
+
workers, keyed yields, cancellation, and aggregate delivery after explicit
|
|
185
|
+
`close()`; none is implemented in a kernel. Aggregate support requires a host
|
|
186
|
+
that implements it. Reset only removes kernel variables: save `pool_id` and use
|
|
187
|
+
`tool.workpool({op: "inspect", pool_id})` from a new JS cell (equivalent keyword
|
|
188
|
+
arguments in other languages). An open pool's adapter can be recreated with the
|
|
189
|
+
same name/spec/mode; no worker state is reconstructed in the prelude.
|
|
190
|
+
|
|
167
191
|
## Required summary
|
|
168
192
|
|
|
169
193
|
Every `eval` run call MUST include a `summary` — one line in the user's
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-yeongyu/senpi-codemode",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.16-2",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Source-only senpi extension package for codemode evaluation tools",
|
|
6
6
|
"type": "module",
|
|
@@ -31,14 +31,14 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@babel/parser": "8.0.4",
|
|
34
|
-
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.9.
|
|
34
|
+
"@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.9.16-2",
|
|
35
35
|
"typebox": "1.3.27"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"@code-yeongyu/senpi": "2026.9.
|
|
38
|
+
"@code-yeongyu/senpi": "2026.9.16-2"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@code-yeongyu/senpi": "2026.9.
|
|
41
|
+
"@code-yeongyu/senpi": "2026.9.16-2"
|
|
42
42
|
},
|
|
43
43
|
"keywords": [
|
|
44
44
|
"senpi",
|
|
@@ -217,7 +217,13 @@ function sendJson(response: ServerResponse, status: number, body: JsonReply): vo
|
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
function bridgeError(error: unknown): BridgeError {
|
|
220
|
-
if (error instanceof Error)
|
|
220
|
+
if (error instanceof Error)
|
|
221
|
+
return {
|
|
222
|
+
name: error.name,
|
|
223
|
+
message: error.message,
|
|
224
|
+
stack: error.stack,
|
|
225
|
+
...("code" in error && typeof error.code === "string" ? { code: error.code } : {}),
|
|
226
|
+
};
|
|
221
227
|
return { message: String(error) };
|
|
222
228
|
}
|
|
223
229
|
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { type Static, Type } from "typebox";
|
|
2
|
+
|
|
3
|
+
const kernelToolErrorSchema = Type.Object({
|
|
4
|
+
message: Type.String(),
|
|
5
|
+
name: Type.Optional(Type.String()),
|
|
6
|
+
stack: Type.Optional(Type.String()),
|
|
7
|
+
code: Type.Optional(Type.String()),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export const kernelToolDescriptorSchema = Type.Object({
|
|
11
|
+
name: Type.String({ minLength: 1 }),
|
|
12
|
+
description: Type.String(),
|
|
13
|
+
input_schema: Type.Unknown(),
|
|
14
|
+
language: Type.Literal("js"),
|
|
15
|
+
kernel_generation: Type.Integer({ minimum: 0 }),
|
|
16
|
+
definition_revision: Type.Integer({ minimum: 1 }),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const describeResultSchema = Type.Union([
|
|
20
|
+
Type.Object({
|
|
21
|
+
name: Type.String({ minLength: 1 }),
|
|
22
|
+
ok: Type.Literal(true),
|
|
23
|
+
descriptor: kernelToolDescriptorSchema,
|
|
24
|
+
}),
|
|
25
|
+
Type.Object({
|
|
26
|
+
name: Type.String({ minLength: 1 }),
|
|
27
|
+
ok: Type.Literal(false),
|
|
28
|
+
error: kernelToolErrorSchema,
|
|
29
|
+
}),
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
export const kernelToolHostToKernelSchemas = [
|
|
33
|
+
Type.Object({
|
|
34
|
+
type: Type.Literal("kernel-tool-describe"),
|
|
35
|
+
requestId: Type.String({ minLength: 1 }),
|
|
36
|
+
names: Type.Array(Type.String({ minLength: 1 })),
|
|
37
|
+
}),
|
|
38
|
+
Type.Object({
|
|
39
|
+
type: Type.Literal("kernel-tool-invoke"),
|
|
40
|
+
requestId: Type.String({ minLength: 1 }),
|
|
41
|
+
name: Type.String({ minLength: 1 }),
|
|
42
|
+
kernel_generation: Type.Integer({ minimum: 0 }),
|
|
43
|
+
definition_revision: Type.Integer({ minimum: 1 }),
|
|
44
|
+
args: Type.Unknown(),
|
|
45
|
+
call_id: Type.String({ minLength: 1 }),
|
|
46
|
+
}),
|
|
47
|
+
Type.Object({
|
|
48
|
+
type: Type.Literal("kernel-tool-cancel"),
|
|
49
|
+
requestId: Type.String({ minLength: 1 }),
|
|
50
|
+
}),
|
|
51
|
+
Type.Object({
|
|
52
|
+
type: Type.Literal("kernel-tools-names"),
|
|
53
|
+
hostToolNames: Type.Array(Type.String({ minLength: 1 })),
|
|
54
|
+
foreignLanguageNames: Type.Array(Type.String({ minLength: 1 })),
|
|
55
|
+
}),
|
|
56
|
+
] as const;
|
|
57
|
+
|
|
58
|
+
export const kernelToolKernelToHostSchemas = [
|
|
59
|
+
Type.Object({
|
|
60
|
+
type: Type.Literal("kernel-tool-describe-reply"),
|
|
61
|
+
requestId: Type.String({ minLength: 1 }),
|
|
62
|
+
ok: Type.Literal(true),
|
|
63
|
+
results: Type.Array(describeResultSchema),
|
|
64
|
+
}),
|
|
65
|
+
Type.Object({
|
|
66
|
+
type: Type.Literal("kernel-tool-describe-reply"),
|
|
67
|
+
requestId: Type.String({ minLength: 1 }),
|
|
68
|
+
ok: Type.Literal(false),
|
|
69
|
+
error: kernelToolErrorSchema,
|
|
70
|
+
}),
|
|
71
|
+
Type.Object({
|
|
72
|
+
type: Type.Literal("kernel-tool-invoke-reply"),
|
|
73
|
+
requestId: Type.String({ minLength: 1 }),
|
|
74
|
+
ok: Type.Literal(true),
|
|
75
|
+
value: Type.Unknown(),
|
|
76
|
+
}),
|
|
77
|
+
Type.Object({
|
|
78
|
+
type: Type.Literal("kernel-tool-invoke-reply"),
|
|
79
|
+
requestId: Type.String({ minLength: 1 }),
|
|
80
|
+
ok: Type.Literal(false),
|
|
81
|
+
error: kernelToolErrorSchema,
|
|
82
|
+
}),
|
|
83
|
+
] as const;
|
|
84
|
+
|
|
85
|
+
export type KernelToolDescriptorMessage = Static<typeof kernelToolDescriptorSchema>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
2
2
|
import { type Static, Type } from "typebox";
|
|
3
3
|
import { Value } from "typebox/value";
|
|
4
|
+
import { kernelToolHostToKernelSchemas, kernelToolKernelToHostSchemas } from "./kernel-tools-protocol.ts";
|
|
4
5
|
|
|
5
6
|
export const BRIDGE_FRAME_MAX_BYTES = 10 * 1024 * 1024;
|
|
6
7
|
|
|
@@ -27,6 +28,9 @@ const hostToKernelMessageSchema = Type.Union([
|
|
|
27
28
|
sessionId: Type.String({ minLength: 1 }),
|
|
28
29
|
connection: connectionConfigSchema,
|
|
29
30
|
sessionEnv: Type.Optional(Type.Record(Type.String(), Type.String())),
|
|
31
|
+
kernelGeneration: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
32
|
+
hostToolNames: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
33
|
+
foreignLanguageNames: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
30
34
|
}),
|
|
31
35
|
Type.Object({
|
|
32
36
|
type: Type.Literal("run"),
|
|
@@ -53,6 +57,7 @@ const hostToKernelMessageSchema = Type.Union([
|
|
|
53
57
|
Type.Object({
|
|
54
58
|
type: Type.Literal("close"),
|
|
55
59
|
}),
|
|
60
|
+
...kernelToolHostToKernelSchemas,
|
|
56
61
|
]);
|
|
57
62
|
|
|
58
63
|
const kernelToHostMessageSchema = Type.Union([
|
|
@@ -92,6 +97,7 @@ const kernelToHostMessageSchema = Type.Union([
|
|
|
92
97
|
durationMs: Type.Integer({ minimum: 0 }),
|
|
93
98
|
}),
|
|
94
99
|
Type.Object({ type: Type.Literal("closed") }),
|
|
100
|
+
...kernelToolKernelToHostSchemas,
|
|
95
101
|
]);
|
|
96
102
|
|
|
97
103
|
const bridgeMessageSchema = Type.Union([hostToKernelMessageSchema, kernelToHostMessageSchema]);
|
|
@@ -11,6 +11,7 @@ const agentArgsSchema = Type.Object(
|
|
|
11
11
|
label: Type.Optional(Type.String()),
|
|
12
12
|
schema: Type.Optional(Type.Unknown()),
|
|
13
13
|
handle: Type.Optional(Type.Boolean()),
|
|
14
|
+
tools: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
14
15
|
isolated: Type.Optional(Type.Boolean()),
|
|
15
16
|
apply: Type.Optional(Type.Boolean()),
|
|
16
17
|
merge: Type.Optional(Type.Boolean()),
|
|
@@ -19,7 +20,19 @@ const agentArgsSchema = Type.Object(
|
|
|
19
20
|
);
|
|
20
21
|
|
|
21
22
|
const unsupportedIsolationWarning = "isolated/apply/merge unsupported (no isolation in task engine)";
|
|
22
|
-
const
|
|
23
|
+
const taskHandleSchema = Type.Object(
|
|
24
|
+
{
|
|
25
|
+
task_id: Type.String({ pattern: "^st_[0-9a-f]+$" }),
|
|
26
|
+
run_epoch: Type.Integer({ minimum: 0 }),
|
|
27
|
+
},
|
|
28
|
+
{ additionalProperties: true },
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
/** Structural host contract; no orchestration package dependency. */
|
|
32
|
+
export type TaskHandleDetails = {
|
|
33
|
+
readonly task_id: string;
|
|
34
|
+
readonly run_epoch: number;
|
|
35
|
+
};
|
|
23
36
|
const droppedOptionNames = ["isolated", "apply", "merge"] as const;
|
|
24
37
|
|
|
25
38
|
type AgentArgs = Static<typeof agentArgsSchema>;
|
|
@@ -29,6 +42,7 @@ type TaskParams = {
|
|
|
29
42
|
readonly model?: string;
|
|
30
43
|
readonly name?: string;
|
|
31
44
|
readonly run_in_background: boolean;
|
|
45
|
+
readonly tools?: readonly string[];
|
|
32
46
|
};
|
|
33
47
|
type ProgressContext = { readonly fallbackId: string; readonly warning?: string };
|
|
34
48
|
|
|
@@ -48,7 +62,7 @@ export type EvalAgentResult =
|
|
|
48
62
|
| { readonly text: string }
|
|
49
63
|
| { readonly text: string; readonly data: unknown }
|
|
50
64
|
| { readonly text: string; readonly parseError: string }
|
|
51
|
-
| { readonly text: string; readonly id: string; readonly handle: string };
|
|
65
|
+
| { readonly text: string; readonly id: string; readonly handle: string; readonly run_epoch: number };
|
|
52
66
|
|
|
53
67
|
class AgentArgumentsError extends Error {
|
|
54
68
|
readonly name = "AgentArgumentsError";
|
|
@@ -68,9 +82,10 @@ class AgentUnavailableError extends Error {
|
|
|
68
82
|
|
|
69
83
|
class AgentHandleError extends Error {
|
|
70
84
|
readonly name = "AgentHandleError";
|
|
85
|
+
readonly code = "invalid_task_handle";
|
|
71
86
|
|
|
72
87
|
constructor() {
|
|
73
|
-
super("agent()
|
|
88
|
+
super("agent() requires successful task details with a valid task_id and nonnegative integer run_epoch");
|
|
74
89
|
}
|
|
75
90
|
}
|
|
76
91
|
|
|
@@ -105,9 +120,8 @@ export async function runEvalAgent(args: unknown, options: RunEvalAgentOptions):
|
|
|
105
120
|
|
|
106
121
|
const text = resultText(result);
|
|
107
122
|
if (parsed.handle === true) {
|
|
108
|
-
const id =
|
|
109
|
-
|
|
110
|
-
return { text, id, handle: `agent://${id}` };
|
|
123
|
+
const { task_id: id, run_epoch } = resultTaskHandle(result);
|
|
124
|
+
return { text, id, handle: `agent://${id}`, run_epoch };
|
|
111
125
|
}
|
|
112
126
|
if (!structured) return { text };
|
|
113
127
|
return parseStructuredText(text);
|
|
@@ -130,6 +144,7 @@ function toTaskParams(args: AgentArgs, structured: boolean): TaskParams {
|
|
|
130
144
|
...(args.model === undefined ? {} : { model: args.model }),
|
|
131
145
|
...(args.label === undefined ? {} : { name: args.label }),
|
|
132
146
|
run_in_background: args.handle === true,
|
|
147
|
+
...(args.tools === undefined ? {} : { tools: args.tools }),
|
|
133
148
|
};
|
|
134
149
|
}
|
|
135
150
|
|
|
@@ -168,11 +183,15 @@ function resultText(result: AgentToolResult<unknown>): string {
|
|
|
168
183
|
.join("\n");
|
|
169
184
|
}
|
|
170
185
|
|
|
171
|
-
function
|
|
172
|
-
const details =
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
186
|
+
function resultTaskHandle(result: AgentToolResult<unknown>): TaskHandleDetails {
|
|
187
|
+
const details = result.details;
|
|
188
|
+
if (
|
|
189
|
+
("isError" in result && result.isError === true) ||
|
|
190
|
+
(isRecord(details) && (details.isError === true || details.error !== undefined)) ||
|
|
191
|
+
!Check(taskHandleSchema, details)
|
|
192
|
+
)
|
|
193
|
+
throw new AgentHandleError();
|
|
194
|
+
return details;
|
|
176
195
|
}
|
|
177
196
|
|
|
178
197
|
function firstString(
|
|
@@ -215,6 +215,8 @@ class DefaultCodemodeSessionManager implements CodemodeSessionManager {
|
|
|
215
215
|
cwd: this.#options.cwd,
|
|
216
216
|
parallelPoolWidth,
|
|
217
217
|
onMessage,
|
|
218
|
+
hostToolNames: () => this.#options.listTools?.().map((tool) => tool.name) ?? [],
|
|
219
|
+
foreignLanguageNames: () => this.#foreignKernelToolNames(),
|
|
218
220
|
...(this.#options.sessionEnv ? { sessionEnv: this.#options.sessionEnv } : {}),
|
|
219
221
|
...(localRoots ? { localRoots: { ...localRoots } } : {}),
|
|
220
222
|
...(this.#options.artifactsDir ? { artifactsDir: this.#options.artifactsDir } : {}),
|
|
@@ -259,6 +261,15 @@ class DefaultCodemodeSessionManager implements CodemodeSessionManager {
|
|
|
259
261
|
});
|
|
260
262
|
}
|
|
261
263
|
|
|
264
|
+
#foreignKernelToolNames(): string[] {
|
|
265
|
+
const names: string[] = [];
|
|
266
|
+
for (const [language, kernel] of this.#kernels) {
|
|
267
|
+
if (language === "js") continue;
|
|
268
|
+
names.push(...(kernel.listKernelToolNames?.() ?? []));
|
|
269
|
+
}
|
|
270
|
+
return names;
|
|
271
|
+
}
|
|
272
|
+
|
|
262
273
|
#contextFor(signal: AbortSignal): ExtensionContext {
|
|
263
274
|
const ctx = this.#context;
|
|
264
275
|
if (!ctx) throw new CodemodeContextUnavailableError();
|
|
@@ -23,6 +23,7 @@ import type { CodemodeSessionManager, CreateCodemodeSessionManagerOptions } from
|
|
|
23
23
|
import { SessionManagerProxy } from "./extension/session-manager-proxy.ts";
|
|
24
24
|
import { activeBunSkillPath, registerBunSkillContribution } from "./extension/skill-contribution.ts";
|
|
25
25
|
import { WAKE_SOURCE_STATE_EVENT, type WakeSourceState } from "./extension/wake-source-state.ts";
|
|
26
|
+
import type { KernelToolsCapability } from "./kernels/js/kernel-tools-types.ts";
|
|
26
27
|
import { EvalDetachedCellManager, type EvalDetachedCellStatusEntry } from "./tool/detached-cell-manager.ts";
|
|
27
28
|
import {
|
|
28
29
|
EVAL_EXECUTION_EVENT,
|
|
@@ -48,6 +49,8 @@ export interface CodemodeExtensionAPI {
|
|
|
48
49
|
registerRemovedToolHint(name: string, hint: string): void;
|
|
49
50
|
on(event: CodemodeEvent | "resources_discover", handler: (event: unknown, ctx: ExtensionContext) => unknown): void;
|
|
50
51
|
executeTool: AgentExecuteTool;
|
|
52
|
+
/** Present only while a live JavaScript eval owns the host-tool context. */
|
|
53
|
+
kernelTools?: KernelToolsCapability;
|
|
51
54
|
getActiveTools(): string[];
|
|
52
55
|
getAllTools(): readonly EvalSchemaToolInfo[];
|
|
53
56
|
sendMessage(
|
|
@@ -259,4 +262,11 @@ function modelIdFrom(event: unknown): string | undefined {
|
|
|
259
262
|
return typeof model.id === "string" ? model.id : undefined;
|
|
260
263
|
}
|
|
261
264
|
|
|
265
|
+
export {
|
|
266
|
+
KERNEL_TOOLS_UNSUPPORTED,
|
|
267
|
+
type KernelToolDescriptor,
|
|
268
|
+
type KernelToolsCapability,
|
|
269
|
+
type KernelToolsDescribeResult,
|
|
270
|
+
type KernelToolsInvokeRequest,
|
|
271
|
+
} from "./kernels/js/kernel-tools-types.ts";
|
|
262
272
|
export { enabledLanguagesFrom };
|
|
@@ -181,6 +181,51 @@ end
|
|
|
181
181
|
Base.getproperty(::SenpiToolProxy, name::Symbol) = SenpiToolCallable(string(name))
|
|
182
182
|
const tool = SenpiToolProxy()
|
|
183
183
|
|
|
184
|
+
struct SenpiBridgeError <: Exception
|
|
185
|
+
message::String
|
|
186
|
+
code::Union{String, Nothing}
|
|
187
|
+
end
|
|
188
|
+
Base.showerror(io::IO, error::SenpiBridgeError) = Base.print(io, error.message)
|
|
189
|
+
|
|
190
|
+
function senpi_workpool_call(args)
|
|
191
|
+
try
|
|
192
|
+
return tool.workpool(args)
|
|
193
|
+
catch error
|
|
194
|
+
if error isa SenpiBridgeError && error.code in ("unknown_tool", "inactive_tool")
|
|
195
|
+
throw(SenpiBridgeError("No active host workpool tool", "workpool_unavailable"))
|
|
196
|
+
end
|
|
197
|
+
rethrow()
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
struct SenpiWorkpool
|
|
202
|
+
pool_id::String
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
function Base.getproperty(pool::SenpiWorkpool, name::Symbol)
|
|
206
|
+
pool_id = getfield(pool, :pool_id)
|
|
207
|
+
name === :pool_id && return pool_id
|
|
208
|
+
name === :push && return items -> senpi_workpool_call(Dict("op" => "push", "pool_id" => pool_id, "items" => items))
|
|
209
|
+
name in (:close, :inspect, :cancel) && return () -> senpi_workpool_call(Dict("op" => string(name), "pool_id" => pool_id))
|
|
210
|
+
getfield(pool, name)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
function workpool(agent::AbstractDict, name::AbstractString; mode=nothing)
|
|
214
|
+
args = Dict{String, Any}("op" => "create", "agent" => agent, "name" => name)
|
|
215
|
+
mode !== nothing && (args["mode"] = mode)
|
|
216
|
+
result = senpi_workpool_call(args)
|
|
217
|
+
details = get(result, "details", nothing)
|
|
218
|
+
if details isa AbstractDict
|
|
219
|
+
error = get(details, "error", nothing)
|
|
220
|
+
error isa AbstractDict && throw(SenpiBridgeError(error["message"], error["code"]))
|
|
221
|
+
pool_id = get(details, "pool_id", nothing)
|
|
222
|
+
if get(result, "hasError", false) !== true && pool_id isa AbstractString && occursin(r"^wp_[0-9a-f]{32}$", pool_id)
|
|
223
|
+
return SenpiWorkpool(pool_id)
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
throw(SenpiBridgeError("Host did not return a workpool identity", "workpool_unavailable"))
|
|
227
|
+
end
|
|
228
|
+
|
|
184
229
|
function completion(prompt::AbstractString; model="default", system=nothing, schema=nothing, kwargs...)
|
|
185
230
|
options = Dict{String, Any}("model" => model)
|
|
186
231
|
system !== nothing && (options["system"] = system)
|
|
@@ -225,6 +270,7 @@ function agent(prompt::AbstractString; agent="task", model=nothing, label=nothin
|
|
|
225
270
|
handle || return parsed
|
|
226
271
|
result = Dict{String, Any}("text" => text_value, "output" => text_value, "id" => get(record, "id", nothing), "agent" => get(record, "agent", agent))
|
|
227
272
|
result["handle"] = get(record, "handle", result["id"] === nothing ? nothing : "agent://" * string(result["id"]))
|
|
273
|
+
result["run_epoch"] = get(record, "run_epoch", nothing)
|
|
228
274
|
schema !== nothing && (result["data"] = parsed)
|
|
229
275
|
result
|
|
230
276
|
end
|
|
@@ -244,7 +244,7 @@ function senpi_bridge_request(path::String, payload)
|
|
|
244
244
|
parsed isa AbstractDict || error("Bridge returned invalid JSON")
|
|
245
245
|
get(parsed, "ok", false) === true && return get(parsed, "value", nothing)
|
|
246
246
|
failure = get(parsed, "error", parsed)
|
|
247
|
-
|
|
247
|
+
throw(SenpiBridgeError(failure isa AbstractDict ? string(get(failure, "message", failure)) : string(failure), failure isa AbstractDict ? get(failure, "code", nothing) : nothing))
|
|
248
248
|
finally
|
|
249
249
|
close(socket)
|
|
250
250
|
end
|
|
@@ -8,8 +8,12 @@ import {
|
|
|
8
8
|
type JavaScriptRunInput,
|
|
9
9
|
type LifecycleState,
|
|
10
10
|
type ResultMessage,
|
|
11
|
+
resolveKernelToolNameSource,
|
|
11
12
|
type ToolCallMessage,
|
|
12
13
|
} from "./kernel-contract.ts";
|
|
14
|
+
import { kernelToolError } from "./kernel-tools-errors.ts";
|
|
15
|
+
import { KernelToolHostPump } from "./kernel-tools-host.ts";
|
|
16
|
+
import type { KernelToolsDescribeResult, KernelToolsInvokeRequest } from "./kernel-tools-types.ts";
|
|
13
17
|
import { type JavaScriptKernelOptions, LocalModuleLoader } from "./local-module-loader.ts";
|
|
14
18
|
import { terminateProcessTrees } from "./process-tree-host.ts";
|
|
15
19
|
import { JavaScriptRunQueue, type PendingJavaScriptRun, stoppedResult } from "./run-queue.ts";
|
|
@@ -23,6 +27,10 @@ export { type JavaScriptWorkerEntryUrlOptions, resolveJsWorkerEntryUrl } from ".
|
|
|
23
27
|
/** How long a lost worker's children get to honour SIGTERM before the host sends SIGKILL. */
|
|
24
28
|
const WORKER_LOSS_CHILD_GRACE_MS = 1_000;
|
|
25
29
|
|
|
30
|
+
// Pull-API fallback queue bound: a nextToolCall consumer this far behind is already stalled, and the
|
|
31
|
+
// normal push path (onMessage) never reads the queue, so unbounded growth only pins tool args (#1695).
|
|
32
|
+
const MAX_PENDING_TOOL_CALLS = 256;
|
|
33
|
+
|
|
26
34
|
export class JavaScriptKernel {
|
|
27
35
|
readonly #options: JavaScriptKernelOptions;
|
|
28
36
|
readonly #moduleLoader: LocalModuleLoader;
|
|
@@ -32,6 +40,10 @@ export class JavaScriptKernel {
|
|
|
32
40
|
#recovery: Promise<void> | null = null;
|
|
33
41
|
#closePromise: Promise<void> | null = null;
|
|
34
42
|
readonly #runs = new JavaScriptRunQueue();
|
|
43
|
+
readonly #kernelTools = new KernelToolHostPump(
|
|
44
|
+
(message) => this.#slot.postMessage(message),
|
|
45
|
+
() => this.#lifecycle === "open" && this.#slot.present,
|
|
46
|
+
);
|
|
35
47
|
#timeout: NodeJS.Timeout | null = null;
|
|
36
48
|
#toolWaiters: Array<(message: ToolCallMessage) => void> = [];
|
|
37
49
|
#pendingToolCalls: ToolCallMessage[] = [];
|
|
@@ -52,6 +64,18 @@ export class JavaScriptKernel {
|
|
|
52
64
|
return this.#slot.mode;
|
|
53
65
|
}
|
|
54
66
|
|
|
67
|
+
get kernelToolEvents(): EventTarget {
|
|
68
|
+
return this.#kernelTools.events;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
describeKernelTools(names: readonly string[]): Promise<KernelToolsDescribeResult> {
|
|
72
|
+
return this.#kernelTools.describe(names);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
invokeKernelTool(request: KernelToolsInvokeRequest, signal?: AbortSignal): Promise<unknown> {
|
|
76
|
+
return this.#kernelTools.invoke(request, signal);
|
|
77
|
+
}
|
|
78
|
+
|
|
55
79
|
async run(input: JavaScriptRunInput): Promise<ResultMessage> {
|
|
56
80
|
assertJavaScriptKernelOpen(this.#lifecycle, "run");
|
|
57
81
|
const promise = this.#runs.enqueue(input);
|
|
@@ -77,6 +101,7 @@ export class JavaScriptKernel {
|
|
|
77
101
|
async reset(): Promise<void> {
|
|
78
102
|
assertJavaScriptKernelOpen(this.#lifecycle, "reset");
|
|
79
103
|
await this.#terminate();
|
|
104
|
+
this.#clearToolCalls();
|
|
80
105
|
assertJavaScriptKernelOpen(this.#lifecycle, "reset");
|
|
81
106
|
await this.#ensureReady();
|
|
82
107
|
this.#startNext();
|
|
@@ -97,6 +122,8 @@ export class JavaScriptKernel {
|
|
|
97
122
|
this.#slot.postMessage({ type: "close" });
|
|
98
123
|
this.#lifecycle = "closing";
|
|
99
124
|
this.#runs.settleAll("JS kernel closed");
|
|
125
|
+
this.#kernelTools.rejectAll(kernelToolError("kernel_tool_stale", "JS kernel closed"));
|
|
126
|
+
this.#clearToolCalls();
|
|
100
127
|
const recovery = this.#recovery;
|
|
101
128
|
const closePromise = (async () => {
|
|
102
129
|
if (recovery) await recovery;
|
|
@@ -140,6 +167,11 @@ export class JavaScriptKernel {
|
|
|
140
167
|
if (next.input.timeoutMs) {
|
|
141
168
|
this.#timeout = setTimeout(() => void this.#timeoutActive(next), next.input.timeoutMs);
|
|
142
169
|
}
|
|
170
|
+
this.#slot.postMessage({
|
|
171
|
+
type: "kernel-tools-names",
|
|
172
|
+
hostToolNames: resolveKernelToolNameSource(this.#options.hostToolNames),
|
|
173
|
+
foreignLanguageNames: resolveKernelToolNameSource(this.#options.foreignLanguageNames),
|
|
174
|
+
});
|
|
143
175
|
this.#slot.postMessage({
|
|
144
176
|
type: "run",
|
|
145
177
|
cellId: next.input.cellId,
|
|
@@ -170,15 +202,19 @@ export class JavaScriptKernel {
|
|
|
170
202
|
message: string,
|
|
171
203
|
durationMs = 0,
|
|
172
204
|
): Promise<{ readonly retained: boolean; readonly note?: string }> {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
205
|
+
try {
|
|
206
|
+
run.interruptResult = { type: "result", cellId: run.input.cellId, ok: false, error: { message }, durationMs };
|
|
207
|
+
run.interruptAck ??= Promise.withResolvers<void>();
|
|
208
|
+
this.#slot.postMessage({ type: "interrupt", reason });
|
|
209
|
+
if ((await awaitCooperativeSettlement(run)) === "settled") return { retained: run.settledByWorker };
|
|
210
|
+
if (!this.#runs.releaseActive(run)) return { retained: run.settledByWorker };
|
|
211
|
+
const retirement = await this.#terminate();
|
|
212
|
+
this.#runs.settle(run, run.interruptResult ?? stoppedResult(run.input.cellId, message));
|
|
213
|
+
void this.#recover(() => Promise.resolve());
|
|
214
|
+
return retirement === "abandoned" ? { retained: false, note: abandonedWorkerNote() } : { retained: false };
|
|
215
|
+
} finally {
|
|
216
|
+
this.#clearToolCalls();
|
|
217
|
+
}
|
|
182
218
|
}
|
|
183
219
|
|
|
184
220
|
async #restartAfterStop(): Promise<void> {
|
|
@@ -210,6 +246,7 @@ export class JavaScriptKernel {
|
|
|
210
246
|
}
|
|
211
247
|
|
|
212
248
|
#handleMessage(message: KernelToHostMessage): void {
|
|
249
|
+
if (this.#kernelTools.consume(message) && message.type !== "tool-call") return;
|
|
213
250
|
if (message.type === "status" && message.event.op === INTERRUPT_ACK_OP) {
|
|
214
251
|
this.#runs.active?.interruptAck?.resolve();
|
|
215
252
|
return;
|
|
@@ -223,7 +260,10 @@ export class JavaScriptKernel {
|
|
|
223
260
|
if (message.type === "tool-call") {
|
|
224
261
|
const waiter = this.#toolWaiters.shift();
|
|
225
262
|
if (waiter) waiter(message);
|
|
226
|
-
else
|
|
263
|
+
else {
|
|
264
|
+
this.#pendingToolCalls.push(message);
|
|
265
|
+
if (this.#pendingToolCalls.length > MAX_PENDING_TOOL_CALLS) this.#pendingToolCalls.shift();
|
|
266
|
+
}
|
|
227
267
|
return;
|
|
228
268
|
}
|
|
229
269
|
if (message.type !== "result") return;
|
|
@@ -240,6 +280,7 @@ export class JavaScriptKernel {
|
|
|
240
280
|
const active = this.#runs.active;
|
|
241
281
|
if (!active && this.#slot.startingUp) return;
|
|
242
282
|
this.#clearTimeout();
|
|
283
|
+
this.#kernelTools.rejectAll(kernelToolError("kernel_tool_stale", error.message));
|
|
243
284
|
if (active) {
|
|
244
285
|
this.#runs.releaseActive(active);
|
|
245
286
|
this.#runs.settle(active, {
|
|
@@ -250,9 +291,15 @@ export class JavaScriptKernel {
|
|
|
250
291
|
durationMs: this.#runs.durationMs(active, performance.now()),
|
|
251
292
|
});
|
|
252
293
|
}
|
|
294
|
+
this.#clearToolCalls();
|
|
253
295
|
void this.#restartAfterStop();
|
|
254
296
|
}
|
|
255
297
|
|
|
298
|
+
#clearToolCalls(): void {
|
|
299
|
+
this.#pendingToolCalls.length = 0;
|
|
300
|
+
this.#toolWaiters.length = 0;
|
|
301
|
+
}
|
|
302
|
+
|
|
256
303
|
#clearTimeout(): void {
|
|
257
304
|
if (this.#timeout) clearTimeout(this.#timeout);
|
|
258
305
|
this.#timeout = null;
|
|
@@ -271,6 +318,7 @@ export class JavaScriptKernel {
|
|
|
271
318
|
*/
|
|
272
319
|
async #terminate(): Promise<WorkerRetirement> {
|
|
273
320
|
this.#clearTimeout();
|
|
321
|
+
this.#kernelTools.rejectAll(kernelToolError("kernel_tool_stale", "JavaScript worker reset"));
|
|
274
322
|
const retirement = await this.#slot.retire();
|
|
275
323
|
await this.#retireWorkerChildren();
|
|
276
324
|
return retirement;
|