@wix/pathgrade 0.29.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 (238) hide show
  1. package/bin/pathgrade.js +2 -0
  2. package/dist/affected/anchor.d.ts +18 -0
  3. package/dist/affected/anchor.js +42 -0
  4. package/dist/affected/config.d.ts +25 -0
  5. package/dist/affected/config.js +98 -0
  6. package/dist/affected/format.d.ts +45 -0
  7. package/dist/affected/format.js +87 -0
  8. package/dist/affected/git.d.ts +33 -0
  9. package/dist/affected/git.js +68 -0
  10. package/dist/affected/glob.d.ts +17 -0
  11. package/dist/affected/glob.js +25 -0
  12. package/dist/affected/meta.d.ts +26 -0
  13. package/dist/affected/meta.js +158 -0
  14. package/dist/affected/select.d.ts +30 -0
  15. package/dist/affected/select.js +101 -0
  16. package/dist/affected/sidecar.d.ts +32 -0
  17. package/dist/affected/sidecar.js +86 -0
  18. package/dist/affected/types.d.ts +43 -0
  19. package/dist/affected/types.js +7 -0
  20. package/dist/agents/claude/ask-user-answer-store.d.ts +28 -0
  21. package/dist/agents/claude/ask-user-answer-store.js +29 -0
  22. package/dist/agents/claude/ask-user-bridge.d.ts +56 -0
  23. package/dist/agents/claude/ask-user-bridge.js +162 -0
  24. package/dist/agents/claude/sdk-message-projector.d.ts +43 -0
  25. package/dist/agents/claude/sdk-message-projector.js +208 -0
  26. package/dist/agents/claude/sdk-options.d.ts +52 -0
  27. package/dist/agents/claude/sdk-options.js +79 -0
  28. package/dist/agents/claude.d.ts +56 -0
  29. package/dist/agents/claude.js +143 -0
  30. package/dist/agents/codex-app-server/agent.d.ts +35 -0
  31. package/dist/agents/codex-app-server/agent.js +388 -0
  32. package/dist/agents/codex-app-server/fixtures/app-server-client.d.ts +19 -0
  33. package/dist/agents/codex-app-server/fixtures/app-server-client.js +104 -0
  34. package/dist/agents/codex-app-server/fixtures/run-gate.d.ts +25 -0
  35. package/dist/agents/codex-app-server/fixtures/run-gate.js +48 -0
  36. package/dist/agents/codex-app-server/protocol/ClientRequest.d.ts +27 -0
  37. package/dist/agents/codex-app-server/protocol/ClientRequest.js +7 -0
  38. package/dist/agents/codex-app-server/protocol/DynamicToolCallParams.d.ts +9 -0
  39. package/dist/agents/codex-app-server/protocol/DynamicToolCallParams.js +4 -0
  40. package/dist/agents/codex-app-server/protocol/GrantedPermissionProfile.d.ts +6 -0
  41. package/dist/agents/codex-app-server/protocol/GrantedPermissionProfile.js +7 -0
  42. package/dist/agents/codex-app-server/protocol/McpElicitationRequestParams.d.ts +23 -0
  43. package/dist/agents/codex-app-server/protocol/McpElicitationRequestParams.js +8 -0
  44. package/dist/agents/codex-app-server/protocol/Op.d.ts +11 -0
  45. package/dist/agents/codex-app-server/protocol/Op.js +5 -0
  46. package/dist/agents/codex-app-server/protocol/PermissionsRequestApprovalParams.d.ts +10 -0
  47. package/dist/agents/codex-app-server/protocol/PermissionsRequestApprovalParams.js +6 -0
  48. package/dist/agents/codex-app-server/protocol/PermissionsRequestApprovalResponse.d.ts +15 -0
  49. package/dist/agents/codex-app-server/protocol/PermissionsRequestApprovalResponse.js +4 -0
  50. package/dist/agents/codex-app-server/protocol/SandboxMode.d.ts +1 -0
  51. package/dist/agents/codex-app-server/protocol/SandboxMode.js +4 -0
  52. package/dist/agents/codex-app-server/protocol/ServerRequest.d.ts +42 -0
  53. package/dist/agents/codex-app-server/protocol/ServerRequest.js +7 -0
  54. package/dist/agents/codex-app-server/protocol/ThreadStartParams.d.ts +45 -0
  55. package/dist/agents/codex-app-server/protocol/ThreadStartParams.js +10 -0
  56. package/dist/agents/codex-app-server/protocol/ToolRequestUserInputAnswer.d.ts +6 -0
  57. package/dist/agents/codex-app-server/protocol/ToolRequestUserInputAnswer.js +4 -0
  58. package/dist/agents/codex-app-server/protocol/ToolRequestUserInputOption.d.ts +7 -0
  59. package/dist/agents/codex-app-server/protocol/ToolRequestUserInputOption.js +4 -0
  60. package/dist/agents/codex-app-server/protocol/ToolRequestUserInputParams.d.ts +10 -0
  61. package/dist/agents/codex-app-server/protocol/ToolRequestUserInputParams.js +4 -0
  62. package/dist/agents/codex-app-server/protocol/ToolRequestUserInputQuestion.d.ts +12 -0
  63. package/dist/agents/codex-app-server/protocol/ToolRequestUserInputQuestion.js +4 -0
  64. package/dist/agents/codex-app-server/protocol/ToolRequestUserInputResponse.d.ts +9 -0
  65. package/dist/agents/codex-app-server/protocol/ToolRequestUserInputResponse.js +4 -0
  66. package/dist/agents/codex-app-server/protocol/TurnCompletedNotification.d.ts +5 -0
  67. package/dist/agents/codex-app-server/protocol/TurnCompletedNotification.js +4 -0
  68. package/dist/agents/codex-app-server/protocol/index.d.ts +16 -0
  69. package/dist/agents/codex-app-server/protocol/index.js +11 -0
  70. package/dist/agents/codex-app-server/transport.d.ts +84 -0
  71. package/dist/agents/codex-app-server/transport.js +246 -0
  72. package/dist/agents/codex-app-server/wire-translators.d.ts +19 -0
  73. package/dist/agents/codex-app-server/wire-translators.js +52 -0
  74. package/dist/agents/codex.d.ts +7 -0
  75. package/dist/agents/codex.js +164 -0
  76. package/dist/agents/cursor.d.ts +39 -0
  77. package/dist/agents/cursor.js +233 -0
  78. package/dist/agents/registry.d.ts +17 -0
  79. package/dist/agents/registry.js +27 -0
  80. package/dist/agents/transcript-agent.d.ts +14 -0
  81. package/dist/agents/transcript-agent.js +59 -0
  82. package/dist/analytics/engine.d.ts +18 -0
  83. package/dist/analytics/engine.js +66 -0
  84. package/dist/commands/affected.d.ts +31 -0
  85. package/dist/commands/affected.js +150 -0
  86. package/dist/commands/analyze.d.ts +21 -0
  87. package/dist/commands/analyze.js +167 -0
  88. package/dist/commands/init.d.ts +3 -0
  89. package/dist/commands/init.js +242 -0
  90. package/dist/commands/preview-reactions.d.ts +10 -0
  91. package/dist/commands/preview-reactions.js +88 -0
  92. package/dist/commands/preview.d.ts +5 -0
  93. package/dist/commands/preview.js +17 -0
  94. package/dist/commands/report.d.ts +39 -0
  95. package/dist/commands/report.js +108 -0
  96. package/dist/commands/run-args.d.ts +22 -0
  97. package/dist/commands/run-args.js +69 -0
  98. package/dist/commands/run-changed.d.ts +26 -0
  99. package/dist/commands/run-changed.js +138 -0
  100. package/dist/commands/validate.d.ts +13 -0
  101. package/dist/commands/validate.js +233 -0
  102. package/dist/core/mcp-mock.d.ts +3 -0
  103. package/dist/core/mcp-mock.js +23 -0
  104. package/dist/core/mcp-mock.types.d.ts +15 -0
  105. package/dist/core/mcp-mock.types.js +1 -0
  106. package/dist/core/skills.d.ts +15 -0
  107. package/dist/core/skills.js +76 -0
  108. package/dist/evals/discovery.d.ts +7 -0
  109. package/dist/evals/discovery.js +69 -0
  110. package/dist/mcp-mock-server.d.ts +1 -0
  111. package/dist/mcp-mock-server.js +116 -0
  112. package/dist/pathgrade.d.ts +10 -0
  113. package/dist/pathgrade.js +243 -0
  114. package/dist/plugin/index.d.ts +9 -0
  115. package/dist/plugin/index.js +81 -0
  116. package/dist/plugin/lifecycle.d.ts +45 -0
  117. package/dist/plugin/lifecycle.js +146 -0
  118. package/dist/plugin/reporter.d.ts +36 -0
  119. package/dist/plugin/reporter.js +275 -0
  120. package/dist/plugin/setup.d.ts +1 -0
  121. package/dist/plugin/setup.js +3 -0
  122. package/dist/providers/copy-filter.d.ts +18 -0
  123. package/dist/providers/copy-filter.js +66 -0
  124. package/dist/providers/credentials.d.ts +36 -0
  125. package/dist/providers/credentials.js +202 -0
  126. package/dist/providers/mcp-config.d.ts +34 -0
  127. package/dist/providers/mcp-config.js +55 -0
  128. package/dist/providers/sandbox-exec.d.ts +8 -0
  129. package/dist/providers/sandbox-exec.js +82 -0
  130. package/dist/providers/sandbox.d.ts +22 -0
  131. package/dist/providers/sandbox.js +158 -0
  132. package/dist/providers/sandboxed-claude-spawn.d.ts +24 -0
  133. package/dist/providers/sandboxed-claude-spawn.js +57 -0
  134. package/dist/providers/workspace.d.ts +15 -0
  135. package/dist/providers/workspace.js +73 -0
  136. package/dist/reporters/browser.d.ts +2 -0
  137. package/dist/reporters/browser.js +85 -0
  138. package/dist/reporters/cli.d.ts +2 -0
  139. package/dist/reporters/cli.js +131 -0
  140. package/dist/reporters/diagnostics.d.ts +46 -0
  141. package/dist/reporters/diagnostics.js +147 -0
  142. package/dist/reporters/github-comment.d.ts +67 -0
  143. package/dist/reporters/github-comment.js +252 -0
  144. package/dist/reporters/loader.d.ts +8 -0
  145. package/dist/reporters/loader.js +36 -0
  146. package/dist/reporters/results-path.d.ts +2 -0
  147. package/dist/reporters/results-path.js +10 -0
  148. package/dist/reporters/verbose-emitter.d.ts +69 -0
  149. package/dist/reporters/verbose-emitter.js +103 -0
  150. package/dist/sdk/agent-crash.d.ts +19 -0
  151. package/dist/sdk/agent-crash.js +20 -0
  152. package/dist/sdk/agent-resolution.d.ts +12 -0
  153. package/dist/sdk/agent-resolution.js +21 -0
  154. package/dist/sdk/agent-result-log.d.ts +19 -0
  155. package/dist/sdk/agent-result-log.js +45 -0
  156. package/dist/sdk/agent.d.ts +4 -0
  157. package/dist/sdk/agent.js +354 -0
  158. package/dist/sdk/ask-bus/bus.d.ts +21 -0
  159. package/dist/sdk/ask-bus/bus.js +129 -0
  160. package/dist/sdk/ask-bus/handler.d.ts +31 -0
  161. package/dist/sdk/ask-bus/handler.js +244 -0
  162. package/dist/sdk/ask-bus/parsers.d.ts +9 -0
  163. package/dist/sdk/ask-bus/parsers.js +30 -0
  164. package/dist/sdk/ask-bus/projection.d.ts +26 -0
  165. package/dist/sdk/ask-bus/projection.js +45 -0
  166. package/dist/sdk/ask-bus/types.d.ts +69 -0
  167. package/dist/sdk/ask-bus/types.js +1 -0
  168. package/dist/sdk/chat.d.ts +29 -0
  169. package/dist/sdk/chat.js +93 -0
  170. package/dist/sdk/conversation-window.d.ts +14 -0
  171. package/dist/sdk/conversation-window.js +38 -0
  172. package/dist/sdk/converse.d.ts +42 -0
  173. package/dist/sdk/converse.js +329 -0
  174. package/dist/sdk/eval-runtime.d.ts +10 -0
  175. package/dist/sdk/eval-runtime.js +17 -0
  176. package/dist/sdk/evaluate.d.ts +13 -0
  177. package/dist/sdk/evaluate.js +325 -0
  178. package/dist/sdk/index.d.ts +31 -0
  179. package/dist/sdk/index.js +20 -0
  180. package/dist/sdk/judge-pipeline.d.ts +7 -0
  181. package/dist/sdk/judge-pipeline.js +178 -0
  182. package/dist/sdk/judge-prompt-builder.d.ts +7 -0
  183. package/dist/sdk/judge-prompt-builder.js +76 -0
  184. package/dist/sdk/judge-tool-runner.d.ts +19 -0
  185. package/dist/sdk/judge-tool-runner.js +51 -0
  186. package/dist/sdk/judge-tool-session.d.ts +47 -0
  187. package/dist/sdk/judge-tool-session.js +213 -0
  188. package/dist/sdk/judge-tools.d.ts +29 -0
  189. package/dist/sdk/judge-tools.js +283 -0
  190. package/dist/sdk/managed-session.d.ts +50 -0
  191. package/dist/sdk/managed-session.js +110 -0
  192. package/dist/sdk/persona.d.ts +5 -0
  193. package/dist/sdk/persona.js +33 -0
  194. package/dist/sdk/reaction-loader.d.ts +4 -0
  195. package/dist/sdk/reaction-loader.js +119 -0
  196. package/dist/sdk/reaction-preview.d.ts +6 -0
  197. package/dist/sdk/reaction-preview.js +94 -0
  198. package/dist/sdk/run-scorer.d.ts +10 -0
  199. package/dist/sdk/run-scorer.js +124 -0
  200. package/dist/sdk/runtime-policy.d.ts +21 -0
  201. package/dist/sdk/runtime-policy.js +89 -0
  202. package/dist/sdk/scorer-utils.d.ts +6 -0
  203. package/dist/sdk/scorer-utils.js +39 -0
  204. package/dist/sdk/scorers.d.ts +64 -0
  205. package/dist/sdk/scorers.js +80 -0
  206. package/dist/sdk/snapshots.d.ts +41 -0
  207. package/dist/sdk/snapshots.js +148 -0
  208. package/dist/sdk/types.d.ts +416 -0
  209. package/dist/sdk/types.js +17 -0
  210. package/dist/sdk/visible-turn.d.ts +4 -0
  211. package/dist/sdk/visible-turn.js +23 -0
  212. package/dist/tool-events.d.ts +45 -0
  213. package/dist/tool-events.js +167 -0
  214. package/dist/types.d.ts +372 -0
  215. package/dist/types.js +37 -0
  216. package/dist/utils/cli.d.ts +55 -0
  217. package/dist/utils/cli.js +131 -0
  218. package/dist/utils/env.d.ts +8 -0
  219. package/dist/utils/env.js +26 -0
  220. package/dist/utils/llm-mocks.d.ts +43 -0
  221. package/dist/utils/llm-mocks.js +108 -0
  222. package/dist/utils/llm-providers/anthropic.d.ts +2 -0
  223. package/dist/utils/llm-providers/anthropic.js +132 -0
  224. package/dist/utils/llm-providers/cli.d.ts +20 -0
  225. package/dist/utils/llm-providers/cli.js +162 -0
  226. package/dist/utils/llm-providers/openai.d.ts +2 -0
  227. package/dist/utils/llm-providers/openai.js +56 -0
  228. package/dist/utils/llm-types.d.ts +107 -0
  229. package/dist/utils/llm-types.js +1 -0
  230. package/dist/utils/llm.d.ts +36 -0
  231. package/dist/utils/llm.js +209 -0
  232. package/dist/utils/shutdown.d.ts +15 -0
  233. package/dist/utils/shutdown.js +41 -0
  234. package/dist/utils/timeout.d.ts +8 -0
  235. package/dist/utils/timeout.js +32 -0
  236. package/dist/viewer.html +1178 -0
  237. package/package.json +88 -0
  238. package/templates/eval.ts.template +28 -0
