@llm4ts/core 0.1.0

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 (284) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +47 -0
  3. package/dist/AgentSession.d.ts +66 -0
  4. package/dist/AgentSession.d.ts.map +1 -0
  5. package/dist/AgentSession.js +53 -0
  6. package/dist/AgentSession.js.map +1 -0
  7. package/dist/Capability.d.ts +101 -0
  8. package/dist/Capability.d.ts.map +1 -0
  9. package/dist/Capability.js +210 -0
  10. package/dist/Capability.js.map +1 -0
  11. package/dist/Connector.d.ts +69 -0
  12. package/dist/Connector.d.ts.map +1 -0
  13. package/dist/Connector.js +86 -0
  14. package/dist/Connector.js.map +1 -0
  15. package/dist/ConnectorConfig.d.ts +70 -0
  16. package/dist/ConnectorConfig.d.ts.map +1 -0
  17. package/dist/ConnectorConfig.js +110 -0
  18. package/dist/ConnectorConfig.js.map +1 -0
  19. package/dist/ConnectorRegistry.d.ts +28 -0
  20. package/dist/ConnectorRegistry.d.ts.map +1 -0
  21. package/dist/ConnectorRegistry.js +87 -0
  22. package/dist/ConnectorRegistry.js.map +1 -0
  23. package/dist/ContextManagement.d.ts +78 -0
  24. package/dist/ContextManagement.d.ts.map +1 -0
  25. package/dist/ContextManagement.js +221 -0
  26. package/dist/ContextManagement.js.map +1 -0
  27. package/dist/Conversation.d.ts +18 -0
  28. package/dist/Conversation.d.ts.map +1 -0
  29. package/dist/Conversation.js +17 -0
  30. package/dist/Conversation.js.map +1 -0
  31. package/dist/Errors.d.ts +64 -0
  32. package/dist/Errors.d.ts.map +1 -0
  33. package/dist/Errors.js +76 -0
  34. package/dist/Errors.js.map +1 -0
  35. package/dist/HttpClient.d.ts +43 -0
  36. package/dist/HttpClient.d.ts.map +1 -0
  37. package/dist/HttpClient.js +99 -0
  38. package/dist/HttpClient.js.map +1 -0
  39. package/dist/LlmService.d.ts +24 -0
  40. package/dist/LlmService.d.ts.map +1 -0
  41. package/dist/LlmService.js +4 -0
  42. package/dist/LlmService.js.map +1 -0
  43. package/dist/Models.d.ts +138 -0
  44. package/dist/Models.d.ts.map +1 -0
  45. package/dist/Models.js +209 -0
  46. package/dist/Models.js.map +1 -0
  47. package/dist/ProcessExecutor.d.ts +57 -0
  48. package/dist/ProcessExecutor.d.ts.map +1 -0
  49. package/dist/ProcessExecutor.js +71 -0
  50. package/dist/ProcessExecutor.js.map +1 -0
  51. package/dist/RateLimiter.d.ts +44 -0
  52. package/dist/RateLimiter.d.ts.map +1 -0
  53. package/dist/RateLimiter.js +155 -0
  54. package/dist/RateLimiter.js.map +1 -0
  55. package/dist/Streaming.d.ts +17 -0
  56. package/dist/Streaming.d.ts.map +1 -0
  57. package/dist/Streaming.js +65 -0
  58. package/dist/Streaming.js.map +1 -0
  59. package/dist/StructuredOutput.d.ts +10 -0
  60. package/dist/StructuredOutput.d.ts.map +1 -0
  61. package/dist/StructuredOutput.js +97 -0
  62. package/dist/StructuredOutput.js.map +1 -0
  63. package/dist/TemporaryFiles.d.ts +20 -0
  64. package/dist/TemporaryFiles.d.ts.map +1 -0
  65. package/dist/TemporaryFiles.js +18 -0
  66. package/dist/TemporaryFiles.js.map +1 -0
  67. package/dist/UsageLimits.d.ts +5 -0
  68. package/dist/UsageLimits.d.ts.map +1 -0
  69. package/dist/UsageLimits.js +187 -0
  70. package/dist/UsageLimits.js.map +1 -0
  71. package/dist/eval/Checks.d.ts +6 -0
  72. package/dist/eval/Checks.d.ts.map +1 -0
  73. package/dist/eval/Checks.js +39 -0
  74. package/dist/eval/Checks.js.map +1 -0
  75. package/dist/eval/Eval.d.ts +57 -0
  76. package/dist/eval/Eval.d.ts.map +1 -0
  77. package/dist/eval/Eval.js +91 -0
  78. package/dist/eval/Eval.js.map +1 -0
  79. package/dist/eval/EvalSuite.d.ts +21 -0
  80. package/dist/eval/EvalSuite.d.ts.map +1 -0
  81. package/dist/eval/EvalSuite.js +30 -0
  82. package/dist/eval/EvalSuite.js.map +1 -0
  83. package/dist/eval/Evaluator.d.ts +10 -0
  84. package/dist/eval/Evaluator.d.ts.map +1 -0
  85. package/dist/eval/Evaluator.js +14 -0
  86. package/dist/eval/Evaluator.js.map +1 -0
  87. package/dist/eval/Judge.d.ts +15 -0
  88. package/dist/eval/Judge.d.ts.map +1 -0
  89. package/dist/eval/Judge.js +73 -0
  90. package/dist/eval/Judge.js.map +1 -0
  91. package/dist/observability/MeteredLlmService.d.ts +16 -0
  92. package/dist/observability/MeteredLlmService.d.ts.map +1 -0
  93. package/dist/observability/MeteredLlmService.js +66 -0
  94. package/dist/observability/MeteredLlmService.js.map +1 -0
  95. package/dist/observability/Metrics.d.ts +71 -0
  96. package/dist/observability/Metrics.d.ts.map +1 -0
  97. package/dist/observability/Metrics.js +147 -0
  98. package/dist/observability/Metrics.js.map +1 -0
  99. package/dist/observability/Redaction.d.ts +8 -0
  100. package/dist/observability/Redaction.d.ts.map +1 -0
  101. package/dist/observability/Redaction.js +38 -0
  102. package/dist/observability/Redaction.js.map +1 -0
  103. package/dist/observability/StreamRecorder.d.ts +9 -0
  104. package/dist/observability/StreamRecorder.d.ts.map +1 -0
  105. package/dist/observability/StreamRecorder.js +10 -0
  106. package/dist/observability/StreamRecorder.js.map +1 -0
  107. package/dist/observability/StructuredLogger.d.ts +31 -0
  108. package/dist/observability/StructuredLogger.d.ts.map +1 -0
  109. package/dist/observability/StructuredLogger.js +34 -0
  110. package/dist/observability/StructuredLogger.js.map +1 -0
  111. package/dist/observability/Tracing.d.ts +38 -0
  112. package/dist/observability/Tracing.d.ts.map +1 -0
  113. package/dist/observability/Tracing.js +75 -0
  114. package/dist/observability/Tracing.js.map +1 -0
  115. package/dist/providers/AnthropicModels.d.ts +95 -0
  116. package/dist/providers/AnthropicModels.d.ts.map +1 -0
  117. package/dist/providers/AnthropicModels.js +83 -0
  118. package/dist/providers/AnthropicModels.js.map +1 -0
  119. package/dist/providers/AnthropicProvider.d.ts +13 -0
  120. package/dist/providers/AnthropicProvider.d.ts.map +1 -0
  121. package/dist/providers/AnthropicProvider.js +177 -0
  122. package/dist/providers/AnthropicProvider.js.map +1 -0
  123. package/dist/providers/AntigravityConnector.d.ts +7 -0
  124. package/dist/providers/AntigravityConnector.d.ts.map +1 -0
  125. package/dist/providers/AntigravityConnector.js +52 -0
  126. package/dist/providers/AntigravityConnector.js.map +1 -0
  127. package/dist/providers/ClaudeAgentSession.d.ts +11 -0
  128. package/dist/providers/ClaudeAgentSession.d.ts.map +1 -0
  129. package/dist/providers/ClaudeAgentSession.js +158 -0
  130. package/dist/providers/ClaudeAgentSession.js.map +1 -0
  131. package/dist/providers/ClaudeCliConnector.d.ts +9 -0
  132. package/dist/providers/ClaudeCliConnector.d.ts.map +1 -0
  133. package/dist/providers/ClaudeCliConnector.js +123 -0
  134. package/dist/providers/ClaudeCliConnector.js.map +1 -0
  135. package/dist/providers/CliSupport.d.ts +21 -0
  136. package/dist/providers/CliSupport.d.ts.map +1 -0
  137. package/dist/providers/CliSupport.js +62 -0
  138. package/dist/providers/CliSupport.js.map +1 -0
  139. package/dist/providers/CodexConnector.d.ts +12 -0
  140. package/dist/providers/CodexConnector.d.ts.map +1 -0
  141. package/dist/providers/CodexConnector.js +175 -0
  142. package/dist/providers/CodexConnector.js.map +1 -0
  143. package/dist/providers/ConnectorFactories.d.ts +15 -0
  144. package/dist/providers/ConnectorFactories.d.ts.map +1 -0
  145. package/dist/providers/ConnectorFactories.js +74 -0
  146. package/dist/providers/ConnectorFactories.js.map +1 -0
  147. package/dist/providers/CopilotConnector.d.ts +6 -0
  148. package/dist/providers/CopilotConnector.d.ts.map +1 -0
  149. package/dist/providers/CopilotConnector.js +35 -0
  150. package/dist/providers/CopilotConnector.js.map +1 -0
  151. package/dist/providers/CursorConnector.d.ts +8 -0
  152. package/dist/providers/CursorConnector.d.ts.map +1 -0
  153. package/dist/providers/CursorConnector.js +82 -0
  154. package/dist/providers/CursorConnector.js.map +1 -0
  155. package/dist/providers/GeminiApiProvider.d.ts +9 -0
  156. package/dist/providers/GeminiApiProvider.d.ts.map +1 -0
  157. package/dist/providers/GeminiApiProvider.js +181 -0
  158. package/dist/providers/GeminiApiProvider.js.map +1 -0
  159. package/dist/providers/GeminiCliProvider.d.ts +106 -0
  160. package/dist/providers/GeminiCliProvider.d.ts.map +1 -0
  161. package/dist/providers/GeminiCliProvider.js +596 -0
  162. package/dist/providers/GeminiCliProvider.js.map +1 -0
  163. package/dist/providers/GeminiModels.d.ts +92 -0
  164. package/dist/providers/GeminiModels.d.ts.map +1 -0
  165. package/dist/providers/GeminiModels.js +80 -0
  166. package/dist/providers/GeminiModels.js.map +1 -0
  167. package/dist/providers/GrokCliConnector.d.ts +8 -0
  168. package/dist/providers/GrokCliConnector.d.ts.map +1 -0
  169. package/dist/providers/GrokCliConnector.js +82 -0
  170. package/dist/providers/GrokCliConnector.js.map +1 -0
  171. package/dist/providers/LmStudioModels.d.ts +114 -0
  172. package/dist/providers/LmStudioModels.d.ts.map +1 -0
  173. package/dist/providers/LmStudioModels.js +102 -0
  174. package/dist/providers/LmStudioModels.js.map +1 -0
  175. package/dist/providers/LmStudioProvider.d.ts +13 -0
  176. package/dist/providers/LmStudioProvider.d.ts.map +1 -0
  177. package/dist/providers/LmStudioProvider.js +158 -0
  178. package/dist/providers/LmStudioProvider.js.map +1 -0
  179. package/dist/providers/MockProvider.d.ts +6 -0
  180. package/dist/providers/MockProvider.d.ts.map +1 -0
  181. package/dist/providers/MockProvider.js +271 -0
  182. package/dist/providers/MockProvider.js.map +1 -0
  183. package/dist/providers/OllamaModels.d.ts +68 -0
  184. package/dist/providers/OllamaModels.d.ts.map +1 -0
  185. package/dist/providers/OllamaModels.js +59 -0
  186. package/dist/providers/OllamaModels.js.map +1 -0
  187. package/dist/providers/OllamaProvider.d.ts +7 -0
  188. package/dist/providers/OllamaProvider.d.ts.map +1 -0
  189. package/dist/providers/OllamaProvider.js +134 -0
  190. package/dist/providers/OllamaProvider.js.map +1 -0
  191. package/dist/providers/OpenAIModels.d.ts +139 -0
  192. package/dist/providers/OpenAIModels.d.ts.map +1 -0
  193. package/dist/providers/OpenAIModels.js +123 -0
  194. package/dist/providers/OpenAIModels.js.map +1 -0
  195. package/dist/providers/OpenAIProvider.d.ts +9 -0
  196. package/dist/providers/OpenAIProvider.d.ts.map +1 -0
  197. package/dist/providers/OpenAIProvider.js +185 -0
  198. package/dist/providers/OpenAIProvider.js.map +1 -0
  199. package/dist/providers/OpenCodeCliConnector.d.ts +8 -0
  200. package/dist/providers/OpenCodeCliConnector.d.ts.map +1 -0
  201. package/dist/providers/OpenCodeCliConnector.js +93 -0
  202. package/dist/providers/OpenCodeCliConnector.js.map +1 -0
  203. package/dist/providers/OpenCodeModels.d.ts +61 -0
  204. package/dist/providers/OpenCodeModels.d.ts.map +1 -0
  205. package/dist/providers/OpenCodeModels.js +53 -0
  206. package/dist/providers/OpenCodeModels.js.map +1 -0
  207. package/dist/providers/OpenCodeProvider.d.ts +10 -0
  208. package/dist/providers/OpenCodeProvider.d.ts.map +1 -0
  209. package/dist/providers/OpenCodeProvider.js +154 -0
  210. package/dist/providers/OpenCodeProvider.js.map +1 -0
  211. package/dist/providers/PiConnector.d.ts +8 -0
  212. package/dist/providers/PiConnector.d.ts.map +1 -0
  213. package/dist/providers/PiConnector.js +99 -0
  214. package/dist/providers/PiConnector.js.map +1 -0
  215. package/dist/tools/Tool.d.ts +83 -0
  216. package/dist/tools/Tool.d.ts.map +1 -0
  217. package/dist/tools/Tool.js +278 -0
  218. package/dist/tools/Tool.js.map +1 -0
  219. package/dist/tools/ToolCallingExecutor.d.ts +18 -0
  220. package/dist/tools/ToolCallingExecutor.d.ts.map +1 -0
  221. package/dist/tools/ToolCallingExecutor.js +64 -0
  222. package/dist/tools/ToolCallingExecutor.js.map +1 -0
  223. package/dist/tools/ToolRegistry.d.ts +45 -0
  224. package/dist/tools/ToolRegistry.d.ts.map +1 -0
  225. package/dist/tools/ToolRegistry.js +133 -0
  226. package/dist/tools/ToolRegistry.js.map +1 -0
  227. package/package.json +105 -0
  228. package/src/.gitkeep +1 -0
  229. package/src/AgentSession.ts +69 -0
  230. package/src/Capability.ts +240 -0
  231. package/src/Connector.ts +220 -0
  232. package/src/ConnectorConfig.ts +146 -0
  233. package/src/ConnectorRegistry.ts +184 -0
  234. package/src/ContextManagement.ts +372 -0
  235. package/src/Conversation.ts +21 -0
  236. package/src/Errors.ts +87 -0
  237. package/src/HttpClient.ts +202 -0
  238. package/src/LlmService.ts +45 -0
  239. package/src/Models.ts +250 -0
  240. package/src/ProcessExecutor.ts +157 -0
  241. package/src/RateLimiter.ts +238 -0
  242. package/src/Streaming.ts +164 -0
  243. package/src/StructuredOutput.ts +138 -0
  244. package/src/TemporaryFiles.ts +51 -0
  245. package/src/UsageLimits.ts +242 -0
  246. package/src/eval/Checks.ts +68 -0
  247. package/src/eval/Eval.ts +112 -0
  248. package/src/eval/EvalSuite.ts +63 -0
  249. package/src/eval/Evaluator.ts +31 -0
  250. package/src/eval/Judge.ts +92 -0
  251. package/src/observability/MeteredLlmService.ts +156 -0
  252. package/src/observability/Metrics.ts +195 -0
  253. package/src/observability/Redaction.ts +55 -0
  254. package/src/observability/StreamRecorder.ts +32 -0
  255. package/src/observability/StructuredLogger.ts +62 -0
  256. package/src/observability/Tracing.ts +141 -0
  257. package/src/providers/AnthropicModels.ts +96 -0
  258. package/src/providers/AnthropicProvider.ts +320 -0
  259. package/src/providers/AntigravityConnector.ts +77 -0
  260. package/src/providers/ClaudeAgentSession.ts +226 -0
  261. package/src/providers/ClaudeCliConnector.ts +201 -0
  262. package/src/providers/CliSupport.ts +100 -0
  263. package/src/providers/CodexConnector.ts +251 -0
  264. package/src/providers/ConnectorFactories.ts +142 -0
  265. package/src/providers/CopilotConnector.ts +47 -0
  266. package/src/providers/CursorConnector.ts +120 -0
  267. package/src/providers/GeminiApiProvider.ts +337 -0
  268. package/src/providers/GeminiCliProvider.ts +793 -0
  269. package/src/providers/GeminiModels.ts +97 -0
  270. package/src/providers/GrokCliConnector.ts +117 -0
  271. package/src/providers/LmStudioModels.ts +117 -0
  272. package/src/providers/LmStudioProvider.ts +260 -0
  273. package/src/providers/MockProvider.ts +322 -0
  274. package/src/providers/OllamaModels.ts +64 -0
  275. package/src/providers/OllamaProvider.ts +236 -0
  276. package/src/providers/OpenAIModels.ts +152 -0
  277. package/src/providers/OpenAIProvider.ts +331 -0
  278. package/src/providers/OpenCodeCliConnector.ts +135 -0
  279. package/src/providers/OpenCodeModels.ts +60 -0
  280. package/src/providers/OpenCodeProvider.ts +278 -0
  281. package/src/providers/PiConnector.ts +142 -0
  282. package/src/tools/Tool.ts +379 -0
  283. package/src/tools/ToolCallingExecutor.ts +106 -0
  284. package/src/tools/ToolRegistry.ts +224 -0
