@stonerzju/opencode 1.2.17 → 1.2.19

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.
Files changed (261) hide show
  1. package/bin/opencode +29 -157
  2. package/package.json +29 -29
  3. package/src/acp/agent.ts +4 -4
  4. package/src/acp/session.ts +1 -1
  5. package/src/agent/agent.ts +3 -3
  6. package/src/bun/index.ts +2 -2
  7. package/src/cli/cmd/acp.ts +3 -3
  8. package/src/cli/cmd/debug/file.ts +1 -1
  9. package/src/cli/cmd/github.ts +2 -2
  10. package/src/cli/cmd/pr.ts +1 -1
  11. package/src/cli/cmd/tui/app.tsx +24 -24
  12. package/src/cli/cmd/tui/attach.ts +3 -3
  13. package/src/cli/cmd/tui/component/dialog-agent.tsx +3 -3
  14. package/src/cli/cmd/tui/component/dialog-command.tsx +3 -3
  15. package/src/cli/cmd/tui/component/dialog-mcp.tsx +5 -5
  16. package/src/cli/cmd/tui/component/dialog-model.tsx +4 -4
  17. package/src/cli/cmd/tui/component/dialog-provider.tsx +4 -4
  18. package/src/cli/cmd/tui/component/dialog-session-list.tsx +5 -5
  19. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +3 -3
  20. package/src/cli/cmd/tui/component/dialog-skill.tsx +3 -3
  21. package/src/cli/cmd/tui/component/dialog-stash.tsx +3 -3
  22. package/src/cli/cmd/tui/component/dialog-status.tsx +2 -2
  23. package/src/cli/cmd/tui/component/dialog-tag.tsx +3 -3
  24. package/src/cli/cmd/tui/component/logo.tsx +2 -2
  25. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +6 -6
  26. package/src/cli/cmd/tui/component/prompt/frecency.tsx +2 -2
  27. package/src/cli/cmd/tui/component/prompt/history.tsx +2 -2
  28. package/src/cli/cmd/tui/component/prompt/index.tsx +14 -14
  29. package/src/cli/cmd/tui/component/prompt/stash.tsx +2 -2
  30. package/src/cli/cmd/tui/component/textarea-keybindings.ts +1 -1
  31. package/src/cli/cmd/tui/component/tips.tsx +1 -1
  32. package/src/cli/cmd/tui/context/directory.ts +1 -1
  33. package/src/cli/cmd/tui/context/exit.tsx +1 -1
  34. package/src/cli/cmd/tui/context/keybind.tsx +2 -2
  35. package/src/cli/cmd/tui/context/kv.tsx +2 -2
  36. package/src/cli/cmd/tui/context/local.tsx +6 -6
  37. package/src/cli/cmd/tui/context/sync.tsx +4 -4
  38. package/src/cli/cmd/tui/context/theme/opencode.json +245 -0
  39. package/src/cli/cmd/tui/context/theme.tsx +2 -2
  40. package/src/cli/cmd/tui/context/tui-config.tsx +1 -1
  41. package/src/cli/cmd/tui/event.ts +2 -2
  42. package/src/cli/cmd/tui/routes/home.tsx +6 -6
  43. package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +6 -6
  44. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +6 -6
  45. package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +2 -2
  46. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +3 -3
  47. package/src/cli/cmd/tui/routes/session/header.tsx +5 -5
  48. package/src/cli/cmd/tui/routes/session/index.tsx +32 -32
  49. package/src/cli/cmd/tui/routes/session/permission.tsx +4 -4
  50. package/src/cli/cmd/tui/routes/session/sidebar.tsx +4 -4
  51. package/src/cli/cmd/tui/thread.ts +9 -9
  52. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +1 -1
  53. package/src/cli/cmd/tui/ui/dialog-help.tsx +2 -2
  54. package/src/cli/cmd/tui/ui/dialog-select.tsx +5 -5
  55. package/src/cli/cmd/tui/ui/dialog.tsx +3 -3
  56. package/src/cli/cmd/tui/ui/toast.tsx +1 -1
  57. package/src/cli/cmd/tui/util/editor.ts +3 -3
  58. package/src/cli/cmd/tui/util/transcript.ts +1 -1
  59. package/src/cli/cmd/tui/worker.ts +10 -10
  60. package/src/cli/error.ts +1 -1
  61. package/src/cli/ui.ts +1 -1
  62. package/src/cli/upgrade.ts +4 -4
  63. package/src/command/index.ts +1 -1
  64. package/src/config/config.ts +10 -10
  65. package/src/config/markdown.ts +1 -1
  66. package/src/config/migrate-tui-config.ts +5 -5
  67. package/src/config/paths.ts +4 -4
  68. package/src/config/tui.ts +4 -4
  69. package/src/control/control.sql.ts +1 -1
  70. package/src/control/index.ts +1 -1
  71. package/src/control-plane/adaptors/worktree.ts +1 -1
  72. package/src/control-plane/session-proxy-middleware.ts +1 -1
  73. package/src/control-plane/workspace.sql.ts +1 -1
  74. package/src/control-plane/workspace.ts +7 -7
  75. package/src/file/index.ts +1 -1
  76. package/src/file/ripgrep.ts +2 -2
  77. package/src/file/watcher.ts +5 -5
  78. package/src/format/formatter.ts +1 -1
  79. package/src/ide/index.ts +3 -3
  80. package/src/index.ts +1 -1
  81. package/src/installation/index.ts +3 -3
  82. package/src/lsp/client.ts +3 -3
  83. package/src/lsp/index.ts +3 -3
  84. package/src/mcp/index.ts +4 -4
  85. package/src/permission/index.ts +2 -2
  86. package/src/permission/next.ts +10 -10
  87. package/src/plugin/codex.ts +1 -1
  88. package/src/plugin/copilot.ts +2 -2
  89. package/src/plugin/index.ts +1 -1
  90. package/src/project/bootstrap.ts +2 -2
  91. package/src/project/instance.ts +4 -4
  92. package/src/project/project.sql.ts +1 -1
  93. package/src/project/project.ts +5 -5
  94. package/src/project/state.ts +1 -1
  95. package/src/project/vcs.ts +4 -4
  96. package/src/provider/auth.ts +4 -4
  97. package/src/provider/error.ts +1 -1
  98. package/src/provider/models-snapshot.ts +2 -0
  99. package/src/provider/models.ts +1 -1
  100. package/src/provider/provider.ts +2 -2
  101. package/src/provider/transform.ts +2 -2
  102. package/src/pty/index.ts +5 -5
  103. package/src/question/index.ts +5 -5
  104. package/src/server/event.ts +1 -1
  105. package/src/server/mdns.ts +1 -1
  106. package/src/server/routes/global.ts +3 -3
  107. package/src/server/routes/permission.ts +1 -1
  108. package/src/server/routes/pty.ts +1 -1
  109. package/src/server/routes/session.ts +4 -4
  110. package/src/server/routes/tui.ts +1 -1
  111. package/src/server/server.ts +3 -3
  112. package/src/session/compaction.ts +7 -7
  113. package/src/session/index.ts +10 -10
  114. package/src/session/instruction.ts +1 -1
  115. package/src/session/llm.ts +11 -11
  116. package/src/session/message-v2.ts +10 -10
  117. package/src/session/message.ts +1 -1
  118. package/src/session/processor.ts +10 -10
  119. package/src/session/prompt.ts +8 -8
  120. package/src/session/retry.ts +2 -2
  121. package/src/session/revert.ts +1 -1
  122. package/src/session/session.sql.ts +3 -3
  123. package/src/session/status.ts +3 -3
  124. package/src/session/summary.ts +5 -5
  125. package/src/session/system.ts +1 -1
  126. package/src/session/todo.ts +2 -2
  127. package/src/share/share-next.ts +7 -7
  128. package/src/share/share.sql.ts +1 -1
  129. package/src/shell/shell.ts +3 -3
  130. package/src/skill/skill.ts +6 -6
  131. package/src/storage/db.ts +1 -1
  132. package/src/storage/storage.ts +1 -1
  133. package/src/tool/bash.ts +6 -6
  134. package/src/tool/edit.ts +1 -1
  135. package/src/tool/registry.ts +2 -2
  136. package/src/tool/skill.ts +1 -1
  137. package/src/tool/task.ts +3 -3
  138. package/src/util/array.ts +10 -0
  139. package/src/util/binary.ts +41 -0
  140. package/src/util/encode.ts +51 -0
  141. package/src/util/error.ts +54 -0
  142. package/src/util/identifier.ts +48 -0
  143. package/src/util/lazy.ts +4 -16
  144. package/src/util/path.ts +37 -0
  145. package/src/util/retry.ts +41 -0
  146. package/src/util/slug.ts +74 -0
  147. package/src/worktree/index.ts +3 -3
  148. package/AGENTS.md +0 -10
  149. package/BUN_SHELL_MIGRATION_PLAN.md +0 -136
  150. package/Dockerfile +0 -18
  151. package/README.md +0 -15
  152. package/bunfig.toml +0 -7
  153. package/drizzle.config.ts +0 -10
  154. package/script/build.ts +0 -224
  155. package/script/check-migrations.ts +0 -16
  156. package/script/postinstall.mjs +0 -131
  157. package/script/publish.ts +0 -181
  158. package/script/schema.ts +0 -63
  159. package/script/seed-e2e.ts +0 -50
  160. package/sst-env.d.ts +0 -10
  161. package/test/AGENTS.md +0 -81
  162. package/test/acp/agent-interface.test.ts +0 -51
  163. package/test/acp/event-subscription.test.ts +0 -683
  164. package/test/agent/agent.test.ts +0 -689
  165. package/test/bun.test.ts +0 -53
  166. package/test/cli/github-action.test.ts +0 -197
  167. package/test/cli/github-remote.test.ts +0 -80
  168. package/test/cli/import.test.ts +0 -38
  169. package/test/cli/plugin-auth-picker.test.ts +0 -120
  170. package/test/cli/tui/transcript.test.ts +0 -322
  171. package/test/config/agent-color.test.ts +0 -71
  172. package/test/config/config.test.ts +0 -1886
  173. package/test/config/fixtures/empty-frontmatter.md +0 -4
  174. package/test/config/fixtures/frontmatter.md +0 -28
  175. package/test/config/fixtures/markdown-header.md +0 -11
  176. package/test/config/fixtures/no-frontmatter.md +0 -1
  177. package/test/config/fixtures/weird-model-id.md +0 -13
  178. package/test/config/markdown.test.ts +0 -228
  179. package/test/config/tui.test.ts +0 -510
  180. package/test/control-plane/session-proxy-middleware.test.ts +0 -147
  181. package/test/control-plane/sse.test.ts +0 -56
  182. package/test/control-plane/workspace-server-sse.test.ts +0 -65
  183. package/test/control-plane/workspace-sync.test.ts +0 -97
  184. package/test/file/ignore.test.ts +0 -10
  185. package/test/file/index.test.ts +0 -394
  186. package/test/file/path-traversal.test.ts +0 -198
  187. package/test/file/ripgrep.test.ts +0 -39
  188. package/test/file/time.test.ts +0 -361
  189. package/test/fixture/db.ts +0 -11
  190. package/test/fixture/fixture.ts +0 -45
  191. package/test/fixture/lsp/fake-lsp-server.js +0 -77
  192. package/test/fixture/skills/agents-sdk/SKILL.md +0 -152
  193. package/test/fixture/skills/agents-sdk/references/callable.md +0 -92
  194. package/test/fixture/skills/cloudflare/SKILL.md +0 -211
  195. package/test/fixture/skills/index.json +0 -6
  196. package/test/ide/ide.test.ts +0 -82
  197. package/test/keybind.test.ts +0 -421
  198. package/test/lsp/client.test.ts +0 -95
  199. package/test/mcp/headers.test.ts +0 -153
  200. package/test/mcp/oauth-browser.test.ts +0 -249
  201. package/test/memory/abort-leak.test.ts +0 -136
  202. package/test/patch/patch.test.ts +0 -348
  203. package/test/permission/arity.test.ts +0 -33
  204. package/test/permission/next.test.ts +0 -689
  205. package/test/permission-task.test.ts +0 -319
  206. package/test/plugin/auth-override.test.ts +0 -44
  207. package/test/plugin/codex.test.ts +0 -123
  208. package/test/preload.ts +0 -80
  209. package/test/project/project.test.ts +0 -348
  210. package/test/project/worktree-remove.test.ts +0 -65
  211. package/test/provider/amazon-bedrock.test.ts +0 -446
  212. package/test/provider/copilot/convert-to-copilot-messages.test.ts +0 -523
  213. package/test/provider/copilot/copilot-chat-model.test.ts +0 -592
  214. package/test/provider/gitlab-duo.test.ts +0 -262
  215. package/test/provider/provider.test.ts +0 -2220
  216. package/test/provider/transform.test.ts +0 -2353
  217. package/test/pty/pty-output-isolation.test.ts +0 -140
  218. package/test/question/question.test.ts +0 -300
  219. package/test/scheduler.test.ts +0 -73
  220. package/test/server/global-session-list.test.ts +0 -89
  221. package/test/server/session-list.test.ts +0 -90
  222. package/test/server/session-select.test.ts +0 -78
  223. package/test/session/compaction.test.ts +0 -423
  224. package/test/session/instruction.test.ts +0 -170
  225. package/test/session/llm.test.ts +0 -667
  226. package/test/session/message-v2.test.ts +0 -924
  227. package/test/session/prompt.test.ts +0 -211
  228. package/test/session/retry.test.ts +0 -188
  229. package/test/session/revert-compact.test.ts +0 -285
  230. package/test/session/session.test.ts +0 -71
  231. package/test/session/structured-output-integration.test.ts +0 -233
  232. package/test/session/structured-output.test.ts +0 -385
  233. package/test/skill/discovery.test.ts +0 -110
  234. package/test/skill/skill.test.ts +0 -388
  235. package/test/snapshot/snapshot.test.ts +0 -1180
  236. package/test/storage/json-migration.test.ts +0 -846
  237. package/test/tool/__snapshots__/tool.test.ts.snap +0 -9
  238. package/test/tool/apply_patch.test.ts +0 -566
  239. package/test/tool/bash.test.ts +0 -402
  240. package/test/tool/edit.test.ts +0 -496
  241. package/test/tool/external-directory.test.ts +0 -127
  242. package/test/tool/fixtures/large-image.png +0 -0
  243. package/test/tool/fixtures/models-api.json +0 -38413
  244. package/test/tool/grep.test.ts +0 -110
  245. package/test/tool/question.test.ts +0 -107
  246. package/test/tool/read.test.ts +0 -504
  247. package/test/tool/registry.test.ts +0 -122
  248. package/test/tool/skill.test.ts +0 -112
  249. package/test/tool/truncation.test.ts +0 -160
  250. package/test/tool/webfetch.test.ts +0 -100
  251. package/test/tool/write.test.ts +0 -348
  252. package/test/util/filesystem.test.ts +0 -443
  253. package/test/util/format.test.ts +0 -59
  254. package/test/util/glob.test.ts +0 -164
  255. package/test/util/iife.test.ts +0 -36
  256. package/test/util/lazy.test.ts +0 -50
  257. package/test/util/lock.test.ts +0 -72
  258. package/test/util/process.test.ts +0 -59
  259. package/test/util/timeout.test.ts +0 -21
  260. package/test/util/wildcard.test.ts +0 -90
  261. package/tsconfig.json +0 -16
