1
0

tool-execute.test.ts 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import { expect, test } from "bun:test"
  2. import { ExecuteTool } from "@opencode-ai/core/tool/execute"
  3. import { Tool } from "@opencode-ai/core/tool/tool"
  4. import { Agent } from "@opencode-ai/schema/agent"
  5. import { Session } from "@opencode-ai/schema/session"
  6. import { SessionMessage } from "@opencode-ai/schema/session-message"
  7. import { Effect, Schema } from "effect"
  8. test("execute preserves successful results with visible unhandled rejections", async () => {
  9. const child = Tool.make({
  10. description: "Always fail",
  11. input: Schema.Struct({}),
  12. output: Schema.String,
  13. execute: () => Effect.fail(new Tool.Failure({ message: "Lookup refused" })),
  14. })
  15. const execute = ExecuteTool.create(new Map([["fail", { tool: child, name: "fail" }]]))
  16. const result = await Effect.runPromise(
  17. Tool.settle(
  18. execute,
  19. {
  20. type: "tool-call",
  21. id: "call_execute",
  22. name: "execute",
  23. input: { code: `tools.fail({}); return "done"` },
  24. },
  25. {
  26. sessionID: Session.ID.make("ses_execute"),
  27. agent: Agent.ID.make("build"),
  28. messageID: SessionMessage.ID.make("msg_execute"),
  29. callID: "call_execute",
  30. progress: () => Effect.void,
  31. },
  32. ),
  33. )
  34. expect(result.structured).toEqual({ toolCalls: [{ tool: "fail", status: "error" }] })
  35. expect(result.content).toEqual([
  36. {
  37. type: "text",
  38. text: [
  39. "done",
  40. "",
  41. "Warnings:",
  42. "- [ToolFailure] Unhandled rejection from an un-awaited promise: Lookup refused",
  43. ].join("\n"),
  44. },
  45. ])
  46. })
  47. test("execute supports callable namespace tools", async () => {
  48. const callable = Tool.make({
  49. description: "Administer Slack",
  50. input: Schema.Struct({}),
  51. output: Schema.String,
  52. execute: () => Effect.succeed("admin"),
  53. })
  54. const child = Tool.make({
  55. description: "Create a Slack resource",
  56. input: Schema.Struct({}),
  57. output: Schema.String,
  58. execute: () => Effect.succeed("created"),
  59. })
  60. const execute = ExecuteTool.create(
  61. new Map([
  62. ["slack_admin", { tool: callable, name: "admin", namespace: "slack" }],
  63. ["slack_admin_create", { tool: child, name: "create", namespace: "slack.admin" }],
  64. ]),
  65. )
  66. const result = await Effect.runPromise(
  67. Tool.settle(
  68. execute,
  69. {
  70. type: "tool-call",
  71. id: "call_execute",
  72. name: "execute",
  73. input: { code: "return [await tools.slack.admin({}), await tools.slack.admin.create({})]" },
  74. },
  75. {
  76. sessionID: Session.ID.make("ses_execute"),
  77. agent: Agent.ID.make("build"),
  78. messageID: SessionMessage.ID.make("msg_execute"),
  79. callID: "call_execute",
  80. progress: () => Effect.void,
  81. },
  82. ),
  83. )
  84. expect(result.structured).toEqual({
  85. toolCalls: [
  86. { tool: "slack.admin", status: "completed" },
  87. { tool: "slack.admin.create", status: "completed" },
  88. ],
  89. })
  90. expect(result.content).toEqual([{ type: "text", text: '[\n "admin",\n "created"\n]' }])
  91. })