@kilnai/core 0.23.2 → 1.0.1

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 (297) hide show
  1. package/dist/agents/execution-identity.d.ts +15 -0
  2. package/dist/agents/execution-identity.d.ts.map +1 -0
  3. package/dist/agents/execution-identity.js +42 -0
  4. package/dist/agents/execution-identity.js.map +1 -0
  5. package/dist/agents/index.d.ts +6 -1
  6. package/dist/agents/index.d.ts.map +1 -1
  7. package/dist/agents/index.js +4 -1
  8. package/dist/agents/index.js.map +1 -1
  9. package/dist/agents/infrastructure/__tests__/codex-oauth-auth.test.d.ts +2 -0
  10. package/dist/agents/infrastructure/__tests__/codex-oauth-auth.test.d.ts.map +1 -0
  11. package/dist/agents/infrastructure/__tests__/codex-oauth-auth.test.js +313 -0
  12. package/dist/agents/infrastructure/__tests__/codex-oauth-auth.test.js.map +1 -0
  13. package/dist/agents/infrastructure/__tests__/codex-oauth.test.d.ts +2 -0
  14. package/dist/agents/infrastructure/__tests__/codex-oauth.test.d.ts.map +1 -0
  15. package/dist/agents/infrastructure/__tests__/codex-oauth.test.js +720 -0
  16. package/dist/agents/infrastructure/__tests__/codex-oauth.test.js.map +1 -0
  17. package/dist/agents/infrastructure/codex-oauth-auth.d.ts +41 -0
  18. package/dist/agents/infrastructure/codex-oauth-auth.d.ts.map +1 -0
  19. package/dist/agents/infrastructure/codex-oauth-auth.js +204 -0
  20. package/dist/agents/infrastructure/codex-oauth-auth.js.map +1 -0
  21. package/dist/agents/infrastructure/codex-oauth.d.ts +32 -0
  22. package/dist/agents/infrastructure/codex-oauth.d.ts.map +1 -0
  23. package/dist/agents/infrastructure/codex-oauth.js +371 -0
  24. package/dist/agents/infrastructure/codex-oauth.js.map +1 -0
  25. package/dist/agents/model-capability-registry.d.ts +2 -0
  26. package/dist/agents/model-capability-registry.d.ts.map +1 -1
  27. package/dist/agents/model-capability-registry.js +11 -2
  28. package/dist/agents/model-capability-registry.js.map +1 -1
  29. package/dist/agents/model-pricing.d.ts +2 -0
  30. package/dist/agents/model-pricing.d.ts.map +1 -1
  31. package/dist/agents/model-pricing.js +10 -0
  32. package/dist/agents/model-pricing.js.map +1 -1
  33. package/dist/cost/cost-tracker.d.ts +1 -0
  34. package/dist/cost/cost-tracker.d.ts.map +1 -1
  35. package/dist/cost/cost-tracker.js +6 -3
  36. package/dist/cost/cost-tracker.js.map +1 -1
  37. package/dist/cost/index.d.ts +2 -0
  38. package/dist/cost/index.d.ts.map +1 -1
  39. package/dist/cost/index.js +1 -0
  40. package/dist/cost/index.js.map +1 -1
  41. package/dist/cost/models-dev-client.d.ts +20 -0
  42. package/dist/cost/models-dev-client.d.ts.map +1 -0
  43. package/dist/cost/models-dev-client.js +91 -0
  44. package/dist/cost/models-dev-client.js.map +1 -0
  45. package/dist/domain/index.d.ts +1 -0
  46. package/dist/domain/index.d.ts.map +1 -1
  47. package/dist/domain/index.js.map +1 -1
  48. package/dist/engine/composites/team.d.ts +1 -1
  49. package/dist/engine/composites/team.d.ts.map +1 -1
  50. package/dist/engine/composites/team.js +1 -17
  51. package/dist/engine/composites/team.js.map +1 -1
  52. package/dist/engine/domain/cron.d.ts +3 -1
  53. package/dist/engine/domain/cron.d.ts.map +1 -1
  54. package/dist/engine/domain/cron.js +41 -10
  55. package/dist/engine/domain/cron.js.map +1 -1
  56. package/dist/engine/domain/tool-execution.d.ts +21 -0
  57. package/dist/engine/domain/tool-execution.d.ts.map +1 -1
  58. package/dist/engine/error-catalog.d.ts.map +1 -1
  59. package/dist/engine/error-catalog.js +8 -0
  60. package/dist/engine/error-catalog.js.map +1 -1
  61. package/dist/engine/errors.d.ts +2 -1
  62. package/dist/engine/errors.d.ts.map +1 -1
  63. package/dist/engine/errors.js +10 -0
  64. package/dist/engine/errors.js.map +1 -1
  65. package/dist/engine/index.d.ts +1 -1
  66. package/dist/engine/index.d.ts.map +1 -1
  67. package/dist/engine/index.js +1 -1
  68. package/dist/engine/index.js.map +1 -1
  69. package/dist/engine/loader/app-loader.js +1 -1
  70. package/dist/engine/loader/app-loader.js.map +1 -1
  71. package/dist/events/index.d.ts +18 -1
  72. package/dist/events/index.d.ts.map +1 -1
  73. package/dist/events/index.js +2 -0
  74. package/dist/events/index.js.map +1 -1
  75. package/dist/field/domain/field-config.d.ts +8 -0
  76. package/dist/field/domain/field-config.d.ts.map +1 -0
  77. package/dist/field/domain/field-config.js +7 -0
  78. package/dist/field/domain/field-config.js.map +1 -0
  79. package/dist/field/domain/field-store.d.ts +8 -0
  80. package/dist/field/domain/field-store.d.ts.map +1 -0
  81. package/dist/field/domain/field-store.js +2 -0
  82. package/dist/field/domain/field-store.js.map +1 -0
  83. package/dist/field/domain/field.d.ts +22 -0
  84. package/dist/field/domain/field.d.ts.map +1 -0
  85. package/dist/field/domain/field.js +2 -0
  86. package/dist/field/domain/field.js.map +1 -0
  87. package/dist/field/field-inhibitor.d.ts +17 -0
  88. package/dist/field/field-inhibitor.d.ts.map +1 -0
  89. package/dist/field/field-inhibitor.js +50 -0
  90. package/dist/field/field-inhibitor.js.map +1 -0
  91. package/dist/field/field-propagator.d.ts +19 -0
  92. package/dist/field/field-propagator.d.ts.map +1 -0
  93. package/dist/field/field-propagator.js +69 -0
  94. package/dist/field/field-propagator.js.map +1 -0
  95. package/dist/field/field-service.d.ts +18 -0
  96. package/dist/field/field-service.d.ts.map +1 -0
  97. package/dist/field/field-service.js +57 -0
  98. package/dist/field/field-service.js.map +1 -0
  99. package/dist/field/field-updater.d.ts +19 -0
  100. package/dist/field/field-updater.d.ts.map +1 -0
  101. package/dist/field/field-updater.js +64 -0
  102. package/dist/field/field-updater.js.map +1 -0
  103. package/dist/field/index.d.ts +15 -0
  104. package/dist/field/index.d.ts.map +1 -0
  105. package/dist/field/index.js +9 -0
  106. package/dist/field/index.js.map +1 -0
  107. package/dist/field/infrastructure/in-memory-field-store.d.ts +16 -0
  108. package/dist/field/infrastructure/in-memory-field-store.d.ts.map +1 -0
  109. package/dist/field/infrastructure/in-memory-field-store.js +72 -0
  110. package/dist/field/infrastructure/in-memory-field-store.js.map +1 -0
  111. package/dist/field/infrastructure/sqlite-field-store.d.ts +20 -0
  112. package/dist/field/infrastructure/sqlite-field-store.d.ts.map +1 -0
  113. package/dist/field/infrastructure/sqlite-field-store.js +107 -0
  114. package/dist/field/infrastructure/sqlite-field-store.js.map +1 -0
  115. package/dist/field/stability-monitor.d.ts +24 -0
  116. package/dist/field/stability-monitor.d.ts.map +1 -0
  117. package/dist/field/stability-monitor.js +72 -0
  118. package/dist/field/stability-monitor.js.map +1 -0
  119. package/dist/index.d.ts +5 -1
  120. package/dist/index.d.ts.map +1 -1
  121. package/dist/index.js +4 -1
  122. package/dist/index.js.map +1 -1
  123. package/dist/memory/context-budget.d.ts +17 -0
  124. package/dist/memory/context-budget.d.ts.map +1 -0
  125. package/dist/memory/context-budget.js +29 -0
  126. package/dist/memory/context-budget.js.map +1 -0
  127. package/dist/memory/context-cache.d.ts +23 -0
  128. package/dist/memory/context-cache.d.ts.map +1 -0
  129. package/dist/memory/context-cache.js +34 -0
  130. package/dist/memory/context-cache.js.map +1 -0
  131. package/dist/memory/index.d.ts +15 -3
  132. package/dist/memory/index.d.ts.map +1 -1
  133. package/dist/memory/index.js +5 -0
  134. package/dist/memory/index.js.map +1 -1
  135. package/dist/memory/memory-manager.d.ts +1 -1
  136. package/dist/memory/memory-manager.d.ts.map +1 -1
  137. package/dist/memory/memory-manager.js +2 -2
  138. package/dist/memory/memory-manager.js.map +1 -1
  139. package/dist/memory/project-store.js +1 -1
  140. package/dist/memory/project-store.js.map +1 -1
  141. package/dist/memory/resume-policy.d.ts +21 -0
  142. package/dist/memory/resume-policy.d.ts.map +1 -0
  143. package/dist/memory/resume-policy.js +70 -0
  144. package/dist/memory/resume-policy.js.map +1 -0
  145. package/dist/memory/resume-signals.d.ts +8 -0
  146. package/dist/memory/resume-signals.d.ts.map +1 -0
  147. package/dist/memory/resume-signals.js +8 -0
  148. package/dist/memory/resume-signals.js.map +1 -0
  149. package/dist/memory/sqlite-store.d.ts +6 -1
  150. package/dist/memory/sqlite-store.d.ts.map +1 -1
  151. package/dist/memory/sqlite-store.js +88 -11
  152. package/dist/memory/sqlite-store.js.map +1 -1
  153. package/dist/memory/task-shape.d.ts +2 -0
  154. package/dist/memory/task-shape.d.ts.map +1 -0
  155. package/dist/memory/task-shape.js +9 -0
  156. package/dist/memory/task-shape.js.map +1 -0
  157. package/dist/observability/span-mapper.d.ts.map +1 -1
  158. package/dist/observability/span-mapper.js +18 -0
  159. package/dist/observability/span-mapper.js.map +1 -1
  160. package/dist/orchestrator/chain-governor.d.ts +62 -0
  161. package/dist/orchestrator/chain-governor.d.ts.map +1 -0
  162. package/dist/orchestrator/chain-governor.js +80 -0
  163. package/dist/orchestrator/chain-governor.js.map +1 -0
  164. package/dist/orchestrator/demand-allocator.d.ts +64 -0
  165. package/dist/orchestrator/demand-allocator.d.ts.map +1 -0
  166. package/dist/orchestrator/demand-allocator.js +139 -0
  167. package/dist/orchestrator/demand-allocator.js.map +1 -0
  168. package/dist/orchestrator/demand-signal.d.ts +9 -0
  169. package/dist/orchestrator/demand-signal.d.ts.map +1 -0
  170. package/dist/orchestrator/demand-signal.js +23 -0
  171. package/dist/orchestrator/demand-signal.js.map +1 -0
  172. package/dist/orchestrator/index.d.ts +8 -2
  173. package/dist/orchestrator/index.d.ts.map +1 -1
  174. package/dist/orchestrator/index.js +8 -1
  175. package/dist/orchestrator/index.js.map +1 -1
  176. package/dist/orchestrator/orchestrator-checkpoint-support.d.ts +43 -0
  177. package/dist/orchestrator/orchestrator-checkpoint-support.d.ts.map +1 -0
  178. package/dist/orchestrator/orchestrator-checkpoint-support.js +147 -0
  179. package/dist/orchestrator/orchestrator-checkpoint-support.js.map +1 -0
  180. package/dist/orchestrator/orchestrator-dev-tool-support.d.ts +30 -0
  181. package/dist/orchestrator/orchestrator-dev-tool-support.d.ts.map +1 -0
  182. package/dist/orchestrator/orchestrator-dev-tool-support.js +125 -0
  183. package/dist/orchestrator/orchestrator-dev-tool-support.js.map +1 -0
  184. package/dist/orchestrator/orchestrator-interrupt-support.d.ts +22 -0
  185. package/dist/orchestrator/orchestrator-interrupt-support.d.ts.map +1 -0
  186. package/dist/orchestrator/orchestrator-interrupt-support.js +64 -0
  187. package/dist/orchestrator/orchestrator-interrupt-support.js.map +1 -0
  188. package/dist/orchestrator/orchestrator-memory-sync-support.d.ts +16 -0
  189. package/dist/orchestrator/orchestrator-memory-sync-support.d.ts.map +1 -0
  190. package/dist/orchestrator/orchestrator-memory-sync-support.js +25 -0
  191. package/dist/orchestrator/orchestrator-memory-sync-support.js.map +1 -0
  192. package/dist/orchestrator/orchestrator-verification-support.d.ts +17 -0
  193. package/dist/orchestrator/orchestrator-verification-support.d.ts.map +1 -0
  194. package/dist/orchestrator/orchestrator-verification-support.js +29 -0
  195. package/dist/orchestrator/orchestrator-verification-support.js.map +1 -0
  196. package/dist/orchestrator/orchestrator.d.ts +24 -16
  197. package/dist/orchestrator/orchestrator.d.ts.map +1 -1
  198. package/dist/orchestrator/orchestrator.js +106 -235
  199. package/dist/orchestrator/orchestrator.js.map +1 -1
  200. package/dist/orchestrator/strategies/index.d.ts +0 -1
  201. package/dist/orchestrator/strategies/index.d.ts.map +1 -1
  202. package/dist/orchestrator/strategies/index.js +1 -5
  203. package/dist/orchestrator/strategies/index.js.map +1 -1
  204. package/dist/orchestrator/task-registry.d.ts +94 -0
  205. package/dist/orchestrator/task-registry.d.ts.map +1 -0
  206. package/dist/orchestrator/task-registry.js +167 -0
  207. package/dist/orchestrator/task-registry.js.map +1 -0
  208. package/dist/safety/pii-scanner.d.ts.map +1 -1
  209. package/dist/safety/pii-scanner.js +7 -8
  210. package/dist/safety/pii-scanner.js.map +1 -1
  211. package/dist/safety/rails.d.ts.map +1 -1
  212. package/dist/safety/rails.js +9 -5
  213. package/dist/safety/rails.js.map +1 -1
  214. package/dist/security/dangerous-command-detector.d.ts +5 -0
  215. package/dist/security/dangerous-command-detector.d.ts.map +1 -0
  216. package/dist/security/dangerous-command-detector.js +92 -0
  217. package/dist/security/dangerous-command-detector.js.map +1 -0
  218. package/dist/security/index.d.ts +1 -0
  219. package/dist/security/index.d.ts.map +1 -1
  220. package/dist/security/index.js +1 -0
  221. package/dist/security/index.js.map +1 -1
  222. package/dist/security/prompt-scanner.d.ts.map +1 -1
  223. package/dist/security/prompt-scanner.js +117 -4
  224. package/dist/security/prompt-scanner.js.map +1 -1
  225. package/dist/skill/index.d.ts +4 -0
  226. package/dist/skill/index.d.ts.map +1 -1
  227. package/dist/skill/index.js +2 -0
  228. package/dist/skill/index.js.map +1 -1
  229. package/dist/skill/skill-capture.d.ts +46 -0
  230. package/dist/skill/skill-capture.d.ts.map +1 -0
  231. package/dist/skill/skill-capture.js +150 -0
  232. package/dist/skill/skill-capture.js.map +1 -0
  233. package/dist/skill/skill-generator.d.ts +20 -0
  234. package/dist/skill/skill-generator.d.ts.map +1 -0
  235. package/dist/skill/skill-generator.js +106 -0
  236. package/dist/skill/skill-generator.js.map +1 -0
  237. package/dist/tools/domain/tool-environment.d.ts +16 -0
  238. package/dist/tools/domain/tool-environment.d.ts.map +1 -0
  239. package/dist/tools/domain/tool-environment.js +94 -0
  240. package/dist/tools/domain/tool-environment.js.map +1 -0
  241. package/dist/tools/domain/tool-registry.d.ts +10 -0
  242. package/dist/tools/domain/tool-registry.d.ts.map +1 -0
  243. package/dist/tools/domain/tool-registry.js +23 -0
  244. package/dist/tools/domain/tool-registry.js.map +1 -0
  245. package/dist/tools/domain/tool.d.ts +29 -0
  246. package/dist/tools/domain/tool.d.ts.map +1 -0
  247. package/dist/tools/domain/tool.js +123 -0
  248. package/dist/tools/domain/tool.js.map +1 -0
  249. package/dist/tools/index.d.ts +18 -0
  250. package/dist/tools/index.d.ts.map +1 -0
  251. package/dist/tools/index.js +13 -0
  252. package/dist/tools/index.js.map +1 -0
  253. package/dist/tools/infrastructure/bash-tool.d.ts +20 -0
  254. package/dist/tools/infrastructure/bash-tool.d.ts.map +1 -0
  255. package/dist/tools/infrastructure/bash-tool.js +84 -0
  256. package/dist/tools/infrastructure/bash-tool.js.map +1 -0
  257. package/dist/tools/infrastructure/edit-tool.d.ts +9 -0
  258. package/dist/tools/infrastructure/edit-tool.d.ts.map +1 -0
  259. package/dist/tools/infrastructure/edit-tool.js +80 -0
  260. package/dist/tools/infrastructure/edit-tool.js.map +1 -0
  261. package/dist/tools/infrastructure/git-tool.d.ts +20 -0
  262. package/dist/tools/infrastructure/git-tool.d.ts.map +1 -0
  263. package/dist/tools/infrastructure/git-tool.js +85 -0
  264. package/dist/tools/infrastructure/git-tool.js.map +1 -0
  265. package/dist/tools/infrastructure/glob-tool.d.ts +23 -0
  266. package/dist/tools/infrastructure/glob-tool.d.ts.map +1 -0
  267. package/dist/tools/infrastructure/glob-tool.js +88 -0
  268. package/dist/tools/infrastructure/glob-tool.js.map +1 -0
  269. package/dist/tools/infrastructure/grep-tool.d.ts +23 -0
  270. package/dist/tools/infrastructure/grep-tool.d.ts.map +1 -0
  271. package/dist/tools/infrastructure/grep-tool.js +146 -0
  272. package/dist/tools/infrastructure/grep-tool.js.map +1 -0
  273. package/dist/tools/infrastructure/read-tool.d.ts +9 -0
  274. package/dist/tools/infrastructure/read-tool.d.ts.map +1 -0
  275. package/dist/tools/infrastructure/read-tool.js +45 -0
  276. package/dist/tools/infrastructure/read-tool.js.map +1 -0
  277. package/dist/tools/infrastructure/tool-helpers.d.ts +37 -0
  278. package/dist/tools/infrastructure/tool-helpers.d.ts.map +1 -0
  279. package/dist/tools/infrastructure/tool-helpers.js +145 -0
  280. package/dist/tools/infrastructure/tool-helpers.js.map +1 -0
  281. package/dist/tools/infrastructure/write-tool.d.ts +9 -0
  282. package/dist/tools/infrastructure/write-tool.d.ts.map +1 -0
  283. package/dist/tools/infrastructure/write-tool.js +38 -0
  284. package/dist/tools/infrastructure/write-tool.js.map +1 -0
  285. package/dist/tools/mcp/dev-tools-server.d.ts +37 -0
  286. package/dist/tools/mcp/dev-tools-server.d.ts.map +1 -0
  287. package/dist/tools/mcp/dev-tools-server.js +94 -0
  288. package/dist/tools/mcp/dev-tools-server.js.map +1 -0
  289. package/dist/tools/tool-executor.d.ts +34 -0
  290. package/dist/tools/tool-executor.d.ts.map +1 -0
  291. package/dist/tools/tool-executor.js +123 -0
  292. package/dist/tools/tool-executor.js.map +1 -0
  293. package/package.json +6 -5
  294. package/dist/orchestrator/strategies/swarm-strategy.d.ts +0 -36
  295. package/dist/orchestrator/strategies/swarm-strategy.d.ts.map +0 -1
  296. package/dist/orchestrator/strategies/swarm-strategy.js +0 -126
  297. package/dist/orchestrator/strategies/swarm-strategy.js.map +0 -1
