agent.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. export * as AgentV2 from "./agent"
  2. import { Array, Context, Effect, Layer, Types } from "effect"
  3. import { Agent } from "@opencode-ai/schema/agent"
  4. import { State } from "./state"
  5. export const ID = Agent.ID
  6. export type ID = typeof ID.Type
  7. export const defaultID = ID.make("build")
  8. export const Color = Agent.Color
  9. export const Info = Agent.Info
  10. export type Info = Agent.Info
  11. export interface Selection {
  12. readonly id: ID
  13. readonly info: Info | undefined
  14. }
  15. type Data = {
  16. agents: Map<ID, Types.DeepMutable<Info>>
  17. default?: ID
  18. }
  19. export type Draft = {
  20. list: () => readonly Info[]
  21. get: (id: ID) => Info | undefined
  22. default: (id: ID | undefined) => void
  23. update: (id: ID, fn: (agent: Types.DeepMutable<Info>) => void) => void
  24. remove: (id: ID) => void
  25. }
  26. export interface Interface extends State.Transformable<Draft> {
  27. readonly get: (id: ID) => Effect.Effect<Info | undefined>
  28. readonly default: () => Effect.Effect<Info | undefined>
  29. readonly resolve: (id?: ID | string) => Effect.Effect<Info | undefined>
  30. readonly select: (id?: ID | string) => Effect.Effect<Selection>
  31. readonly all: () => Effect.Effect<Info[]>
  32. }
  33. export class Service extends Context.Service<Service, Interface>()("@opencode/v2/Agent") {}
  34. export const layer = Layer.effect(
  35. Service,
  36. Effect.gen(function* () {
  37. const state = State.create<Data, Draft>({
  38. initial: () => ({ agents: new Map() }),
  39. draft: (draft) => ({
  40. list: () => Array.fromIterable(draft.agents.values()) as Info[],
  41. get: (id) => draft.agents.get(id),
  42. default: (id) => {
  43. draft.default = id
  44. },
  45. update: (id, fn) => {
  46. const current = draft.agents.get(id) ?? (Info.empty(id) as Types.DeepMutable<Info>)
  47. if (!draft.agents.has(id)) draft.agents.set(id, current)
  48. fn(current)
  49. current.id = id
  50. },
  51. remove: (id) => {
  52. draft.agents.delete(id)
  53. },
  54. }),
  55. })
  56. const selectable = (agent: Info | undefined) =>
  57. agent && agent.mode !== "subagent" && !agent.hidden ? agent : undefined
  58. const selectedDefault = () => {
  59. const data = state.get()
  60. const configured = data.default ? selectable(data.agents.get(data.default)) : undefined
  61. if (configured) return configured
  62. const build = selectable(data.agents.get(ID.make("build")))
  63. if (build) return build
  64. for (const agent of data.agents.values()) {
  65. const fallback = selectable(agent)
  66. if (fallback) return fallback
  67. }
  68. }
  69. return Service.of({
  70. transform: state.transform,
  71. reload: state.reload,
  72. get: Effect.fn("AgentV2.get")(function* (id) {
  73. return state.get().agents.get(id)
  74. }),
  75. default: Effect.fn("AgentV2.default")(function* () {
  76. return selectedDefault()
  77. }),
  78. resolve: Effect.fn("AgentV2.resolve")(function* (id) {
  79. if (id !== undefined) return state.get().agents.get(ID.make(id))
  80. return selectedDefault()
  81. }),
  82. select: Effect.fn("AgentV2.select")(function* (id) {
  83. if (id !== undefined) {
  84. const selected = ID.make(id)
  85. return { id: selected, info: state.get().agents.get(selected) }
  86. }
  87. const info = selectedDefault()
  88. return { id: info?.id ?? defaultID, info }
  89. }),
  90. all: Effect.fn("AgentV2.all")(function* () {
  91. return Array.fromIterable(state.get().agents.values())
  92. }),
  93. })
  94. }),
  95. )
  96. export const locationLayer = layer