@@ -0,0 +1,108 @@
1
+ function isToolResponse(r) {
2
+ return typeof r === 'object' && 'kind' in r;
3
+ }
4
+ function isThrowResponse(r) {
5
+ return typeof r === 'object' && 'throws' in r;
6
+ }
7
+ export function createMockLLM(opts = {}) {
8
+ const queue = [...(opts.responses ?? [])];
9
+ const calls = [];
10
+ const toolCalls = [];
11
+ const defaultResponse = opts.defaultResponse;
12
+ let inputTokens = 0;
13
+ let outputTokens = 0;
14
+ let costUsd = 0;
15
+ let callCount = 0;
16
+ const take = () => {
17
+ callCount++;
18
+ const r = queue.shift();
19
+ if (r !== undefined)
20
+ return r;
21
+ if (defaultResponse !== undefined)
22
+ return defaultResponse;
23
+ throw new Error(`createMockLLM: out of responses (call #${callCount})`);
24
+ };
25
+ const port = {
26
+ get tokenUsage() {
27
+ return { inputTokens, outputTokens };
28
+ },
29
+ get costUsd() {
30
+ return costUsd;
31
+ },
32
+ get supportsToolUse() {
33
+ return true;
34
+ },
35
+ get lastProvider() {
36
+ return 'mock';
37
+ },
38
+ addTokens(input, output) {
39
+ inputTokens += input;
40
+ outputTokens += output;
41
+ },
42
+ addCost(usd) {
43
+ if (!Number.isFinite(usd) || usd < 0)
44
+ return;
45
+ costUsd += usd;
46
+ },
47
+ async measure(fn) {
48
+ const inBefore = inputTokens;
49
+ const outBefore = outputTokens;
50
+ const costBefore = costUsd;
51
+ const result = await fn();
52
+ return {
53
+ result,
54
+ tokens: {
55
+ inputTokens: inputTokens - inBefore,
56
+ outputTokens: outputTokens - outBefore,
57
+ },
58
+ costUsd: costUsd - costBefore,
59
+ };
60
+ },
61
+ async call(prompt, callOpts) {
62
+ calls.push({ prompt, opts: callOpts });
63
+ const r = opts.respond ? await opts.respond(prompt, callOpts) : take();
64
+ if (isThrowResponse(r))
65
+ throw r.throws;
66
+ if (isToolResponse(r)) {
67
+ throw new Error(`createMockLLM: call() got a tool-use response (kind='${r.kind}') — expected a plain call response`);
68
+ }
69
+ const body = typeof r === 'string' ? { text: r } : r;
70
+ inputTokens += body.inputTokens ?? 0;
71
+ outputTokens += body.outputTokens ?? 0;
72
+ return {
73
+ text: body.text,
74
+ inputTokens: body.inputTokens,
75
+ outputTokens: body.outputTokens,
76
+ provider: body.provider ?? 'anthropic',
77
+ model: body.model ?? 'mock',
78
+ };
79
+ },
80
+ async callWithTools(messages, callOpts) {
81
+ toolCalls.push({ messages, opts: callOpts });
82
+ const r = take();
83
+ if (isThrowResponse(r))
84
+ throw r.throws;
85
+ if (!isToolResponse(r)) {
86
+ throw new Error('createMockLLM: callWithTools() expected a response with kind=\'tool_use\' or kind=\'final\'');
87
+ }
88
+ inputTokens += r.inputTokens ?? 0;
89
+ outputTokens += r.outputTokens ?? 0;
90
+ return r;
91
+ },
92
+ queueResponse(r) {
93
+ queue.push(r);
94
+ },
95
+ clearCalls() {
96
+ calls.length = 0;
97
+ toolCalls.length = 0;
98
+ callCount = 0;
99
+ },
100
+ get calls() {
101
+ return calls;
102
+ },
103
+ get toolCalls() {
104
+ return toolCalls;
105
+ },
106
+ };
107
+ return port;
108
+ }
@@ -0,0 +1,2 @@
1
+ import type { LLMProvider } from '../llm-types.js';
2
+ export declare const anthropicProvider: LLMProvider;
@@ -0,0 +1,132 @@
1
+ function getApiKey(env) {
2
+ return env?.ANTHROPIC_API_KEY || process.env.ANTHROPIC_API_KEY;
3
+ }
4
+ function resolveBaseUrl(env) {
5
+ return env?.ANTHROPIC_BASE_URL || process.env.ANTHROPIC_BASE_URL || 'https://api.anthropic.com';
6
+ }
7
+ function buildHeaders(apiKey, useCache) {
8
+ const headers = {
9
+ 'Content-Type': 'application/json',
10
+ 'x-api-key': apiKey,
11
+ 'anthropic-version': '2023-06-01',
12
+ };
13
+ if (useCache) {
14
+ headers['anthropic-beta'] = 'prompt-caching-2024-07-31';
15
+ }
16
+ return headers;
17
+ }
18
+ function sumInputTokens(usage) {
19
+ return (usage?.input_tokens ?? 0)
20
+ + (usage?.cache_creation_input_tokens ?? 0)
21
+ + (usage?.cache_read_input_tokens ?? 0);
22
+ }
23
+ async function postAnthropic(apiKey, useCache, body, env) {
24
+ const response = await fetch(`${resolveBaseUrl(env)}/v1/messages`, {
25
+ method: 'POST',
26
+ headers: buildHeaders(apiKey, useCache),
27
+ body: JSON.stringify(body),
28
+ });
29
+ if (!response.ok) {
30
+ const errBody = await response.text().catch(() => '');
31
+ throw new Error(`Anthropic API error (${response.status}): ${errBody.slice(0, 300)}`);
32
+ }
33
+ return await response.json();
34
+ }
35
+ export const anthropicProvider = {
36
+ name: 'anthropic',
37
+ async isAvailable(env) {
38
+ return !!getApiKey(env);
39
+ },
40
+ supportsModel(model) {
41
+ return model.trim().toLowerCase().startsWith('claude');
42
+ },
43
+ async call(prompt, opts) {
44
+ const apiKey = getApiKey(opts.env);
45
+ if (!apiKey) {
46
+ throw new Error('No ANTHROPIC_API_KEY available');
47
+ }
48
+ const model = opts.model || 'claude-sonnet-4-20250514';
49
+ const temperature = opts.temperature ?? 0;
50
+ const useCache = opts.cacheControl ?? false;
51
+ const content = useCache
52
+ ? [{ type: 'text', text: prompt, cache_control: { type: 'ephemeral' } }]
53
+ : prompt;
54
+ try {
55
+ const data = await postAnthropic(apiKey, useCache, {
56
+ model,
57
+ max_tokens: 4096,
58
+ temperature,
59
+ messages: [{ role: 'user', content }],
60
+ }, opts.env);
61
+ const inputTokens = sumInputTokens(data?.usage);
62
+ const firstText = data?.content?.find((b) => b.type === 'text');
63
+ return {
64
+ text: firstText?.text || '',
65
+ inputTokens: inputTokens || undefined,
66
+ outputTokens: data?.usage?.output_tokens,
67
+ provider: 'anthropic',
68
+ model,
69
+ };
70
+ }
71
+ catch (error) {
72
+ throw new Error(`Anthropic API call failed`, { cause: error });
73
+ }
74
+ },
75
+ async callWithTools(messages, opts) {
76
+ const apiKey = getApiKey(opts.env);
77
+ if (!apiKey) {
78
+ throw new Error('No ANTHROPIC_API_KEY available');
79
+ }
80
+ const model = opts.model || 'claude-sonnet-4-20250514';
81
+ const temperature = opts.temperature ?? 0;
82
+ const maxTokens = opts.maxTokens ?? 4096;
83
+ const useCache = opts.cacheControl === true;
84
+ // Tool schemas: only the last gets cache_control, serving as a cache
85
+ // breakpoint covering the entire (stable) schema block.
86
+ const tools = useCache && opts.tools.length > 0
87
+ ? opts.tools.map((t, i) => i === opts.tools.length - 1 ? { ...t, cache_control: { type: 'ephemeral' } } : t)
88
+ : opts.tools;
89
+ const body = {
90
+ model,
91
+ max_tokens: maxTokens,
92
+ temperature,
93
+ messages,
94
+ tools,
95
+ };
96
+ if (opts.system) {
97
+ body.system = useCache
98
+ ? [{ type: 'text', text: opts.system, cache_control: { type: 'ephemeral' } }]
99
+ : opts.system;
100
+ }
101
+ const data = await postAnthropic(apiKey, useCache, body, opts.env);
102
+ const inputTokens = sumInputTokens(data?.usage);
103
+ const blocks = [];
104
+ let text = '';
105
+ for (const block of data?.content ?? []) {
106
+ if (block.type === 'tool_use') {
107
+ const b = block;
108
+ if (b.id && b.name) {
109
+ blocks.push({ type: 'tool_use', id: b.id, name: b.name, input: b.input ?? {} });
110
+ }
111
+ }
112
+ else if (block.type === 'text') {
113
+ text += block.text ?? '';
114
+ }
115
+ }
116
+ if (blocks.length > 0) {
117
+ return {
118
+ kind: 'tool_use',
119
+ blocks,
120
+ text: text || undefined,
121
+ inputTokens: inputTokens || undefined,
122
+ outputTokens: data?.usage?.output_tokens,
123
+ };
124
+ }
125
+ return {
126
+ kind: 'final',
127
+ text,
128
+ inputTokens: inputTokens || undefined,
129
+ outputTokens: data?.usage?.output_tokens,
130
+ };
131
+ },
132
+ };
@@ -0,0 +1,20 @@
1
+ import type { LLMProvider } from '../llm-types.js';
2
+ /** Reset the availability cache (for testing). */
3
+ export declare function resetCliCache(): void;
4
+ /** Check if claude CLI is installed and authenticated. */
5
+ export declare function isClaudeCliAvailable(): Promise<boolean>;
6
+ export declare function isCodexCliAvailable(): Promise<boolean>;
7
+ interface CliEnvelope {
8
+ result?: string;
9
+ structured_output?: unknown;
10
+ usage?: {
11
+ input_tokens?: number;
12
+ output_tokens?: number;
13
+ cache_creation_input_tokens?: number;
14
+ cache_read_input_tokens?: number;
15
+ };
16
+ }
17
+ export declare function parseCliEnvelope(raw: string): CliEnvelope;
18
+ export declare function extractStructuredOutput(raw: string): string;
19
+ export declare const cliProvider: LLMProvider;
20
+ export {};
@@ -0,0 +1,162 @@
1
+ import { spawn } from 'child_process';
2
+ // --- Availability check with promise-based dedup ---
3
+ const availabilityPromises = new Map();
4
+ function getCliAvailability(command, args, timeoutSec) {
5
+ const cacheKey = `${command} ${args.join(' ')}`;
6
+ const existing = availabilityPromises.get(cacheKey);
7
+ if (existing)
8
+ return existing;
9
+ const promise = checkCliAvailability(command, args, timeoutSec);
10
+ availabilityPromises.set(cacheKey, promise);
11
+ return promise;
12
+ }
13
+ async function checkCliAvailability(command, args, timeoutSec) {
14
+ try {
15
+ const result = await runCli(command, args, {}, timeoutSec);
16
+ return result.exitCode === 0;
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ /** Reset the availability cache (for testing). */
23
+ export function resetCliCache() {
24
+ availabilityPromises.clear();
25
+ }
26
+ /** Check if claude CLI is installed and authenticated. */
27
+ export async function isClaudeCliAvailable() {
28
+ return getCliAvailability('claude', ['auth', 'status'], 5);
29
+ }
30
+ export async function isCodexCliAvailable() {
31
+ return getCliAvailability('codex', ['login', 'status'], 5);
32
+ }
33
+ export function parseCliEnvelope(raw) {
34
+ const jsonMatch = raw.match(/\{[\s\S]*\}/);
35
+ if (!jsonMatch)
36
+ return {};
37
+ try {
38
+ return JSON.parse(jsonMatch[0]);
39
+ }
40
+ catch {
41
+ return {};
42
+ }
43
+ }
44
+ export function extractStructuredOutput(raw) {
45
+ const envelope = parseCliEnvelope(raw);
46
+ if (!envelope || (!envelope.structured_output && !envelope.result)) {
47
+ const jsonMatch = raw.match(/\{[\s\S]*\}/);
48
+ return jsonMatch ? jsonMatch[0] : raw.trim();
49
+ }
50
+ if (envelope.structured_output && typeof envelope.structured_output === 'object') {
51
+ return JSON.stringify(envelope.structured_output);
52
+ }
53
+ if (typeof envelope.result === 'string' && envelope.result) {
54
+ return envelope.result;
55
+ }
56
+ return raw.trim();
57
+ }
58
+ // --- CLI adapter ---
59
+ export const cliProvider = {
60
+ name: 'cli',
61
+ async isAvailable() {
62
+ return isClaudeCliAvailable();
63
+ },
64
+ supportsModel(model) {
65
+ const normalized = model.trim().toLowerCase();
66
+ // CLI supports Claude models and unknown models (defaults to claude-cli)
67
+ if (normalized.startsWith('claude'))
68
+ return true;
69
+ // CLI does NOT support non-Claude models like gpt-*, o1, o3, o4
70
+ if (normalized.startsWith('gpt-')
71
+ || normalized.startsWith('chatgpt-')
72
+ || normalized.startsWith('o1')
73
+ || normalized.startsWith('o3')
74
+ || normalized.startsWith('o4')) {
75
+ return false;
76
+ }
77
+ // Unknown models — let CLI try (it will fail gracefully)
78
+ return true;
79
+ },
80
+ async call(prompt, opts) {
81
+ const args = ['-p', '--no-session-persistence'];
82
+ if (opts.model) {
83
+ args.push('--model', opts.model);
84
+ }
85
+ // Always use JSON output to get token usage from the envelope
86
+ args.push('--output-format', 'json');
87
+ if (opts.jsonSchema) {
88
+ args.push('--json-schema', opts.jsonSchema);
89
+ }
90
+ const result = await runCli('claude', args, opts.env ?? {}, 120, prompt);
91
+ if (result.exitCode !== 0) {
92
+ throw new Error(`Claude CLI exited with code ${result.exitCode}: ${result.stderr.slice(0, 300)}`);
93
+ }
94
+ const envelope = parseCliEnvelope(result.stdout);
95
+ let text;
96
+ if (opts.jsonSchema) {
97
+ // For structured output, extract structured_output or result
98
+ if (envelope.structured_output && typeof envelope.structured_output === 'object') {
99
+ text = JSON.stringify(envelope.structured_output);
100
+ }
101
+ else if (typeof envelope.result === 'string' && envelope.result) {
102
+ text = envelope.result;
103
+ }
104
+ else {
105
+ text = extractStructuredOutput(result.stdout);
106
+ }
107
+ }
108
+ else {
109
+ // For plain text, extract result from the JSON envelope
110
+ text = typeof envelope.result === 'string' ? envelope.result : result.stdout.trim();
111
+ }
112
+ const usage = envelope.usage;
113
+ const inputTokens = usage
114
+ ? (usage.input_tokens ?? 0)
115
+ + (usage.cache_creation_input_tokens ?? 0)
116
+ + (usage.cache_read_input_tokens ?? 0)
117
+ : undefined;
118
+ return {
119
+ text,
120
+ inputTokens: inputTokens || undefined,
121
+ outputTokens: usage?.output_tokens,
122
+ provider: 'cli',
123
+ model: opts.model || 'claude-cli',
124
+ };
125
+ },
126
+ };
127
+ function runCli(command, args, env, timeoutSec, stdin) {
128
+ return new Promise((resolve) => {
129
+ const child = spawn(command, args, {
130
+ env: { ...process.env, ...env },
131
+ });
132
+ let stdout = '';
133
+ let stderr = '';
134
+ let settled = false;
135
+ const timer = setTimeout(() => {
136
+ if (!settled) {
137
+ child.kill('SIGTERM');
138
+ setTimeout(() => { if (!child.killed)
139
+ child.kill('SIGKILL'); }, 500);
140
+ }
141
+ }, timeoutSec * 1000);
142
+ if (stdin) {
143
+ child.stdin.write(stdin, () => child.stdin.end());
144
+ }
145
+ child.stdout.on('data', (d) => { stdout += d.toString(); });
146
+ child.stderr.on('data', (d) => { stderr += d.toString(); });
147
+ child.on('close', (code) => {
148
+ if (settled)
149
+ return;
150
+ settled = true;
151
+ clearTimeout(timer);
152
+ resolve({ stdout, stderr, exitCode: code ?? 1 });
153
+ });
154
+ child.on('error', (err) => {
155
+ if (settled)
156
+ return;
157
+ settled = true;
158
+ clearTimeout(timer);
159
+ resolve({ stdout, stderr: stderr + '\n' + err.message, exitCode: 1 });
160
+ });
161
+ });
162
+ }
@@ -0,0 +1,2 @@
1
+ import type { LLMProvider } from '../llm-types.js';
2
+ export declare const openaiProvider: LLMProvider;
@@ -0,0 +1,56 @@
1
+ function getApiKey(env) {
2
+ return env?.OPENAI_API_KEY || process.env.OPENAI_API_KEY;
3
+ }
4
+ export const openaiProvider = {
5
+ name: 'openai',
6
+ async isAvailable(env) {
7
+ return !!getApiKey(env);
8
+ },
9
+ supportsModel(model) {
10
+ const normalized = model.trim().toLowerCase();
11
+ return (normalized.startsWith('gpt-')
12
+ || normalized.startsWith('chatgpt-')
13
+ || normalized.startsWith('o1')
14
+ || normalized.startsWith('o3')
15
+ || normalized.startsWith('o4'));
16
+ },
17
+ async call(prompt, opts) {
18
+ const apiKey = getApiKey(opts.env);
19
+ if (!apiKey) {
20
+ throw new Error('No OPENAI_API_KEY available');
21
+ }
22
+ const model = opts.model || 'gpt-4o';
23
+ const temperature = opts.temperature ?? 0;
24
+ const baseUrl = (opts.env?.OPENAI_BASE_URL || process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1').replace(/\/$/, '');
25
+ try {
26
+ const response = await fetch(`${baseUrl}/chat/completions`, {
27
+ method: 'POST',
28
+ headers: {
29
+ 'Content-Type': 'application/json',
30
+ Authorization: `Bearer ${apiKey}`,
31
+ },
32
+ body: JSON.stringify({
33
+ model,
34
+ max_tokens: 4096,
35
+ temperature,
36
+ messages: [{ role: 'user', content: prompt }],
37
+ }),
38
+ });
39
+ if (!response.ok) {
40
+ const errBody = await response.text().catch(() => '');
41
+ throw new Error(`OpenAI API error (${response.status}): ${errBody.slice(0, 300)}`);
42
+ }
43
+ const data = await response.json();
44
+ return {
45
+ text: data?.choices?.[0]?.message?.content || '',
46
+ inputTokens: data?.usage?.prompt_tokens,
47
+ outputTokens: data?.usage?.completion_tokens,
48
+ provider: 'openai',
49
+ model,
50
+ };
51
+ }
52
+ catch (error) {
53
+ throw new Error(`OpenAI API error: ${error}`);
54
+ }
55
+ },
56
+ };
@@ -0,0 +1,107 @@
1
+ export interface LLMCallOptions {
2
+ model?: string;
3
+ env?: Record<string, string>;
4
+ temperature?: number;
5
+ /** When set, use --json-schema for structured output via CLI. */
6
+ jsonSchema?: string;
7
+ /** When true, enable Anthropic prompt caching via cache_control on content blocks. */
8
+ cacheControl?: boolean;
9
+ }
10
+ export interface LLMCallResult {
11
+ text: string;
12
+ inputTokens?: number;
13
+ outputTokens?: number;
14
+ provider: 'anthropic' | 'openai' | 'cli';
15
+ model: string;
16
+ }
17
+ export interface TokenUsage {
18
+ inputTokens: number;
19
+ outputTokens: number;
20
+ }
21
+ /** Caller-facing type — just call(). */
22
+ export interface LLMPort {
23
+ call(prompt: string, opts?: LLMCallOptions): Promise<LLMCallResult>;
24
+ /** Present only on providers that opt in to multi-turn tool use. */
25
+ callWithTools?(messages: ToolUseMessage[], opts: CallWithToolsOptions): Promise<CallWithToolsResult>;
26
+ /** Running total of tokens consumed through this port. Present on clients from createLLMClient. */
27
+ readonly tokenUsage?: TokenUsage;
28
+ /**
29
+ * Running total of USD cost consumed through this port. Present on
30
+ * clients from `createLLMClient`. Accumulates Claude SDK turn-cost
31
+ * deltas via `addCost` and judge-LLM cost once judge providers expose
32
+ * comparable metadata.
33
+ */
34
+ readonly costUsd?: number;
35
+ /** Add externally-observed tokens (e.g. from agent CLI turns). */
36
+ addTokens?(input: number, output: number): void;
37
+ /** Add externally-observed cost (e.g. from Claude SDK `total_cost_usd`). */
38
+ addCost?(usd: number): void;
39
+ /** Run `fn` and return its token + cost delta. */
40
+ measure?<T>(fn: () => Promise<T>): Promise<{
41
+ result: T;
42
+ tokens: TokenUsage;
43
+ costUsd: number;
44
+ }>;
45
+ /** Name of the adapter that resolved the last call (e.g. 'anthropic'). */
46
+ readonly lastProvider?: string;
47
+ /** True when this port can dispatch tool use. Type-guard form preferred at call sites. */
48
+ readonly supportsToolUse?: boolean;
49
+ }
50
+ /** Narrower interface the tool-use runner accepts. */
51
+ export interface ToolCapableLLMPort extends LLMPort {
52
+ callWithTools(messages: ToolUseMessage[], opts: CallWithToolsOptions): Promise<CallWithToolsResult>;
53
+ }
54
+ export interface ToolSchema {
55
+ name: string;
56
+ description: string;
57
+ input_schema: Record<string, unknown>;
58
+ }
59
+ export interface CallWithToolsOptions extends LLMCallOptions {
60
+ system?: string;
61
+ tools: ToolSchema[];
62
+ maxTokens?: number;
63
+ }
64
+ export interface ToolUseBlock {
65
+ type: 'tool_use';
66
+ id: string;
67
+ name: string;
68
+ input: Record<string, unknown>;
69
+ }
70
+ export interface ToolResultBlock {
71
+ type: 'tool_result';
72
+ tool_use_id: string;
73
+ content: string;
74
+ is_error?: boolean;
75
+ }
76
+ export interface TextBlock {
77
+ type: 'text';
78
+ text: string;
79
+ }
80
+ export type AssistantContentBlock = TextBlock | ToolUseBlock;
81
+ export type UserContentBlock = TextBlock | ToolResultBlock;
82
+ export interface ToolUseMessage {
83
+ role: 'user' | 'assistant';
84
+ content: string | Array<AssistantContentBlock | UserContentBlock>;
85
+ }
86
+ export type CallWithToolsResult = {
87
+ kind: 'tool_use';
88
+ blocks: ToolUseBlock[];
89
+ text?: string;
90
+ inputTokens?: number;
91
+ outputTokens?: number;
92
+ } | {
93
+ kind: 'final';
94
+ text: string;
95
+ inputTokens?: number;
96
+ outputTokens?: number;
97
+ };
98
+ /** Wide implementor-facing type — each provider adapter implements this. */
99
+ export interface LLMProviderAdapter {
100
+ name: string;
101
+ isAvailable(env?: Record<string, string>): Promise<boolean>;
102
+ call(prompt: string, opts: LLMCallOptions): Promise<LLMCallResult>;
103
+ callWithTools?(messages: ToolUseMessage[], opts: CallWithToolsOptions): Promise<CallWithToolsResult>;
104
+ supportsModel?(model: string): boolean;
105
+ }
106
+ /** @deprecated Use `LLMProviderAdapter`. Alias retained only during the RFC 010a migration. */
107
+ export type LLMProvider = LLMProviderAdapter;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,36 @@
1
+ import type { LLMProviderAdapter, LLMCallOptions, LLMCallResult, LLMPort } from './llm-types.js';
2
+ export type { LLMCallOptions, LLMCallResult, LLMPort, LLMProviderAdapter, LLMProvider, TokenUsage } from './llm-types.js';
3
+ export interface CreateLLMClientOptions {
4
+ adapters?: LLMProviderAdapter[];
5
+ agent?: string;
6
+ env?: Record<string, string>;
7
+ silent?: boolean;
8
+ }
9
+ export declare class ProviderNotSupportedError extends Error {
10
+ readonly name = "ProviderNotSupportedError";
11
+ readonly adapterName: string;
12
+ constructor(adapterName: string);
13
+ }
14
+ export { isClaudeCliAvailable, isCodexCliAvailable, resetCliCache, extractStructuredOutput, parseCliEnvelope } from './llm-providers/cli.js';
15
+ /**
16
+ * Create an LLM client that tries providers in order.
17
+ *
18
+ * For each call, the client:
19
+ * 1. Filters providers by supportsModel() if a model is specified
20
+ * 2. Checks isAvailable() for each candidate
21
+ * 3. Calls the first available provider
22
+ * 4. On failure, falls through to the next provider if one exists
23
+ */
24
+ export declare function createLLMClient(opts: CreateLLMClientOptions): LLMPort;
25
+ export declare function createLLMClient(providers: LLMProviderAdapter[], agentName?: string): LLMPort;
26
+ export declare function callLLM(prompt: string, opts?: LLMCallOptions): Promise<LLMCallResult>;
27
+ /**
28
+ * Create an LLM client scoped to the providers that match the given agent.
29
+ *
30
+ * - claude → CLI + Anthropic API (no OpenAI fallthrough)
31
+ * - codex → OpenAI API
32
+ *
33
+ * If `agentEnv` is provided, it is merged into every LLM call so that
34
+ * the agent's env propagates to persona/judge/summarization calls.
35
+ */
36
+ export declare function createAgentLLM(agentName: string, agentEnv?: Record<string, string>): LLMPort;