todowrite.ts 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. export * as TodoWriteTool from "./todowrite"
  2. import { ToolFailure } from "@kirincode-ai/llm"
  3. import { Effect, Layer, Schema } from "effect"
  4. import { makeLocationNode } from "../effect/app-node"
  5. import { PermissionV2 } from "../permission"
  6. import { SessionTodo } from "../session/todo"
  7. import { ToolRegistry } from "./registry"
  8. import { Tool } from "./tool"
  9. import { Tools } from "./tools"
  10. export const name = "todowrite"
  11. export const Input = Schema.Struct({
  12. todos: Schema.Array(SessionTodo.Info).annotate({ description: "The updated todo list" }),
  13. })
  14. export const Output = Schema.Struct({
  15. todos: Schema.Array(SessionTodo.Info),
  16. })
  17. export type Output = typeof Output.Type
  18. export const toModelOutput = (output: Output) => JSON.stringify(output.todos, null, 2)
  19. const layer = Layer.effectDiscard(
  20. Effect.gen(function* () {
  21. const tools = yield* Tools.Service
  22. const todos = yield* SessionTodo.Service
  23. const permission = yield* PermissionV2.Service
  24. yield* tools
  25. .register({
  26. [name]: Tool.make({
  27. description:
  28. "Create and maintain a structured task list for the current coding session. Use it to track progress during multi-step work and keep todo statuses current.",
  29. input: Input,
  30. output: Output,
  31. toModelOutput: ({ output }) => [{ type: "text", text: toModelOutput(output) }],
  32. execute: (input, context) =>
  33. Effect.gen(function* () {
  34. yield* permission.assert({
  35. action: name,
  36. resources: ["*"],
  37. save: ["*"],
  38. sessionID: context.sessionID,
  39. agent: context.agent,
  40. source: { type: "tool", messageID: context.assistantMessageID, callID: context.toolCallID },
  41. })
  42. yield* todos.update({ sessionID: context.sessionID, todos: input.todos })
  43. return { todos: input.todos }
  44. }).pipe(Effect.mapError(() => new ToolFailure({ message: "Unable to update todos" }))),
  45. }),
  46. })
  47. .pipe(Effect.orDie)
  48. }),
  49. )
  50. export const node = makeLocationNode({
  51. name: "tool/todowrite",
  52. layer,
  53. deps: [ToolRegistry.node, PermissionV2.node, SessionTodo.node],
  54. })