rird 1.0.200
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 +27 -0
- package/Dockerfile +18 -0
- package/README.md +15 -0
- package/bin/opencode +336 -0
- package/bin/pty-wrapper.js +285 -0
- package/bunfig.toml +4 -0
- package/facebook_ads_library.png +0 -0
- package/nul`nif +0 -0
- package/package.json +111 -0
- package/parsers-config.ts +239 -0
- package/rird-1.0.199.tgz +0 -0
- package/script/build-windows.ts +54 -0
- package/script/build.ts +167 -0
- package/script/postinstall.mjs +544 -0
- package/script/publish-registries.ts +187 -0
- package/script/publish.ts +72 -0
- package/script/schema.ts +47 -0
- package/src/acp/README.md +164 -0
- package/src/acp/agent.ts +1063 -0
- package/src/acp/session.ts +101 -0
- package/src/acp/types.ts +22 -0
- package/src/agent/agent.ts +367 -0
- package/src/agent/generate.txt +75 -0
- package/src/agent/prompt/compaction.txt +12 -0
- package/src/agent/prompt/explore.txt +18 -0
- package/src/agent/prompt/summary.txt +10 -0
- package/src/agent/prompt/title.txt +36 -0
- package/src/auth/index.ts +70 -0
- package/src/bun/index.ts +114 -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 +88 -0
- package/src/cli/cmd/agent.ts +256 -0
- package/src/cli/cmd/auth.ts +391 -0
- package/src/cli/cmd/cmd.ts +7 -0
- package/src/cli/cmd/debug/config.ts +15 -0
- package/src/cli/cmd/debug/file.ts +91 -0
- package/src/cli/cmd/debug/index.ts +43 -0
- package/src/cli/cmd/debug/lsp.ts +48 -0
- package/src/cli/cmd/debug/ripgrep.ts +83 -0
- package/src/cli/cmd/debug/scrap.ts +15 -0
- package/src/cli/cmd/debug/skill.ts +15 -0
- package/src/cli/cmd/debug/snapshot.ts +48 -0
- package/src/cli/cmd/export.ts +88 -0
- package/src/cli/cmd/generate.ts +38 -0
- package/src/cli/cmd/github.ts +1400 -0
- package/src/cli/cmd/import.ts +98 -0
- package/src/cli/cmd/mcp.ts +654 -0
- package/src/cli/cmd/models.ts +77 -0
- package/src/cli/cmd/pr.ts +112 -0
- package/src/cli/cmd/run.ts +368 -0
- package/src/cli/cmd/serve.ts +31 -0
- package/src/cli/cmd/session.ts +106 -0
- package/src/cli/cmd/stats.ts +298 -0
- package/src/cli/cmd/tui/app.tsx +696 -0
- package/src/cli/cmd/tui/attach.ts +30 -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 +124 -0
- package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
- package/src/cli/cmd/tui/component/dialog-model.tsx +245 -0
- package/src/cli/cmd/tui/component/dialog-provider.tsx +224 -0
- package/src/cli/cmd/tui/component/dialog-session-list.tsx +102 -0
- package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
- package/src/cli/cmd/tui/component/dialog-stash.tsx +86 -0
- package/src/cli/cmd/tui/component/dialog-status.tsx +162 -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/did-you-know.tsx +85 -0
- package/src/cli/cmd/tui/component/logo.tsx +35 -0
- package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +574 -0
- package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
- package/src/cli/cmd/tui/component/prompt/index.tsx +1090 -0
- package/src/cli/cmd/tui/component/prompt/stash.tsx +101 -0
- package/src/cli/cmd/tui/component/tips.ts +27 -0
- package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
- package/src/cli/cmd/tui/context/args.tsx +14 -0
- package/src/cli/cmd/tui/context/directory.ts +13 -0
- package/src/cli/cmd/tui/context/exit.tsx +23 -0
- package/src/cli/cmd/tui/context/helper.tsx +25 -0
- package/src/cli/cmd/tui/context/keybind.tsx +101 -0
- package/src/cli/cmd/tui/context/kv.tsx +49 -0
- package/src/cli/cmd/tui/context/local.tsx +354 -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 +74 -0
- package/src/cli/cmd/tui/context/sync.tsx +372 -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/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 +95 -0
- package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
- package/src/cli/cmd/tui/context/theme/lucent-orng.json +227 -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 +245 -0
- package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
- package/src/cli/cmd/tui/context/theme/rird.json +245 -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 +1109 -0
- package/src/cli/cmd/tui/event.ts +40 -0
- package/src/cli/cmd/tui/routes/home.tsx +138 -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 +88 -0
- package/src/cli/cmd/tui/routes/session/header.tsx +125 -0
- package/src/cli/cmd/tui/routes/session/index.tsx +1864 -0
- package/src/cli/cmd/tui/routes/session/sidebar.tsx +318 -0
- package/src/cli/cmd/tui/spawn.ts +60 -0
- package/src/cli/cmd/tui/thread.ts +142 -0
- package/src/cli/cmd/tui/ui/dialog-alert.tsx +57 -0
- package/src/cli/cmd/tui/ui/dialog-confirm.tsx +83 -0
- package/src/cli/cmd/tui/ui/dialog-help.tsx +38 -0
- package/src/cli/cmd/tui/ui/dialog-prompt.tsx +77 -0
- package/src/cli/cmd/tui/ui/dialog-select.tsx +332 -0
- package/src/cli/cmd/tui/ui/dialog.tsx +170 -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 +127 -0
- package/src/cli/cmd/tui/util/editor.ts +32 -0
- package/src/cli/cmd/tui/util/terminal.ts +114 -0
- package/src/cli/cmd/tui/worker.ts +63 -0
- package/src/cli/cmd/uninstall.ts +344 -0
- package/src/cli/cmd/upgrade.ts +100 -0
- package/src/cli/cmd/web.ts +84 -0
- package/src/cli/error.ts +56 -0
- package/src/cli/ui.ts +84 -0
- package/src/cli/upgrade.ts +25 -0
- package/src/command/index.ts +80 -0
- package/src/command/template/initialize.txt +10 -0
- package/src/command/template/review.txt +97 -0
- package/src/config/config.ts +995 -0
- package/src/config/markdown.ts +41 -0
- package/src/env/index.ts +26 -0
- package/src/file/ignore.ts +83 -0
- package/src/file/index.ts +328 -0
- package/src/file/ripgrep.ts +393 -0
- package/src/file/time.ts +64 -0
- package/src/file/watcher.ts +103 -0
- package/src/flag/flag.ts +46 -0
- package/src/format/formatter.ts +315 -0
- package/src/format/index.ts +137 -0
- package/src/global/index.ts +52 -0
- package/src/id/id.ts +73 -0
- package/src/ide/index.ts +76 -0
- package/src/index.ts +240 -0
- package/src/installation/index.ts +239 -0
- package/src/lsp/client.ts +229 -0
- package/src/lsp/index.ts +485 -0
- package/src/lsp/language.ts +116 -0
- package/src/lsp/server.ts +1895 -0
- package/src/mcp/auth.ts +135 -0
- package/src/mcp/index.ts +690 -0
- package/src/mcp/oauth-callback.ts +200 -0
- package/src/mcp/oauth-provider.ts +154 -0
- package/src/patch/index.ts +622 -0
- package/src/permission/index.ts +199 -0
- package/src/plugin/index.ts +91 -0
- package/src/project/bootstrap.ts +31 -0
- package/src/project/instance.ts +78 -0
- package/src/project/project.ts +221 -0
- package/src/project/state.ts +65 -0
- package/src/project/vcs.ts +76 -0
- package/src/provider/auth.ts +143 -0
- package/src/provider/models-macro.ts +11 -0
- package/src/provider/models.ts +106 -0
- package/src/provider/provider.ts +1071 -0
- package/src/provider/sdk/openai-compatible/src/README.md +5 -0
- package/src/provider/sdk/openai-compatible/src/index.ts +2 -0
- package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +100 -0
- package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +303 -0
- package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +22 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +18 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +22 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +207 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +1713 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +177 -0
- package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +1 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +88 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +128 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +115 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +65 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +104 -0
- package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +103 -0
- package/src/provider/transform.ts +455 -0
- package/src/pty/index.ts +231 -0
- package/src/security/guardrails.test.ts +341 -0
- package/src/security/guardrails.ts +558 -0
- package/src/security/index.ts +19 -0
- package/src/server/error.ts +36 -0
- package/src/server/project.ts +79 -0
- package/src/server/server.ts +2642 -0
- package/src/server/tui.ts +71 -0
- package/src/session/compaction.ts +223 -0
- package/src/session/index.ts +461 -0
- package/src/session/llm.ts +201 -0
- package/src/session/message-v2.ts +690 -0
- package/src/session/message.ts +189 -0
- package/src/session/processor.ts +409 -0
- package/src/session/prompt/act-switch.txt +5 -0
- package/src/session/prompt/anthropic-20250930.txt +166 -0
- package/src/session/prompt/anthropic.txt +85 -0
- package/src/session/prompt/anthropic_spoof.txt +1 -0
- package/src/session/prompt/beast.txt +103 -0
- package/src/session/prompt/codex.txt +304 -0
- package/src/session/prompt/copilot-gpt-5.txt +138 -0
- package/src/session/prompt/gemini.txt +85 -0
- package/src/session/prompt/max-steps.txt +16 -0
- package/src/session/prompt/plan-reminder-anthropic.txt +35 -0
- package/src/session/prompt/plan.txt +24 -0
- package/src/session/prompt/polaris.txt +84 -0
- package/src/session/prompt/qwen.txt +106 -0
- package/src/session/prompt.ts +1509 -0
- package/src/session/retry.ts +86 -0
- package/src/session/revert.ts +108 -0
- package/src/session/sensitive-filter.test.ts +327 -0
- package/src/session/sensitive-filter.ts +466 -0
- package/src/session/status.ts +76 -0
- package/src/session/summary.ts +194 -0
- package/src/session/system.ts +120 -0
- package/src/session/todo.ts +37 -0
- package/src/share/share-next.ts +194 -0
- package/src/share/share.ts +87 -0
- package/src/shell/shell.ts +67 -0
- package/src/skill/index.ts +1 -0
- package/src/skill/skill.ts +83 -0
- package/src/snapshot/index.ts +197 -0
- package/src/storage/storage.ts +226 -0
- package/src/tests/agent.test.ts +308 -0
- package/src/tests/build-guards.test.ts +267 -0
- package/src/tests/config.test.ts +664 -0
- package/src/tests/tool-registry.test.ts +589 -0
- package/src/tool/bash.ts +317 -0
- package/src/tool/bash.txt +158 -0
- package/src/tool/batch.ts +175 -0
- package/src/tool/batch.txt +24 -0
- package/src/tool/codesearch.ts +168 -0
- package/src/tool/codesearch.txt +12 -0
- package/src/tool/edit.ts +675 -0
- package/src/tool/edit.txt +10 -0
- package/src/tool/glob.ts +65 -0
- package/src/tool/glob.txt +6 -0
- package/src/tool/grep.ts +121 -0
- package/src/tool/grep.txt +8 -0
- package/src/tool/invalid.ts +17 -0
- package/src/tool/ls.ts +110 -0
- package/src/tool/ls.txt +1 -0
- package/src/tool/lsp-diagnostics.ts +26 -0
- package/src/tool/lsp-diagnostics.txt +1 -0
- package/src/tool/lsp-hover.ts +31 -0
- package/src/tool/lsp-hover.txt +1 -0
- package/src/tool/lsp.ts +87 -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/patch.ts +233 -0
- package/src/tool/patch.txt +1 -0
- package/src/tool/read.ts +219 -0
- package/src/tool/read.txt +12 -0
- package/src/tool/registry.ts +162 -0
- package/src/tool/skill.ts +100 -0
- package/src/tool/task.ts +136 -0
- package/src/tool/task.txt +51 -0
- package/src/tool/todo.ts +39 -0
- package/src/tool/todoread.txt +14 -0
- package/src/tool/todowrite.txt +167 -0
- package/src/tool/tool.ts +71 -0
- package/src/tool/webfetch.ts +198 -0
- package/src/tool/webfetch.txt +13 -0
- package/src/tool/websearch.ts +180 -0
- package/src/tool/websearch.txt +11 -0
- package/src/tool/write.ts +110 -0
- package/src/tool/write.txt +8 -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 +83 -0
- package/src/util/fn.ts +11 -0
- package/src/util/iife.ts +3 -0
- package/src/util/keybind.ts +102 -0
- package/src/util/lazy.ts +11 -0
- package/src/util/license.ts +325 -0
- package/src/util/locale.ts +81 -0
- package/src/util/lock.ts +98 -0
- package/src/util/log.ts +180 -0
- package/src/util/queue.ts +32 -0
- package/src/util/rpc.ts +42 -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 +54 -0
- package/sst-env.d.ts +9 -0
- package/test/agent/agent.test.ts +146 -0
- package/test/bun.test.ts +53 -0
- package/test/cli/github-remote.test.ts +80 -0
- package/test/config/agent-color.test.ts +66 -0
- package/test/config/config.test.ts +535 -0
- package/test/config/markdown.test.ts +89 -0
- package/test/file/ignore.test.ts +10 -0
- package/test/fixture/fixture.ts +36 -0
- package/test/fixture/lsp/fake-lsp-server.js +77 -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/patch/patch.test.ts +348 -0
- package/test/preload.ts +57 -0
- package/test/project/project.test.ts +72 -0
- package/test/provider/provider.test.ts +1809 -0
- package/test/provider/transform.test.ts +411 -0
- package/test/session/retry.test.ts +111 -0
- package/test/session/session.test.ts +71 -0
- package/test/skill/skill.test.ts +131 -0
- package/test/snapshot/snapshot.test.ts +939 -0
- package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
- package/test/tool/bash.test.ts +434 -0
- package/test/tool/grep.test.ts +108 -0
- package/test/tool/patch.test.ts +259 -0
- package/test/tool/read.test.ts +42 -0
- package/test/util/iife.test.ts +36 -0
- package/test/util/lazy.test.ts +50 -0
- package/test/util/timeout.test.ts +21 -0
- package/test/util/wildcard.test.ts +55 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import { ProviderTransform } from "../../src/provider/transform"
|
|
3
|
+
|
|
4
|
+
const OUTPUT_TOKEN_MAX = 32000
|
|
5
|
+
|
|
6
|
+
describe("ProviderTransform.options - setCacheKey", () => {
|
|
7
|
+
const sessionID = "test-session-123"
|
|
8
|
+
|
|
9
|
+
const mockModel = {
|
|
10
|
+
id: "anthropic/claude-3-5-sonnet",
|
|
11
|
+
providerID: "anthropic",
|
|
12
|
+
api: {
|
|
13
|
+
id: "claude-3-5-sonnet-20241022",
|
|
14
|
+
url: "https://api.anthropic.com",
|
|
15
|
+
npm: "@ai-sdk/anthropic",
|
|
16
|
+
},
|
|
17
|
+
name: "Claude 3.5 Sonnet",
|
|
18
|
+
capabilities: {
|
|
19
|
+
temperature: true,
|
|
20
|
+
reasoning: false,
|
|
21
|
+
attachment: true,
|
|
22
|
+
toolcall: true,
|
|
23
|
+
input: { text: true, audio: false, image: true, video: false, pdf: true },
|
|
24
|
+
output: { text: true, audio: false, image: false, video: false, pdf: false },
|
|
25
|
+
interleaved: false,
|
|
26
|
+
},
|
|
27
|
+
cost: {
|
|
28
|
+
input: 0.003,
|
|
29
|
+
output: 0.015,
|
|
30
|
+
cache: { read: 0.0003, write: 0.00375 },
|
|
31
|
+
},
|
|
32
|
+
limit: {
|
|
33
|
+
context: 200000,
|
|
34
|
+
output: 8192,
|
|
35
|
+
},
|
|
36
|
+
status: "active",
|
|
37
|
+
options: {},
|
|
38
|
+
headers: {},
|
|
39
|
+
} as any
|
|
40
|
+
|
|
41
|
+
test("should set promptCacheKey when providerOptions.setCacheKey is true", () => {
|
|
42
|
+
const result = ProviderTransform.options(mockModel, sessionID, { setCacheKey: true })
|
|
43
|
+
expect(result.promptCacheKey).toBe(sessionID)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test("should not set promptCacheKey when providerOptions.setCacheKey is false", () => {
|
|
47
|
+
const result = ProviderTransform.options(mockModel, sessionID, { setCacheKey: false })
|
|
48
|
+
expect(result.promptCacheKey).toBeUndefined()
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test("should not set promptCacheKey when providerOptions is undefined", () => {
|
|
52
|
+
const result = ProviderTransform.options(mockModel, sessionID, undefined)
|
|
53
|
+
expect(result.promptCacheKey).toBeUndefined()
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test("should not set promptCacheKey when providerOptions does not have setCacheKey", () => {
|
|
57
|
+
const result = ProviderTransform.options(mockModel, sessionID, {})
|
|
58
|
+
expect(result.promptCacheKey).toBeUndefined()
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test("should set promptCacheKey for openai provider regardless of setCacheKey", () => {
|
|
62
|
+
const openaiModel = {
|
|
63
|
+
...mockModel,
|
|
64
|
+
providerID: "openai",
|
|
65
|
+
api: {
|
|
66
|
+
id: "gpt-4",
|
|
67
|
+
url: "https://api.openai.com",
|
|
68
|
+
npm: "@ai-sdk/openai",
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
const result = ProviderTransform.options(openaiModel, sessionID, {})
|
|
72
|
+
expect(result.promptCacheKey).toBe(sessionID)
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
describe("ProviderTransform.maxOutputTokens", () => {
|
|
77
|
+
test("returns 32k when modelLimit > 32k", () => {
|
|
78
|
+
const modelLimit = 100000
|
|
79
|
+
const result = ProviderTransform.maxOutputTokens("@ai-sdk/openai", {}, modelLimit, OUTPUT_TOKEN_MAX)
|
|
80
|
+
expect(result).toBe(OUTPUT_TOKEN_MAX)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test("returns modelLimit when modelLimit < 32k", () => {
|
|
84
|
+
const modelLimit = 16000
|
|
85
|
+
const result = ProviderTransform.maxOutputTokens("@ai-sdk/openai", {}, modelLimit, OUTPUT_TOKEN_MAX)
|
|
86
|
+
expect(result).toBe(16000)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
describe("azure", () => {
|
|
90
|
+
test("returns 32k when modelLimit > 32k", () => {
|
|
91
|
+
const modelLimit = 100000
|
|
92
|
+
const result = ProviderTransform.maxOutputTokens("@ai-sdk/azure", {}, modelLimit, OUTPUT_TOKEN_MAX)
|
|
93
|
+
expect(result).toBe(OUTPUT_TOKEN_MAX)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test("returns modelLimit when modelLimit < 32k", () => {
|
|
97
|
+
const modelLimit = 16000
|
|
98
|
+
const result = ProviderTransform.maxOutputTokens("@ai-sdk/azure", {}, modelLimit, OUTPUT_TOKEN_MAX)
|
|
99
|
+
expect(result).toBe(16000)
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
describe("bedrock", () => {
|
|
104
|
+
test("returns 32k when modelLimit > 32k", () => {
|
|
105
|
+
const modelLimit = 100000
|
|
106
|
+
const result = ProviderTransform.maxOutputTokens("@ai-sdk/amazon-bedrock", {}, modelLimit, OUTPUT_TOKEN_MAX)
|
|
107
|
+
expect(result).toBe(OUTPUT_TOKEN_MAX)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test("returns modelLimit when modelLimit < 32k", () => {
|
|
111
|
+
const modelLimit = 16000
|
|
112
|
+
const result = ProviderTransform.maxOutputTokens("@ai-sdk/amazon-bedrock", {}, modelLimit, OUTPUT_TOKEN_MAX)
|
|
113
|
+
expect(result).toBe(16000)
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
describe("anthropic without thinking options", () => {
|
|
118
|
+
test("returns 32k when modelLimit > 32k", () => {
|
|
119
|
+
const modelLimit = 100000
|
|
120
|
+
const result = ProviderTransform.maxOutputTokens("@ai-sdk/anthropic", {}, modelLimit, OUTPUT_TOKEN_MAX)
|
|
121
|
+
expect(result).toBe(OUTPUT_TOKEN_MAX)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test("returns modelLimit when modelLimit < 32k", () => {
|
|
125
|
+
const modelLimit = 16000
|
|
126
|
+
const result = ProviderTransform.maxOutputTokens("@ai-sdk/anthropic", {}, modelLimit, OUTPUT_TOKEN_MAX)
|
|
127
|
+
expect(result).toBe(16000)
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
describe("anthropic with thinking options", () => {
|
|
132
|
+
test("returns 32k when budgetTokens + 32k <= modelLimit", () => {
|
|
133
|
+
const modelLimit = 100000
|
|
134
|
+
const options = {
|
|
135
|
+
thinking: {
|
|
136
|
+
type: "enabled",
|
|
137
|
+
budgetTokens: 10000,
|
|
138
|
+
},
|
|
139
|
+
}
|
|
140
|
+
const result = ProviderTransform.maxOutputTokens("@ai-sdk/anthropic", options, modelLimit, OUTPUT_TOKEN_MAX)
|
|
141
|
+
expect(result).toBe(OUTPUT_TOKEN_MAX)
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
test("returns modelLimit - budgetTokens when budgetTokens + 32k > modelLimit", () => {
|
|
145
|
+
const modelLimit = 50000
|
|
146
|
+
const options = {
|
|
147
|
+
thinking: {
|
|
148
|
+
type: "enabled",
|
|
149
|
+
budgetTokens: 30000,
|
|
150
|
+
},
|
|
151
|
+
}
|
|
152
|
+
const result = ProviderTransform.maxOutputTokens("@ai-sdk/anthropic", options, modelLimit, OUTPUT_TOKEN_MAX)
|
|
153
|
+
expect(result).toBe(20000)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
test("returns 32k when thinking type is not enabled", () => {
|
|
157
|
+
const modelLimit = 100000
|
|
158
|
+
const options = {
|
|
159
|
+
thinking: {
|
|
160
|
+
type: "disabled",
|
|
161
|
+
budgetTokens: 10000,
|
|
162
|
+
},
|
|
163
|
+
}
|
|
164
|
+
const result = ProviderTransform.maxOutputTokens("@ai-sdk/anthropic", options, modelLimit, OUTPUT_TOKEN_MAX)
|
|
165
|
+
expect(result).toBe(OUTPUT_TOKEN_MAX)
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
describe("ProviderTransform.schema - gemini array items", () => {
|
|
171
|
+
test("adds missing items for array properties", () => {
|
|
172
|
+
const geminiModel = {
|
|
173
|
+
providerID: "google",
|
|
174
|
+
api: {
|
|
175
|
+
id: "gemini-3-pro",
|
|
176
|
+
},
|
|
177
|
+
} as any
|
|
178
|
+
|
|
179
|
+
const schema = {
|
|
180
|
+
type: "object",
|
|
181
|
+
properties: {
|
|
182
|
+
nodes: { type: "array" },
|
|
183
|
+
edges: { type: "array", items: { type: "string" } },
|
|
184
|
+
},
|
|
185
|
+
} as any
|
|
186
|
+
|
|
187
|
+
const result = ProviderTransform.schema(geminiModel, schema) as any
|
|
188
|
+
|
|
189
|
+
expect(result.properties.nodes.items).toBeDefined()
|
|
190
|
+
expect(result.properties.edges.items.type).toBe("string")
|
|
191
|
+
})
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
describe("ProviderTransform.message - DeepSeek reasoning content", () => {
|
|
195
|
+
test("DeepSeek with tool calls includes reasoning_content in providerOptions", () => {
|
|
196
|
+
const msgs = [
|
|
197
|
+
{
|
|
198
|
+
role: "assistant",
|
|
199
|
+
content: [
|
|
200
|
+
{ type: "reasoning", text: "Let me think about this..." },
|
|
201
|
+
{
|
|
202
|
+
type: "tool-call",
|
|
203
|
+
toolCallId: "test",
|
|
204
|
+
toolName: "bash",
|
|
205
|
+
input: { command: "echo hello" },
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
},
|
|
209
|
+
] as any[]
|
|
210
|
+
|
|
211
|
+
const result = ProviderTransform.message(msgs, {
|
|
212
|
+
id: "deepseek/deepseek-chat",
|
|
213
|
+
providerID: "deepseek",
|
|
214
|
+
api: {
|
|
215
|
+
id: "deepseek-chat",
|
|
216
|
+
url: "https://api.deepseek.com",
|
|
217
|
+
npm: "@ai-sdk/openai-compatible",
|
|
218
|
+
},
|
|
219
|
+
name: "DeepSeek Chat",
|
|
220
|
+
capabilities: {
|
|
221
|
+
temperature: true,
|
|
222
|
+
reasoning: true,
|
|
223
|
+
attachment: false,
|
|
224
|
+
toolcall: true,
|
|
225
|
+
input: { text: true, audio: false, image: false, video: false, pdf: false },
|
|
226
|
+
output: { text: true, audio: false, image: false, video: false, pdf: false },
|
|
227
|
+
interleaved: {
|
|
228
|
+
field: "reasoning_content",
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
cost: {
|
|
232
|
+
input: 0.001,
|
|
233
|
+
output: 0.002,
|
|
234
|
+
cache: { read: 0.0001, write: 0.0002 },
|
|
235
|
+
},
|
|
236
|
+
limit: {
|
|
237
|
+
context: 128000,
|
|
238
|
+
output: 8192,
|
|
239
|
+
},
|
|
240
|
+
status: "active",
|
|
241
|
+
options: {},
|
|
242
|
+
headers: {},
|
|
243
|
+
release_date: "2023-04-01",
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
expect(result).toHaveLength(1)
|
|
247
|
+
expect(result[0].content).toEqual([
|
|
248
|
+
{
|
|
249
|
+
type: "tool-call",
|
|
250
|
+
toolCallId: "test",
|
|
251
|
+
toolName: "bash",
|
|
252
|
+
input: { command: "echo hello" },
|
|
253
|
+
},
|
|
254
|
+
])
|
|
255
|
+
expect(result[0].providerOptions?.openaiCompatible?.reasoning_content).toBe("Let me think about this...")
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
test("Non-DeepSeek providers leave reasoning content unchanged", () => {
|
|
259
|
+
const msgs = [
|
|
260
|
+
{
|
|
261
|
+
role: "assistant",
|
|
262
|
+
content: [
|
|
263
|
+
{ type: "reasoning", text: "Should not be processed" },
|
|
264
|
+
{ type: "text", text: "Answer" },
|
|
265
|
+
],
|
|
266
|
+
},
|
|
267
|
+
] as any[]
|
|
268
|
+
|
|
269
|
+
const result = ProviderTransform.message(msgs, {
|
|
270
|
+
id: "openai/gpt-4",
|
|
271
|
+
providerID: "openai",
|
|
272
|
+
api: {
|
|
273
|
+
id: "gpt-4",
|
|
274
|
+
url: "https://api.openai.com",
|
|
275
|
+
npm: "@ai-sdk/openai",
|
|
276
|
+
},
|
|
277
|
+
name: "GPT-4",
|
|
278
|
+
capabilities: {
|
|
279
|
+
temperature: true,
|
|
280
|
+
reasoning: false,
|
|
281
|
+
attachment: true,
|
|
282
|
+
toolcall: true,
|
|
283
|
+
input: { text: true, audio: false, image: true, video: false, pdf: false },
|
|
284
|
+
output: { text: true, audio: false, image: false, video: false, pdf: false },
|
|
285
|
+
interleaved: false,
|
|
286
|
+
},
|
|
287
|
+
cost: {
|
|
288
|
+
input: 0.03,
|
|
289
|
+
output: 0.06,
|
|
290
|
+
cache: { read: 0.001, write: 0.002 },
|
|
291
|
+
},
|
|
292
|
+
limit: {
|
|
293
|
+
context: 128000,
|
|
294
|
+
output: 4096,
|
|
295
|
+
},
|
|
296
|
+
status: "active",
|
|
297
|
+
options: {},
|
|
298
|
+
headers: {},
|
|
299
|
+
release_date: "2023-04-01",
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
expect(result[0].content).toEqual([
|
|
303
|
+
{ type: "reasoning", text: "Should not be processed" },
|
|
304
|
+
{ type: "text", text: "Answer" },
|
|
305
|
+
])
|
|
306
|
+
expect(result[0].providerOptions?.openaiCompatible?.reasoning_content).toBeUndefined()
|
|
307
|
+
})
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
describe("ProviderTransform.message - empty image handling", () => {
|
|
311
|
+
const mockModel = {
|
|
312
|
+
id: "anthropic/claude-3-5-sonnet",
|
|
313
|
+
providerID: "anthropic",
|
|
314
|
+
api: {
|
|
315
|
+
id: "claude-3-5-sonnet-20241022",
|
|
316
|
+
url: "https://api.anthropic.com",
|
|
317
|
+
npm: "@ai-sdk/anthropic",
|
|
318
|
+
},
|
|
319
|
+
name: "Claude 3.5 Sonnet",
|
|
320
|
+
capabilities: {
|
|
321
|
+
temperature: true,
|
|
322
|
+
reasoning: false,
|
|
323
|
+
attachment: true,
|
|
324
|
+
toolcall: true,
|
|
325
|
+
input: { text: true, audio: false, image: true, video: false, pdf: true },
|
|
326
|
+
output: { text: true, audio: false, image: false, video: false, pdf: false },
|
|
327
|
+
interleaved: false,
|
|
328
|
+
},
|
|
329
|
+
cost: {
|
|
330
|
+
input: 0.003,
|
|
331
|
+
output: 0.015,
|
|
332
|
+
cache: { read: 0.0003, write: 0.00375 },
|
|
333
|
+
},
|
|
334
|
+
limit: {
|
|
335
|
+
context: 200000,
|
|
336
|
+
output: 8192,
|
|
337
|
+
},
|
|
338
|
+
status: "active",
|
|
339
|
+
options: {},
|
|
340
|
+
headers: {},
|
|
341
|
+
} as any
|
|
342
|
+
|
|
343
|
+
test("should replace empty base64 image with error text", () => {
|
|
344
|
+
const msgs = [
|
|
345
|
+
{
|
|
346
|
+
role: "user",
|
|
347
|
+
content: [
|
|
348
|
+
{ type: "text", text: "What is in this image?" },
|
|
349
|
+
{ type: "image", image: "data:image/png;base64," },
|
|
350
|
+
],
|
|
351
|
+
},
|
|
352
|
+
] as any[]
|
|
353
|
+
|
|
354
|
+
const result = ProviderTransform.message(msgs, mockModel)
|
|
355
|
+
|
|
356
|
+
expect(result).toHaveLength(1)
|
|
357
|
+
expect(result[0].content).toHaveLength(2)
|
|
358
|
+
expect(result[0].content[0]).toEqual({ type: "text", text: "What is in this image?" })
|
|
359
|
+
expect(result[0].content[1]).toEqual({
|
|
360
|
+
type: "text",
|
|
361
|
+
text: "ERROR: Image file is empty or corrupted. Please provide a valid image.",
|
|
362
|
+
})
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
test("should keep valid base64 images unchanged", () => {
|
|
366
|
+
const validBase64 =
|
|
367
|
+
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
|
|
368
|
+
const msgs = [
|
|
369
|
+
{
|
|
370
|
+
role: "user",
|
|
371
|
+
content: [
|
|
372
|
+
{ type: "text", text: "What is in this image?" },
|
|
373
|
+
{ type: "image", image: `data:image/png;base64,${validBase64}` },
|
|
374
|
+
],
|
|
375
|
+
},
|
|
376
|
+
] as any[]
|
|
377
|
+
|
|
378
|
+
const result = ProviderTransform.message(msgs, mockModel)
|
|
379
|
+
|
|
380
|
+
expect(result).toHaveLength(1)
|
|
381
|
+
expect(result[0].content).toHaveLength(2)
|
|
382
|
+
expect(result[0].content[0]).toEqual({ type: "text", text: "What is in this image?" })
|
|
383
|
+
expect(result[0].content[1]).toEqual({ type: "image", image: `data:image/png;base64,${validBase64}` })
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
test("should handle mixed valid and empty images", () => {
|
|
387
|
+
const validBase64 =
|
|
388
|
+
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
|
|
389
|
+
const msgs = [
|
|
390
|
+
{
|
|
391
|
+
role: "user",
|
|
392
|
+
content: [
|
|
393
|
+
{ type: "text", text: "Compare these images" },
|
|
394
|
+
{ type: "image", image: `data:image/png;base64,${validBase64}` },
|
|
395
|
+
{ type: "image", image: "data:image/jpeg;base64," },
|
|
396
|
+
],
|
|
397
|
+
},
|
|
398
|
+
] as any[]
|
|
399
|
+
|
|
400
|
+
const result = ProviderTransform.message(msgs, mockModel)
|
|
401
|
+
|
|
402
|
+
expect(result).toHaveLength(1)
|
|
403
|
+
expect(result[0].content).toHaveLength(3)
|
|
404
|
+
expect(result[0].content[0]).toEqual({ type: "text", text: "Compare these images" })
|
|
405
|
+
expect(result[0].content[1]).toEqual({ type: "image", image: `data:image/png;base64,${validBase64}` })
|
|
406
|
+
expect(result[0].content[2]).toEqual({
|
|
407
|
+
type: "text",
|
|
408
|
+
text: "ERROR: Image file is empty or corrupted. Please provide a valid image.",
|
|
409
|
+
})
|
|
410
|
+
})
|
|
411
|
+
})
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import { SessionRetry } from "../../src/session/retry"
|
|
3
|
+
import { MessageV2 } from "../../src/session/message-v2"
|
|
4
|
+
|
|
5
|
+
function apiError(headers?: Record<string, string>): MessageV2.APIError {
|
|
6
|
+
return new MessageV2.APIError({
|
|
7
|
+
message: "boom",
|
|
8
|
+
isRetryable: true,
|
|
9
|
+
responseHeaders: headers,
|
|
10
|
+
}).toObject() as MessageV2.APIError
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe("session.retry.delay", () => {
|
|
14
|
+
test("caps delay at 30 seconds when headers missing", () => {
|
|
15
|
+
const error = apiError()
|
|
16
|
+
const delays = Array.from({ length: 10 }, (_, index) => SessionRetry.delay(index + 1, error))
|
|
17
|
+
expect(delays).toStrictEqual([2000, 4000, 8000, 16000, 30000, 30000, 30000, 30000, 30000, 30000])
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test("prefers retry-after-ms when shorter than exponential", () => {
|
|
21
|
+
const error = apiError({ "retry-after-ms": "1500" })
|
|
22
|
+
expect(SessionRetry.delay(4, error)).toBe(1500)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test("uses retry-after seconds when reasonable", () => {
|
|
26
|
+
const error = apiError({ "retry-after": "30" })
|
|
27
|
+
expect(SessionRetry.delay(3, error)).toBe(30000)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test("accepts http-date retry-after values", () => {
|
|
31
|
+
const date = new Date(Date.now() + 20000).toUTCString()
|
|
32
|
+
const error = apiError({ "retry-after": date })
|
|
33
|
+
const d = SessionRetry.delay(1, error)
|
|
34
|
+
expect(d).toBeGreaterThanOrEqual(19000)
|
|
35
|
+
expect(d).toBeLessThanOrEqual(20000)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test("ignores invalid retry hints", () => {
|
|
39
|
+
const error = apiError({ "retry-after": "not-a-number" })
|
|
40
|
+
expect(SessionRetry.delay(1, error)).toBe(2000)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test("ignores malformed date retry hints", () => {
|
|
44
|
+
const error = apiError({ "retry-after": "Invalid Date String" })
|
|
45
|
+
expect(SessionRetry.delay(1, error)).toBe(2000)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test("ignores past date retry hints", () => {
|
|
49
|
+
const pastDate = new Date(Date.now() - 5000).toUTCString()
|
|
50
|
+
const error = apiError({ "retry-after": pastDate })
|
|
51
|
+
expect(SessionRetry.delay(1, error)).toBe(2000)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test("uses retry-after values even when exceeding 10 minutes with headers", () => {
|
|
55
|
+
const error = apiError({ "retry-after": "50" })
|
|
56
|
+
expect(SessionRetry.delay(1, error)).toBe(50000)
|
|
57
|
+
|
|
58
|
+
const longError = apiError({ "retry-after-ms": "700000" })
|
|
59
|
+
expect(SessionRetry.delay(1, longError)).toBe(700000)
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
describe("session.message-v2.fromError", () => {
|
|
64
|
+
test.concurrent(
|
|
65
|
+
"converts ECONNRESET socket errors to retryable APIError",
|
|
66
|
+
async () => {
|
|
67
|
+
using server = Bun.serve({
|
|
68
|
+
port: 0,
|
|
69
|
+
idleTimeout: 8,
|
|
70
|
+
async fetch(req) {
|
|
71
|
+
return new Response(
|
|
72
|
+
new ReadableStream({
|
|
73
|
+
async pull(controller) {
|
|
74
|
+
controller.enqueue("Hello,")
|
|
75
|
+
await Bun.sleep(10000)
|
|
76
|
+
controller.enqueue(" World!")
|
|
77
|
+
controller.close()
|
|
78
|
+
},
|
|
79
|
+
}),
|
|
80
|
+
{ headers: { "Content-Type": "text/plain" } },
|
|
81
|
+
)
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const error = await fetch(new URL("/", server.url.origin))
|
|
86
|
+
.then((res) => res.text())
|
|
87
|
+
.catch((e) => e)
|
|
88
|
+
|
|
89
|
+
const result = MessageV2.fromError(error, { providerID: "test" })
|
|
90
|
+
|
|
91
|
+
expect(MessageV2.APIError.isInstance(result)).toBe(true)
|
|
92
|
+
expect((result as MessageV2.APIError).data.isRetryable).toBe(true)
|
|
93
|
+
expect((result as MessageV2.APIError).data.message).toBe("Connection reset by server")
|
|
94
|
+
expect((result as MessageV2.APIError).data.metadata?.code).toBe("ECONNRESET")
|
|
95
|
+
expect((result as MessageV2.APIError).data.metadata?.message).toInclude("socket connection")
|
|
96
|
+
},
|
|
97
|
+
15_000,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
test("ECONNRESET socket error is retryable", () => {
|
|
101
|
+
const error = new MessageV2.APIError({
|
|
102
|
+
message: "Connection reset by server",
|
|
103
|
+
isRetryable: true,
|
|
104
|
+
metadata: { code: "ECONNRESET", message: "The socket connection was closed unexpectedly" },
|
|
105
|
+
}).toObject() as MessageV2.APIError
|
|
106
|
+
|
|
107
|
+
const retryable = SessionRetry.retryable(error)
|
|
108
|
+
expect(retryable).toBeDefined()
|
|
109
|
+
expect(retryable).toBe("Connection reset by server")
|
|
110
|
+
})
|
|
111
|
+
})
|
|
@@ -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
|
+
})
|