@asterxsk/kiln 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 (263) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +170 -0
  3. package/agent/AGENTS.md +67 -0
  4. package/agent/README.md +5 -0
  5. package/agent/extensions/AGENTS.md +68 -0
  6. package/agent/extensions/ask-user/index.ts +418 -0
  7. package/agent/extensions/ask-user/install.ps1 +23 -0
  8. package/agent/extensions/ask-user/install.sh +21 -0
  9. package/agent/extensions/ask-user/package-lock.json +769 -0
  10. package/agent/extensions/ask-user/package.json +19 -0
  11. package/agent/extensions/ask-user/prompt.ts +45 -0
  12. package/agent/extensions/ask-user/tsconfig.json +7 -0
  13. package/agent/extensions/background-terminals/docs/implementation-guide.md +942 -0
  14. package/agent/extensions/background-terminals/index.ts +627 -0
  15. package/agent/extensions/background-terminals/install.ps1 +23 -0
  16. package/agent/extensions/background-terminals/install.sh +21 -0
  17. package/agent/extensions/background-terminals/manager.test.ts +735 -0
  18. package/agent/extensions/background-terminals/output.test.ts +109 -0
  19. package/agent/extensions/background-terminals/package-lock.json +769 -0
  20. package/agent/extensions/background-terminals/package.json +17 -0
  21. package/agent/extensions/background-terminals/prompt.test.ts +125 -0
  22. package/agent/extensions/background-terminals/ps.test.ts +82 -0
  23. package/agent/extensions/background-terminals/result-delivery.test.ts +44 -0
  24. package/agent/extensions/background-terminals/src/domain.ts +87 -0
  25. package/agent/extensions/background-terminals/src/manager.ts +907 -0
  26. package/agent/extensions/background-terminals/src/output.ts +84 -0
  27. package/agent/extensions/background-terminals/src/prompt.ts +142 -0
  28. package/agent/extensions/background-terminals/src/result-delivery.ts +27 -0
  29. package/agent/extensions/background-terminals/src/runtime.ts +36 -0
  30. package/agent/extensions/background-terminals/src/ui/output-view.ts +79 -0
  31. package/agent/extensions/background-terminals/src/ui/ps.ts +621 -0
  32. package/agent/extensions/background-terminals/tsconfig.json +7 -0
  33. package/agent/extensions/destructive/README.md +31 -0
  34. package/agent/extensions/destructive/index.ts +88 -0
  35. package/agent/extensions/destructive/install.ps1 +23 -0
  36. package/agent/extensions/destructive/install.sh +21 -0
  37. package/agent/extensions/file-search/index.spec.ts +443 -0
  38. package/agent/extensions/file-search/index.ts +459 -0
  39. package/agent/extensions/file-search/install.ps1 +23 -0
  40. package/agent/extensions/file-search/install.sh +21 -0
  41. package/agent/extensions/file-search/package-lock.json +2253 -0
  42. package/agent/extensions/file-search/package.json +23 -0
  43. package/agent/extensions/file-search/src/args.ts +122 -0
  44. package/agent/extensions/file-search/src/binaries.ts +422 -0
  45. package/agent/extensions/file-search/src/output.ts +126 -0
  46. package/agent/extensions/file-search/src/process.ts +146 -0
  47. package/agent/extensions/file-search/src/prompt.ts +52 -0
  48. package/agent/extensions/file-search/tsconfig.json +7 -0
  49. package/agent/extensions/goal/README.md +50 -0
  50. package/agent/extensions/goal/index.ts +155 -0
  51. package/agent/extensions/goal/install.ps1 +23 -0
  52. package/agent/extensions/goal/install.sh +21 -0
  53. package/agent/extensions/modelconf/PLAN.md +915 -0
  54. package/agent/extensions/modelconf/README.md +66 -0
  55. package/agent/extensions/modelconf/index.ts +296 -0
  56. package/agent/extensions/modelconf/install.ps1 +23 -0
  57. package/agent/extensions/modelconf/install.sh +21 -0
  58. package/agent/extensions/modelconf/package-lock.json +1809 -0
  59. package/agent/extensions/modelconf/package.json +13 -0
  60. package/agent/extensions/modelconf/src/fuzzy.ts +26 -0
  61. package/agent/extensions/modelconf/src/glob.ts +13 -0
  62. package/agent/extensions/modelconf/src/persistence.test.ts +46 -0
  63. package/agent/extensions/modelconf/src/persistence.ts +164 -0
  64. package/agent/extensions/modelconf/src/ui/ModelConfView.test.ts +75 -0
  65. package/agent/extensions/modelconf/src/ui/ModelConfView.ts +1101 -0
  66. package/agent/extensions/modelconf/tsconfig.json +15 -0
  67. package/agent/extensions/pi-web-access/CHANGELOG.md +690 -0
  68. package/agent/extensions/pi-web-access/LICENSE +21 -0
  69. package/agent/extensions/pi-web-access/README.md +470 -0
  70. package/agent/extensions/pi-web-access/SECURITY.md +5 -0
  71. package/agent/extensions/pi-web-access/activity.ts +101 -0
  72. package/agent/extensions/pi-web-access/auth-fetch.ts +148 -0
  73. package/agent/extensions/pi-web-access/banner.png +0 -0
  74. package/agent/extensions/pi-web-access/brightdata-unlocker.ts +272 -0
  75. package/agent/extensions/pi-web-access/chrome-cookies.ts +669 -0
  76. package/agent/extensions/pi-web-access/content-find.ts +139 -0
  77. package/agent/extensions/pi-web-access/credential-source.ts +191 -0
  78. package/agent/extensions/pi-web-access/data-uri-sanitize.ts +406 -0
  79. package/agent/extensions/pi-web-access/datalab-pdf-extract.ts +568 -0
  80. package/agent/extensions/pi-web-access/declared-web-links.ts +173 -0
  81. package/agent/extensions/pi-web-access/evidence/CONTRACT-EVIDENCE.md +496 -0
  82. package/agent/extensions/pi-web-access/evidence/contract-probe.mjs +140 -0
  83. package/agent/extensions/pi-web-access/exa.ts +526 -0
  84. package/agent/extensions/pi-web-access/extract.ts +1196 -0
  85. package/agent/extensions/pi-web-access/feature-config.ts +29 -0
  86. package/agent/extensions/pi-web-access/fetch-params.ts +111 -0
  87. package/agent/extensions/pi-web-access/gemini-adc.ts +298 -0
  88. package/agent/extensions/pi-web-access/gemini-api.ts +353 -0
  89. package/agent/extensions/pi-web-access/gemini-pdf-extract.ts +108 -0
  90. package/agent/extensions/pi-web-access/gemini-search.ts +21 -0
  91. package/agent/extensions/pi-web-access/gemini-url-context.ts +128 -0
  92. package/agent/extensions/pi-web-access/gemini-web-config.ts +101 -0
  93. package/agent/extensions/pi-web-access/gemini-web.ts +487 -0
  94. package/agent/extensions/pi-web-access/github-api.ts +197 -0
  95. package/agent/extensions/pi-web-access/github-extract.ts +746 -0
  96. package/agent/extensions/pi-web-access/github-issue-pr.ts +700 -0
  97. package/agent/extensions/pi-web-access/index.ts +1737 -0
  98. package/agent/extensions/pi-web-access/package-lock.json +5808 -0
  99. package/agent/extensions/pi-web-access/package.json +64 -0
  100. package/agent/extensions/pi-web-access/page-query.ts +96 -0
  101. package/agent/extensions/pi-web-access/pdf-extract.ts +409 -0
  102. package/agent/extensions/pi-web-access/pi-web-fetch-demo.mp4 +0 -0
  103. package/agent/extensions/pi-web-access/promise-try.d.ts +7 -0
  104. package/agent/extensions/pi-web-access/query-rewrite.ts +51 -0
  105. package/agent/extensions/pi-web-access/render-search-error.ts +170 -0
  106. package/agent/extensions/pi-web-access/rsc-extract.ts +338 -0
  107. package/agent/extensions/pi-web-access/search-types.ts +20 -0
  108. package/agent/extensions/pi-web-access/source-check.ts +282 -0
  109. package/agent/extensions/pi-web-access/ssrf-protection.ts +526 -0
  110. package/agent/extensions/pi-web-access/storage.ts +521 -0
  111. package/agent/extensions/pi-web-access/summary-model-scope.ts +125 -0
  112. package/agent/extensions/pi-web-access/test/auth-fetch.test.mjs +208 -0
  113. package/agent/extensions/pi-web-access/test/brightdata-unlocker.test.mjs +840 -0
  114. package/agent/extensions/pi-web-access/test/chrome-cookie-extraction.test.mjs +441 -0
  115. package/agent/extensions/pi-web-access/test/config-path.test.mjs +283 -0
  116. package/agent/extensions/pi-web-access/test/content-find.test.mjs +25 -0
  117. package/agent/extensions/pi-web-access/test/credential-source.test.mjs +118 -0
  118. package/agent/extensions/pi-web-access/test/data-uri-sanitize.test.mjs +210 -0
  119. package/agent/extensions/pi-web-access/test/datalab-pdf-extract.test.mjs +552 -0
  120. package/agent/extensions/pi-web-access/test/declared-web-links.test.mjs +212 -0
  121. package/agent/extensions/pi-web-access/test/fetch-answer-storage.test.mjs +40 -0
  122. package/agent/extensions/pi-web-access/test/fetch-cache-storage.test.mjs +334 -0
  123. package/agent/extensions/pi-web-access/test/fetch-content-domain-policy.test.mjs +95 -0
  124. package/agent/extensions/pi-web-access/test/fetch-modes.test.mjs +53 -0
  125. package/agent/extensions/pi-web-access/test/fetch-not-found-guidance.test.mjs +92 -0
  126. package/agent/extensions/pi-web-access/test/fetch-params.test.mjs +86 -0
  127. package/agent/extensions/pi-web-access/test/fetch-render-call.test.mjs +34 -0
  128. package/agent/extensions/pi-web-access/test/fetch-routing.test.mjs +173 -0
  129. package/agent/extensions/pi-web-access/test/gemini-adc-auth.test.mjs +257 -0
  130. package/agent/extensions/pi-web-access/test/gemini-api-transport.test.mjs +170 -0
  131. package/agent/extensions/pi-web-access/test/gemini-pdf-extract.test.mjs +133 -0
  132. package/agent/extensions/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +178 -0
  133. package/agent/extensions/pi-web-access/test/gemini-web-header-overflow.test.mjs +148 -0
  134. package/agent/extensions/pi-web-access/test/get-search-content.test.mjs +223 -0
  135. package/agent/extensions/pi-web-access/test/github-extract.test.mjs +378 -0
  136. package/agent/extensions/pi-web-access/test/github-issue-pr.test.mjs +565 -0
  137. package/agent/extensions/pi-web-access/test/inline-content-config.test.mjs +99 -0
  138. package/agent/extensions/pi-web-access/test/lazy-extract-load.test.mjs +118 -0
  139. package/agent/extensions/pi-web-access/test/local-video-oversize.test.mjs +52 -0
  140. package/agent/extensions/pi-web-access/test/package-typebox-dependency.test.mjs +50 -0
  141. package/agent/extensions/pi-web-access/test/page-query.test.mjs +51 -0
  142. package/agent/extensions/pi-web-access/test/pdf-config.test.mjs +140 -0
  143. package/agent/extensions/pi-web-access/test/pdf-extract.test.mjs +500 -0
  144. package/agent/extensions/pi-web-access/test/proxy-transport.test.mjs +286 -0
  145. package/agent/extensions/pi-web-access/test/query-rewrite.test.mjs +52 -0
  146. package/agent/extensions/pi-web-access/test/rsc-fallback.test.mjs +102 -0
  147. package/agent/extensions/pi-web-access/test/search-error-render.test.mjs +152 -0
  148. package/agent/extensions/pi-web-access/test/search-providers.test.mjs +274 -0
  149. package/agent/extensions/pi-web-access/test/source-check.test.mjs +179 -0
  150. package/agent/extensions/pi-web-access/test/ssrf-allow-ranges-config.test.mjs +205 -0
  151. package/agent/extensions/pi-web-access/test/ssrf-protection.test.mjs +456 -0
  152. package/agent/extensions/pi-web-access/test/summary-model-scope.test.mjs +106 -0
  153. package/agent/extensions/pi-web-access/test/tool-registration-config.test.mjs +182 -0
  154. package/agent/extensions/pi-web-access/test/web-search-answer-render.test.mjs +66 -0
  155. package/agent/extensions/pi-web-access/test/youtube-extract-errors.test.mjs +64 -0
  156. package/agent/extensions/pi-web-access/tsconfig.json +11 -0
  157. package/agent/extensions/pi-web-access/utils.ts +451 -0
  158. package/agent/extensions/pi-web-access/video-extract.ts +392 -0
  159. package/agent/extensions/pi-web-access/youtube-extract.ts +328 -0
  160. package/agent/extensions/shared/activity-status.ts +31 -0
  161. package/agent/extensions/shared/child-session.test.ts +270 -0
  162. package/agent/extensions/shared/child-session.ts +148 -0
  163. package/agent/extensions/shared/context-utilization.test.ts +48 -0
  164. package/agent/extensions/shared/context-utilization.ts +47 -0
  165. package/agent/extensions/shared/dashboard-state.ts +99 -0
  166. package/agent/extensions/shared/install.ps1 +23 -0
  167. package/agent/extensions/shared/install.sh +21 -0
  168. package/agent/extensions/shared/tool-call-timeout.test.ts +117 -0
  169. package/agent/extensions/shared/tool-call-timeout.ts +104 -0
  170. package/agent/extensions/skillsconf/README.md +74 -0
  171. package/agent/extensions/skillsconf/index.ts +92 -0
  172. package/agent/extensions/skillsconf/install.ps1 +23 -0
  173. package/agent/extensions/skillsconf/install.sh +21 -0
  174. package/agent/extensions/skillsconf/package-lock.json +1809 -0
  175. package/agent/extensions/skillsconf/package.json +18 -0
  176. package/agent/extensions/skillsconf/src/delete-skill.test.ts +64 -0
  177. package/agent/extensions/skillsconf/src/delete-skill.ts +45 -0
  178. package/agent/extensions/skillsconf/src/filter.test.ts +80 -0
  179. package/agent/extensions/skillsconf/src/filter.ts +74 -0
  180. package/agent/extensions/skillsconf/src/fuzzy.ts +26 -0
  181. package/agent/extensions/skillsconf/src/persistence.test.ts +72 -0
  182. package/agent/extensions/skillsconf/src/persistence.ts +169 -0
  183. package/agent/extensions/skillsconf/src/ui/SkillConfView.test.ts +337 -0
  184. package/agent/extensions/skillsconf/src/ui/SkillConfView.ts +758 -0
  185. package/agent/extensions/skillsconf/src/ui/text-input.ts +91 -0
  186. package/agent/extensions/skillsconf/src/ui/tui-helpers.ts +144 -0
  187. package/agent/extensions/skillsconf/tsconfig.json +15 -0
  188. package/agent/extensions/status line/index.ts +282 -0
  189. package/agent/extensions/status line/install.ps1 +23 -0
  190. package/agent/extensions/status line/install.sh +21 -0
  191. package/agent/extensions/subagents/by-the-way.test.ts +29 -0
  192. package/agent/extensions/subagents/claude.test.ts +119 -0
  193. package/agent/extensions/subagents/codex.test.ts +102 -0
  194. package/agent/extensions/subagents/context-usage.test.ts +107 -0
  195. package/agent/extensions/subagents/docs/design-plan.md +568 -0
  196. package/agent/extensions/subagents/docs/effect-v4-extension-guide.md +354 -0
  197. package/agent/extensions/subagents/docs/effect-v4-notes.md +571 -0
  198. package/agent/extensions/subagents/index.ts +779 -0
  199. package/agent/extensions/subagents/install.ps1 +23 -0
  200. package/agent/extensions/subagents/install.sh +21 -0
  201. package/agent/extensions/subagents/manager.test.ts +276 -0
  202. package/agent/extensions/subagents/package-lock.json +2244 -0
  203. package/agent/extensions/subagents/package.json +19 -0
  204. package/agent/extensions/subagents/result-delivery.test.ts +27 -0
  205. package/agent/extensions/subagents/src/backend.ts +73 -0
  206. package/agent/extensions/subagents/src/backends/claude.ts +701 -0
  207. package/agent/extensions/subagents/src/backends/codex.ts +1060 -0
  208. package/agent/extensions/subagents/src/backends/pi.ts +575 -0
  209. package/agent/extensions/subagents/src/backends/stub.ts +300 -0
  210. package/agent/extensions/subagents/src/by-the-way.ts +21 -0
  211. package/agent/extensions/subagents/src/domain.ts +253 -0
  212. package/agent/extensions/subagents/src/format.ts +74 -0
  213. package/agent/extensions/subagents/src/manager.ts +736 -0
  214. package/agent/extensions/subagents/src/prompt.ts +92 -0
  215. package/agent/extensions/subagents/src/result-delivery.ts +20 -0
  216. package/agent/extensions/subagents/src/runtime.ts +53 -0
  217. package/agent/extensions/subagents/src/ui/takeover.ts +583 -0
  218. package/agent/extensions/subagents/src/ui/transcript.ts +201 -0
  219. package/agent/extensions/subagents/takeover.test.ts +29 -0
  220. package/agent/extensions/subagents/tsconfig.json +7 -0
  221. package/agent/extensions/taste/index.ts +443 -0
  222. package/agent/extensions/taste/install.ps1 +23 -0
  223. package/agent/extensions/taste/install.sh +21 -0
  224. package/agent/extensions/todo/AGENTS.md +38 -0
  225. package/agent/extensions/todo/LICENSE +21 -0
  226. package/agent/extensions/todo/config.ts +55 -0
  227. package/agent/extensions/todo/index.ts +151 -0
  228. package/agent/extensions/todo/install.ps1 +23 -0
  229. package/agent/extensions/todo/install.sh +21 -0
  230. package/agent/extensions/todo/locales/de.json +17 -0
  231. package/agent/extensions/todo/locales/en.json +15 -0
  232. package/agent/extensions/todo/locales/es.json +17 -0
  233. package/agent/extensions/todo/locales/fr.json +17 -0
  234. package/agent/extensions/todo/locales/pt-BR.json +17 -0
  235. package/agent/extensions/todo/locales/pt.json +17 -0
  236. package/agent/extensions/todo/locales/ru.json +17 -0
  237. package/agent/extensions/todo/locales/uk.json +17 -0
  238. package/agent/extensions/todo/locales/zh.json +17 -0
  239. package/agent/extensions/todo/package-lock.json +3358 -0
  240. package/agent/extensions/todo/package.json +67 -0
  241. package/agent/extensions/todo/state/i18n-bridge.ts +64 -0
  242. package/agent/extensions/todo/state/invariants.ts +20 -0
  243. package/agent/extensions/todo/state/replay.ts +38 -0
  244. package/agent/extensions/todo/state/selectors.ts +107 -0
  245. package/agent/extensions/todo/state/state-reducer.ts +326 -0
  246. package/agent/extensions/todo/state/state.ts +18 -0
  247. package/agent/extensions/todo/state/store.ts +82 -0
  248. package/agent/extensions/todo/state/task-graph.ts +57 -0
  249. package/agent/extensions/todo/todo-overlay.ts +200 -0
  250. package/agent/extensions/todo/todo.ts +155 -0
  251. package/agent/extensions/todo/tool/response-envelope.ts +109 -0
  252. package/agent/extensions/todo/tool/types.ts +206 -0
  253. package/agent/extensions/todo/verify-ref-system.js +0 -0
  254. package/agent/extensions/todo/view/format.ts +177 -0
  255. package/agent/extensions/trim-context/README.md +54 -0
  256. package/agent/extensions/trim-context/index.ts +487 -0
  257. package/agent/extensions/trim-context/install.ps1 +23 -0
  258. package/agent/extensions/trim-context/install.sh +21 -0
  259. package/agent/install.ps1 +527 -0
  260. package/agent/install.sh +511 -0
  261. package/agent/keybindings.json +7 -0
  262. package/bin/kiln.js +359 -0
  263. package/package.json +21 -0