@@ -0,0 +1,251 @@
1
+ import * as Effect from "effect/Effect"
2
+ import type * as Schema from "effect/Schema"
3
+ import * as Stream from "effect/Stream"
4
+ import { makeCliConnector, type CliConnectorShape } from "../Connector.ts"
5
+ import type { CliConnectorConfig } from "../ConnectorConfig.ts"
6
+ import { ProviderError, type LlmError } from "../Errors.ts"
7
+ import type { StructuredResult } from "../LlmService.ts"
8
+ import { ConnectorIds, LlmChunk, TokenUsage, type JsonSchema } from "../Models.ts"
9
+ import type { ProcessExecutorShape } from "../ProcessExecutor.ts"
10
+ import { collect } from "../Streaming.ts"
11
+ import type { TemporaryFilesShape } from "../TemporaryFiles.ts"
12
+ import { parseFromText, withSchemaHint } from "../StructuredOutput.ts"
13
+ import {
14
+ failClassifiedCliError,
15
+ isJsonRecord,
16
+ jsonField,
17
+ jsonIntField,
18
+ jsonStringField,
19
+ jsonText,
20
+ optionalModelArgs,
21
+ parseJsonLine,
22
+ sortedFlagArgs,
23
+ toolEventChunk,
24
+ usageEventChunk,
25
+ type JsonValue
26
+ } from "./CliSupport.ts"
27
+
28
+ export const codexExtraArgs = (config: CliConnectorConfig): ReadonlyArray<string> => {
29
+ const effectiveFlags = config.readOnly
30
+ ? {
31
+ ...config.flags,
32
+ sandbox: "read-only"
33
+ }
34
+ : config.flags
35
+ return [...optionalModelArgs(config.model), ...sortedFlagArgs(effectiveFlags)]
36
+ }
37
+
38
+ export const codexSchemaEnforceable = (schema: JsonSchema): boolean => {
39
+ const properties = schema.properties
40
+ return properties !== undefined && isJsonRecord(properties) && Object.keys(properties).length > 0
41
+ }
42
+
43
+ export const codexStrictSchema = (json: JsonValue): JsonValue => {
44
+ if (Array.isArray(json)) {
45
+ return json.map(codexStrictSchema)
46
+ }
47
+ if (!isJsonRecord(json)) {
48
+ return json
49
+ }
50
+
51
+ const recursed: Record<string, JsonValue> = {}
52
+ for (const [key, value] of Object.entries(json)) {
53
+ recursed[key] = codexStrictSchema(value)
54
+ }
55
+ const properties = recursed.properties
56
+ if (properties === undefined || !isJsonRecord(properties)) {
57
+ return recursed
58
+ }
59
+
60
+ const strict: Record<string, JsonValue> = {}
61
+ for (const [key, value] of Object.entries(recursed)) {
62
+ if (key !== "additionalProperties" && key !== "required") {
63
+ strict[key] = value
64
+ }
65
+ }
66
+ strict.additionalProperties = false
67
+ strict.required = Object.keys(properties)
68
+ return strict
69
+ }
70
+
71
+ export const parseCodexStreamLine = (line: string): ReadonlyArray<LlmChunk> => {
72
+ const json = parseJsonLine(line)
73
+ if (json === undefined) {
74
+ return []
75
+ }
76
+
77
+ switch (jsonStringField(json, "type")) {
78
+ case "item.completed": {
79
+ const item = jsonField(json, "item")
80
+ switch (jsonStringField(item, "type")) {
81
+ case "agent_message": {
82
+ const text = jsonStringField(item, "text")
83
+ return text === undefined || text.length === 0 ? [] : [LlmChunk.make({ delta: text })]
84
+ }
85
+ case "command_execution":
86
+ return [
87
+ toolEventChunk("Bash", {
88
+ command: jsonStringField(item, "command") ?? ""
89
+ })
90
+ ]
91
+ default:
92
+ return []
93
+ }
94
+ }
95
+ case "turn.completed": {
96
+ const usage = jsonField(json, "usage")
97
+ if (usage === undefined) {
98
+ return []
99
+ }
100
+ const prompt = jsonIntField(usage, "input_tokens") ?? 0
101
+ const completion = jsonIntField(usage, "output_tokens") ?? 0
102
+ return [
103
+ usageEventChunk(
104
+ undefined,
105
+ TokenUsage.make({
106
+ prompt,
107
+ completion,
108
+ total: prompt + completion
109
+ })
110
+ )
111
+ ]
112
+ }
113
+ case "error":
114
+ return [
115
+ LlmChunk.make({
116
+ delta: "",
117
+ metadata: {
118
+ codexError: jsonStringField(json, "message") ?? "codex error"
119
+ }
120
+ })
121
+ ]
122
+ case "turn.failed":
123
+ return [
124
+ LlmChunk.make({
125
+ delta: "",
126
+ metadata: {
127
+ codexError: jsonStringField(jsonField(json, "error"), "message") ?? "turn failed"
128
+ }
129
+ })
130
+ ]
131
+ default:
132
+ return []
133
+ }
134
+ }
135
+
136
+ export const makeCodexConnector = (
137
+ config: CliConnectorConfig,
138
+ executor: ProcessExecutorShape,
139
+ temporaryFiles: TemporaryFilesShape
140
+ ): CliConnectorShape => {
141
+ const cwd = config.workingDir ?? "."
142
+ const extraArgs = codexExtraArgs(config)
143
+
144
+ const stampModel = (chunk: LlmChunk): LlmChunk =>
145
+ chunk.usage === undefined || chunk.metadata.model !== undefined || config.model === undefined
146
+ ? chunk
147
+ : LlmChunk.make({
148
+ ...chunk,
149
+ metadata: {
150
+ ...chunk.metadata,
151
+ model: config.model
152
+ }
153
+ })
154
+
155
+ const rawStream = (
156
+ argv: ReadonlyArray<string>,
157
+ prompt: string
158
+ ): Stream.Stream<LlmChunk, LlmError> =>
159
+ executor.runStreamingWithStdin(argv, cwd, config.envVars, prompt).pipe(
160
+ Stream.flatMap((line) => Stream.fromIterable(parseCodexStreamLine(line))),
161
+ Stream.map(stampModel)
162
+ )
163
+
164
+ const failCodexChunk = (chunk: LlmChunk): Effect.Effect<LlmChunk, LlmError> => {
165
+ const message = chunk.metadata.codexError
166
+ return message === undefined
167
+ ? Effect.succeed(chunk)
168
+ : failClassifiedCliError("codex", "codex error", message)
169
+ }
170
+
171
+ const complete = Effect.fn("@llm4ts/core/providers/CodexConnector.complete")(function* (
172
+ prompt: string
173
+ ) {
174
+ const result = yield* executor.runWithStdin(
175
+ ["codex", "exec", ...extraArgs],
176
+ cwd,
177
+ config.envVars,
178
+ prompt
179
+ )
180
+ if (result.exitCode !== 0) {
181
+ return yield* ProviderError.make({
182
+ message: `codex exited with code ${result.exitCode}: ${result.stdout.join("\n")}`
183
+ })
184
+ }
185
+ return result.stdout.join("\n")
186
+ })
187
+
188
+ const completeStream = (prompt: string) =>
189
+ rawStream(["codex", "exec", "--json", ...extraArgs], prompt).pipe(
190
+ Stream.mapEffect(failCodexChunk)
191
+ )
192
+
193
+ const base = makeCliConnector({
194
+ id: ConnectorIds.Codex,
195
+ interactionSupport: "InteractiveStdin",
196
+ buildArgv: (prompt, _context) => ["codex", "exec", ...extraArgs, prompt],
197
+ buildInteractiveArgv: (_context) => ["codex", ...extraArgs],
198
+ complete,
199
+ completeStream,
200
+ versionProbe: { executor, binary: "codex", cwd }
201
+ })
202
+
203
+ const executeStructuredWithUsage = <A, E, RD, RE>(
204
+ prompt: string,
205
+ schema: Schema.ConstraintCodec<A, E, RD, RE>,
206
+ jsonSchema: JsonSchema
207
+ ): Effect.Effect<StructuredResult<A>, LlmError, RD> => {
208
+ if (!codexSchemaEnforceable(jsonSchema)) {
209
+ return Effect.gen(function* () {
210
+ const text = yield* complete(withSchemaHint(prompt, jsonSchema))
211
+ const value = yield* parseFromText(text, schema, jsonSchema)
212
+ const result: StructuredResult<A> = [value, undefined, config.model]
213
+ return result
214
+ })
215
+ }
216
+
217
+ return Effect.scoped(
218
+ Effect.gen(function* () {
219
+ const path = yield* temporaryFiles.write(
220
+ "codex-schema-",
221
+ ".json",
222
+ jsonText(codexStrictSchema(jsonSchema))
223
+ )
224
+ const reply = yield* collect(
225
+ rawStream(["codex", "exec", "--json", "--output-schema", path, ...extraArgs], prompt)
226
+ )
227
+ if (reply.metadata.codexError !== undefined) {
228
+ return yield* failClassifiedCliError("codex", "codex error", reply.metadata.codexError)
229
+ }
230
+ const value = yield* parseFromText(reply.content, schema, jsonSchema)
231
+ const result: StructuredResult<A> = [
232
+ value,
233
+ reply.usage,
234
+ reply.metadata.model ?? config.model
235
+ ]
236
+ return result
237
+ })
238
+ )
239
+ }
240
+
241
+ return {
242
+ ...base,
243
+ executeStructured: <A, E, RD, RE>(
244
+ prompt: string,
245
+ schema: Schema.ConstraintCodec<A, E, RD, RE>,
246
+ jsonSchema: JsonSchema
247
+ ): Effect.Effect<A, LlmError, RD> =>
248
+ Effect.map(executeStructuredWithUsage(prompt, schema, jsonSchema), ([value]) => value),
249
+ executeStructuredWithUsage
250
+ }
251
+ }
@@ -0,0 +1,142 @@
1
+ import * as Effect from "effect/Effect"
2
+ import * as Layer from "effect/Layer"
3
+ import {
4
+ ApiConnectorConfig,
5
+ CliConnectorConfig,
6
+ toLlmConfig,
7
+ type ConnectorConfig
8
+ } from "../ConnectorConfig.ts"
9
+ import {
10
+ ConnectorRegistry,
11
+ makeConnectorRegistry,
12
+ type ConnectorFactory,
13
+ type ConnectorRegistryShape
14
+ } from "../ConnectorRegistry.ts"
15
+ import type { ConnectorShape } from "../Connector.ts"
16
+ import { ConfigError, type LlmError } from "../Errors.ts"
17
+ import { HttpClient, type HttpClientShape } from "../HttpClient.ts"
18
+ import { ConnectorIds, LlmConfig, type ConnectorId, type ConnectorKind } from "../Models.ts"
19
+ import { ProcessExecutor, type ProcessExecutorShape } from "../ProcessExecutor.ts"
20
+ import { TemporaryFiles, type TemporaryFilesShape } from "../TemporaryFiles.ts"
21
+ import { makeAnthropicProvider } from "./AnthropicProvider.ts"
22
+ import { makeAntigravityConnector } from "./AntigravityConnector.ts"
23
+ import { makeClaudeCliConnector } from "./ClaudeCliConnector.ts"
24
+ import { makeCodexConnector } from "./CodexConnector.ts"
25
+ import { makeCopilotConnector } from "./CopilotConnector.ts"
26
+ import { makeCursorConnector } from "./CursorConnector.ts"
27
+ import { makeGeminiApiProvider } from "./GeminiApiProvider.ts"
28
+ import {
29
+ GeminiCliExecutor,
30
+ geminiCliExecutionContextFrom,
31
+ makeGeminiCliProvider,
32
+ type GeminiCliExecutorShape
33
+ } from "./GeminiCliProvider.ts"
34
+ import { makeGrokCliConnector } from "./GrokCliConnector.ts"
35
+ import { makeLmStudioProvider } from "./LmStudioProvider.ts"
36
+ import { makeMockProvider } from "./MockProvider.ts"
37
+ import { makeOllamaProvider } from "./OllamaProvider.ts"
38
+ import { makeOpenAIProvider } from "./OpenAIProvider.ts"
39
+ import { makeOpenCodeCliConnector } from "./OpenCodeCliConnector.ts"
40
+ import { makePiConnector } from "./PiConnector.ts"
41
+
42
+ export interface ConnectorFactoryDependencies {
43
+ readonly http: HttpClientShape
44
+ readonly process: ProcessExecutorShape
45
+ readonly temporaryFiles: TemporaryFilesShape
46
+ readonly geminiCli: GeminiCliExecutorShape
47
+ }
48
+
49
+ const expectedConfig = (kind: ConnectorKind, id: ConnectorId): ConfigError =>
50
+ ConfigError.make({
51
+ message: `Expected ${kind === "Api" ? "ApiConnectorConfig" : "CliConnectorConfig"} for ${id.value}`
52
+ })
53
+
54
+ const apiFactory = (
55
+ id: ConnectorId,
56
+ build: (config: ApiConnectorConfig) => ConnectorShape
57
+ ): ConnectorFactory => ({
58
+ connectorId: id,
59
+ kind: "Api",
60
+ create: (config: ConnectorConfig): Effect.Effect<ConnectorShape, LlmError> =>
61
+ config instanceof ApiConnectorConfig
62
+ ? Effect.succeed(build(config))
63
+ : Effect.fail(expectedConfig("Api", id))
64
+ })
65
+
66
+ const cliFactory = (
67
+ id: ConnectorId,
68
+ build: (config: CliConnectorConfig) => ConnectorShape
69
+ ): ConnectorFactory => ({
70
+ connectorId: id,
71
+ kind: "Cli",
72
+ create: (config: ConnectorConfig): Effect.Effect<ConnectorShape, LlmError> =>
73
+ config instanceof CliConnectorConfig
74
+ ? Effect.succeed(build(config))
75
+ : Effect.fail(expectedConfig("Cli", id))
76
+ })
77
+
78
+ export const createConnectorRegistry = (
79
+ dependencies: ConnectorFactoryDependencies
80
+ ): ConnectorRegistryShape =>
81
+ makeConnectorRegistry([
82
+ apiFactory(ConnectorIds.OpenAI, (config) =>
83
+ makeOpenAIProvider(toLlmConfig(config), dependencies.http)
84
+ ),
85
+ apiFactory(ConnectorIds.Anthropic, (config) =>
86
+ makeAnthropicProvider(toLlmConfig(config), dependencies.http)
87
+ ),
88
+ apiFactory(ConnectorIds.GeminiApi, (config) =>
89
+ makeGeminiApiProvider(toLlmConfig(config), dependencies.http)
90
+ ),
91
+ apiFactory(ConnectorIds.LmStudio, (config) =>
92
+ makeLmStudioProvider(toLlmConfig(config), dependencies.http)
93
+ ),
94
+ apiFactory(ConnectorIds.Ollama, (config) =>
95
+ makeOllamaProvider(toLlmConfig(config), dependencies.http)
96
+ ),
97
+ cliFactory(ConnectorIds.ClaudeCli, (config) =>
98
+ makeClaudeCliConnector(config, dependencies.process)
99
+ ),
100
+ cliFactory(ConnectorIds.OpenCode, (config) =>
101
+ makeOpenCodeCliConnector(config, dependencies.process)
102
+ ),
103
+ cliFactory(ConnectorIds.GeminiCli, (config) =>
104
+ makeGeminiCliProvider(
105
+ LlmConfig.make({
106
+ provider: "GeminiCli",
107
+ model: config.model ?? "gemini-2.5-flash"
108
+ }),
109
+ dependencies.geminiCli,
110
+ geminiCliExecutionContextFrom(config)
111
+ )
112
+ ),
113
+ cliFactory(ConnectorIds.Codex, (config) =>
114
+ makeCodexConnector(config, dependencies.process, dependencies.temporaryFiles)
115
+ ),
116
+ cliFactory(ConnectorIds.Copilot, (config) =>
117
+ makeCopilotConnector(config, dependencies.process)
118
+ ),
119
+ cliFactory(ConnectorIds.Pi, (config) => makePiConnector(config, dependencies.process)),
120
+ cliFactory(ConnectorIds.AntigravityCli, (config) =>
121
+ makeAntigravityConnector(config, dependencies.process)
122
+ ),
123
+ cliFactory(ConnectorIds.Grok, (config) => makeGrokCliConnector(config, dependencies.process)),
124
+ cliFactory(ConnectorIds.Cursor, (config) => makeCursorConnector(config, dependencies.process)),
125
+ apiFactory(ConnectorIds.Mock, (config) => makeMockProvider(toLlmConfig(config)))
126
+ ])
127
+
128
+ export const ConnectorRegistryLive = Layer.effect(
129
+ ConnectorRegistry,
130
+ Effect.gen(function* () {
131
+ const http = yield* HttpClient
132
+ const process = yield* ProcessExecutor
133
+ const temporaryFiles = yield* TemporaryFiles
134
+ const geminiCli = yield* GeminiCliExecutor
135
+ return createConnectorRegistry({
136
+ http,
137
+ process,
138
+ temporaryFiles,
139
+ geminiCli
140
+ })
141
+ })
142
+ )
@@ -0,0 +1,47 @@
1
+ import * as Effect from "effect/Effect"
2
+ import * as Stream from "effect/Stream"
3
+ import { makeCliConnector, type CliConnectorShape } from "../Connector.ts"
4
+ import type { CliConnectorConfig } from "../ConnectorConfig.ts"
5
+ import { ProviderError } from "../Errors.ts"
6
+ import { ConnectorIds, LlmChunk } from "../Models.ts"
7
+ import type { ProcessExecutorShape } from "../ProcessExecutor.ts"
8
+
9
+ export const buildCopilotArgv = (prompt: string): ReadonlyArray<string> => [
10
+ "gh",
11
+ "copilot",
12
+ "suggest",
13
+ "-t",
14
+ "shell",
15
+ prompt
16
+ ]
17
+
18
+ export const makeCopilotConnector = (
19
+ config: CliConnectorConfig,
20
+ executor: ProcessExecutorShape
21
+ ): CliConnectorShape => {
22
+ const complete = Effect.fn("@llm4ts/core/providers/CopilotConnector.complete")(function* (
23
+ prompt: string
24
+ ) {
25
+ const argv = buildCopilotArgv(prompt)
26
+ const result = yield* executor.run(argv, ".", config.envVars)
27
+ if (result.exitCode !== 0) {
28
+ return yield* ProviderError.make({
29
+ message: `gh copilot exited with code ${result.exitCode}: ${result.stdout.join("\n")}`
30
+ })
31
+ }
32
+ return result.stdout.join("\n")
33
+ })
34
+
35
+ return makeCliConnector({
36
+ id: ConnectorIds.Copilot,
37
+ interactionSupport: "ContinuationOnly",
38
+ buildArgv: (prompt, _context) => buildCopilotArgv(prompt),
39
+ buildInteractiveArgv: (_context) => ["gh", "copilot"],
40
+ complete,
41
+ completeStream: (prompt) =>
42
+ Stream.fromEffect(complete(prompt)).pipe(
43
+ Stream.map((text) => LlmChunk.make({ delta: text }))
44
+ ),
45
+ versionProbe: { executor, binary: "gh", versionArgs: ["copilot", "--version"], cwd: "." }
46
+ })
47
+ }
@@ -0,0 +1,120 @@
1
+ import * as Effect from "effect/Effect"
2
+ import * as Stream from "effect/Stream"
3
+ import { makeCliConnector, type CliConnectorShape } from "../Connector.ts"
4
+ import type { CliConnectorConfig } from "../ConnectorConfig.ts"
5
+ import { ConnectorIds, LlmChunk } from "../Models.ts"
6
+ import type { ProcessExecutorShape } from "../ProcessExecutor.ts"
7
+ import {
8
+ failClassifiedCliError,
9
+ jsonArray,
10
+ jsonBooleanField,
11
+ jsonField,
12
+ jsonObjectEntries,
13
+ jsonStringField,
14
+ optionalModelArgs,
15
+ parseJsonLine,
16
+ sortedFlagArgs,
17
+ toolEventChunk,
18
+ type JsonValue
19
+ } from "./CliSupport.ts"
20
+
21
+ export const cursorExtraArgs = (config: CliConnectorConfig): ReadonlyArray<string> => [
22
+ ...optionalModelArgs(config.model),
23
+ ...(config.readOnly ? [] : ["--force"]),
24
+ ...sortedFlagArgs(config.flags)
25
+ ]
26
+
27
+ const cursorMessageText = (message: JsonValue) =>
28
+ jsonArray(jsonField(message, "content"))
29
+ .map((block) => jsonStringField(block, "text") ?? "")
30
+ .join("")
31
+
32
+ export const parseCursorStreamLine = (line: string): ReadonlyArray<LlmChunk> => {
33
+ const json = parseJsonLine(line)
34
+ if (json === undefined) {
35
+ return []
36
+ }
37
+
38
+ switch (jsonStringField(json, "type")) {
39
+ case "assistant": {
40
+ const message = jsonField(json, "message")
41
+ const text = message === undefined ? "" : cursorMessageText(message)
42
+ return text.length === 0 ? [] : [LlmChunk.make({ delta: text })]
43
+ }
44
+ case "tool_call": {
45
+ if (jsonStringField(json, "subtype") !== "started") {
46
+ return []
47
+ }
48
+ const entry = jsonObjectEntries(jsonField(json, "tool_call"))[0]
49
+ return entry === undefined ? [] : [toolEventChunk(entry[0], jsonField(entry[1], "args"))]
50
+ }
51
+ case "result":
52
+ return jsonBooleanField(json, "is_error") === true
53
+ ? [
54
+ LlmChunk.make({
55
+ delta: "",
56
+ metadata: {
57
+ cursorError: jsonStringField(json, "result") ?? "cursor error"
58
+ }
59
+ })
60
+ ]
61
+ : [LlmChunk.make({ delta: "", finishReason: "stop" })]
62
+ default:
63
+ return []
64
+ }
65
+ }
66
+
67
+ export const makeCursorConnector = (
68
+ config: CliConnectorConfig,
69
+ executor: ProcessExecutorShape
70
+ ): CliConnectorShape => {
71
+ const cwd = config.workingDir ?? "."
72
+ const extraArgs = cursorExtraArgs(config)
73
+ const buildArgv = (prompt: string): ReadonlyArray<string> => [
74
+ "cursor-agent",
75
+ ...extraArgs,
76
+ "-p",
77
+ prompt
78
+ ]
79
+ const complete = Effect.fn("@llm4ts/core/providers/CursorConnector.complete")(function* (
80
+ prompt: string
81
+ ) {
82
+ const result = yield* executor.run(buildArgv(prompt), cwd, config.envVars)
83
+ if (result.exitCode !== 0) {
84
+ const raw = result.stdout.join("\n")
85
+ return yield* failClassifiedCliError(
86
+ "cursor",
87
+ `cursor-agent exited with code ${result.exitCode}`,
88
+ raw
89
+ )
90
+ }
91
+ return result.stdout.join("\n")
92
+ })
93
+
94
+ const completeStream = (prompt: string) =>
95
+ executor
96
+ .runStreaming(
97
+ ["cursor-agent", "--output-format", "stream-json", ...extraArgs, "-p", prompt],
98
+ cwd,
99
+ config.envVars
100
+ )
101
+ .pipe(
102
+ Stream.flatMap((line) => Stream.fromIterable(parseCursorStreamLine(line))),
103
+ Stream.mapEffect((chunk) => {
104
+ const message = chunk.metadata.cursorError
105
+ return message === undefined
106
+ ? Effect.succeed(chunk)
107
+ : failClassifiedCliError("cursor", "cursor error", message)
108
+ })
109
+ )
110
+
111
+ return makeCliConnector({
112
+ id: ConnectorIds.Cursor,
113
+ interactionSupport: "ContinuationOnly",
114
+ buildArgv: (prompt, _context) => buildArgv(prompt),
115
+ buildInteractiveArgv: (_context) => ["cursor-agent", ...extraArgs],
116
+ complete,
117
+ completeStream,
118
+ versionProbe: { executor, binary: "cursor-agent", cwd }
119
+ })
120
+ }