@@ -0,0 +1,720 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { KilnError } from "../../../engine/errors.js";
3
+ const mockFetch = vi.fn();
4
+ const mockGetValidAccessToken = vi.fn();
5
+ vi.mock("../codex-oauth-auth.js", () => ({
6
+ CodexOAuthAuth: vi.fn(function MockCodexOAuthAuth() {
7
+ return {
8
+ getValidAccessToken: mockGetValidAccessToken,
9
+ };
10
+ }).mockImplementation(function MockCodexOAuthAuth() {
11
+ return {
12
+ getValidAccessToken: mockGetValidAccessToken,
13
+ };
14
+ }),
15
+ }));
16
+ function jsonResponse(status, body, headers) {
17
+ return new Response(JSON.stringify(body), {
18
+ status,
19
+ headers: {
20
+ "Content-Type": "application/json",
21
+ ...headers,
22
+ },
23
+ });
24
+ }
25
+ function sseResponse(events, status = 200) {
26
+ const encoder = new TextEncoder();
27
+ const body = new ReadableStream({
28
+ start(controller) {
29
+ for (const entry of events) {
30
+ controller.enqueue(encoder.encode(`event: ${entry.event}\ndata: ${JSON.stringify(entry.data)}\n\n`));
31
+ }
32
+ controller.close();
33
+ },
34
+ });
35
+ return new Response(body, {
36
+ status,
37
+ headers: { "Content-Type": "text/event-stream" },
38
+ });
39
+ }
40
+ function sseResponseWithCrLf(events, status = 200) {
41
+ const encoder = new TextEncoder();
42
+ const body = new ReadableStream({
43
+ start(controller) {
44
+ for (const entry of events) {
45
+ controller.enqueue(encoder.encode(`event: ${entry.event}\r\ndata: ${JSON.stringify(entry.data)}\r\n\r\n`));
46
+ }
47
+ controller.close();
48
+ },
49
+ });
50
+ return new Response(body, {
51
+ status,
52
+ headers: { "Content-Type": "text/event-stream" },
53
+ });
54
+ }
55
+ function createOptions(overrides = {}) {
56
+ return {
57
+ system: "You are a Codex agent.",
58
+ messages: [
59
+ { role: "user", parts: [{ type: "text", text: "Hello there" }] },
60
+ { role: "assistant", parts: [{ type: "text", text: "Previous reply" }] },
61
+ ],
62
+ maxTokens: 512,
63
+ ...overrides,
64
+ };
65
+ }
66
+ async function collectEvents(stream) {
67
+ const events = [];
68
+ for await (const event of stream) {
69
+ events.push(event);
70
+ }
71
+ return events;
72
+ }
73
+ async function createAdapter(defaultModel) {
74
+ const { CodexOAuthAdapter } = await import("../codex-oauth.js");
75
+ const { CodexOAuthAuth } = await import("../codex-oauth-auth.js");
76
+ const auth = new CodexOAuthAuth();
77
+ const adapter = new CodexOAuthAdapter({
78
+ auth,
79
+ ...(defaultModel ? { defaultModel } : {}),
80
+ });
81
+ return { adapter, auth };
82
+ }
83
+ beforeEach(() => {
84
+ vi.stubGlobal("fetch", mockFetch);
85
+ mockFetch.mockReset();
86
+ mockGetValidAccessToken.mockReset();
87
+ mockGetValidAccessToken.mockResolvedValue("test-token");
88
+ });
89
+ afterEach(() => {
90
+ vi.unstubAllGlobals();
91
+ vi.resetModules();
92
+ vi.clearAllMocks();
93
+ });
94
+ describe("CodexOAuthAdapter", () => {
95
+ describe("constructor", () => {
96
+ it("name property returns codex-oauth and defaultModel defaults to gpt-5.4", async () => {
97
+ mockFetch.mockResolvedValueOnce(sseResponse([
98
+ {
99
+ event: "response.completed",
100
+ data: {
101
+ response: {
102
+ id: "resp_1",
103
+ status: "completed",
104
+ output: [],
105
+ usage: { input_tokens: 1, output_tokens: 0 },
106
+ },
107
+ },
108
+ },
109
+ ]));
110
+ const { adapter } = await createAdapter();
111
+ await adapter.createMessage(createOptions());
112
+ expect(adapter.name).toBe("codex-oauth");
113
+ const [, init] = mockFetch.mock.calls[0];
114
+ const body = JSON.parse(String(init.body));
115
+ expect(body.model).toBe("gpt-5.4");
116
+ });
117
+ });
118
+ describe("createMessage", () => {
119
+ it("sends POST to https://chatgpt.com/backend-api/codex/responses with Authorization Bearer token", async () => {
120
+ mockFetch.mockResolvedValueOnce(sseResponse([
121
+ {
122
+ event: "response.completed",
123
+ data: {
124
+ response: {
125
+ id: "resp_1",
126
+ status: "completed",
127
+ output: [],
128
+ usage: { input_tokens: 4, output_tokens: 2 },
129
+ },
130
+ },
131
+ },
132
+ ]));
133
+ const { adapter } = await createAdapter("gpt-5.4");
134
+ await adapter.createMessage(createOptions());
135
+ expect(mockFetch).toHaveBeenCalledTimes(1);
136
+ const [url, init] = mockFetch.mock.calls[0];
137
+ expect(url).toBe("https://chatgpt.com/backend-api/codex/responses");
138
+ expect(init.method).toBe("POST");
139
+ expect(init.headers).toEqual({
140
+ Authorization: "Bearer test-token",
141
+ "Content-Type": "application/json",
142
+ });
143
+ });
144
+ it("calls auth.getValidAccessToken() before each request", async () => {
145
+ mockFetch.mockResolvedValueOnce(sseResponse([
146
+ {
147
+ event: "response.completed",
148
+ data: {
149
+ response: {
150
+ id: "resp_2",
151
+ status: "completed",
152
+ output: [],
153
+ usage: { input_tokens: 2, output_tokens: 1 },
154
+ },
155
+ },
156
+ },
157
+ ]));
158
+ const { adapter, auth } = await createAdapter();
159
+ await adapter.createMessage(createOptions());
160
+ expect(auth.getValidAccessToken).toHaveBeenCalledTimes(1);
161
+ expect(auth.getValidAccessToken.mock.invocationCallOrder[0]).toBeLessThan(mockFetch.mock.invocationCallOrder[0] ?? Number.POSITIVE_INFINITY);
162
+ });
163
+ it("maps Kiln system+messages to Responses API input format", async () => {
164
+ mockFetch.mockResolvedValueOnce(sseResponse([
165
+ {
166
+ event: "response.completed",
167
+ data: {
168
+ response: {
169
+ id: "resp_3",
170
+ status: "completed",
171
+ output: [],
172
+ usage: { input_tokens: 3, output_tokens: 1 },
173
+ },
174
+ },
175
+ },
176
+ ]));
177
+ const { adapter } = await createAdapter("gpt-5.4");
178
+ await adapter.createMessage(createOptions({
179
+ system: "System instruction",
180
+ messages: [
181
+ { role: "user", parts: [{ type: "text", text: "User prompt" }] },
182
+ { role: "assistant", parts: [{ type: "text", text: "Assistant reply" }] },
183
+ ],
184
+ }));
185
+ const [, init] = mockFetch.mock.calls[0];
186
+ const body = JSON.parse(String(init.body));
187
+ expect(body).toMatchObject({
188
+ model: "gpt-5.4",
189
+ instructions: "System instruction",
190
+ store: false,
191
+ stream: true,
192
+ max_output_tokens: 512,
193
+ input: [
194
+ { role: "user", content: "User prompt" },
195
+ { role: "assistant", content: "Assistant reply" },
196
+ ],
197
+ });
198
+ });
199
+ it("maps tools from Kiln ToolDefinition[] to Responses API tools format", async () => {
200
+ mockFetch.mockResolvedValueOnce(sseResponse([
201
+ {
202
+ event: "response.completed",
203
+ data: {
204
+ response: {
205
+ id: "resp_4",
206
+ status: "completed",
207
+ output: [],
208
+ usage: { input_tokens: 5, output_tokens: 1 },
209
+ },
210
+ },
211
+ },
212
+ ]));
213
+ const { adapter } = await createAdapter();
214
+ await adapter.createMessage(createOptions({
215
+ tools: [
216
+ {
217
+ name: "lookup_weather",
218
+ description: "Looks up weather for a city",
219
+ inputSchema: {
220
+ type: "object",
221
+ properties: {
222
+ city: { type: "string" },
223
+ },
224
+ required: ["city"],
225
+ },
226
+ tags: new Set(["weather"]),
227
+ },
228
+ ],
229
+ }));
230
+ const [, init] = mockFetch.mock.calls[0];
231
+ const body = JSON.parse(String(init.body));
232
+ expect(body.tools).toEqual([
233
+ {
234
+ type: "function",
235
+ name: "lookup_weather",
236
+ description: "Looks up weather for a city",
237
+ parameters: {
238
+ type: "object",
239
+ properties: {
240
+ city: { type: "string" },
241
+ },
242
+ required: ["city"],
243
+ },
244
+ },
245
+ ]);
246
+ });
247
+ it("serializes tool_result parts as function_call_output input items", async () => {
248
+ mockFetch.mockResolvedValueOnce(sseResponse([
249
+ {
250
+ event: "response.completed",
251
+ data: {
252
+ response: {
253
+ id: "resp_tool_result_1",
254
+ status: "completed",
255
+ output: [],
256
+ usage: { input_tokens: 6, output_tokens: 1 },
257
+ },
258
+ },
259
+ },
260
+ ]));
261
+ const { adapter } = await createAdapter();
262
+ await adapter.createMessage(createOptions({
263
+ messages: [
264
+ {
265
+ role: "assistant",
266
+ parts: [
267
+ { type: "text", text: "Let me check that." },
268
+ { type: "tool_use", id: "call_123", name: "read", input: { filePath: "HOTFIX.MD" } },
269
+ ],
270
+ },
271
+ {
272
+ role: "user",
273
+ parts: [
274
+ { type: "tool_result", toolUseId: "call_123", content: "hotfix content" },
275
+ ],
276
+ },
277
+ ],
278
+ }));
279
+ const [, init] = mockFetch.mock.calls[0];
280
+ const body = JSON.parse(String(init.body));
281
+ expect(body.input).toEqual([
282
+ { role: "assistant", content: "Let me check that." },
283
+ {
284
+ type: "function_call",
285
+ call_id: "call_123",
286
+ name: "read",
287
+ arguments: "{\"filePath\":\"HOTFIX.MD\"}",
288
+ },
289
+ { type: "function_call_output", call_id: "call_123", output: "hotfix content" },
290
+ ]);
291
+ });
292
+ it("serializes assistant tool_use parts as function_call input items", async () => {
293
+ mockFetch.mockResolvedValueOnce(sseResponse([
294
+ {
295
+ event: "response.completed",
296
+ data: {
297
+ response: {
298
+ id: "resp_tool_use_1",
299
+ status: "completed",
300
+ output: [],
301
+ usage: { input_tokens: 7, output_tokens: 1 },
302
+ },
303
+ },
304
+ },
305
+ ]));
306
+ const { adapter } = await createAdapter();
307
+ await adapter.createMessage(createOptions({
308
+ messages: [
309
+ {
310
+ role: "assistant",
311
+ parts: [
312
+ { type: "tool_use", id: "call_456", name: "read", input: { filePath: "HOTFIX.MD" } },
313
+ ],
314
+ },
315
+ ],
316
+ }));
317
+ const [, init] = mockFetch.mock.calls[0];
318
+ const body = JSON.parse(String(init.body));
319
+ expect(body.input).toEqual([
320
+ {
321
+ type: "function_call",
322
+ call_id: "call_456",
323
+ name: "read",
324
+ arguments: "{\"filePath\":\"HOTFIX.MD\"}",
325
+ },
326
+ ]);
327
+ });
328
+ it("maps response output back to AgentResponse (parts, toolCalls, token counts)", async () => {
329
+ mockFetch.mockResolvedValueOnce(sseResponse([
330
+ { event: "response.output_text.delta", data: { delta: "Adapter " } },
331
+ { event: "response.output_text.delta", data: { delta: "response text" } },
332
+ {
333
+ event: "response.completed",
334
+ data: {
335
+ response: {
336
+ id: "resp_5",
337
+ status: "completed",
338
+ output: [
339
+ {
340
+ type: "message",
341
+ content: [
342
+ { type: "output_text", text: "Adapter response text" },
343
+ ],
344
+ },
345
+ {
346
+ type: "function_call",
347
+ id: "call_1",
348
+ name: "lookup_weather",
349
+ arguments: "{\"city\":\"Tijuana\"}",
350
+ },
351
+ ],
352
+ usage: {
353
+ input_tokens: 123,
354
+ output_tokens: 45,
355
+ input_tokens_details: {
356
+ cached_tokens: 7,
357
+ },
358
+ },
359
+ },
360
+ },
361
+ },
362
+ ]));
363
+ const { adapter } = await createAdapter();
364
+ const response = await adapter.createMessage(createOptions());
365
+ expect(response).toMatchObject({
366
+ parts: [{ type: "text", text: "Adapter response text" }],
367
+ inputTokens: 123,
368
+ outputTokens: 45,
369
+ cacheReadTokens: 7,
370
+ cacheWriteTokens: 0,
371
+ toolCalls: [
372
+ {
373
+ id: "call_1",
374
+ name: "lookup_weather",
375
+ input: { city: "Tijuana" },
376
+ },
377
+ ],
378
+ stopReason: "completed",
379
+ });
380
+ expect(response.cost).toEqual({
381
+ inputPer1M: 0,
382
+ outputPer1M: 0,
383
+ });
384
+ });
385
+ it("falls back to collected text deltas when response.completed omits message output", async () => {
386
+ mockFetch.mockResolvedValueOnce(sseResponse([
387
+ { event: "response.output_text.delta", data: { delta: "Hello " } },
388
+ { event: "response.output_text.delta", data: { delta: "world" } },
389
+ {
390
+ event: "response.completed",
391
+ data: {
392
+ response: {
393
+ id: "resp_delta_fallback_1",
394
+ status: "completed",
395
+ output: [],
396
+ usage: { input_tokens: 8, output_tokens: 2 },
397
+ },
398
+ },
399
+ },
400
+ ]));
401
+ const { adapter } = await createAdapter();
402
+ const response = await adapter.createMessage(createOptions());
403
+ expect(response.parts).toEqual([{ type: "text", text: "Hello world" }]);
404
+ expect(response.inputTokens).toBe(8);
405
+ expect(response.outputTokens).toBe(2);
406
+ });
407
+ it("preserves function calls from response.output_item.added when response.completed omits them", async () => {
408
+ mockFetch.mockResolvedValueOnce(sseResponse([
409
+ {
410
+ event: "response.output_item.added",
411
+ data: {
412
+ item: {
413
+ type: "function_call",
414
+ id: "call_added_1",
415
+ name: "read",
416
+ arguments: "{\"filePath\":\"HOTFIX.MD\"}",
417
+ },
418
+ },
419
+ },
420
+ {
421
+ event: "response.completed",
422
+ data: {
423
+ response: {
424
+ id: "resp_tool_call_fallback_1",
425
+ status: "completed",
426
+ output: [],
427
+ usage: { input_tokens: 12, output_tokens: 3 },
428
+ },
429
+ },
430
+ },
431
+ ]));
432
+ const { adapter } = await createAdapter();
433
+ const response = await adapter.createMessage(createOptions());
434
+ expect(response.toolCalls).toEqual([
435
+ {
436
+ id: "call_added_1",
437
+ name: "read",
438
+ input: { filePath: "HOTFIX.MD" },
439
+ },
440
+ ]);
441
+ expect(response.parts).toEqual([]);
442
+ });
443
+ it("parses SSE streams that use CRLF separators", async () => {
444
+ mockFetch.mockResolvedValueOnce(sseResponseWithCrLf([
445
+ { event: "response.output_text.delta", data: { delta: "Hello from " } },
446
+ { event: "response.output_text.delta", data: { delta: "CRLF" } },
447
+ {
448
+ event: "response.completed",
449
+ data: {
450
+ response: {
451
+ id: "resp_crlf_1",
452
+ status: "completed",
453
+ output: [
454
+ {
455
+ type: "message",
456
+ content: [{ type: "output_text", text: "Hello from CRLF" }],
457
+ },
458
+ ],
459
+ usage: { input_tokens: 11, output_tokens: 4 },
460
+ },
461
+ },
462
+ },
463
+ ]));
464
+ const { adapter } = await createAdapter();
465
+ const response = await adapter.createMessage(createOptions());
466
+ expect(response.parts).toEqual([{ type: "text", text: "Hello from CRLF" }]);
467
+ expect(response.inputTokens).toBe(11);
468
+ expect(response.outputTokens).toBe(4);
469
+ });
470
+ it("sets inputPer1M=0, outputPer1M=0 in cost (subscription = zero marginal)", async () => {
471
+ mockFetch.mockResolvedValueOnce(sseResponse([
472
+ {
473
+ event: "response.completed",
474
+ data: {
475
+ response: {
476
+ id: "resp_6",
477
+ status: "completed",
478
+ output: [],
479
+ usage: {
480
+ input_tokens: 9,
481
+ output_tokens: 3,
482
+ },
483
+ },
484
+ },
485
+ },
486
+ ]));
487
+ const { adapter } = await createAdapter();
488
+ const response = await adapter.createMessage(createOptions());
489
+ expect(response.cost).toEqual({
490
+ inputPer1M: 0,
491
+ outputPer1M: 0,
492
+ });
493
+ });
494
+ it("retries once on 401 (refreshes token, retries request)", async () => {
495
+ mockGetValidAccessToken
496
+ .mockResolvedValueOnce("expired-token")
497
+ .mockResolvedValueOnce("fresh-token");
498
+ mockFetch
499
+ .mockResolvedValueOnce(jsonResponse(401, { error: "unauthorized" }))
500
+ .mockResolvedValueOnce(sseResponse([
501
+ {
502
+ event: "response.completed",
503
+ data: {
504
+ response: {
505
+ id: "resp_7",
506
+ status: "completed",
507
+ output: [],
508
+ usage: { input_tokens: 1, output_tokens: 1 },
509
+ },
510
+ },
511
+ },
512
+ ]));
513
+ const { adapter, auth } = await createAdapter();
514
+ await adapter.createMessage(createOptions());
515
+ expect(auth.getValidAccessToken).toHaveBeenCalledTimes(2);
516
+ expect(mockFetch).toHaveBeenCalledTimes(2);
517
+ const firstHeaders = (mockFetch.mock.calls[0]?.[1]).headers;
518
+ const secondHeaders = (mockFetch.mock.calls[1]?.[1]).headers;
519
+ expect(firstHeaders).toEqual({
520
+ Authorization: "Bearer expired-token",
521
+ "Content-Type": "application/json",
522
+ });
523
+ expect(secondHeaders).toEqual({
524
+ Authorization: "Bearer fresh-token",
525
+ "Content-Type": "application/json",
526
+ });
527
+ });
528
+ it("throws KilnError on 401 after retry", async () => {
529
+ mockGetValidAccessToken
530
+ .mockResolvedValueOnce("token-1")
531
+ .mockResolvedValueOnce("token-2");
532
+ mockFetch
533
+ .mockResolvedValueOnce(jsonResponse(401, { error: "unauthorized" }))
534
+ .mockResolvedValueOnce(jsonResponse(401, { error: "still-unauthorized" }));
535
+ const { adapter } = await createAdapter();
536
+ await expect(adapter.createMessage(createOptions())).rejects.toBeInstanceOf(KilnError);
537
+ });
538
+ it("throws KilnError on non-200 non-401 responses", async () => {
539
+ mockFetch.mockResolvedValueOnce(jsonResponse(500, { error: "server_error" }));
540
+ const { adapter } = await createAdapter();
541
+ await expect(adapter.createMessage(createOptions())).rejects.toBeInstanceOf(KilnError);
542
+ });
543
+ });
544
+ describe("streamMessage", () => {
545
+ it("sends POST with store:false and stream:true, returns async generator", async () => {
546
+ mockFetch.mockResolvedValueOnce(sseResponse([
547
+ {
548
+ event: "response.completed",
549
+ data: {
550
+ response: {
551
+ id: "resp_stream_1",
552
+ status: "completed",
553
+ output: [],
554
+ usage: { input_tokens: 4, output_tokens: 2 },
555
+ },
556
+ },
557
+ },
558
+ ]));
559
+ const { adapter } = await createAdapter();
560
+ const stream = adapter.streamMessage(createOptions());
561
+ const events = await collectEvents(stream);
562
+ expect(Symbol.asyncIterator in stream).toBe(true);
563
+ expect(events.at(-1)?.type).toBe("done");
564
+ const [, init] = mockFetch.mock.calls[0];
565
+ const body = JSON.parse(String(init.body));
566
+ expect(body.store).toBe(false);
567
+ expect(body.stream).toBe(true);
568
+ });
569
+ it("yields text delta events from response.output_text.delta SSE", async () => {
570
+ mockFetch.mockResolvedValueOnce(sseResponse([
571
+ { event: "response.output_text.delta", data: { delta: "Hello " } },
572
+ { event: "response.output_text.delta", data: { delta: "world" } },
573
+ {
574
+ event: "response.completed",
575
+ data: {
576
+ response: {
577
+ id: "resp_stream_2",
578
+ status: "completed",
579
+ output: [
580
+ {
581
+ type: "message",
582
+ content: [{ type: "output_text", text: "Hello world" }],
583
+ },
584
+ ],
585
+ usage: { input_tokens: 10, output_tokens: 3 },
586
+ },
587
+ },
588
+ },
589
+ ]));
590
+ const { adapter } = await createAdapter();
591
+ const events = await collectEvents(adapter.streamMessage(createOptions()));
592
+ expect(events.filter((event) => event.type === "text")).toEqual([
593
+ { type: "text", content: "Hello " },
594
+ { type: "text", content: "world" },
595
+ ]);
596
+ });
597
+ it("yields tool call events from response.output_item.added SSE", async () => {
598
+ mockFetch.mockResolvedValueOnce(sseResponse([
599
+ {
600
+ event: "response.output_item.added",
601
+ data: {
602
+ item: {
603
+ type: "function_call",
604
+ id: "call_stream_1",
605
+ name: "lookup_weather",
606
+ arguments: "{\"city\":\"Tijuana\"}",
607
+ call_id: "call_stream_1",
608
+ },
609
+ },
610
+ },
611
+ {
612
+ event: "response.completed",
613
+ data: {
614
+ response: {
615
+ id: "resp_stream_3",
616
+ status: "completed",
617
+ output: [
618
+ {
619
+ type: "function_call",
620
+ id: "call_stream_1",
621
+ name: "lookup_weather",
622
+ arguments: "{\"city\":\"Tijuana\"}",
623
+ },
624
+ ],
625
+ usage: { input_tokens: 5, output_tokens: 2 },
626
+ },
627
+ },
628
+ },
629
+ ]));
630
+ const { adapter } = await createAdapter();
631
+ const events = await collectEvents(adapter.streamMessage(createOptions()));
632
+ const toolEvent = events.find((event) => event.type === "tool_use");
633
+ expect(toolEvent).toBeDefined();
634
+ expect(JSON.parse(toolEvent.content)).toEqual({
635
+ id: "call_stream_1",
636
+ name: "lookup_weather",
637
+ input: { city: "Tijuana" },
638
+ });
639
+ });
640
+ it("yields final done event with full AgentResponse on response.completed", async () => {
641
+ mockFetch.mockResolvedValueOnce(sseResponse([
642
+ { event: "response.output_text.delta", data: { delta: "Final " } },
643
+ { event: "response.output_text.delta", data: { delta: "answer" } },
644
+ {
645
+ event: "response.completed",
646
+ data: {
647
+ response: {
648
+ id: "resp_stream_4",
649
+ status: "completed",
650
+ output: [
651
+ {
652
+ type: "message",
653
+ content: [{ type: "output_text", text: "Final answer" }],
654
+ },
655
+ {
656
+ type: "function_call",
657
+ id: "call_stream_2",
658
+ name: "lookup_weather",
659
+ arguments: "{\"city\":\"Mexico City\"}",
660
+ },
661
+ ],
662
+ usage: {
663
+ input_tokens: 22,
664
+ output_tokens: 6,
665
+ input_tokens_details: {
666
+ cached_tokens: 3,
667
+ },
668
+ },
669
+ },
670
+ },
671
+ },
672
+ ]));
673
+ const { adapter } = await createAdapter();
674
+ const events = await collectEvents(adapter.streamMessage(createOptions()));
675
+ const doneEvent = events.at(-1);
676
+ expect(doneEvent.type).toBe("done");
677
+ expect(doneEvent.inputTokens).toBe(22);
678
+ expect(doneEvent.outputTokens).toBe(6);
679
+ expect(doneEvent.response).toMatchObject({
680
+ parts: [{ type: "text", text: "Final answer" }],
681
+ inputTokens: 22,
682
+ outputTokens: 6,
683
+ cacheReadTokens: 3,
684
+ cacheWriteTokens: 0,
685
+ toolCalls: [
686
+ {
687
+ id: "call_stream_2",
688
+ name: "lookup_weather",
689
+ input: { city: "Mexico City" },
690
+ },
691
+ ],
692
+ stopReason: "completed",
693
+ cost: {
694
+ inputPer1M: 0,
695
+ outputPer1M: 0,
696
+ },
697
+ });
698
+ });
699
+ it("calls getValidAccessToken() before streaming", async () => {
700
+ mockFetch.mockResolvedValueOnce(sseResponse([
701
+ {
702
+ event: "response.completed",
703
+ data: {
704
+ response: {
705
+ id: "resp_stream_5",
706
+ status: "completed",
707
+ output: [],
708
+ usage: { input_tokens: 1, output_tokens: 1 },
709
+ },
710
+ },
711
+ },
712
+ ]));
713
+ const { adapter, auth } = await createAdapter();
714
+ await collectEvents(adapter.streamMessage(createOptions()));
715
+ expect(auth.getValidAccessToken).toHaveBeenCalledTimes(1);
716
+ expect(auth.getValidAccessToken.mock.invocationCallOrder[0]).toBeLessThan(mockFetch.mock.invocationCallOrder[0] ?? Number.POSITIVE_INFINITY);
717
+ });
718
+ });
719
+ });
720
+ //# sourceMappingURL=codex-oauth.test.js.map