@@ -0,0 +1,119 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { Effect } from "effect";
4
+ import { SubagentManager } from "./src/manager.ts";
5
+ import { claudeBackend } from "./src/backends/claude.ts";
6
+ import type { ParentContext, SpawnTask } from "./src/domain.ts";
7
+ import { createSubagentRuntime, runTool } from "./src/runtime.ts";
8
+
9
+ const parent: ParentContext = {
10
+ parentCwd: process.cwd(),
11
+ projectTrusted: false,
12
+ };
13
+
14
+ function task(prompt: string): SpawnTask {
15
+ return {
16
+ prompt,
17
+ title: "live Claude test",
18
+ cwd: process.cwd(),
19
+ model: "haiku",
20
+ reasoningEffort: "off",
21
+ parent,
22
+ };
23
+ }
24
+
25
+ async function claudeAvailable() {
26
+ return Effect.runPromise(claudeBackend.available);
27
+ }
28
+
29
+ /** Rejecting deadline so a hung wait still reaches finally() and disposes. */
30
+ function deadline<A>(operation: Promise<A>, timeoutMs: number) {
31
+ let timer: ReturnType<typeof setTimeout> | undefined;
32
+ const timeout = new Promise<never>((_resolve, reject) => {
33
+ timer = setTimeout(
34
+ () => reject(new Error(`Live Claude test exceeded ${timeoutMs}ms`)),
35
+ timeoutMs,
36
+ );
37
+ });
38
+ return Promise.race([operation, timeout]).finally(() => {
39
+ if (timer) clearTimeout(timer);
40
+ });
41
+ }
42
+
43
+ test(
44
+ "Claude backend completes a live manager run",
45
+ { timeout: 60_000 },
46
+ async (t) => {
47
+ if (!(await claudeAvailable())) {
48
+ t.skip("Claude Code executable is unavailable");
49
+ return;
50
+ }
51
+
52
+ const runtime = createSubagentRuntime();
53
+ try {
54
+ const manager = await runtime.runPromise(SubagentManager);
55
+ const started = await runTool(
56
+ runtime,
57
+ manager.spawn("claude", task("Reply with exactly: hello claude")),
58
+ );
59
+ await deadline(runTool(runtime, manager.waitFor([started.id])), 45_000);
60
+
61
+ const done = manager.view.get(started.id);
62
+ assert.equal(done?.status, "done");
63
+ assert.match(done?.finalText ?? "", /hello claude/i);
64
+ assert.ok(done?.meta.nativeSessionId);
65
+ assert.ok(done?.meta.sessionFilePath?.endsWith(".jsonl"));
66
+ } finally {
67
+ await runtime.dispose();
68
+ }
69
+ },
70
+ );
71
+
72
+ test(
73
+ "Claude backend interrupt settles a live run as aborted",
74
+ { timeout: 60_000 },
75
+ async (t) => {
76
+ if (!(await claudeAvailable())) {
77
+ t.skip("Claude Code executable is unavailable");
78
+ return;
79
+ }
80
+
81
+ const runtime = createSubagentRuntime();
82
+ try {
83
+ const manager = await runtime.runPromise(SubagentManager);
84
+ const started = await runTool(
85
+ runtime,
86
+ manager.spawn(
87
+ "claude",
88
+ task(
89
+ "Write a detailed 10,000-word essay about the history of computing.",
90
+ ),
91
+ ),
92
+ );
93
+
94
+ // Wait for streamed output so cancellation definitely lands mid-run and
95
+ // exercises the SDK's normal interrupt receipt/result path.
96
+ const streamDeadline = Date.now() + 15_000;
97
+ while (
98
+ manager.view.get(started.id)?.status === "running" &&
99
+ !manager.view.get(started.id)?.liveAssistant?.text &&
100
+ Date.now() < streamDeadline
101
+ ) {
102
+ await new Promise((resolve) => setTimeout(resolve, 20));
103
+ }
104
+ assert.equal(manager.view.get(started.id)?.status, "running");
105
+ assert.ok(manager.view.get(started.id)?.liveAssistant?.text);
106
+
107
+ const report = await deadline(
108
+ runTool(runtime, manager.cancel([started.id])),
109
+ 20_000,
110
+ );
111
+
112
+ assert.equal(report[0]?.cancelled, true);
113
+ assert.equal(manager.view.get(started.id)?.status, "error");
114
+ assert.equal(manager.view.get(started.id)?.errorText, "Run was aborted");
115
+ } finally {
116
+ await runtime.dispose();
117
+ }
118
+ },
119
+ );
@@ -0,0 +1,102 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { Effect } from "effect";
4
+ import { codexBackend } from "./src/backends/codex.ts";
5
+ import type { ParentContext, SpawnTask } from "./src/domain.ts";
6
+ import { SubagentManager } from "./src/manager.ts";
7
+ import { createSubagentRuntime, runTool } from "./src/runtime.ts";
8
+
9
+ const parent: ParentContext = {
10
+ parentCwd: process.cwd(),
11
+ projectTrusted: false,
12
+ };
13
+
14
+ function task(prompt: string): SpawnTask {
15
+ return {
16
+ prompt,
17
+ title: "live Codex test",
18
+ cwd: process.cwd(),
19
+ parent,
20
+ };
21
+ }
22
+
23
+ function deadline<A>(operation: Promise<A>, timeoutMs: number) {
24
+ let timer: ReturnType<typeof setTimeout> | undefined;
25
+ const timeout = new Promise<never>((_resolve, reject) => {
26
+ timer = setTimeout(
27
+ () => reject(new Error(`Live Codex test exceeded ${timeoutMs}ms`)),
28
+ timeoutMs,
29
+ );
30
+ });
31
+ return Promise.race([operation, timeout]).finally(() => {
32
+ if (timer) clearTimeout(timer);
33
+ });
34
+ }
35
+
36
+ async function codexAvailable() {
37
+ return Effect.runPromise(codexBackend.available);
38
+ }
39
+
40
+ test(
41
+ "Codex backend completes a live manager run",
42
+ { timeout: 75_000 },
43
+ async (t) => {
44
+ if (!(await codexAvailable())) {
45
+ t.skip("codex executable is unavailable");
46
+ return;
47
+ }
48
+
49
+ const runtime = createSubagentRuntime();
50
+ try {
51
+ const manager = await runtime.runPromise(SubagentManager);
52
+ const spawned = await runTool(
53
+ runtime,
54
+ manager.spawn("codex", task("Reply with exactly: hello codex")),
55
+ );
56
+
57
+ await deadline(runTool(runtime, manager.waitFor([spawned.id])), 60_000);
58
+ const done = manager.view.get(spawned.id);
59
+ assert.equal(done?.status, "done");
60
+ assert.match(done?.finalText ?? "", /hello codex/i);
61
+ assert.equal(done?.meta.backend, "codex");
62
+ assert.ok(done?.meta.nativeSessionId);
63
+ assert.ok(done?.meta.sessionFilePath);
64
+ } finally {
65
+ await runtime.dispose();
66
+ }
67
+ },
68
+ );
69
+
70
+ test(
71
+ "Codex backend interrupt settles a live manager run",
72
+ { timeout: 30_000 },
73
+ async (t) => {
74
+ if (!(await codexAvailable())) {
75
+ t.skip("codex executable is unavailable");
76
+ return;
77
+ }
78
+
79
+ const runtime = createSubagentRuntime();
80
+ try {
81
+ const manager = await runtime.runPromise(SubagentManager);
82
+ const spawned = await runTool(
83
+ runtime,
84
+ manager.spawn(
85
+ "codex",
86
+ task("Run `sleep 30`, then reply with the word finished."),
87
+ ),
88
+ );
89
+
90
+ await new Promise((resolve) => setTimeout(resolve, 250));
91
+ const result = await deadline(
92
+ runTool(runtime, manager.cancel([spawned.id])),
93
+ 10_000,
94
+ );
95
+ assert.equal(result[0]?.cancelled, true);
96
+ assert.equal(manager.view.get(spawned.id)?.status, "error");
97
+ assert.equal(manager.view.get(spawned.id)?.errorText, "Run was aborted");
98
+ } finally {
99
+ await runtime.dispose();
100
+ }
101
+ },
102
+ );
@@ -0,0 +1,107 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { contextOccupancyTokens } from "./src/backends/claude.ts";
4
+ import { parseThreadTokenUsage } from "./src/backends/codex.ts";
5
+
6
+ // --- Claude: per-request occupancy, never the run aggregate ------------------
7
+
8
+ test("Claude occupancy sums one request's input, cache, and output tokens", () => {
9
+ assert.equal(
10
+ contextOccupancyTokens({
11
+ input_tokens: 12,
12
+ cache_read_input_tokens: 45_000,
13
+ cache_creation_input_tokens: 3_000,
14
+ output_tokens: 700,
15
+ }),
16
+ 48_712,
17
+ );
18
+ });
19
+
20
+ test("Claude occupancy treats null cache/output counts as zero", () => {
21
+ assert.equal(
22
+ contextOccupancyTokens({
23
+ input_tokens: 9_000,
24
+ cache_read_input_tokens: null,
25
+ cache_creation_input_tokens: null,
26
+ output_tokens: 250,
27
+ }),
28
+ 9_250,
29
+ );
30
+ });
31
+
32
+ test("Claude occupancy is unknown without a usable per-request usage", () => {
33
+ assert.equal(contextOccupancyTokens(undefined), undefined);
34
+ assert.equal(contextOccupancyTokens(null), undefined);
35
+ assert.equal(
36
+ contextOccupancyTokens({ input_tokens: null, output_tokens: 5 }),
37
+ undefined,
38
+ );
39
+ });
40
+
41
+ test("Claude occupancy from the last request stays below the window where the run aggregate would not", () => {
42
+ // A 10-request tool loop over a mostly-cached 150k prompt: the aggregate
43
+ // usage (what SDKResultMessage.usage reports) re-counts the cache per
44
+ // request and blows past the 200k window; the final request's usage is the
45
+ // true occupancy.
46
+ const perRequest = {
47
+ input_tokens: 500,
48
+ cache_read_input_tokens: 150_000,
49
+ cache_creation_input_tokens: 0,
50
+ output_tokens: 400,
51
+ };
52
+ const aggregate = {
53
+ input_tokens: 500 * 10,
54
+ cache_read_input_tokens: 150_000 * 10,
55
+ cache_creation_input_tokens: 0,
56
+ output_tokens: 400 * 10,
57
+ };
58
+ const occupancy = contextOccupancyTokens(perRequest);
59
+ assert.ok(occupancy !== undefined && occupancy < 200_000);
60
+ const misleading = contextOccupancyTokens(aggregate);
61
+ assert.ok(misleading !== undefined && misleading > 200_000);
62
+ });
63
+
64
+ // --- Codex: last request's total, never the thread-cumulative total ----------
65
+
66
+ const codexParams = (tokenUsage: unknown) => ({
67
+ threadId: "t",
68
+ turnId: "u",
69
+ tokenUsage,
70
+ });
71
+
72
+ test("Codex occupancy uses tokenUsage.last.totalTokens, not the cumulative total", () => {
73
+ const { tokens, contextWindow } = parseThreadTokenUsage(
74
+ codexParams({
75
+ total: {
76
+ totalTokens: 1_450_000,
77
+ inputTokens: 1_400_000,
78
+ cachedInputTokens: 1_300_000,
79
+ outputTokens: 50_000,
80
+ reasoningOutputTokens: 20_000,
81
+ },
82
+ last: {
83
+ totalTokens: 61_000,
84
+ inputTokens: 60_000,
85
+ cachedInputTokens: 55_000,
86
+ outputTokens: 1_000,
87
+ reasoningOutputTokens: 400,
88
+ },
89
+ modelContextWindow: 272_000,
90
+ }),
91
+ );
92
+ assert.equal(tokens, 61_000);
93
+ assert.equal(contextWindow, 272_000);
94
+ });
95
+
96
+ test("Codex occupancy is unknown when last usage or window is absent", () => {
97
+ assert.deepEqual(
98
+ parseThreadTokenUsage(
99
+ codexParams({ total: { totalTokens: 10 }, modelContextWindow: null }),
100
+ ),
101
+ { tokens: undefined, contextWindow: undefined },
102
+ );
103
+ assert.deepEqual(parseThreadTokenUsage({ threadId: "t" }), {
104
+ tokens: undefined,
105
+ contextWindow: undefined,
106
+ });
107
+ });