@armadra/agent 0.0.0-stage → 0.2.1
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 +22 -0
- package/LICENSE +21 -0
- package/README.md +426 -2
- package/dist/agent/agent.d.ts +56 -0
- package/dist/agent/agent.js +129 -0
- package/dist/agent/loop.d.ts +51 -0
- package/dist/agent/loop.js +158 -0
- package/dist/agent/queue.d.ts +25 -0
- package/dist/agent/queue.js +55 -0
- package/dist/agent/retry.d.ts +22 -0
- package/dist/agent/retry.js +92 -0
- package/dist/agent/schema.d.ts +27 -0
- package/dist/agent/schema.js +186 -0
- package/dist/agent/session-cache.d.ts +100 -0
- package/dist/agent/session-cache.js +511 -0
- package/dist/agent/session-compaction.d.ts +49 -0
- package/dist/agent/session-compaction.js +251 -0
- package/dist/agent/session-core.d.ts +103 -0
- package/dist/agent/session-core.js +7 -0
- package/dist/agent/session-run.d.ts +55 -0
- package/dist/agent/session-run.js +246 -0
- package/dist/agent/session-state.d.ts +39 -0
- package/dist/agent/session-state.js +118 -0
- package/dist/agent/session-subagent.d.ts +54 -0
- package/dist/agent/session-subagent.js +157 -0
- package/dist/agent/session-sync.d.ts +20 -0
- package/dist/agent/session-sync.js +104 -0
- package/dist/agent/session-tools.d.ts +24 -0
- package/dist/agent/session-tools.js +277 -0
- package/dist/agent/session.d.ts +117 -0
- package/dist/agent/session.js +484 -0
- package/dist/agent/system-prompt.d.ts +49 -0
- package/dist/agent/system-prompt.js +121 -0
- package/dist/agent/tool-runner.d.ts +64 -0
- package/dist/agent/tool-runner.js +309 -0
- package/dist/agent/transform.d.ts +26 -0
- package/dist/agent/transform.js +171 -0
- package/dist/agent/types.d.ts +378 -0
- package/dist/agent/types.js +21 -0
- package/dist/ai/apis/anthropic-messages.d.ts +26 -0
- package/dist/ai/apis/anthropic-messages.js +214 -0
- package/dist/ai/apis/anthropic-request.d.ts +42 -0
- package/dist/ai/apis/anthropic-request.js +282 -0
- package/dist/ai/apis/api.d.ts +28 -0
- package/dist/ai/apis/api.js +102 -0
- package/dist/ai/apis/cache-params.d.ts +48 -0
- package/dist/ai/apis/cache-params.js +163 -0
- package/dist/ai/apis/google-generative-ai.d.ts +32 -0
- package/dist/ai/apis/google-generative-ai.js +249 -0
- package/dist/ai/apis/google-request.d.ts +40 -0
- package/dist/ai/apis/google-request.js +255 -0
- package/dist/ai/apis/openai-compat.d.ts +35 -0
- package/dist/ai/apis/openai-compat.js +142 -0
- package/dist/ai/apis/openai-completions.d.ts +28 -0
- package/dist/ai/apis/openai-completions.js +246 -0
- package/dist/ai/apis/openai-request.d.ts +35 -0
- package/dist/ai/apis/openai-request.js +303 -0
- package/dist/ai/apis/openai-responses-request.d.ts +52 -0
- package/dist/ai/apis/openai-responses-request.js +266 -0
- package/dist/ai/apis/openai-responses.d.ts +28 -0
- package/dist/ai/apis/openai-responses.js +303 -0
- package/dist/ai/apis/shared.d.ts +56 -0
- package/dist/ai/apis/shared.js +179 -0
- package/dist/ai/cache/economics.d.ts +24 -0
- package/dist/ai/cache/economics.js +69 -0
- package/dist/ai/cache/fingerprint.d.ts +17 -0
- package/dist/ai/cache/fingerprint.js +39 -0
- package/dist/ai/cache/miss.d.ts +49 -0
- package/dist/ai/cache/miss.js +96 -0
- package/dist/ai/cache/reporting.d.ts +39 -0
- package/dist/ai/cache/reporting.js +102 -0
- package/dist/ai/cache/types.d.ts +94 -0
- package/dist/ai/cache/types.js +19 -0
- package/dist/ai/cache/warmer.d.ts +94 -0
- package/dist/ai/cache/warmer.js +224 -0
- package/dist/ai/context.d.ts +35 -0
- package/dist/ai/context.js +114 -0
- package/dist/ai/cost.d.ts +21 -0
- package/dist/ai/cost.js +60 -0
- package/dist/ai/event-stream.d.ts +33 -0
- package/dist/ai/event-stream.js +101 -0
- package/dist/ai/fake/fake-provider.d.ts +68 -0
- package/dist/ai/fake/fake-provider.js +278 -0
- package/dist/ai/fake/fake-script.d.ts +69 -0
- package/dist/ai/fake/fake-script.js +142 -0
- package/dist/ai/http.d.ts +54 -0
- package/dist/ai/http.js +212 -0
- package/dist/ai/json-partial.d.ts +17 -0
- package/dist/ai/json-partial.js +243 -0
- package/dist/ai/overflow.d.ts +22 -0
- package/dist/ai/overflow.js +61 -0
- package/dist/ai/providers/auth.d.ts +71 -0
- package/dist/ai/providers/auth.js +202 -0
- package/dist/ai/providers/builtin.d.ts +23 -0
- package/dist/ai/providers/builtin.js +148 -0
- package/dist/ai/providers/catalog-data.d.ts +5 -0
- package/dist/ai/providers/catalog-data.js +19 -0
- package/dist/ai/providers/catalog.d.ts +28 -0
- package/dist/ai/providers/catalog.js +166 -0
- package/dist/ai/providers/registry.d.ts +83 -0
- package/dist/ai/providers/registry.js +305 -0
- package/dist/ai/sse.d.ts +32 -0
- package/dist/ai/sse.js +140 -0
- package/dist/ai/thinking.d.ts +42 -0
- package/dist/ai/thinking.js +101 -0
- package/dist/ai/types.d.ts +428 -0
- package/dist/ai/types.js +19 -0
- package/dist/bundle/ama-sandbox.cjs +324 -0
- package/dist/bundle/ama.cjs +25770 -0
- package/dist/bundle.d.ts +9 -0
- package/dist/bundle.js +15 -0
- package/dist/cli/args.d.ts +82 -0
- package/dist/cli/args.js +381 -0
- package/dist/cli/bootstrap.d.ts +18 -0
- package/dist/cli/bootstrap.js +398 -0
- package/dist/cli/compose-providers.d.ts +37 -0
- package/dist/cli/compose-providers.js +104 -0
- package/dist/cli/compose-session.d.ts +69 -0
- package/dist/cli/compose-session.js +370 -0
- package/dist/cli/compose-store.d.ts +24 -0
- package/dist/cli/compose-store.js +125 -0
- package/dist/cli/compose.d.ts +106 -0
- package/dist/cli/compose.js +206 -0
- package/dist/cli/default-model.d.ts +16 -0
- package/dist/cli/default-model.js +24 -0
- package/dist/cli/deps.d.ts +205 -0
- package/dist/cli/deps.js +12 -0
- package/dist/cli/exit-codes.d.ts +29 -0
- package/dist/cli/exit-codes.js +40 -0
- package/dist/cli/main.d.ts +26 -0
- package/dist/cli/main.js +165 -0
- package/dist/cli/runtime.d.ts +90 -0
- package/dist/cli/runtime.js +8 -0
- package/dist/cli/startup-screen.d.ts +15 -0
- package/dist/cli/startup-screen.js +56 -0
- package/dist/cli/startup-steps.d.ts +35 -0
- package/dist/cli/startup-steps.js +197 -0
- package/dist/cli/subcommands/auth.d.ts +12 -0
- package/dist/cli/subcommands/auth.js +91 -0
- package/dist/cli/subcommands/config.d.ts +44 -0
- package/dist/cli/subcommands/config.js +201 -0
- package/dist/cli/subcommands/context.d.ts +21 -0
- package/dist/cli/subcommands/context.js +41 -0
- package/dist/cli/subcommands/doctor.d.ts +11 -0
- package/dist/cli/subcommands/doctor.js +246 -0
- package/dist/cli/subcommands/models-cache-probe.d.ts +34 -0
- package/dist/cli/subcommands/models-cache-probe.js +190 -0
- package/dist/cli/subcommands/models-discover.d.ts +40 -0
- package/dist/cli/subcommands/models-discover.js +204 -0
- package/dist/cli/subcommands/models.d.ts +40 -0
- package/dist/cli/subcommands/models.js +144 -0
- package/dist/cli/subcommands/sessions.d.ts +11 -0
- package/dist/cli/subcommands/sessions.js +100 -0
- package/dist/codemode/capability.d.ts +37 -0
- package/dist/codemode/capability.js +55 -0
- package/dist/codemode/declarations.d.ts +44 -0
- package/dist/codemode/declarations.js +111 -0
- package/dist/codemode/host-side.d.ts +59 -0
- package/dist/codemode/host-side.js +253 -0
- package/dist/codemode/modes.d.ts +23 -0
- package/dist/codemode/modes.js +40 -0
- package/dist/codemode/protocol.d.ts +91 -0
- package/dist/codemode/protocol.js +114 -0
- package/dist/codemode/sandbox-entry.d.ts +25 -0
- package/dist/codemode/sandbox-entry.js +323 -0
- package/dist/codemode/store.d.ts +26 -0
- package/dist/codemode/store.js +53 -0
- package/dist/codemode/tool.d.ts +105 -0
- package/dist/codemode/tool.js +283 -0
- package/dist/compaction/branch-summary.d.ts +30 -0
- package/dist/compaction/branch-summary.js +79 -0
- package/dist/compaction/breaker.d.ts +32 -0
- package/dist/compaction/breaker.js +65 -0
- package/dist/compaction/cut-point.d.ts +23 -0
- package/dist/compaction/cut-point.js +70 -0
- package/dist/compaction/estimate.d.ts +30 -0
- package/dist/compaction/estimate.js +114 -0
- package/dist/compaction/prune-tier.d.ts +31 -0
- package/dist/compaction/prune-tier.js +83 -0
- package/dist/compaction/serialize.d.ts +20 -0
- package/dist/compaction/serialize.js +115 -0
- package/dist/compaction/summarize-tier.d.ts +111 -0
- package/dist/compaction/summarize-tier.js +305 -0
- package/dist/config/auth-file.d.ts +39 -0
- package/dist/config/auth-file.js +92 -0
- package/dist/config/context-files.d.ts +30 -0
- package/dist/config/context-files.js +77 -0
- package/dist/config/load.d.ts +45 -0
- package/dist/config/load.js +97 -0
- package/dist/config/merge.d.ts +73 -0
- package/dist/config/merge.js +248 -0
- package/dist/config/paths.d.ts +38 -0
- package/dist/config/paths.js +108 -0
- package/dist/config/profile.d.ts +27 -0
- package/dist/config/profile.js +65 -0
- package/dist/config/schema.d.ts +34 -0
- package/dist/config/schema.js +413 -0
- package/dist/config/trust.d.ts +42 -0
- package/dist/config/trust.js +122 -0
- package/dist/config/types.d.ts +172 -0
- package/dist/config/types.js +27 -0
- package/dist/config/write.d.ts +10 -0
- package/dist/config/write.js +25 -0
- package/dist/errors.d.ts +28 -0
- package/dist/errors.js +30 -0
- package/dist/hooks/config.d.ts +49 -0
- package/dist/hooks/config.js +102 -0
- package/dist/hooks/dispatcher.d.ts +41 -0
- package/dist/hooks/dispatcher.js +104 -0
- package/dist/hooks/matcher.d.ts +22 -0
- package/dist/hooks/matcher.js +108 -0
- package/dist/hooks/protocol.d.ts +33 -0
- package/dist/hooks/protocol.js +240 -0
- package/dist/hooks/runner.d.ts +30 -0
- package/dist/hooks/runner.js +143 -0
- package/dist/hooks/types.d.ts +147 -0
- package/dist/hooks/types.js +20 -0
- package/dist/host/api-impl.d.ts +75 -0
- package/dist/host/api-impl.js +173 -0
- package/dist/host/loader.d.ts +31 -0
- package/dist/host/loader.js +147 -0
- package/dist/host/types.d.ts +169 -0
- package/dist/host/types.js +14 -0
- package/dist/host.d.ts +5 -0
- package/dist/host.js +4 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +28 -0
- package/dist/modes/commands-core.d.ts +44 -0
- package/dist/modes/commands-core.js +171 -0
- package/dist/modes/interactive/approval-dialog.d.ts +48 -0
- package/dist/modes/interactive/approval-dialog.js +202 -0
- package/dist/modes/interactive/commands.d.ts +41 -0
- package/dist/modes/interactive/commands.js +202 -0
- package/dist/modes/interactive/completion.d.ts +53 -0
- package/dist/modes/interactive/completion.js +157 -0
- package/dist/modes/interactive/interactive-mode.d.ts +46 -0
- package/dist/modes/interactive/interactive-mode.js +487 -0
- package/dist/modes/interactive/key-dispatch.d.ts +29 -0
- package/dist/modes/interactive/key-dispatch.js +107 -0
- package/dist/modes/interactive/line/line-editor.d.ts +50 -0
- package/dist/modes/interactive/line/line-editor.js +232 -0
- package/dist/modes/interactive/line/line-mode.d.ts +20 -0
- package/dist/modes/interactive/line/line-mode.js +216 -0
- package/dist/modes/interactive/line/line-render.d.ts +35 -0
- package/dist/modes/interactive/line/line-render.js +171 -0
- package/dist/modes/interactive/line/paste-state.d.ts +26 -0
- package/dist/modes/interactive/line/paste-state.js +72 -0
- package/dist/modes/interactive/message-view.d.ts +77 -0
- package/dist/modes/interactive/message-view.js +269 -0
- package/dist/modes/interactive/pickers.d.ts +42 -0
- package/dist/modes/interactive/pickers.js +115 -0
- package/dist/modes/interactive/startup-ui.d.ts +28 -0
- package/dist/modes/interactive/startup-ui.js +238 -0
- package/dist/modes/interactive/status-bar.d.ts +48 -0
- package/dist/modes/interactive/status-bar.js +139 -0
- package/dist/modes/interactive/tool-view.d.ts +95 -0
- package/dist/modes/interactive/tool-view.js +344 -0
- package/dist/modes/print/json-event.d.ts +15 -0
- package/dist/modes/print/json-event.js +39 -0
- package/dist/modes/print/print-mode.d.ts +15 -0
- package/dist/modes/print/print-mode.js +96 -0
- package/dist/modes/rpc/commands.d.ts +40 -0
- package/dist/modes/rpc/commands.js +258 -0
- package/dist/modes/rpc/jsonl.d.ts +16 -0
- package/dist/modes/rpc/jsonl.js +55 -0
- package/dist/modes/rpc/rpc-mode.d.ts +22 -0
- package/dist/modes/rpc/rpc-mode.js +153 -0
- package/dist/modes/session-report.d.ts +55 -0
- package/dist/modes/session-report.js +278 -0
- package/dist/modes/shared.d.ts +13 -0
- package/dist/modes/shared.js +52 -0
- package/dist/modes/startup-ui-text.d.ts +16 -0
- package/dist/modes/startup-ui-text.js +103 -0
- package/dist/permissions/broker.d.ts +47 -0
- package/dist/permissions/broker.js +107 -0
- package/dist/permissions/dangerous.d.ts +47 -0
- package/dist/permissions/dangerous.js +370 -0
- package/dist/permissions/pipeline.d.ts +51 -0
- package/dist/permissions/pipeline.js +181 -0
- package/dist/permissions/preview.d.ts +43 -0
- package/dist/permissions/preview.js +463 -0
- package/dist/permissions/rules.d.ts +58 -0
- package/dist/permissions/rules.js +249 -0
- package/dist/permissions/types.d.ts +100 -0
- package/dist/permissions/types.js +18 -0
- package/dist/rpc.d.ts +171 -0
- package/dist/rpc.js +13 -0
- package/dist/sdk.d.ts +98 -0
- package/dist/sdk.js +195 -0
- package/dist/session/manager.d.ts +70 -0
- package/dist/session/manager.js +278 -0
- package/dist/session/migrate.d.ts +19 -0
- package/dist/session/migrate.js +53 -0
- package/dist/session/projection.d.ts +50 -0
- package/dist/session/projection.js +180 -0
- package/dist/session/store.d.ts +47 -0
- package/dist/session/store.js +210 -0
- package/dist/session/tree.d.ts +21 -0
- package/dist/session/tree.js +88 -0
- package/dist/session/types.d.ts +199 -0
- package/dist/session/types.js +18 -0
- package/dist/skills/discover.d.ts +60 -0
- package/dist/skills/discover.js +160 -0
- package/dist/skills/expand.d.ts +35 -0
- package/dist/skills/expand.js +46 -0
- package/dist/skills/frontmatter.d.ts +27 -0
- package/dist/skills/frontmatter.js +157 -0
- package/dist/skills/index-prompt.d.ts +13 -0
- package/dist/skills/index-prompt.js +42 -0
- package/dist/skills/templates.d.ts +52 -0
- package/dist/skills/templates.js +177 -0
- package/dist/tools/bash.d.ts +49 -0
- package/dist/tools/bash.js +222 -0
- package/dist/tools/edit-fuzzy.d.ts +36 -0
- package/dist/tools/edit-fuzzy.js +129 -0
- package/dist/tools/edit.d.ts +38 -0
- package/dist/tools/edit.js +241 -0
- package/dist/tools/file-mutex.d.ts +8 -0
- package/dist/tools/file-mutex.js +29 -0
- package/dist/tools/glob.d.ts +44 -0
- package/dist/tools/glob.js +217 -0
- package/dist/tools/grep.d.ts +39 -0
- package/dist/tools/grep.js +179 -0
- package/dist/tools/ignore.d.ts +40 -0
- package/dist/tools/ignore.js +174 -0
- package/dist/tools/ls.d.ts +21 -0
- package/dist/tools/ls.js +96 -0
- package/dist/tools/output-accumulator.d.ts +48 -0
- package/dist/tools/output-accumulator.js +106 -0
- package/dist/tools/paths.d.ts +20 -0
- package/dist/tools/paths.js +58 -0
- package/dist/tools/presets.d.ts +71 -0
- package/dist/tools/presets.js +128 -0
- package/dist/tools/process-tree.d.ts +35 -0
- package/dist/tools/process-tree.js +125 -0
- package/dist/tools/read.d.ts +28 -0
- package/dist/tools/read.js +181 -0
- package/dist/tools/registry.d.ts +43 -0
- package/dist/tools/registry.js +106 -0
- package/dist/tools/shell.d.ts +38 -0
- package/dist/tools/shell.js +91 -0
- package/dist/tools/task.d.ts +29 -0
- package/dist/tools/task.js +107 -0
- package/dist/tools/todo.d.ts +28 -0
- package/dist/tools/todo.js +107 -0
- package/dist/tools/truncate.d.ts +44 -0
- package/dist/tools/truncate.js +175 -0
- package/dist/tools/types.d.ts +135 -0
- package/dist/tools/types.js +19 -0
- package/dist/tools/write.d.ts +20 -0
- package/dist/tools/write.js +84 -0
- package/dist/tui/ansi.d.ts +29 -0
- package/dist/tui/ansi.js +420 -0
- package/dist/tui/component.d.ts +38 -0
- package/dist/tui/component.js +13 -0
- package/dist/tui/components/box.d.ts +26 -0
- package/dist/tui/components/box.js +65 -0
- package/dist/tui/components/container.d.ts +14 -0
- package/dist/tui/components/container.js +39 -0
- package/dist/tui/components/editor-buffer.d.ts +67 -0
- package/dist/tui/components/editor-buffer.js +300 -0
- package/dist/tui/components/editor-paste.d.ts +29 -0
- package/dist/tui/components/editor-paste.js +64 -0
- package/dist/tui/components/editor.d.ts +114 -0
- package/dist/tui/components/editor.js +530 -0
- package/dist/tui/components/key-value.d.ts +29 -0
- package/dist/tui/components/key-value.js +48 -0
- package/dist/tui/components/loader.d.ts +35 -0
- package/dist/tui/components/loader.js +71 -0
- package/dist/tui/components/markdown.d.ts +90 -0
- package/dist/tui/components/markdown.js +462 -0
- package/dist/tui/components/meter.d.ts +29 -0
- package/dist/tui/components/meter.js +47 -0
- package/dist/tui/components/overlay.d.ts +24 -0
- package/dist/tui/components/overlay.js +52 -0
- package/dist/tui/components/select-list.d.ts +57 -0
- package/dist/tui/components/select-list.js +179 -0
- package/dist/tui/components/spacer.d.ts +11 -0
- package/dist/tui/components/spacer.js +16 -0
- package/dist/tui/components/text.d.ts +30 -0
- package/dist/tui/components/text.js +65 -0
- package/dist/tui/keybindings.d.ts +66 -0
- package/dist/tui/keybindings.js +120 -0
- package/dist/tui/keys.d.ts +38 -0
- package/dist/tui/keys.js +275 -0
- package/dist/tui/stdin-buffer.d.ts +41 -0
- package/dist/tui/stdin-buffer.js +206 -0
- package/dist/tui/terminal.d.ts +79 -0
- package/dist/tui/terminal.js +153 -0
- package/dist/tui/theme.d.ts +31 -0
- package/dist/tui/theme.js +199 -0
- package/dist/tui/tui.d.ts +103 -0
- package/dist/tui/tui.js +363 -0
- package/dist/tui/vt-screen.d.ts +46 -0
- package/dist/tui/vt-screen.js +251 -0
- package/dist/tui.d.ts +30 -0
- package/dist/tui.js +31 -0
- package/dist/version.d.ts +8 -0
- package/dist/version.js +24 -0
- package/docs/codemode.md +102 -0
- package/docs/hooks.md +196 -0
- package/docs/host-api.md +161 -0
- package/docs/providers.md +316 -0
- package/docs/rpc.md +253 -0
- package/docs/session-format.md +119 -0
- package/docs/tui.md +168 -0
- package/package.json +101 -4
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* codemode 三种模式(设计 §5.5 模式表、§5.6;实施计划 §9 修正)。[B10]
|
|
3
|
+
*
|
|
4
|
+
* | 模式 | 模型看到的工具 |
|
|
5
|
+
* | ------ | -------------------------------------------------------------------------- |
|
|
6
|
+
* | `off` | 不注册 codemode(工厂返回 undefined) |
|
|
7
|
+
* | `on` | 预设的工具 + codemode;其它工具描述末尾加一行「也可在 codemode 脚本里调用」 |
|
|
8
|
+
* | `only` | 只有 codemode(宿主 / SDK 工具也不直接暴露);其它工具的声明列在它的描述里 |
|
|
9
|
+
*
|
|
10
|
+
* 活动集由 B6 的 `resolvePreset` 决定(`only` → `["codemode"]`);这里补两件事:`on` 模式下的描述
|
|
11
|
+
* 追加(在注册前包一层,描述在会话开始时就确定、字节稳定)与 `only` 模式的独占活动集
|
|
12
|
+
* (`PresetToolRegistry.setPresetTools(names, { exclusive: true })`)。
|
|
13
|
+
*/
|
|
14
|
+
import type { CodemodeMode } from "../config/types.js";
|
|
15
|
+
import { type PresetResolution, type PresetToolRegistry } from "../tools/presets.js";
|
|
16
|
+
import type { ToolDefinition } from "../tools/types.js";
|
|
17
|
+
export declare function codemodeHint(name: string): string;
|
|
18
|
+
/** 复制工具(保留 getter 与方法),描述末尾追加一行 codemode 提示。 */
|
|
19
|
+
export declare function withCodemodeHint<I>(tool: ToolDefinition<I>): ToolDefinition<I>;
|
|
20
|
+
/** 注册前的包装:`on` 模式给 codemode 以外的工具加提示,其它模式原样返回。 */
|
|
21
|
+
export declare function decorateForMode(mode: CodemodeMode): <I>(tool: ToolDefinition<I>) => ToolDefinition<I>;
|
|
22
|
+
/** 把预设结果落到注册表:`only` 时活动集独占(宿主 / SDK 工具也只能在脚本里调用)。 */
|
|
23
|
+
export declare function applyCodemodeMode(registry: PresetToolRegistry, resolution: Pick<PresetResolution, "builtin" | "codemode">): void;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* codemode 三种模式(设计 §5.5 模式表、§5.6;实施计划 §9 修正)。[B10]
|
|
3
|
+
*
|
|
4
|
+
* | 模式 | 模型看到的工具 |
|
|
5
|
+
* | ------ | -------------------------------------------------------------------------- |
|
|
6
|
+
* | `off` | 不注册 codemode(工厂返回 undefined) |
|
|
7
|
+
* | `on` | 预设的工具 + codemode;其它工具描述末尾加一行「也可在 codemode 脚本里调用」 |
|
|
8
|
+
* | `only` | 只有 codemode(宿主 / SDK 工具也不直接暴露);其它工具的声明列在它的描述里 |
|
|
9
|
+
*
|
|
10
|
+
* 活动集由 B6 的 `resolvePreset` 决定(`only` → `["codemode"]`);这里补两件事:`on` 模式下的描述
|
|
11
|
+
* 追加(在注册前包一层,描述在会话开始时就确定、字节稳定)与 `only` 模式的独占活动集
|
|
12
|
+
* (`PresetToolRegistry.setPresetTools(names, { exclusive: true })`)。
|
|
13
|
+
*/
|
|
14
|
+
import { CODEMODE_TOOL } from "../tools/presets.js";
|
|
15
|
+
export function codemodeHint(name) {
|
|
16
|
+
return `Also callable inside codemode scripts as tools.${name}(args).`;
|
|
17
|
+
}
|
|
18
|
+
/** 复制工具(保留 getter 与方法),描述末尾追加一行 codemode 提示。 */
|
|
19
|
+
export function withCodemodeHint(tool) {
|
|
20
|
+
const copy = Object.create(Object.getPrototypeOf(tool), {
|
|
21
|
+
...Object.getOwnPropertyDescriptors(tool),
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(copy, "description", {
|
|
24
|
+
value: `${tool.description}\n${codemodeHint(tool.name)}`,
|
|
25
|
+
enumerable: true,
|
|
26
|
+
writable: false,
|
|
27
|
+
configurable: true,
|
|
28
|
+
});
|
|
29
|
+
return copy;
|
|
30
|
+
}
|
|
31
|
+
/** 注册前的包装:`on` 模式给 codemode 以外的工具加提示,其它模式原样返回。 */
|
|
32
|
+
export function decorateForMode(mode) {
|
|
33
|
+
if (mode !== "on")
|
|
34
|
+
return (tool) => tool;
|
|
35
|
+
return (tool) => (tool.name === CODEMODE_TOOL ? tool : withCodemodeHint(tool));
|
|
36
|
+
}
|
|
37
|
+
/** 把预设结果落到注册表:`only` 时活动集独占(宿主 / SDK 工具也只能在脚本里调用)。 */
|
|
38
|
+
export function applyCodemodeMode(registry, resolution) {
|
|
39
|
+
registry.setPresetTools(resolution.builtin, { exclusive: resolution.codemode === "only" });
|
|
40
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* codemode 父子进程的 JSON 行协议(设计 §5.5、实施计划 §5.1)。[B10]
|
|
3
|
+
*
|
|
4
|
+
* 父 → 子(子进程 stdin,一行一个 JSON):
|
|
5
|
+
* - `run`:脚本、选项、可调用工具的声明、当前 store 快照;每个子进程只收一次;
|
|
6
|
+
* - `tool_result`:对某次 `tool_call` 的应答(`ok` 时带 `value`,否则带 `error` 文本);
|
|
7
|
+
* - `abort`:父进程要求结束(子进程拒绝所有未完成调用并以失败结束)。
|
|
8
|
+
*
|
|
9
|
+
* 子 → 父(子进程 stdout):
|
|
10
|
+
* - `tool_call`:脚本里的 `tools.<name>(input)`;
|
|
11
|
+
* - `output`:`text()` / `console.log()` / `return` 产出的一段输出;
|
|
12
|
+
* - `store`:脚本成功结束且写过 store 时的完整快照,父进程只在 `done.ok` 时提交;
|
|
13
|
+
* - `done`:脚本结束(成功或失败),带用时。
|
|
14
|
+
*
|
|
15
|
+
* 本文件只有纯函数与类型;子进程入口(sandbox-entry.ts)不 import 它的运行时代码(子进程只被
|
|
16
|
+
* 允许读自己的入口文件),只 import 类型。
|
|
17
|
+
*/
|
|
18
|
+
/** 脚本首行 `// @options: {...}` 可设的选项。 */
|
|
19
|
+
export interface ScriptOptions {
|
|
20
|
+
/** 输出上限(估算 token,字符 / 4);超出保留首尾,全文落盘。缺省 10 000。 */
|
|
21
|
+
maxOutputTokens: number;
|
|
22
|
+
/** 整个脚本的硬期限(毫秒);到时父进程杀掉子进程树。缺省 300 000。 */
|
|
23
|
+
timeoutMs: number;
|
|
24
|
+
}
|
|
25
|
+
export declare const DEFAULT_MAX_OUTPUT_TOKENS = 10000;
|
|
26
|
+
export declare const DEFAULT_SCRIPT_TIMEOUT_MS = 300000;
|
|
27
|
+
export declare const MAX_SCRIPT_TIMEOUT_MS = 3600000;
|
|
28
|
+
/** 父进程启动子进程时附加的参数(sandbox-entry.ts 见到它才启动)。 */
|
|
29
|
+
export declare const SANDBOX_MAIN_FLAG = "--ama-codemode-sandbox";
|
|
30
|
+
/** 同一脚本内并发工具调用上限。 */
|
|
31
|
+
export declare const MAX_CONCURRENT_TOOL_CALLS = 8;
|
|
32
|
+
export declare const DEFAULT_SCRIPT_OPTIONS: Readonly<ScriptOptions>;
|
|
33
|
+
/** 传给子进程的工具声明:名字 + TypeScript 声明(describeTool 返回它)。 */
|
|
34
|
+
export interface ToolDecl {
|
|
35
|
+
name: string;
|
|
36
|
+
declaration: string;
|
|
37
|
+
}
|
|
38
|
+
export type StoreSnapshot = Record<string, unknown>;
|
|
39
|
+
export type ParentMessage = {
|
|
40
|
+
type: "run";
|
|
41
|
+
script: string;
|
|
42
|
+
options: ScriptOptions;
|
|
43
|
+
tools: ToolDecl[];
|
|
44
|
+
store: StoreSnapshot;
|
|
45
|
+
} | {
|
|
46
|
+
type: "tool_result";
|
|
47
|
+
id: number;
|
|
48
|
+
ok: true;
|
|
49
|
+
value: unknown;
|
|
50
|
+
} | {
|
|
51
|
+
type: "tool_result";
|
|
52
|
+
id: number;
|
|
53
|
+
ok: false;
|
|
54
|
+
error: string;
|
|
55
|
+
} | {
|
|
56
|
+
type: "abort";
|
|
57
|
+
};
|
|
58
|
+
export type ChildMessage = {
|
|
59
|
+
type: "tool_call";
|
|
60
|
+
id: number;
|
|
61
|
+
name: string;
|
|
62
|
+
input: unknown;
|
|
63
|
+
} | {
|
|
64
|
+
type: "output";
|
|
65
|
+
text: string;
|
|
66
|
+
}
|
|
67
|
+
/** 脚本成功结束且写过 store 时发:结束时的完整快照(父进程整体替换)。 */
|
|
68
|
+
| {
|
|
69
|
+
type: "store";
|
|
70
|
+
entries: Record<string, unknown>;
|
|
71
|
+
} | {
|
|
72
|
+
type: "done";
|
|
73
|
+
ok: boolean;
|
|
74
|
+
error?: string;
|
|
75
|
+
elapsedMs: number;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* 解析脚本首行的 `// @options: {...}`。没有选项行 → 缺省值;JSON 非法、键未知或取值越界 →
|
|
79
|
+
* AmaError{code:"invalid_arguments"}(模型能从报错里改正)。
|
|
80
|
+
*/
|
|
81
|
+
export declare function parseOptionsLine(script: string): ScriptOptions;
|
|
82
|
+
/** 编码一行(含结尾 \n);U+2028 / U+2029 转义,保证按 \n 切行安全。 */
|
|
83
|
+
export declare function encodeLine(message: ParentMessage | ChildMessage): string;
|
|
84
|
+
export declare function decodeChildLine(line: string): ChildMessage;
|
|
85
|
+
export declare function decodeParentLine(line: string): ParentMessage;
|
|
86
|
+
/** 按 \n 切行的累加器(多字节 UTF-8 由调用方 setEncoding 处理)。 */
|
|
87
|
+
export declare class LineSplitter {
|
|
88
|
+
private buffer;
|
|
89
|
+
push(chunk: string): string[];
|
|
90
|
+
flush(): string[];
|
|
91
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* codemode 父子进程的 JSON 行协议(设计 §5.5、实施计划 §5.1)。[B10]
|
|
3
|
+
*
|
|
4
|
+
* 父 → 子(子进程 stdin,一行一个 JSON):
|
|
5
|
+
* - `run`:脚本、选项、可调用工具的声明、当前 store 快照;每个子进程只收一次;
|
|
6
|
+
* - `tool_result`:对某次 `tool_call` 的应答(`ok` 时带 `value`,否则带 `error` 文本);
|
|
7
|
+
* - `abort`:父进程要求结束(子进程拒绝所有未完成调用并以失败结束)。
|
|
8
|
+
*
|
|
9
|
+
* 子 → 父(子进程 stdout):
|
|
10
|
+
* - `tool_call`:脚本里的 `tools.<name>(input)`;
|
|
11
|
+
* - `output`:`text()` / `console.log()` / `return` 产出的一段输出;
|
|
12
|
+
* - `store`:脚本成功结束且写过 store 时的完整快照,父进程只在 `done.ok` 时提交;
|
|
13
|
+
* - `done`:脚本结束(成功或失败),带用时。
|
|
14
|
+
*
|
|
15
|
+
* 本文件只有纯函数与类型;子进程入口(sandbox-entry.ts)不 import 它的运行时代码(子进程只被
|
|
16
|
+
* 允许读自己的入口文件),只 import 类型。
|
|
17
|
+
*/
|
|
18
|
+
import { AmaError } from "../errors.js";
|
|
19
|
+
export const DEFAULT_MAX_OUTPUT_TOKENS = 10_000;
|
|
20
|
+
export const DEFAULT_SCRIPT_TIMEOUT_MS = 300_000;
|
|
21
|
+
export const MAX_SCRIPT_TIMEOUT_MS = 3_600_000;
|
|
22
|
+
/** 父进程启动子进程时附加的参数(sandbox-entry.ts 见到它才启动)。 */
|
|
23
|
+
export const SANDBOX_MAIN_FLAG = "--ama-codemode-sandbox";
|
|
24
|
+
/** 同一脚本内并发工具调用上限。 */
|
|
25
|
+
export const MAX_CONCURRENT_TOOL_CALLS = 8;
|
|
26
|
+
export const DEFAULT_SCRIPT_OPTIONS = Object.freeze({
|
|
27
|
+
maxOutputTokens: DEFAULT_MAX_OUTPUT_TOKENS,
|
|
28
|
+
timeoutMs: DEFAULT_SCRIPT_TIMEOUT_MS,
|
|
29
|
+
});
|
|
30
|
+
const OPTIONS_RE = /^\s*\/\/\s*@options:\s*(.*)$/;
|
|
31
|
+
/**
|
|
32
|
+
* 解析脚本首行的 `// @options: {...}`。没有选项行 → 缺省值;JSON 非法、键未知或取值越界 →
|
|
33
|
+
* AmaError{code:"invalid_arguments"}(模型能从报错里改正)。
|
|
34
|
+
*/
|
|
35
|
+
export function parseOptionsLine(script) {
|
|
36
|
+
const firstLine = script.split("\n", 1)[0] ?? "";
|
|
37
|
+
const match = OPTIONS_RE.exec(firstLine);
|
|
38
|
+
const options = { ...DEFAULT_SCRIPT_OPTIONS };
|
|
39
|
+
if (match === null)
|
|
40
|
+
return options;
|
|
41
|
+
let raw;
|
|
42
|
+
try {
|
|
43
|
+
raw = JSON.parse((match[1] ?? "").trim());
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
throw new AmaError("invalid_arguments", `Invalid // @options line: ${error.message}`);
|
|
47
|
+
}
|
|
48
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
49
|
+
throw new AmaError("invalid_arguments", "Invalid // @options line: expected a JSON object");
|
|
50
|
+
}
|
|
51
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
52
|
+
if (key === "max_output_tokens") {
|
|
53
|
+
options.maxOutputTokens = positiveInt(key, value, 1_000_000);
|
|
54
|
+
}
|
|
55
|
+
else if (key === "timeout_ms") {
|
|
56
|
+
options.timeoutMs = positiveInt(key, value, MAX_SCRIPT_TIMEOUT_MS);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
throw new AmaError("invalid_arguments", `Invalid // @options line: unknown option "${key}" (use max_output_tokens, timeout_ms)`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return options;
|
|
63
|
+
}
|
|
64
|
+
function positiveInt(key, value, max) {
|
|
65
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > max) {
|
|
66
|
+
throw new AmaError("invalid_arguments", `Invalid // @options line: ${key} must be an integer between 1 and ${max}`);
|
|
67
|
+
}
|
|
68
|
+
return value;
|
|
69
|
+
}
|
|
70
|
+
/** 编码一行(含结尾 \n);U+2028 / U+2029 转义,保证按 \n 切行安全。 */
|
|
71
|
+
export function encodeLine(message) {
|
|
72
|
+
return `${JSON.stringify(message)
|
|
73
|
+
.replace(/\u2028/g, "\\u2028")
|
|
74
|
+
.replace(/\u2029/g, "\\u2029")}\n`;
|
|
75
|
+
}
|
|
76
|
+
const CHILD_TYPES = new Set(["tool_call", "output", "store", "done"]);
|
|
77
|
+
const PARENT_TYPES = new Set(["run", "tool_result", "abort"]);
|
|
78
|
+
function decode(line, types) {
|
|
79
|
+
let value;
|
|
80
|
+
try {
|
|
81
|
+
value = JSON.parse(line);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
throw new AmaError("invalid_arguments", `codemode protocol: ${error.message}`);
|
|
85
|
+
}
|
|
86
|
+
if (typeof value !== "object" ||
|
|
87
|
+
value === null ||
|
|
88
|
+
typeof value.type !== "string" ||
|
|
89
|
+
!types.has(value.type)) {
|
|
90
|
+
throw new AmaError("invalid_arguments", "codemode protocol: unknown message");
|
|
91
|
+
}
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
export function decodeChildLine(line) {
|
|
95
|
+
return decode(line, CHILD_TYPES);
|
|
96
|
+
}
|
|
97
|
+
export function decodeParentLine(line) {
|
|
98
|
+
return decode(line, PARENT_TYPES);
|
|
99
|
+
}
|
|
100
|
+
/** 按 \n 切行的累加器(多字节 UTF-8 由调用方 setEncoding 处理)。 */
|
|
101
|
+
export class LineSplitter {
|
|
102
|
+
buffer = "";
|
|
103
|
+
push(chunk) {
|
|
104
|
+
this.buffer += chunk;
|
|
105
|
+
const lines = this.buffer.split("\n");
|
|
106
|
+
this.buffer = lines.pop() ?? "";
|
|
107
|
+
return lines.filter((line) => line.trim() !== "");
|
|
108
|
+
}
|
|
109
|
+
flush() {
|
|
110
|
+
const rest = this.buffer;
|
|
111
|
+
this.buffer = "";
|
|
112
|
+
return rest.trim() === "" ? [] : [rest];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* codemode 子进程入口(设计 §5.5 沙箱第 1–3 条;第二个 bundle 入口 dist/bundle/ama-sandbox.cjs)。[B10]
|
|
3
|
+
*
|
|
4
|
+
* 父进程以 `<node> --permission --allow-fs-read=<本文件> --disallow-code-generation-from-strings
|
|
5
|
+
* <本文件>`、空环境启动本文件。本文件**只能 import node: 内置模块与类型**:权限模型只允许读入口
|
|
6
|
+
* 文件本身,其它源文件读不到。
|
|
7
|
+
*
|
|
8
|
+
* - 用 `node:vm` 建一个只含 ECMAScript 内建对象的上下文(`codeGeneration: { strings: false,
|
|
9
|
+
* wasm: false }`),没有 require / process / fetch / console / 定时器;
|
|
10
|
+
* - 注入 `tools`(Proxy:已知工具 → 调用,`codemode` → 拒绝,其它名字 → 拒绝并列出可用工具)、
|
|
11
|
+
* `text / console.* / store / load / ALL_TOOLS / describeTool`;
|
|
12
|
+
* - 上下文里的函数都在上下文内定义(引导脚本),只经一个宿主函数 `host(kind, json)` 与本进程
|
|
13
|
+
* 交换 JSON 字符串;本进程主 realm 也禁止字符串生成代码,拿到宿主函数的 `constructor` 也无法
|
|
14
|
+
* `Function("return process")`;
|
|
15
|
+
* - 脚本包成 `(async () => { ... })()`,`return v` 等同 `text(v)`;并发工具调用 ≤ 8(引导脚本
|
|
16
|
+
* 内的信号量);结束(成功 / 失败 / abort)时拒绝未完成调用,发 `store`(仅成功)与 `done` 后退出。
|
|
17
|
+
*
|
|
18
|
+
* 行协议见 protocol.ts(这里只 import 类型)。
|
|
19
|
+
*/
|
|
20
|
+
/** 与 protocol.ts 的 MAX_CONCURRENT_TOOL_CALLS、store.ts 的上限一致(本文件不能 import 运行时代码)。 */
|
|
21
|
+
export declare const MAX_CONCURRENT = 8;
|
|
22
|
+
export declare const MAX_STORE_VALUE_CHARS = 262144;
|
|
23
|
+
export declare const MAX_STORE_TOTAL_CHARS = 1048576;
|
|
24
|
+
/** 错误 → 一行说明;带上脚本里的行号(若能从栈里找到)。 */
|
|
25
|
+
export declare function describeError(error: unknown): string;
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* codemode 子进程入口(设计 §5.5 沙箱第 1–3 条;第二个 bundle 入口 dist/bundle/ama-sandbox.cjs)。[B10]
|
|
3
|
+
*
|
|
4
|
+
* 父进程以 `<node> --permission --allow-fs-read=<本文件> --disallow-code-generation-from-strings
|
|
5
|
+
* <本文件>`、空环境启动本文件。本文件**只能 import node: 内置模块与类型**:权限模型只允许读入口
|
|
6
|
+
* 文件本身,其它源文件读不到。
|
|
7
|
+
*
|
|
8
|
+
* - 用 `node:vm` 建一个只含 ECMAScript 内建对象的上下文(`codeGeneration: { strings: false,
|
|
9
|
+
* wasm: false }`),没有 require / process / fetch / console / 定时器;
|
|
10
|
+
* - 注入 `tools`(Proxy:已知工具 → 调用,`codemode` → 拒绝,其它名字 → 拒绝并列出可用工具)、
|
|
11
|
+
* `text / console.* / store / load / ALL_TOOLS / describeTool`;
|
|
12
|
+
* - 上下文里的函数都在上下文内定义(引导脚本),只经一个宿主函数 `host(kind, json)` 与本进程
|
|
13
|
+
* 交换 JSON 字符串;本进程主 realm 也禁止字符串生成代码,拿到宿主函数的 `constructor` 也无法
|
|
14
|
+
* `Function("return process")`;
|
|
15
|
+
* - 脚本包成 `(async () => { ... })()`,`return v` 等同 `text(v)`;并发工具调用 ≤ 8(引导脚本
|
|
16
|
+
* 内的信号量);结束(成功 / 失败 / abort)时拒绝未完成调用,发 `store`(仅成功)与 `done` 后退出。
|
|
17
|
+
*
|
|
18
|
+
* 行协议见 protocol.ts(这里只 import 类型)。
|
|
19
|
+
*/
|
|
20
|
+
import { Script, createContext } from "node:vm";
|
|
21
|
+
/** 与 protocol.ts 的 MAX_CONCURRENT_TOOL_CALLS、store.ts 的上限一致(本文件不能 import 运行时代码)。 */
|
|
22
|
+
export const MAX_CONCURRENT = 8;
|
|
23
|
+
export const MAX_STORE_VALUE_CHARS = 262_144;
|
|
24
|
+
export const MAX_STORE_TOTAL_CHARS = 1_048_576;
|
|
25
|
+
const SCRIPT_FILENAME = "codemode-script.js";
|
|
26
|
+
/** 在 vm 上下文里执行的引导脚本:返回 `{ deliver, finish, run }`,全部是上下文 realm 的函数。 */
|
|
27
|
+
const BOOTSTRAP = String.raw `(function (host, config) {
|
|
28
|
+
"use strict";
|
|
29
|
+
const MAX = config.maxConcurrent;
|
|
30
|
+
const stringify = JSON.stringify;
|
|
31
|
+
const parse = JSON.parse;
|
|
32
|
+
const pending = new Map();
|
|
33
|
+
const waiting = [];
|
|
34
|
+
let nextId = 0;
|
|
35
|
+
let active = 0;
|
|
36
|
+
let closed = false;
|
|
37
|
+
const decls = parse(config.tools);
|
|
38
|
+
const declByName = new Map();
|
|
39
|
+
const callName = new Map();
|
|
40
|
+
for (const decl of decls) {
|
|
41
|
+
declByName.set(decl.name, decl.declaration);
|
|
42
|
+
callName.set(decl.name.replace(/[^A-Za-z0-9_$]/g, "_"), decl.name);
|
|
43
|
+
}
|
|
44
|
+
const names = Object.freeze(decls.map((d) => d.name));
|
|
45
|
+
|
|
46
|
+
function format(value) {
|
|
47
|
+
if (typeof value === "string") return value;
|
|
48
|
+
if (value === undefined) return "undefined";
|
|
49
|
+
if (value instanceof Error) return value.name + ": " + value.message;
|
|
50
|
+
try {
|
|
51
|
+
const json = stringify(value, null, 2);
|
|
52
|
+
return json === undefined ? String(value) : json;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
return String(value);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function text(...values) {
|
|
58
|
+
host("output", values.map(format).join(" "));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function callTool(name, input) {
|
|
62
|
+
if (closed) return Promise.reject(new Error("The script has already finished"));
|
|
63
|
+
let payload;
|
|
64
|
+
try {
|
|
65
|
+
payload = stringify({ name: name, input: input === undefined ? {} : input });
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return Promise.reject(new Error("tools." + name + ": arguments must be JSON-serializable"));
|
|
68
|
+
}
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
const start = () => {
|
|
71
|
+
if (closed) return reject(new Error("The script has already finished"));
|
|
72
|
+
const id = ++nextId;
|
|
73
|
+
active++;
|
|
74
|
+
pending.set(id, { resolve: resolve, reject: reject });
|
|
75
|
+
host("tool_call", stringify(id) + "\n" + payload);
|
|
76
|
+
};
|
|
77
|
+
if (active < MAX) start();
|
|
78
|
+
else waiting.push(start);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function deliver(id, ok, json) {
|
|
83
|
+
const entry = pending.get(id);
|
|
84
|
+
if (entry === undefined) return;
|
|
85
|
+
pending.delete(id);
|
|
86
|
+
active--;
|
|
87
|
+
const next = waiting.shift();
|
|
88
|
+
if (next !== undefined) next();
|
|
89
|
+
if (ok) entry.resolve(json === undefined ? undefined : parse(json));
|
|
90
|
+
else entry.reject(new Error(json));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const toolsTarget = Object.create(null);
|
|
94
|
+
const tools = new Proxy(toolsTarget, {
|
|
95
|
+
get(target, prop) {
|
|
96
|
+
if (typeof prop !== "string" || prop === "then") return undefined;
|
|
97
|
+
const real = callName.get(prop);
|
|
98
|
+
if (real !== undefined) return (args) => callTool(real, args);
|
|
99
|
+
if (prop === "codemode") {
|
|
100
|
+
return () => Promise.reject(new Error("codemode cannot be called from a codemode script"));
|
|
101
|
+
}
|
|
102
|
+
return () =>
|
|
103
|
+
Promise.reject(new Error("Unknown tool " + prop + ". Callable tools: " + names.join(", ")));
|
|
104
|
+
},
|
|
105
|
+
has(target, prop) {
|
|
106
|
+
return typeof prop === "string" && callName.has(prop);
|
|
107
|
+
},
|
|
108
|
+
ownKeys() {
|
|
109
|
+
return [...callName.keys()];
|
|
110
|
+
},
|
|
111
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
112
|
+
if (typeof prop !== "string" || !callName.has(prop)) return undefined;
|
|
113
|
+
return { configurable: true, enumerable: true, writable: false, value: tools[prop] };
|
|
114
|
+
},
|
|
115
|
+
set() {
|
|
116
|
+
return false;
|
|
117
|
+
},
|
|
118
|
+
defineProperty() {
|
|
119
|
+
return false;
|
|
120
|
+
},
|
|
121
|
+
deleteProperty() {
|
|
122
|
+
return false;
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const storeMap = new Map(Object.entries(parse(config.store)).map(([k, v]) => [k, stringify(v)]));
|
|
127
|
+
let storeChanged = false;
|
|
128
|
+
function store(key, value) {
|
|
129
|
+
if (typeof key !== "string" || key === "") throw new TypeError("store(key, value): key must be a non-empty string");
|
|
130
|
+
if (value === undefined) {
|
|
131
|
+
storeChanged = storeMap.delete(key) || storeChanged;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
let json;
|
|
135
|
+
try {
|
|
136
|
+
json = stringify(value);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
throw new TypeError("store(" + stringify(key) + "): value must be JSON-serializable");
|
|
139
|
+
}
|
|
140
|
+
if (json === undefined) throw new TypeError("store(" + stringify(key) + "): value must be JSON-serializable");
|
|
141
|
+
if (json.length > config.maxValueChars) {
|
|
142
|
+
throw new RangeError("store(" + stringify(key) + "): value is " + json.length + " characters of JSON (limit " + config.maxValueChars + ")");
|
|
143
|
+
}
|
|
144
|
+
let total = json.length;
|
|
145
|
+
for (const [k, v] of storeMap) if (k !== key) total += v.length;
|
|
146
|
+
if (total > config.maxTotalChars) {
|
|
147
|
+
throw new RangeError("store(" + stringify(key) + "): store would hold " + total + " characters of JSON (limit " + config.maxTotalChars + ")");
|
|
148
|
+
}
|
|
149
|
+
storeMap.set(key, json);
|
|
150
|
+
storeChanged = true;
|
|
151
|
+
}
|
|
152
|
+
function load(key) {
|
|
153
|
+
const json = storeMap.get(key);
|
|
154
|
+
return json === undefined ? undefined : parse(json);
|
|
155
|
+
}
|
|
156
|
+
function describeTool(name) {
|
|
157
|
+
return declByName.get(callName.get(name) ?? name);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const consoleObject = Object.freeze({
|
|
161
|
+
log: text, info: text, warn: text, error: text, debug: text,
|
|
162
|
+
});
|
|
163
|
+
const globals = {
|
|
164
|
+
tools: tools,
|
|
165
|
+
text: (value) => text(value),
|
|
166
|
+
console: consoleObject,
|
|
167
|
+
store: store,
|
|
168
|
+
load: load,
|
|
169
|
+
ALL_TOOLS: names,
|
|
170
|
+
describeTool: describeTool,
|
|
171
|
+
};
|
|
172
|
+
for (const [key, value] of Object.entries(globals)) {
|
|
173
|
+
Object.defineProperty(globalThis, key, { value: value, writable: false, enumerable: false, configurable: false });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function finish() {
|
|
177
|
+
closed = true;
|
|
178
|
+
waiting.length = 0;
|
|
179
|
+
for (const entry of pending.values()) entry.reject(new Error("The script has finished"));
|
|
180
|
+
pending.clear();
|
|
181
|
+
if (!storeChanged) return undefined;
|
|
182
|
+
const out = {};
|
|
183
|
+
for (const [k, v] of storeMap) out[k] = parse(v);
|
|
184
|
+
return stringify(out);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function run(main) {
|
|
188
|
+
const value = await main;
|
|
189
|
+
if (value !== undefined) text(value);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return { deliver: deliver, finish: finish, run: run };
|
|
193
|
+
})`;
|
|
194
|
+
function send(message) {
|
|
195
|
+
process.stdout.write(`${JSON.stringify(message)
|
|
196
|
+
.replace(/\u2028/g, "\\u2028")
|
|
197
|
+
.replace(/\u2029/g, "\\u2029")}\n`);
|
|
198
|
+
}
|
|
199
|
+
/** 错误 → 一行说明;带上脚本里的行号(若能从栈里找到)。 */
|
|
200
|
+
export function describeError(error) {
|
|
201
|
+
if (typeof error !== "object" || error === null)
|
|
202
|
+
return String(error);
|
|
203
|
+
const e = error;
|
|
204
|
+
const base = typeof e.message === "string"
|
|
205
|
+
? `${typeof e.name === "string" ? e.name : "Error"}: ${e.message}`
|
|
206
|
+
: String(error);
|
|
207
|
+
const stack = typeof e.stack === "string" ? e.stack : "";
|
|
208
|
+
const at = new RegExp(`${SCRIPT_FILENAME.replace(".", "\\.")}:(\\d+)(?::(\\d+))?`).exec(stack);
|
|
209
|
+
return at === null ? base : `${base} (line ${at[1]})`;
|
|
210
|
+
}
|
|
211
|
+
let started = false;
|
|
212
|
+
let finished = false;
|
|
213
|
+
let bridge;
|
|
214
|
+
let startedAt = 0;
|
|
215
|
+
function done(ok, error) {
|
|
216
|
+
if (finished)
|
|
217
|
+
return;
|
|
218
|
+
finished = true;
|
|
219
|
+
if (bridge !== undefined) {
|
|
220
|
+
const snapshot = bridge.finish();
|
|
221
|
+
if (ok && snapshot !== undefined) {
|
|
222
|
+
send({ type: "store", entries: JSON.parse(snapshot) });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const message = { type: "done", ok, elapsedMs: Date.now() - startedAt };
|
|
226
|
+
if (error !== undefined)
|
|
227
|
+
message.error = error;
|
|
228
|
+
send(message);
|
|
229
|
+
// 等 stdout 冲完再退出(macOS 上管道写是异步的)。
|
|
230
|
+
process.stdout.write("", () => process.exit(0));
|
|
231
|
+
}
|
|
232
|
+
function host(kind, payload) {
|
|
233
|
+
if (finished)
|
|
234
|
+
return;
|
|
235
|
+
if (kind === "output") {
|
|
236
|
+
send({ type: "output", text: payload });
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (kind === "tool_call") {
|
|
240
|
+
const newline = payload.indexOf("\n");
|
|
241
|
+
const id = Number(payload.slice(0, newline));
|
|
242
|
+
const call = JSON.parse(payload.slice(newline + 1));
|
|
243
|
+
send({ type: "tool_call", id, name: call.name, input: call.input });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function start(run) {
|
|
247
|
+
started = true;
|
|
248
|
+
startedAt = Date.now();
|
|
249
|
+
// 沙箱对象用空原型:上下文的全局属性查找不会落到本进程 realm 的 Object.prototype 上。
|
|
250
|
+
const context = createContext(Object.create(null), {
|
|
251
|
+
name: "codemode",
|
|
252
|
+
codeGeneration: { strings: false, wasm: false },
|
|
253
|
+
});
|
|
254
|
+
const config = {
|
|
255
|
+
maxConcurrent: MAX_CONCURRENT,
|
|
256
|
+
maxValueChars: MAX_STORE_VALUE_CHARS,
|
|
257
|
+
maxTotalChars: MAX_STORE_TOTAL_CHARS,
|
|
258
|
+
tools: JSON.stringify(run.tools),
|
|
259
|
+
store: JSON.stringify(run.store ?? {}),
|
|
260
|
+
};
|
|
261
|
+
let main;
|
|
262
|
+
try {
|
|
263
|
+
const factory = new Script(BOOTSTRAP, { filename: "codemode-bootstrap.js" }).runInContext(context);
|
|
264
|
+
bridge = factory(host, config);
|
|
265
|
+
main = new Script(`(async () => {\n${run.script}\n})()`, {
|
|
266
|
+
filename: SCRIPT_FILENAME,
|
|
267
|
+
lineOffset: -1,
|
|
268
|
+
}).runInContext(context);
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
done(false, describeError(error));
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
bridge.run(main).then(() => done(true), (error) => done(false, describeError(error)));
|
|
275
|
+
}
|
|
276
|
+
function handle(line) {
|
|
277
|
+
let message;
|
|
278
|
+
try {
|
|
279
|
+
message = JSON.parse(line);
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (message.type === "run") {
|
|
285
|
+
if (!started)
|
|
286
|
+
start(message);
|
|
287
|
+
}
|
|
288
|
+
else if (message.type === "tool_result") {
|
|
289
|
+
if (message.ok) {
|
|
290
|
+
const json = message.value === undefined ? undefined : JSON.stringify(message.value);
|
|
291
|
+
bridge?.deliver(message.id, true, json);
|
|
292
|
+
}
|
|
293
|
+
else
|
|
294
|
+
bridge?.deliver(message.id, false, message.error);
|
|
295
|
+
}
|
|
296
|
+
else if (message.type === "abort") {
|
|
297
|
+
done(false, "Script aborted");
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
function main() {
|
|
301
|
+
// 脚本里未 await 的 Promise 被拒绝不应让子进程崩溃。
|
|
302
|
+
process.on("unhandledRejection", () => { });
|
|
303
|
+
process.on("uncaughtException", (error) => done(false, describeError(error)));
|
|
304
|
+
let buffer = "";
|
|
305
|
+
process.stdin.setEncoding("utf8");
|
|
306
|
+
process.stdin.on("data", (chunk) => {
|
|
307
|
+
buffer += chunk;
|
|
308
|
+
const lines = buffer.split("\n");
|
|
309
|
+
buffer = lines.pop() ?? "";
|
|
310
|
+
for (const line of lines)
|
|
311
|
+
if (line.trim() !== "")
|
|
312
|
+
handle(line);
|
|
313
|
+
});
|
|
314
|
+
process.stdin.on("end", () => {
|
|
315
|
+
if (buffer.trim() !== "")
|
|
316
|
+
handle(buffer);
|
|
317
|
+
buffer = "";
|
|
318
|
+
done(false, "Script aborted: parent closed the channel");
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
// 父进程启动时附加该参数(protocol.ts 的 SANDBOX_MAIN_FLAG);没有它(例如被测试 import)就不启动。
|
|
322
|
+
if (process.argv.includes("--ama-codemode-sandbox"))
|
|
323
|
+
main();
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* codemode 的跨次存储(设计 §5.5 `store / load`)。[B10]
|
|
3
|
+
*
|
|
4
|
+
* - 存成 `custom{customType:"ama.codemode-store", data:{ entries }}` 条目(不进上下文);每次成功
|
|
5
|
+
* 的脚本写过 store 就追加一条**完整快照**,读取取活动分支上最近一条——随会话分支走,fork /
|
|
6
|
+
* 切分支后只看本分支写过的值;
|
|
7
|
+
* - 单值 JSON ≤ 262 144 字符、合计 ≤ 1 048 576 字符(子进程里 store() 先检查一次,这里提交前
|
|
8
|
+
* 再检查一次,防止子进程被绕过);
|
|
9
|
+
* - 脚本失败不提交(由 tool.ts 保证:只在 `done.ok` 时调用 commitStore)。
|
|
10
|
+
*/
|
|
11
|
+
import type { ToolContext } from "../tools/types.js";
|
|
12
|
+
import type { StoreSnapshot } from "./protocol.js";
|
|
13
|
+
export declare const STORE_CUSTOM_TYPE = "ama.codemode-store";
|
|
14
|
+
export declare const MAX_STORE_VALUE_CHARS = 262144;
|
|
15
|
+
export declare const MAX_STORE_TOTAL_CHARS = 1048576;
|
|
16
|
+
export interface StoreEntryData {
|
|
17
|
+
entries: StoreSnapshot;
|
|
18
|
+
}
|
|
19
|
+
type SessionAccess = ToolContext["session"];
|
|
20
|
+
/** 活动分支上最近一次提交的快照;没有或形状不对 → 空。 */
|
|
21
|
+
export declare function readStore(session: SessionAccess): StoreSnapshot;
|
|
22
|
+
/** 超限 / 非法 → 说明文字;合法 → undefined。 */
|
|
23
|
+
export declare function validateStore(entries: StoreSnapshot): string | undefined;
|
|
24
|
+
/** 校验并追加快照条目;超限不写,返回说明。 */
|
|
25
|
+
export declare function commitStore(session: SessionAccess, entries: StoreSnapshot): string | undefined;
|
|
26
|
+
export {};
|