@@ -1,112 +0,0 @@
1
- import { describe, expect, test } from "bun:test"
2
- import path from "path"
3
- import { pathToFileURL } from "url"
4
- import type { PermissionNext } from "../../src/permission/next"
5
- import type { Tool } from "../../src/tool/tool"
6
- import { Instance } from "../../src/project/instance"
7
- import { SkillTool } from "../../src/tool/skill"
8
- import { tmpdir } from "../fixture/fixture"
9
-
10
- const baseCtx: Omit<Tool.Context, "ask"> = {
11
- sessionID: "test",
12
- messageID: "",
13
- callID: "",
14
- agent: "build",
15
- abort: AbortSignal.any([]),
16
- messages: [],
17
- metadata: () => {},
18
- }
19
-
20
- describe("tool.skill", () => {
21
- test("description lists skill location URL", async () => {
22
- await using tmp = await tmpdir({
23
- git: true,
24
- init: async (dir) => {
25
- const skillDir = path.join(dir, ".opencode", "skill", "tool-skill")
26
- await Bun.write(
27
- path.join(skillDir, "SKILL.md"),
28
- `---
29
- name: tool-skill
30
- description: Skill for tool tests.
31
- ---
32
-
33
- # Tool Skill
34
- `,
35
- )
36
- },
37
- })
38
-
39
- const home = process.env.OPENCODE_TEST_HOME
40
- process.env.OPENCODE_TEST_HOME = tmp.path
41
-
42
- try {
43
- await Instance.provide({
44
- directory: tmp.path,
45
- fn: async () => {
46
- const tool = await SkillTool.init()
47
- const skillPath = path.join(tmp.path, ".opencode", "skill", "tool-skill", "SKILL.md")
48
- expect(tool.description).toContain(`<location>${pathToFileURL(skillPath).href}</location>`)
49
- },
50
- })
51
- } finally {
52
- process.env.OPENCODE_TEST_HOME = home
53
- }
54
- })
55
-
56
- test("execute returns skill content block with files", async () => {
57
- await using tmp = await tmpdir({
58
- git: true,
59
- init: async (dir) => {
60
- const skillDir = path.join(dir, ".opencode", "skill", "tool-skill")
61
- await Bun.write(
62
- path.join(skillDir, "SKILL.md"),
63
- `---
64
- name: tool-skill
65
- description: Skill for tool tests.
66
- ---
67
-
68
- # Tool Skill
69
-
70
- Use this skill.
71
- `,
72
- )
73
- await Bun.write(path.join(skillDir, "scripts", "demo.txt"), "demo")
74
- },
75
- })
76
-
77
- const home = process.env.OPENCODE_TEST_HOME
78
- process.env.OPENCODE_TEST_HOME = tmp.path
79
-
80
- try {
81
- await Instance.provide({
82
- directory: tmp.path,
83
- fn: async () => {
84
- const tool = await SkillTool.init()
85
- const requests: Array<Omit<PermissionNext.Request, "id" | "sessionID" | "tool">> = []
86
- const ctx: Tool.Context = {
87
- ...baseCtx,
88
- ask: async (req) => {
89
- requests.push(req)
90
- },
91
- }
92
-
93
- const result = await tool.execute({ name: "tool-skill" }, ctx)
94
- const dir = path.join(tmp.path, ".opencode", "skill", "tool-skill")
95
- const file = path.resolve(dir, "scripts", "demo.txt")
96
-
97
- expect(requests.length).toBe(1)
98
- expect(requests[0].permission).toBe("skill")
99
- expect(requests[0].patterns).toContain("tool-skill")
100
- expect(requests[0].always).toContain("tool-skill")
101
-
102
- expect(result.metadata.dir).toBe(dir)
103
- expect(result.output).toContain(`<skill_content name="tool-skill">`)
104
- expect(result.output).toContain(`Base directory for this skill: ${pathToFileURL(dir).href}`)
105
- expect(result.output).toContain(`<file>${file}</file>`)
106
- },
107
- })
108
- } finally {
109
- process.env.OPENCODE_TEST_HOME = home
110
- }
111
- })
112
- })
@@ -1,160 +0,0 @@
1
- import { describe, test, expect, afterAll } from "bun:test"
2
- import { Truncate } from "../../src/tool/truncation"
3
- import { Identifier } from "../../src/id/id"
4
- import { Filesystem } from "../../src/util/filesystem"
5
- import fs from "fs/promises"
6
- import path from "path"
7
-
8
- const FIXTURES_DIR = path.join(import.meta.dir, "fixtures")
9
-
10
- describe("Truncate", () => {
11
- describe("output", () => {
12
- test("truncates large json file by bytes", async () => {
13
- const content = await Filesystem.readText(path.join(FIXTURES_DIR, "models-api.json"))
14
- const result = await Truncate.output(content)
15
-
16
- expect(result.truncated).toBe(true)
17
- expect(result.content).toContain("truncated...")
18
- if (result.truncated) expect(result.outputPath).toBeDefined()
19
- })
20
-
21
- test("returns content unchanged when under limits", async () => {
22
- const content = "line1\nline2\nline3"
23
- const result = await Truncate.output(content)
24
-
25
- expect(result.truncated).toBe(false)
26
- expect(result.content).toBe(content)
27
- })
28
-
29
- test("truncates by line count", async () => {
30
- const lines = Array.from({ length: 100 }, (_, i) => `line${i}`).join("\n")
31
- const result = await Truncate.output(lines, { maxLines: 10 })
32
-
33
- expect(result.truncated).toBe(true)
34
- expect(result.content).toContain("...90 lines truncated...")
35
- })
36
-
37
- test("truncates by byte count", async () => {
38
- const content = "a".repeat(1000)
39
- const result = await Truncate.output(content, { maxBytes: 100 })
40
-
41
- expect(result.truncated).toBe(true)
42
- expect(result.content).toContain("truncated...")
43
- })
44
-
45
- test("truncates from head by default", async () => {
46
- const lines = Array.from({ length: 10 }, (_, i) => `line${i}`).join("\n")
47
- const result = await Truncate.output(lines, { maxLines: 3 })
48
-
49
- expect(result.truncated).toBe(true)
50
- expect(result.content).toContain("line0")
51
- expect(result.content).toContain("line1")
52
- expect(result.content).toContain("line2")
53
- expect(result.content).not.toContain("line9")
54
- })
55
-
56
- test("truncates from tail when direction is tail", async () => {
57
- const lines = Array.from({ length: 10 }, (_, i) => `line${i}`).join("\n")
58
- const result = await Truncate.output(lines, { maxLines: 3, direction: "tail" })
59
-
60
- expect(result.truncated).toBe(true)
61
- expect(result.content).toContain("line7")
62
- expect(result.content).toContain("line8")
63
- expect(result.content).toContain("line9")
64
- expect(result.content).not.toContain("line0")
65
- })
66
-
67
- test("uses default MAX_LINES and MAX_BYTES", () => {
68
- expect(Truncate.MAX_LINES).toBe(2000)
69
- expect(Truncate.MAX_BYTES).toBe(50 * 1024)
70
- })
71
-
72
- test("large single-line file truncates with byte message", async () => {
73
- const content = await Filesystem.readText(path.join(FIXTURES_DIR, "models-api.json"))
74
- const result = await Truncate.output(content)
75
-
76
- expect(result.truncated).toBe(true)
77
- expect(result.content).toContain("bytes truncated...")
78
- expect(Buffer.byteLength(content, "utf-8")).toBeGreaterThan(Truncate.MAX_BYTES)
79
- })
80
-
81
- test("writes full output to file when truncated", async () => {
82
- const lines = Array.from({ length: 100 }, (_, i) => `line${i}`).join("\n")
83
- const result = await Truncate.output(lines, { maxLines: 10 })
84
-
85
- expect(result.truncated).toBe(true)
86
- expect(result.content).toContain("The tool call succeeded but the output was truncated")
87
- expect(result.content).toContain("Grep")
88
- if (!result.truncated) throw new Error("expected truncated")
89
- expect(result.outputPath).toBeDefined()
90
- expect(result.outputPath).toContain("tool_")
91
-
92
- const written = await Filesystem.readText(result.outputPath!)
93
- expect(written).toBe(lines)
94
- })
95
-
96
- test("suggests Task tool when agent has task permission", async () => {
97
- const lines = Array.from({ length: 100 }, (_, i) => `line${i}`).join("\n")
98
- const agent = { permission: [{ permission: "task", pattern: "*", action: "allow" as const }] }
99
- const result = await Truncate.output(lines, { maxLines: 10 }, agent as any)
100
-
101
- expect(result.truncated).toBe(true)
102
- expect(result.content).toContain("Grep")
103
- expect(result.content).toContain("Task tool")
104
- })
105
-
106
- test("omits Task tool hint when agent lacks task permission", async () => {
107
- const lines = Array.from({ length: 100 }, (_, i) => `line${i}`).join("\n")
108
- const agent = { permission: [{ permission: "task", pattern: "*", action: "deny" as const }] }
109
- const result = await Truncate.output(lines, { maxLines: 10 }, agent as any)
110
-
111
- expect(result.truncated).toBe(true)
112
- expect(result.content).toContain("Grep")
113
- expect(result.content).not.toContain("Task tool")
114
- })
115
-
116
- test("does not write file when not truncated", async () => {
117
- const content = "short content"
118
- const result = await Truncate.output(content)
119
-
120
- expect(result.truncated).toBe(false)
121
- if (result.truncated) throw new Error("expected not truncated")
122
- expect("outputPath" in result).toBe(false)
123
- })
124
- })
125
-
126
- describe("cleanup", () => {
127
- const DAY_MS = 24 * 60 * 60 * 1000
128
- let oldFile: string
129
- let recentFile: string
130
-
131
- afterAll(async () => {
132
- await fs.unlink(oldFile).catch(() => {})
133
- await fs.unlink(recentFile).catch(() => {})
134
- })
135
-
136
- test("deletes files older than 7 days and preserves recent files", async () => {
137
- await fs.mkdir(Truncate.DIR, { recursive: true })
138
-
139
- // Create an old file (10 days ago)
140
- const oldTimestamp = Date.now() - 10 * DAY_MS
141
- const oldId = Identifier.create("tool", false, oldTimestamp)
142
- oldFile = path.join(Truncate.DIR, oldId)
143
- await Filesystem.write(oldFile, "old content")
144
-
145
- // Create a recent file (3 days ago)
146
- const recentTimestamp = Date.now() - 3 * DAY_MS
147
- const recentId = Identifier.create("tool", false, recentTimestamp)
148
- recentFile = path.join(Truncate.DIR, recentId)
149
- await Filesystem.write(recentFile, "recent content")
150
-
151
- await Truncate.cleanup()
152
-
153
- // Old file should be deleted
154
- expect(await Filesystem.exists(oldFile)).toBe(false)
155
-
156
- // Recent file should still exist
157
- expect(await Filesystem.exists(recentFile)).toBe(true)
158
- })
159
- })
160
- })
@@ -1,100 +0,0 @@
1
- import { describe, expect, test } from "bun:test"
2
- import path from "path"
3
- import { Instance } from "../../src/project/instance"
4
- import { WebFetchTool } from "../../src/tool/webfetch"
5
-
6
- const projectRoot = path.join(import.meta.dir, "../..")
7
-
8
- const ctx = {
9
- sessionID: "test",
10
- messageID: "message",
11
- callID: "",
12
- agent: "build",
13
- abort: AbortSignal.any([]),
14
- messages: [],
15
- metadata: () => {},
16
- ask: async () => {},
17
- }
18
-
19
- async function withFetch(
20
- mockFetch: (input: string | URL | Request, init?: RequestInit) => Promise<Response>,
21
- fn: () => Promise<void>,
22
- ) {
23
- const originalFetch = globalThis.fetch
24
- globalThis.fetch = mockFetch as unknown as typeof fetch
25
- try {
26
- await fn()
27
- } finally {
28
- globalThis.fetch = originalFetch
29
- }
30
- }
31
-
32
- describe("tool.webfetch", () => {
33
- test("returns image responses as file attachments", async () => {
34
- const bytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10])
35
- await withFetch(
36
- async () => new Response(bytes, { status: 200, headers: { "content-type": "IMAGE/PNG; charset=binary" } }),
37
- async () => {
38
- await Instance.provide({
39
- directory: projectRoot,
40
- fn: async () => {
41
- const webfetch = await WebFetchTool.init()
42
- const result = await webfetch.execute({ url: "https://example.com/image.png", format: "markdown" }, ctx)
43
- expect(result.output).toBe("Image fetched successfully")
44
- expect(result.attachments).toBeDefined()
45
- expect(result.attachments?.length).toBe(1)
46
- expect(result.attachments?.[0].type).toBe("file")
47
- expect(result.attachments?.[0].mime).toBe("image/png")
48
- expect(result.attachments?.[0].url.startsWith("data:image/png;base64,")).toBe(true)
49
- expect(result.attachments?.[0]).not.toHaveProperty("id")
50
- expect(result.attachments?.[0]).not.toHaveProperty("sessionID")
51
- expect(result.attachments?.[0]).not.toHaveProperty("messageID")
52
- },
53
- })
54
- },
55
- )
56
- })
57
-
58
- test("keeps svg as text output", async () => {
59
- const svg = '<svg xmlns="http://www.w3.org/2000/svg"><text>hello</text></svg>'
60
- await withFetch(
61
- async () =>
62
- new Response(svg, {
63
- status: 200,
64
- headers: { "content-type": "image/svg+xml; charset=UTF-8" },
65
- }),
66
- async () => {
67
- await Instance.provide({
68
- directory: projectRoot,
69
- fn: async () => {
70
- const webfetch = await WebFetchTool.init()
71
- const result = await webfetch.execute({ url: "https://example.com/image.svg", format: "html" }, ctx)
72
- expect(result.output).toContain("<svg")
73
- expect(result.attachments).toBeUndefined()
74
- },
75
- })
76
- },
77
- )
78
- })
79
-
80
- test("keeps text responses as text output", async () => {
81
- await withFetch(
82
- async () =>
83
- new Response("hello from webfetch", {
84
- status: 200,
85
- headers: { "content-type": "text/plain; charset=utf-8" },
86
- }),
87
- async () => {
88
- await Instance.provide({
89
- directory: projectRoot,
90
- fn: async () => {
91
- const webfetch = await WebFetchTool.init()
92
- const result = await webfetch.execute({ url: "https://example.com/file.txt", format: "text" }, ctx)
93
- expect(result.output).toBe("hello from webfetch")
94
- expect(result.attachments).toBeUndefined()
95
- },
96
- })
97
- },
98
- )
99
- })
100
- })