@stonerzju/opencode 1.2.16-offline.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/AGENTS.md +10 -0
- package/BUN_SHELL_MIGRATION_PLAN.md +136 -0
- package/Dockerfile +18 -0
- package/README.md +15 -0
- package/bin/opencode +179 -0
- package/bunfig.toml +7 -0
- package/drizzle.config.ts +10 -0
- package/migration/20260127222353_familiar_lady_ursula/migration.sql +90 -0
- package/migration/20260127222353_familiar_lady_ursula/snapshot.json +796 -0
- package/migration/20260211171708_add_project_commands/migration.sql +1 -0
- package/migration/20260211171708_add_project_commands/snapshot.json +806 -0
- package/migration/20260213144116_wakeful_the_professor/migration.sql +11 -0
- package/migration/20260213144116_wakeful_the_professor/snapshot.json +897 -0
- package/migration/20260225215848_workspace/migration.sql +7 -0
- package/migration/20260225215848_workspace/snapshot.json +959 -0
- package/package.json +140 -0
- package/package.json.bak +140 -0
- package/parsers-config.ts +254 -0
- package/script/build.ts +224 -0
- package/script/check-migrations.ts +16 -0
- package/script/postinstall.mjs +131 -0
- package/script/publish.ts +181 -0
- package/script/schema.ts +63 -0
- package/script/seed-e2e.ts +50 -0
- package/src/acp/README.md +174 -0
- package/src/acp/agent.ts +1741 -0
- package/src/acp/session.ts +116 -0
- package/src/acp/types.ts +23 -0
- package/src/agent/agent.ts +339 -0
- package/src/agent/generate.txt +75 -0
- package/src/agent/prompt/compaction.txt +14 -0
- package/src/agent/prompt/explore.txt +18 -0
- package/src/agent/prompt/summary.txt +11 -0
- package/src/agent/prompt/title.txt +44 -0
- package/src/auth/index.ts +68 -0
- package/src/bun/index.ts +131 -0
- package/src/bun/registry.ts +50 -0
- package/src/bus/bus-event.ts +43 -0
- package/src/bus/global.ts +10 -0
- package/src/bus/index.ts +105 -0
- package/src/cli/bootstrap.ts +17 -0
- package/src/cli/cmd/acp.ts +70 -0
- package/src/cli/cmd/agent.ts +257 -0
- package/src/cli/cmd/auth.ts +449 -0
- package/src/cli/cmd/cmd.ts +7 -0
- package/src/cli/cmd/db.ts +118 -0
- package/src/cli/cmd/debug/agent.ts +167 -0
- package/src/cli/cmd/debug/config.ts +16 -0
- package/src/cli/cmd/debug/file.ts +97 -0
- package/src/cli/cmd/debug/index.ts +48 -0
- package/src/cli/cmd/debug/lsp.ts +52 -0
- package/src/cli/cmd/debug/ripgrep.ts +87 -0
- package/src/cli/cmd/debug/scrap.ts +16 -0
- package/src/cli/cmd/debug/skill.ts +16 -0
- package/src/cli/cmd/debug/snapshot.ts +52 -0
- package/src/cli/cmd/export.ts +88 -0
- package/src/cli/cmd/generate.ts +38 -0
- package/src/cli/cmd/github.ts +1631 -0
- package/src/cli/cmd/import.ts +170 -0
- package/src/cli/cmd/mcp.ts +754 -0
- package/src/cli/cmd/models.ts +77 -0
- package/src/cli/cmd/pr.ts +112 -0
- package/src/cli/cmd/run.ts +625 -0
- package/src/cli/cmd/serve.ts +31 -0
- package/src/cli/cmd/session.ts +156 -0
- package/src/cli/cmd/stats.ts +410 -0
- package/src/cli/cmd/tui/app.tsx +845 -0
- package/src/cli/cmd/tui/attach.ts +88 -0
- package/src/cli/cmd/tui/component/border.tsx +21 -0
- package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
- package/src/cli/cmd/tui/component/dialog-command.tsx +147 -0
- package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
- package/src/cli/cmd/tui/component/dialog-model.tsx +165 -0
- package/src/cli/cmd/tui/component/dialog-provider.tsx +259 -0
- package/src/cli/cmd/tui/component/dialog-session-list.tsx +108 -0
- package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
- package/src/cli/cmd/tui/component/dialog-skill.tsx +36 -0
- package/src/cli/cmd/tui/component/dialog-stash.tsx +87 -0
- package/src/cli/cmd/tui/component/dialog-status.tsx +167 -0
- package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
- package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
- package/src/cli/cmd/tui/component/logo.tsx +85 -0
- package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +667 -0
- package/src/cli/cmd/tui/component/prompt/frecency.tsx +90 -0
- package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
- package/src/cli/cmd/tui/component/prompt/index.tsx +1155 -0
- package/src/cli/cmd/tui/component/prompt/stash.tsx +101 -0
- package/src/cli/cmd/tui/component/spinner.tsx +24 -0
- package/src/cli/cmd/tui/component/textarea-keybindings.ts +73 -0
- package/src/cli/cmd/tui/component/tips.tsx +152 -0
- package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
- package/src/cli/cmd/tui/context/args.tsx +15 -0
- package/src/cli/cmd/tui/context/directory.ts +13 -0
- package/src/cli/cmd/tui/context/exit.tsx +53 -0
- package/src/cli/cmd/tui/context/helper.tsx +25 -0
- package/src/cli/cmd/tui/context/keybind.tsx +102 -0
- package/src/cli/cmd/tui/context/kv.tsx +52 -0
- package/src/cli/cmd/tui/context/local.tsx +406 -0
- package/src/cli/cmd/tui/context/prompt.tsx +18 -0
- package/src/cli/cmd/tui/context/route.tsx +46 -0
- package/src/cli/cmd/tui/context/sdk.tsx +101 -0
- package/src/cli/cmd/tui/context/sync.tsx +488 -0
- package/src/cli/cmd/tui/context/theme/aura.json +69 -0
- package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
- package/src/cli/cmd/tui/context/theme/carbonfox.json +248 -0
- package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
- package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
- package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
- package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
- package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
- package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
- package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
- package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
- package/src/cli/cmd/tui/context/theme/github.json +233 -0
- package/src/cli/cmd/tui/context/theme/gruvbox.json +242 -0
- package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
- package/src/cli/cmd/tui/context/theme/lucent-orng.json +237 -0
- package/src/cli/cmd/tui/context/theme/material.json +235 -0
- package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
- package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
- package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
- package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
- package/src/cli/cmd/tui/context/theme/nord.json +223 -0
- package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
- package/src/cli/cmd/tui/context/theme/orng.json +249 -0
- package/src/cli/cmd/tui/context/theme/osaka-jade.json +93 -0
- package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
- package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
- package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
- package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
- package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
- package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
- package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
- package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
- package/src/cli/cmd/tui/context/theme.tsx +1152 -0
- package/src/cli/cmd/tui/context/tui-config.tsx +9 -0
- package/src/cli/cmd/tui/event.ts +48 -0
- package/src/cli/cmd/tui/routes/home.tsx +145 -0
- package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +64 -0
- package/src/cli/cmd/tui/routes/session/dialog-message.tsx +109 -0
- package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +26 -0
- package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
- package/src/cli/cmd/tui/routes/session/footer.tsx +91 -0
- package/src/cli/cmd/tui/routes/session/header.tsx +135 -0
- package/src/cli/cmd/tui/routes/session/index.tsx +2219 -0
- package/src/cli/cmd/tui/routes/session/permission.tsx +685 -0
- package/src/cli/cmd/tui/routes/session/question.tsx +466 -0
- package/src/cli/cmd/tui/routes/session/sidebar.tsx +321 -0
- package/src/cli/cmd/tui/thread.ts +199 -0
- package/src/cli/cmd/tui/ui/dialog-alert.tsx +59 -0
- package/src/cli/cmd/tui/ui/dialog-confirm.tsx +85 -0
- package/src/cli/cmd/tui/ui/dialog-export-options.tsx +207 -0
- package/src/cli/cmd/tui/ui/dialog-help.tsx +40 -0
- package/src/cli/cmd/tui/ui/dialog-prompt.tsx +80 -0
- package/src/cli/cmd/tui/ui/dialog-select.tsx +401 -0
- package/src/cli/cmd/tui/ui/dialog.tsx +182 -0
- package/src/cli/cmd/tui/ui/link.tsx +28 -0
- package/src/cli/cmd/tui/ui/spinner.ts +368 -0
- package/src/cli/cmd/tui/ui/toast.tsx +100 -0
- package/src/cli/cmd/tui/util/clipboard.ts +164 -0
- package/src/cli/cmd/tui/util/editor.ts +33 -0
- package/src/cli/cmd/tui/util/selection.ts +25 -0
- package/src/cli/cmd/tui/util/signal.ts +7 -0
- package/src/cli/cmd/tui/util/terminal.ts +114 -0
- package/src/cli/cmd/tui/util/transcript.ts +98 -0
- package/src/cli/cmd/tui/win32.ts +129 -0
- package/src/cli/cmd/tui/worker.ts +157 -0
- package/src/cli/cmd/uninstall.ts +356 -0
- package/src/cli/cmd/upgrade.ts +73 -0
- package/src/cli/cmd/web.ts +81 -0
- package/src/cli/cmd/workspace-serve.ts +16 -0
- package/src/cli/error.ts +57 -0
- package/src/cli/logo.ts +6 -0
- package/src/cli/network.ts +60 -0
- package/src/cli/ui.ts +116 -0
- package/src/cli/upgrade.ts +25 -0
- package/src/command/index.ts +150 -0
- package/src/command/template/initialize.txt +10 -0
- package/src/command/template/review.txt +101 -0
- package/src/config/config.ts +1408 -0
- package/src/config/markdown.ts +99 -0
- package/src/config/migrate-tui-config.ts +155 -0
- package/src/config/paths.ts +174 -0
- package/src/config/tui-schema.ts +34 -0
- package/src/config/tui.ts +118 -0
- package/src/control/control.sql.ts +22 -0
- package/src/control/index.ts +67 -0
- package/src/control-plane/adaptors/index.ts +10 -0
- package/src/control-plane/adaptors/types.ts +7 -0
- package/src/control-plane/adaptors/worktree.ts +26 -0
- package/src/control-plane/config.ts +10 -0
- package/src/control-plane/session-proxy-middleware.ts +46 -0
- package/src/control-plane/sse.ts +66 -0
- package/src/control-plane/workspace-server/routes.ts +33 -0
- package/src/control-plane/workspace-server/server.ts +24 -0
- package/src/control-plane/workspace.sql.ts +12 -0
- package/src/control-plane/workspace.ts +160 -0
- package/src/env/index.ts +28 -0
- package/src/file/ignore.ts +82 -0
- package/src/file/index.ts +646 -0
- package/src/file/ripgrep.ts +372 -0
- package/src/file/time.ts +71 -0
- package/src/file/watcher.ts +128 -0
- package/src/flag/flag.ts +109 -0
- package/src/format/formatter.ts +395 -0
- package/src/format/index.ts +140 -0
- package/src/global/index.ts +54 -0
- package/src/id/id.ts +84 -0
- package/src/ide/index.ts +76 -0
- package/src/index.ts +210 -0
- package/src/installation/index.ts +266 -0
- package/src/lsp/client.ts +251 -0
- package/src/lsp/index.ts +485 -0
- package/src/lsp/language.ts +120 -0
- package/src/lsp/server.ts +2142 -0
- package/src/mcp/auth.ts +130 -0
- package/src/mcp/index.ts +937 -0
- package/src/mcp/oauth-callback.ts +200 -0
- package/src/mcp/oauth-provider.ts +176 -0
- package/src/patch/index.ts +680 -0
- package/src/permission/arity.ts +163 -0
- package/src/permission/index.ts +210 -0
- package/src/permission/next.ts +286 -0
- package/src/plugin/codex.ts +624 -0
- package/src/plugin/copilot.ts +327 -0
- package/src/plugin/index.ts +143 -0
- package/src/project/bootstrap.ts +33 -0
- package/src/project/instance.ts +114 -0
- package/src/project/project.sql.ts +15 -0
- package/src/project/project.ts +441 -0
- package/src/project/state.ts +70 -0
- package/src/project/vcs.ts +76 -0
- package/src/provider/auth.ts +147 -0
- package/src/provider/error.ts +189 -0
- package/src/provider/models.ts +146 -0
- package/src/provider/provider.ts +1338 -0
- package/src/provider/sdk/copilot/README.md +5 -0
- package/src/provider/sdk/copilot/chat/convert-to-openai-compatible-chat-messages.ts +164 -0
- package/src/provider/sdk/copilot/chat/get-response-metadata.ts +15 -0
- package/src/provider/sdk/copilot/chat/map-openai-compatible-finish-reason.ts +17 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-api-types.ts +64 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-chat-language-model.ts +780 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-chat-options.ts +28 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-metadata-extractor.ts +44 -0
- package/src/provider/sdk/copilot/chat/openai-compatible-prepare-tools.ts +87 -0
- package/src/provider/sdk/copilot/copilot-provider.ts +100 -0
- package/src/provider/sdk/copilot/index.ts +2 -0
- package/src/provider/sdk/copilot/openai-compatible-error.ts +27 -0
- package/src/provider/sdk/copilot/responses/convert-to-openai-responses-input.ts +303 -0
- package/src/provider/sdk/copilot/responses/map-openai-responses-finish-reason.ts +22 -0
- package/src/provider/sdk/copilot/responses/openai-config.ts +18 -0
- package/src/provider/sdk/copilot/responses/openai-error.ts +22 -0
- package/src/provider/sdk/copilot/responses/openai-responses-api-types.ts +207 -0
- package/src/provider/sdk/copilot/responses/openai-responses-language-model.ts +1732 -0
- package/src/provider/sdk/copilot/responses/openai-responses-prepare-tools.ts +177 -0
- package/src/provider/sdk/copilot/responses/openai-responses-settings.ts +1 -0
- package/src/provider/sdk/copilot/responses/tool/code-interpreter.ts +88 -0
- package/src/provider/sdk/copilot/responses/tool/file-search.ts +128 -0
- package/src/provider/sdk/copilot/responses/tool/image-generation.ts +115 -0
- package/src/provider/sdk/copilot/responses/tool/local-shell.ts +65 -0
- package/src/provider/sdk/copilot/responses/tool/web-search-preview.ts +104 -0
- package/src/provider/sdk/copilot/responses/tool/web-search.ts +103 -0
- package/src/provider/transform.ts +955 -0
- package/src/pty/index.ts +324 -0
- package/src/question/index.ts +171 -0
- package/src/scheduler/index.ts +61 -0
- package/src/server/error.ts +36 -0
- package/src/server/event.ts +7 -0
- package/src/server/mdns.ts +60 -0
- package/src/server/routes/config.ts +92 -0
- package/src/server/routes/experimental.ts +270 -0
- package/src/server/routes/file.ts +197 -0
- package/src/server/routes/global.ts +185 -0
- package/src/server/routes/mcp.ts +225 -0
- package/src/server/routes/permission.ts +68 -0
- package/src/server/routes/project.ts +82 -0
- package/src/server/routes/provider.ts +165 -0
- package/src/server/routes/pty.ts +200 -0
- package/src/server/routes/question.ts +98 -0
- package/src/server/routes/session.ts +974 -0
- package/src/server/routes/tui.ts +379 -0
- package/src/server/routes/workspace.ts +104 -0
- package/src/server/server.ts +623 -0
- package/src/session/compaction.ts +261 -0
- package/src/session/index.ts +877 -0
- package/src/session/instruction.ts +192 -0
- package/src/session/llm.ts +279 -0
- package/src/session/message-v2.ts +899 -0
- package/src/session/message.ts +189 -0
- package/src/session/processor.ts +421 -0
- package/src/session/prompt/anthropic-20250930.txt +166 -0
- package/src/session/prompt/anthropic.txt +105 -0
- package/src/session/prompt/beast.txt +147 -0
- package/src/session/prompt/build-switch.txt +5 -0
- package/src/session/prompt/codex_header.txt +79 -0
- package/src/session/prompt/copilot-gpt-5.txt +143 -0
- package/src/session/prompt/gemini.txt +155 -0
- package/src/session/prompt/max-steps.txt +16 -0
- package/src/session/prompt/plan-reminder-anthropic.txt +67 -0
- package/src/session/prompt/plan.txt +26 -0
- package/src/session/prompt/qwen.txt +109 -0
- package/src/session/prompt/trinity.txt +97 -0
- package/src/session/prompt.ts +1959 -0
- package/src/session/retry.ts +101 -0
- package/src/session/revert.ts +138 -0
- package/src/session/session.sql.ts +88 -0
- package/src/session/status.ts +76 -0
- package/src/session/summary.ts +161 -0
- package/src/session/system.ts +54 -0
- package/src/session/todo.ts +56 -0
- package/src/share/share-next.ts +210 -0
- package/src/share/share.sql.ts +13 -0
- package/src/shell/shell.ts +68 -0
- package/src/skill/discovery.ts +98 -0
- package/src/skill/index.ts +1 -0
- package/src/skill/skill.ts +189 -0
- package/src/snapshot/index.ts +297 -0
- package/src/sql.d.ts +4 -0
- package/src/storage/db.ts +155 -0
- package/src/storage/json-migration.ts +425 -0
- package/src/storage/schema.sql.ts +10 -0
- package/src/storage/schema.ts +5 -0
- package/src/storage/storage.ts +220 -0
- package/src/tool/apply_patch.ts +281 -0
- package/src/tool/apply_patch.txt +33 -0
- package/src/tool/bash.ts +274 -0
- package/src/tool/bash.txt +115 -0
- package/src/tool/batch.ts +181 -0
- package/src/tool/batch.txt +24 -0
- package/src/tool/codesearch.ts +132 -0
- package/src/tool/codesearch.txt +12 -0
- package/src/tool/edit.ts +654 -0
- package/src/tool/edit.txt +10 -0
- package/src/tool/external-directory.ts +32 -0
- package/src/tool/glob.ts +78 -0
- package/src/tool/glob.txt +6 -0
- package/src/tool/grep.ts +156 -0
- package/src/tool/grep.txt +8 -0
- package/src/tool/invalid.ts +17 -0
- package/src/tool/ls.ts +121 -0
- package/src/tool/ls.txt +1 -0
- package/src/tool/lsp.ts +97 -0
- package/src/tool/lsp.txt +19 -0
- package/src/tool/multiedit.ts +46 -0
- package/src/tool/multiedit.txt +41 -0
- package/src/tool/plan-enter.txt +14 -0
- package/src/tool/plan-exit.txt +13 -0
- package/src/tool/plan.ts +131 -0
- package/src/tool/question.ts +33 -0
- package/src/tool/question.txt +10 -0
- package/src/tool/read.ts +293 -0
- package/src/tool/read.txt +14 -0
- package/src/tool/registry.ts +173 -0
- package/src/tool/skill.ts +123 -0
- package/src/tool/task.ts +165 -0
- package/src/tool/task.txt +60 -0
- package/src/tool/todo.ts +53 -0
- package/src/tool/todoread.txt +14 -0
- package/src/tool/todowrite.txt +167 -0
- package/src/tool/tool.ts +89 -0
- package/src/tool/truncation.ts +107 -0
- package/src/tool/webfetch.ts +206 -0
- package/src/tool/webfetch.txt +13 -0
- package/src/tool/websearch.ts +150 -0
- package/src/tool/websearch.txt +14 -0
- package/src/tool/write.ts +84 -0
- package/src/tool/write.txt +8 -0
- package/src/util/abort.ts +35 -0
- package/src/util/archive.ts +16 -0
- package/src/util/color.ts +19 -0
- package/src/util/context.ts +25 -0
- package/src/util/defer.ts +12 -0
- package/src/util/eventloop.ts +20 -0
- package/src/util/filesystem.ts +189 -0
- package/src/util/fn.ts +11 -0
- package/src/util/format.ts +20 -0
- package/src/util/git.ts +35 -0
- package/src/util/glob.ts +34 -0
- package/src/util/iife.ts +3 -0
- package/src/util/keybind.ts +103 -0
- package/src/util/lazy.ts +23 -0
- package/src/util/locale.ts +81 -0
- package/src/util/lock.ts +98 -0
- package/src/util/log.ts +182 -0
- package/src/util/process.ts +126 -0
- package/src/util/proxied.ts +3 -0
- package/src/util/queue.ts +32 -0
- package/src/util/rpc.ts +66 -0
- package/src/util/scrap.ts +10 -0
- package/src/util/signal.ts +12 -0
- package/src/util/timeout.ts +14 -0
- package/src/util/token.ts +7 -0
- package/src/util/wildcard.ts +59 -0
- package/src/worktree/index.ts +643 -0
- package/sst-env.d.ts +10 -0
- package/test/AGENTS.md +81 -0
- package/test/acp/agent-interface.test.ts +51 -0
- package/test/acp/event-subscription.test.ts +683 -0
- package/test/agent/agent.test.ts +689 -0
- package/test/bun.test.ts +53 -0
- package/test/cli/github-action.test.ts +197 -0
- package/test/cli/github-remote.test.ts +80 -0
- package/test/cli/import.test.ts +38 -0
- package/test/cli/plugin-auth-picker.test.ts +120 -0
- package/test/cli/tui/transcript.test.ts +322 -0
- package/test/config/agent-color.test.ts +71 -0
- package/test/config/config.test.ts +1886 -0
- package/test/config/fixtures/empty-frontmatter.md +4 -0
- package/test/config/fixtures/frontmatter.md +28 -0
- package/test/config/fixtures/markdown-header.md +11 -0
- package/test/config/fixtures/no-frontmatter.md +1 -0
- package/test/config/fixtures/weird-model-id.md +13 -0
- package/test/config/markdown.test.ts +228 -0
- package/test/config/tui.test.ts +510 -0
- package/test/control-plane/session-proxy-middleware.test.ts +147 -0
- package/test/control-plane/sse.test.ts +56 -0
- package/test/control-plane/workspace-server-sse.test.ts +65 -0
- package/test/control-plane/workspace-sync.test.ts +97 -0
- package/test/file/ignore.test.ts +10 -0
- package/test/file/index.test.ts +394 -0
- package/test/file/path-traversal.test.ts +198 -0
- package/test/file/ripgrep.test.ts +39 -0
- package/test/file/time.test.ts +361 -0
- package/test/fixture/db.ts +11 -0
- package/test/fixture/fixture.ts +45 -0
- package/test/fixture/lsp/fake-lsp-server.js +77 -0
- package/test/fixture/skills/agents-sdk/SKILL.md +152 -0
- package/test/fixture/skills/agents-sdk/references/callable.md +92 -0
- package/test/fixture/skills/cloudflare/SKILL.md +211 -0
- package/test/fixture/skills/index.json +6 -0
- package/test/ide/ide.test.ts +82 -0
- package/test/keybind.test.ts +421 -0
- package/test/lsp/client.test.ts +95 -0
- package/test/mcp/headers.test.ts +153 -0
- package/test/mcp/oauth-browser.test.ts +249 -0
- package/test/memory/abort-leak.test.ts +136 -0
- package/test/patch/patch.test.ts +348 -0
- package/test/permission/arity.test.ts +33 -0
- package/test/permission/next.test.ts +689 -0
- package/test/permission-task.test.ts +319 -0
- package/test/plugin/auth-override.test.ts +44 -0
- package/test/plugin/codex.test.ts +123 -0
- package/test/preload.ts +80 -0
- package/test/project/project.test.ts +348 -0
- package/test/project/worktree-remove.test.ts +65 -0
- package/test/provider/amazon-bedrock.test.ts +446 -0
- package/test/provider/copilot/convert-to-copilot-messages.test.ts +523 -0
- package/test/provider/copilot/copilot-chat-model.test.ts +592 -0
- package/test/provider/gitlab-duo.test.ts +262 -0
- package/test/provider/provider.test.ts +2220 -0
- package/test/provider/transform.test.ts +2353 -0
- package/test/pty/pty-output-isolation.test.ts +140 -0
- package/test/question/question.test.ts +300 -0
- package/test/scheduler.test.ts +73 -0
- package/test/server/global-session-list.test.ts +89 -0
- package/test/server/session-list.test.ts +90 -0
- package/test/server/session-select.test.ts +78 -0
- package/test/session/compaction.test.ts +423 -0
- package/test/session/instruction.test.ts +170 -0
- package/test/session/llm.test.ts +667 -0
- package/test/session/message-v2.test.ts +924 -0
- package/test/session/prompt.test.ts +211 -0
- package/test/session/retry.test.ts +188 -0
- package/test/session/revert-compact.test.ts +285 -0
- package/test/session/session.test.ts +71 -0
- package/test/session/structured-output-integration.test.ts +233 -0
- package/test/session/structured-output.test.ts +385 -0
- package/test/skill/discovery.test.ts +110 -0
- package/test/skill/skill.test.ts +388 -0
- package/test/snapshot/snapshot.test.ts +1180 -0
- package/test/storage/json-migration.test.ts +846 -0
- package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
- package/test/tool/apply_patch.test.ts +566 -0
- package/test/tool/bash.test.ts +402 -0
- package/test/tool/edit.test.ts +496 -0
- package/test/tool/external-directory.test.ts +127 -0
- package/test/tool/fixtures/large-image.png +0 -0
- package/test/tool/fixtures/models-api.json +38413 -0
- package/test/tool/grep.test.ts +110 -0
- package/test/tool/question.test.ts +107 -0
- package/test/tool/read.test.ts +504 -0
- package/test/tool/registry.test.ts +122 -0
- package/test/tool/skill.test.ts +112 -0
- package/test/tool/truncation.test.ts +160 -0
- package/test/tool/webfetch.test.ts +100 -0
- package/test/tool/write.test.ts +348 -0
- package/test/util/filesystem.test.ts +443 -0
- package/test/util/format.test.ts +59 -0
- package/test/util/glob.test.ts +164 -0
- package/test/util/iife.test.ts +36 -0
- package/test/util/lazy.test.ts +50 -0
- package/test/util/lock.test.ts +72 -0
- package/test/util/process.test.ts +59 -0
- package/test/util/timeout.test.ts +21 -0
- package/test/util/wildcard.test.ts +90 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import path from "path"
|
|
3
|
+
import { Session } from "../../src/session"
|
|
4
|
+
import { Bus } from "../../src/bus"
|
|
5
|
+
import { Log } from "../../src/util/log"
|
|
6
|
+
import { Instance } from "../../src/project/instance"
|
|
7
|
+
|
|
8
|
+
const projectRoot = path.join(__dirname, "../..")
|
|
9
|
+
Log.init({ print: false })
|
|
10
|
+
|
|
11
|
+
describe("session.started event", () => {
|
|
12
|
+
test("should emit session.started event when session is created", async () => {
|
|
13
|
+
await Instance.provide({
|
|
14
|
+
directory: projectRoot,
|
|
15
|
+
fn: async () => {
|
|
16
|
+
let eventReceived = false
|
|
17
|
+
let receivedInfo: Session.Info | undefined
|
|
18
|
+
|
|
19
|
+
const unsub = Bus.subscribe(Session.Event.Created, (event) => {
|
|
20
|
+
eventReceived = true
|
|
21
|
+
receivedInfo = event.properties.info as Session.Info
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const session = await Session.create({})
|
|
25
|
+
|
|
26
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
27
|
+
|
|
28
|
+
unsub()
|
|
29
|
+
|
|
30
|
+
expect(eventReceived).toBe(true)
|
|
31
|
+
expect(receivedInfo).toBeDefined()
|
|
32
|
+
expect(receivedInfo?.id).toBe(session.id)
|
|
33
|
+
expect(receivedInfo?.projectID).toBe(session.projectID)
|
|
34
|
+
expect(receivedInfo?.directory).toBe(session.directory)
|
|
35
|
+
expect(receivedInfo?.title).toBe(session.title)
|
|
36
|
+
|
|
37
|
+
await Session.remove(session.id)
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test("session.started event should be emitted before session.updated", async () => {
|
|
43
|
+
await Instance.provide({
|
|
44
|
+
directory: projectRoot,
|
|
45
|
+
fn: async () => {
|
|
46
|
+
const events: string[] = []
|
|
47
|
+
|
|
48
|
+
const unsubStarted = Bus.subscribe(Session.Event.Created, () => {
|
|
49
|
+
events.push("started")
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const unsubUpdated = Bus.subscribe(Session.Event.Updated, () => {
|
|
53
|
+
events.push("updated")
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const session = await Session.create({})
|
|
57
|
+
|
|
58
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
59
|
+
|
|
60
|
+
unsubStarted()
|
|
61
|
+
unsubUpdated()
|
|
62
|
+
|
|
63
|
+
expect(events).toContain("started")
|
|
64
|
+
expect(events).toContain("updated")
|
|
65
|
+
expect(events.indexOf("started")).toBeLessThan(events.indexOf("updated"))
|
|
66
|
+
|
|
67
|
+
await Session.remove(session.id)
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
})
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import path from "path"
|
|
3
|
+
import { Session } from "../../src/session"
|
|
4
|
+
import { SessionPrompt } from "../../src/session/prompt"
|
|
5
|
+
import { Log } from "../../src/util/log"
|
|
6
|
+
import { Instance } from "../../src/project/instance"
|
|
7
|
+
import { MessageV2 } from "../../src/session/message-v2"
|
|
8
|
+
|
|
9
|
+
const projectRoot = path.join(__dirname, "../..")
|
|
10
|
+
Log.init({ print: false })
|
|
11
|
+
|
|
12
|
+
// Skip tests if no API key is available
|
|
13
|
+
const hasApiKey = !!process.env.ANTHROPIC_API_KEY
|
|
14
|
+
|
|
15
|
+
// Helper to run test within Instance context
|
|
16
|
+
async function withInstance<T>(fn: () => Promise<T>): Promise<T> {
|
|
17
|
+
return Instance.provide({
|
|
18
|
+
directory: projectRoot,
|
|
19
|
+
fn,
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("StructuredOutput Integration", () => {
|
|
24
|
+
test.skipIf(!hasApiKey)(
|
|
25
|
+
"produces structured output with simple schema",
|
|
26
|
+
async () => {
|
|
27
|
+
await withInstance(async () => {
|
|
28
|
+
const session = await Session.create({ title: "Structured Output Test" })
|
|
29
|
+
|
|
30
|
+
const result = await SessionPrompt.prompt({
|
|
31
|
+
sessionID: session.id,
|
|
32
|
+
parts: [
|
|
33
|
+
{
|
|
34
|
+
type: "text",
|
|
35
|
+
text: "What is 2 + 2? Provide a simple answer.",
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
format: {
|
|
39
|
+
type: "json_schema",
|
|
40
|
+
schema: {
|
|
41
|
+
type: "object",
|
|
42
|
+
properties: {
|
|
43
|
+
answer: { type: "number", description: "The numerical answer" },
|
|
44
|
+
explanation: { type: "string", description: "Brief explanation" },
|
|
45
|
+
},
|
|
46
|
+
required: ["answer"],
|
|
47
|
+
},
|
|
48
|
+
retryCount: 0,
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
// Verify structured output was captured (only on assistant messages)
|
|
53
|
+
expect(result.info.role).toBe("assistant")
|
|
54
|
+
if (result.info.role === "assistant") {
|
|
55
|
+
expect(result.info.structured).toBeDefined()
|
|
56
|
+
expect(typeof result.info.structured).toBe("object")
|
|
57
|
+
|
|
58
|
+
const output = result.info.structured as any
|
|
59
|
+
expect(output.answer).toBe(4)
|
|
60
|
+
|
|
61
|
+
// Verify no error was set
|
|
62
|
+
expect(result.info.error).toBeUndefined()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Clean up
|
|
66
|
+
// Note: Not removing session to avoid race with background SessionSummary.summarize
|
|
67
|
+
})
|
|
68
|
+
},
|
|
69
|
+
60000,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
test.skipIf(!hasApiKey)(
|
|
73
|
+
"produces structured output with nested objects",
|
|
74
|
+
async () => {
|
|
75
|
+
await withInstance(async () => {
|
|
76
|
+
const session = await Session.create({ title: "Nested Schema Test" })
|
|
77
|
+
|
|
78
|
+
const result = await SessionPrompt.prompt({
|
|
79
|
+
sessionID: session.id,
|
|
80
|
+
parts: [
|
|
81
|
+
{
|
|
82
|
+
type: "text",
|
|
83
|
+
text: "Tell me about Anthropic company in a structured format.",
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
format: {
|
|
87
|
+
type: "json_schema",
|
|
88
|
+
schema: {
|
|
89
|
+
type: "object",
|
|
90
|
+
properties: {
|
|
91
|
+
company: {
|
|
92
|
+
type: "object",
|
|
93
|
+
properties: {
|
|
94
|
+
name: { type: "string" },
|
|
95
|
+
founded: { type: "number" },
|
|
96
|
+
},
|
|
97
|
+
required: ["name", "founded"],
|
|
98
|
+
},
|
|
99
|
+
products: {
|
|
100
|
+
type: "array",
|
|
101
|
+
items: { type: "string" },
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
required: ["company"],
|
|
105
|
+
},
|
|
106
|
+
retryCount: 0,
|
|
107
|
+
},
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
// Verify structured output was captured (only on assistant messages)
|
|
111
|
+
expect(result.info.role).toBe("assistant")
|
|
112
|
+
if (result.info.role === "assistant") {
|
|
113
|
+
expect(result.info.structured).toBeDefined()
|
|
114
|
+
const output = result.info.structured as any
|
|
115
|
+
|
|
116
|
+
expect(output.company).toBeDefined()
|
|
117
|
+
expect(output.company.name).toBe("Anthropic")
|
|
118
|
+
expect(typeof output.company.founded).toBe("number")
|
|
119
|
+
|
|
120
|
+
if (output.products) {
|
|
121
|
+
expect(Array.isArray(output.products)).toBe(true)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Verify no error was set
|
|
125
|
+
expect(result.info.error).toBeUndefined()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Clean up
|
|
129
|
+
// Note: Not removing session to avoid race with background SessionSummary.summarize
|
|
130
|
+
})
|
|
131
|
+
},
|
|
132
|
+
60000,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
test.skipIf(!hasApiKey)(
|
|
136
|
+
"works with text outputFormat (default)",
|
|
137
|
+
async () => {
|
|
138
|
+
await withInstance(async () => {
|
|
139
|
+
const session = await Session.create({ title: "Text Output Test" })
|
|
140
|
+
|
|
141
|
+
const result = await SessionPrompt.prompt({
|
|
142
|
+
sessionID: session.id,
|
|
143
|
+
parts: [
|
|
144
|
+
{
|
|
145
|
+
type: "text",
|
|
146
|
+
text: "Say hello.",
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
format: {
|
|
150
|
+
type: "text",
|
|
151
|
+
},
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
// Verify no structured output (text mode) and no error
|
|
155
|
+
expect(result.info.role).toBe("assistant")
|
|
156
|
+
if (result.info.role === "assistant") {
|
|
157
|
+
expect(result.info.structured).toBeUndefined()
|
|
158
|
+
expect(result.info.error).toBeUndefined()
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Verify we got a response with parts
|
|
162
|
+
expect(result.parts.length).toBeGreaterThan(0)
|
|
163
|
+
|
|
164
|
+
// Clean up
|
|
165
|
+
// Note: Not removing session to avoid race with background SessionSummary.summarize
|
|
166
|
+
})
|
|
167
|
+
},
|
|
168
|
+
60000,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
test.skipIf(!hasApiKey)(
|
|
172
|
+
"stores outputFormat on user message",
|
|
173
|
+
async () => {
|
|
174
|
+
await withInstance(async () => {
|
|
175
|
+
const session = await Session.create({ title: "OutputFormat Storage Test" })
|
|
176
|
+
|
|
177
|
+
await SessionPrompt.prompt({
|
|
178
|
+
sessionID: session.id,
|
|
179
|
+
parts: [
|
|
180
|
+
{
|
|
181
|
+
type: "text",
|
|
182
|
+
text: "What is 1 + 1?",
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
format: {
|
|
186
|
+
type: "json_schema",
|
|
187
|
+
schema: {
|
|
188
|
+
type: "object",
|
|
189
|
+
properties: {
|
|
190
|
+
result: { type: "number" },
|
|
191
|
+
},
|
|
192
|
+
required: ["result"],
|
|
193
|
+
},
|
|
194
|
+
retryCount: 3,
|
|
195
|
+
},
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
// Get all messages from session
|
|
199
|
+
const messages = await Session.messages({ sessionID: session.id })
|
|
200
|
+
const userMessage = messages.find((m) => m.info.role === "user")
|
|
201
|
+
|
|
202
|
+
// Verify outputFormat was stored on user message
|
|
203
|
+
expect(userMessage).toBeDefined()
|
|
204
|
+
if (userMessage?.info.role === "user") {
|
|
205
|
+
expect(userMessage.info.format).toBeDefined()
|
|
206
|
+
expect(userMessage.info.format?.type).toBe("json_schema")
|
|
207
|
+
if (userMessage.info.format?.type === "json_schema") {
|
|
208
|
+
expect(userMessage.info.format.retryCount).toBe(3)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Clean up
|
|
213
|
+
// Note: Not removing session to avoid race with background SessionSummary.summarize
|
|
214
|
+
})
|
|
215
|
+
},
|
|
216
|
+
60000,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
test("unit test: StructuredOutputError is properly structured", () => {
|
|
220
|
+
const error = new MessageV2.StructuredOutputError({
|
|
221
|
+
message: "Failed to produce valid structured output after 3 attempts",
|
|
222
|
+
retries: 3,
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
expect(error.name).toBe("StructuredOutputError")
|
|
226
|
+
expect(error.data.message).toContain("3 attempts")
|
|
227
|
+
expect(error.data.retries).toBe(3)
|
|
228
|
+
|
|
229
|
+
const obj = error.toObject()
|
|
230
|
+
expect(obj.name).toBe("StructuredOutputError")
|
|
231
|
+
expect(obj.data.retries).toBe(3)
|
|
232
|
+
})
|
|
233
|
+
})
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import { MessageV2 } from "../../src/session/message-v2"
|
|
3
|
+
import { SessionPrompt } from "../../src/session/prompt"
|
|
4
|
+
|
|
5
|
+
describe("structured-output.OutputFormat", () => {
|
|
6
|
+
test("parses text format", () => {
|
|
7
|
+
const result = MessageV2.Format.safeParse({ type: "text" })
|
|
8
|
+
expect(result.success).toBe(true)
|
|
9
|
+
if (result.success) {
|
|
10
|
+
expect(result.data.type).toBe("text")
|
|
11
|
+
}
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test("parses json_schema format with defaults", () => {
|
|
15
|
+
const result = MessageV2.Format.safeParse({
|
|
16
|
+
type: "json_schema",
|
|
17
|
+
schema: { type: "object", properties: { name: { type: "string" } } },
|
|
18
|
+
})
|
|
19
|
+
expect(result.success).toBe(true)
|
|
20
|
+
if (result.success) {
|
|
21
|
+
expect(result.data.type).toBe("json_schema")
|
|
22
|
+
if (result.data.type === "json_schema") {
|
|
23
|
+
expect(result.data.retryCount).toBe(2) // default value
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test("parses json_schema format with custom retryCount", () => {
|
|
29
|
+
const result = MessageV2.Format.safeParse({
|
|
30
|
+
type: "json_schema",
|
|
31
|
+
schema: { type: "object" },
|
|
32
|
+
retryCount: 5,
|
|
33
|
+
})
|
|
34
|
+
expect(result.success).toBe(true)
|
|
35
|
+
if (result.success && result.data.type === "json_schema") {
|
|
36
|
+
expect(result.data.retryCount).toBe(5)
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test("rejects invalid type", () => {
|
|
41
|
+
const result = MessageV2.Format.safeParse({ type: "invalid" })
|
|
42
|
+
expect(result.success).toBe(false)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test("rejects json_schema without schema", () => {
|
|
46
|
+
const result = MessageV2.Format.safeParse({ type: "json_schema" })
|
|
47
|
+
expect(result.success).toBe(false)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test("rejects negative retryCount", () => {
|
|
51
|
+
const result = MessageV2.Format.safeParse({
|
|
52
|
+
type: "json_schema",
|
|
53
|
+
schema: { type: "object" },
|
|
54
|
+
retryCount: -1,
|
|
55
|
+
})
|
|
56
|
+
expect(result.success).toBe(false)
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe("structured-output.StructuredOutputError", () => {
|
|
61
|
+
test("creates error with message and retries", () => {
|
|
62
|
+
const error = new MessageV2.StructuredOutputError({
|
|
63
|
+
message: "Failed to validate",
|
|
64
|
+
retries: 3,
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
expect(error.name).toBe("StructuredOutputError")
|
|
68
|
+
expect(error.data.message).toBe("Failed to validate")
|
|
69
|
+
expect(error.data.retries).toBe(3)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
test("converts to object correctly", () => {
|
|
73
|
+
const error = new MessageV2.StructuredOutputError({
|
|
74
|
+
message: "Test error",
|
|
75
|
+
retries: 2,
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
const obj = error.toObject()
|
|
79
|
+
expect(obj.name).toBe("StructuredOutputError")
|
|
80
|
+
expect(obj.data.message).toBe("Test error")
|
|
81
|
+
expect(obj.data.retries).toBe(2)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test("isInstance correctly identifies error", () => {
|
|
85
|
+
const error = new MessageV2.StructuredOutputError({
|
|
86
|
+
message: "Test",
|
|
87
|
+
retries: 1,
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
expect(MessageV2.StructuredOutputError.isInstance(error)).toBe(true)
|
|
91
|
+
expect(MessageV2.StructuredOutputError.isInstance({ name: "other" })).toBe(false)
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
describe("structured-output.UserMessage", () => {
|
|
96
|
+
test("user message accepts outputFormat", () => {
|
|
97
|
+
const result = MessageV2.User.safeParse({
|
|
98
|
+
id: "test-id",
|
|
99
|
+
sessionID: "test-session",
|
|
100
|
+
role: "user",
|
|
101
|
+
time: { created: Date.now() },
|
|
102
|
+
agent: "default",
|
|
103
|
+
model: { providerID: "anthropic", modelID: "claude-3" },
|
|
104
|
+
outputFormat: {
|
|
105
|
+
type: "json_schema",
|
|
106
|
+
schema: { type: "object" },
|
|
107
|
+
},
|
|
108
|
+
})
|
|
109
|
+
expect(result.success).toBe(true)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
test("user message works without outputFormat (optional)", () => {
|
|
113
|
+
const result = MessageV2.User.safeParse({
|
|
114
|
+
id: "test-id",
|
|
115
|
+
sessionID: "test-session",
|
|
116
|
+
role: "user",
|
|
117
|
+
time: { created: Date.now() },
|
|
118
|
+
agent: "default",
|
|
119
|
+
model: { providerID: "anthropic", modelID: "claude-3" },
|
|
120
|
+
})
|
|
121
|
+
expect(result.success).toBe(true)
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
describe("structured-output.AssistantMessage", () => {
|
|
126
|
+
const baseAssistantMessage = {
|
|
127
|
+
id: "test-id",
|
|
128
|
+
sessionID: "test-session",
|
|
129
|
+
role: "assistant" as const,
|
|
130
|
+
parentID: "parent-id",
|
|
131
|
+
modelID: "claude-3",
|
|
132
|
+
providerID: "anthropic",
|
|
133
|
+
mode: "default",
|
|
134
|
+
agent: "default",
|
|
135
|
+
path: { cwd: "/test", root: "/test" },
|
|
136
|
+
cost: 0.001,
|
|
137
|
+
tokens: { input: 100, output: 50, reasoning: 0, cache: { read: 0, write: 0 } },
|
|
138
|
+
time: { created: Date.now() },
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
test("assistant message accepts structured", () => {
|
|
142
|
+
const result = MessageV2.Assistant.safeParse({
|
|
143
|
+
...baseAssistantMessage,
|
|
144
|
+
structured: { company: "Anthropic", founded: 2021 },
|
|
145
|
+
})
|
|
146
|
+
expect(result.success).toBe(true)
|
|
147
|
+
if (result.success) {
|
|
148
|
+
expect(result.data.structured).toEqual({ company: "Anthropic", founded: 2021 })
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
test("assistant message works without structured_output (optional)", () => {
|
|
153
|
+
const result = MessageV2.Assistant.safeParse(baseAssistantMessage)
|
|
154
|
+
expect(result.success).toBe(true)
|
|
155
|
+
})
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
describe("structured-output.createStructuredOutputTool", () => {
|
|
159
|
+
test("creates tool with correct id", () => {
|
|
160
|
+
const tool = SessionPrompt.createStructuredOutputTool({
|
|
161
|
+
schema: { type: "object", properties: { name: { type: "string" } } },
|
|
162
|
+
onSuccess: () => {},
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
// AI SDK tool type doesn't expose id, but we set it internally
|
|
166
|
+
expect((tool as any).id).toBe("StructuredOutput")
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
test("creates tool with description", () => {
|
|
170
|
+
const tool = SessionPrompt.createStructuredOutputTool({
|
|
171
|
+
schema: { type: "object" },
|
|
172
|
+
onSuccess: () => {},
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
expect(tool.description).toContain("structured format")
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
test("creates tool with schema as inputSchema", () => {
|
|
179
|
+
const schema = {
|
|
180
|
+
type: "object",
|
|
181
|
+
properties: {
|
|
182
|
+
company: { type: "string" },
|
|
183
|
+
founded: { type: "number" },
|
|
184
|
+
},
|
|
185
|
+
required: ["company"],
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const tool = SessionPrompt.createStructuredOutputTool({
|
|
189
|
+
schema,
|
|
190
|
+
onSuccess: () => {},
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
// AI SDK wraps schema in { jsonSchema: {...} }
|
|
194
|
+
expect(tool.inputSchema).toBeDefined()
|
|
195
|
+
const inputSchema = tool.inputSchema as any
|
|
196
|
+
expect(inputSchema.jsonSchema?.properties?.company).toBeDefined()
|
|
197
|
+
expect(inputSchema.jsonSchema?.properties?.founded).toBeDefined()
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
test("strips $schema property from inputSchema", () => {
|
|
201
|
+
const schema = {
|
|
202
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
203
|
+
type: "object",
|
|
204
|
+
properties: { name: { type: "string" } },
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const tool = SessionPrompt.createStructuredOutputTool({
|
|
208
|
+
schema,
|
|
209
|
+
onSuccess: () => {},
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
// AI SDK wraps schema in { jsonSchema: {...} }
|
|
213
|
+
const inputSchema = tool.inputSchema as any
|
|
214
|
+
expect(inputSchema.jsonSchema?.$schema).toBeUndefined()
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
test("execute calls onSuccess with valid args", async () => {
|
|
218
|
+
let capturedOutput: unknown
|
|
219
|
+
|
|
220
|
+
const tool = SessionPrompt.createStructuredOutputTool({
|
|
221
|
+
schema: { type: "object", properties: { name: { type: "string" } } },
|
|
222
|
+
onSuccess: (output) => {
|
|
223
|
+
capturedOutput = output
|
|
224
|
+
},
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
expect(tool.execute).toBeDefined()
|
|
228
|
+
const testArgs = { name: "Test Company" }
|
|
229
|
+
const result = await tool.execute!(testArgs, {
|
|
230
|
+
toolCallId: "test-call-id",
|
|
231
|
+
messages: [],
|
|
232
|
+
abortSignal: undefined as any,
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
expect(capturedOutput).toEqual(testArgs)
|
|
236
|
+
expect(result.output).toBe("Structured output captured successfully.")
|
|
237
|
+
expect(result.metadata.valid).toBe(true)
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
test("AI SDK validates schema before execute - missing required field", async () => {
|
|
241
|
+
// Note: The AI SDK validates the input against the schema BEFORE calling execute()
|
|
242
|
+
// So invalid inputs never reach the tool's execute function
|
|
243
|
+
// This test documents the expected schema behavior
|
|
244
|
+
const tool = SessionPrompt.createStructuredOutputTool({
|
|
245
|
+
schema: {
|
|
246
|
+
type: "object",
|
|
247
|
+
properties: {
|
|
248
|
+
name: { type: "string" },
|
|
249
|
+
age: { type: "number" },
|
|
250
|
+
},
|
|
251
|
+
required: ["name", "age"],
|
|
252
|
+
},
|
|
253
|
+
onSuccess: () => {},
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
// The schema requires both 'name' and 'age'
|
|
257
|
+
expect(tool.inputSchema).toBeDefined()
|
|
258
|
+
const inputSchema = tool.inputSchema as any
|
|
259
|
+
expect(inputSchema.jsonSchema?.required).toContain("name")
|
|
260
|
+
expect(inputSchema.jsonSchema?.required).toContain("age")
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
test("AI SDK validates schema types before execute - wrong type", async () => {
|
|
264
|
+
// Note: The AI SDK validates the input against the schema BEFORE calling execute()
|
|
265
|
+
// So invalid inputs never reach the tool's execute function
|
|
266
|
+
// This test documents the expected schema behavior
|
|
267
|
+
const tool = SessionPrompt.createStructuredOutputTool({
|
|
268
|
+
schema: {
|
|
269
|
+
type: "object",
|
|
270
|
+
properties: {
|
|
271
|
+
count: { type: "number" },
|
|
272
|
+
},
|
|
273
|
+
required: ["count"],
|
|
274
|
+
},
|
|
275
|
+
onSuccess: () => {},
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
// The schema defines 'count' as a number
|
|
279
|
+
expect(tool.inputSchema).toBeDefined()
|
|
280
|
+
const inputSchema = tool.inputSchema as any
|
|
281
|
+
expect(inputSchema.jsonSchema?.properties?.count?.type).toBe("number")
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
test("execute handles nested objects", async () => {
|
|
285
|
+
let capturedOutput: unknown
|
|
286
|
+
|
|
287
|
+
const tool = SessionPrompt.createStructuredOutputTool({
|
|
288
|
+
schema: {
|
|
289
|
+
type: "object",
|
|
290
|
+
properties: {
|
|
291
|
+
user: {
|
|
292
|
+
type: "object",
|
|
293
|
+
properties: {
|
|
294
|
+
name: { type: "string" },
|
|
295
|
+
email: { type: "string" },
|
|
296
|
+
},
|
|
297
|
+
required: ["name"],
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
required: ["user"],
|
|
301
|
+
},
|
|
302
|
+
onSuccess: (output) => {
|
|
303
|
+
capturedOutput = output
|
|
304
|
+
},
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
// Valid nested object - AI SDK validates before calling execute()
|
|
308
|
+
const validResult = await tool.execute!(
|
|
309
|
+
{ user: { name: "John", email: "john@test.com" } },
|
|
310
|
+
{
|
|
311
|
+
toolCallId: "test-call-id",
|
|
312
|
+
messages: [],
|
|
313
|
+
abortSignal: undefined as any,
|
|
314
|
+
},
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
expect(capturedOutput).toEqual({ user: { name: "John", email: "john@test.com" } })
|
|
318
|
+
expect(validResult.metadata.valid).toBe(true)
|
|
319
|
+
|
|
320
|
+
// Verify schema has correct nested structure
|
|
321
|
+
const inputSchema = tool.inputSchema as any
|
|
322
|
+
expect(inputSchema.jsonSchema?.properties?.user?.type).toBe("object")
|
|
323
|
+
expect(inputSchema.jsonSchema?.properties?.user?.properties?.name?.type).toBe("string")
|
|
324
|
+
expect(inputSchema.jsonSchema?.properties?.user?.required).toContain("name")
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
test("execute handles arrays", async () => {
|
|
328
|
+
let capturedOutput: unknown
|
|
329
|
+
|
|
330
|
+
const tool = SessionPrompt.createStructuredOutputTool({
|
|
331
|
+
schema: {
|
|
332
|
+
type: "object",
|
|
333
|
+
properties: {
|
|
334
|
+
tags: {
|
|
335
|
+
type: "array",
|
|
336
|
+
items: { type: "string" },
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
required: ["tags"],
|
|
340
|
+
},
|
|
341
|
+
onSuccess: (output) => {
|
|
342
|
+
capturedOutput = output
|
|
343
|
+
},
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
// Valid array - AI SDK validates before calling execute()
|
|
347
|
+
const validResult = await tool.execute!(
|
|
348
|
+
{ tags: ["a", "b", "c"] },
|
|
349
|
+
{
|
|
350
|
+
toolCallId: "test-call-id",
|
|
351
|
+
messages: [],
|
|
352
|
+
abortSignal: undefined as any,
|
|
353
|
+
},
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
expect(capturedOutput).toEqual({ tags: ["a", "b", "c"] })
|
|
357
|
+
expect(validResult.metadata.valid).toBe(true)
|
|
358
|
+
|
|
359
|
+
// Verify schema has correct array structure
|
|
360
|
+
const inputSchema = tool.inputSchema as any
|
|
361
|
+
expect(inputSchema.jsonSchema?.properties?.tags?.type).toBe("array")
|
|
362
|
+
expect(inputSchema.jsonSchema?.properties?.tags?.items?.type).toBe("string")
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
test("toModelOutput returns text value", () => {
|
|
366
|
+
const tool = SessionPrompt.createStructuredOutputTool({
|
|
367
|
+
schema: { type: "object" },
|
|
368
|
+
onSuccess: () => {},
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
expect(tool.toModelOutput).toBeDefined()
|
|
372
|
+
const modelOutput = tool.toModelOutput!({
|
|
373
|
+
output: "Test output",
|
|
374
|
+
title: "Test",
|
|
375
|
+
metadata: { valid: true },
|
|
376
|
+
})
|
|
377
|
+
|
|
378
|
+
expect(modelOutput.type).toBe("text")
|
|
379
|
+
expect(modelOutput.value).toBe("Test output")
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
// Note: Retry behavior is handled by the AI SDK and the prompt loop, not the tool itself
|
|
383
|
+
// The tool simply calls onSuccess when execute() is called with valid args
|
|
384
|
+
// See prompt.ts loop() for actual retry logic
|
|
385
|
+
})
|