@kilnai/cli 0.23.1 → 1.0.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 (300) hide show
  1. package/README.md +33 -0
  2. package/dist/application/__tests__/plan-exit-tool.test.d.ts +2 -0
  3. package/dist/application/__tests__/plan-exit-tool.test.d.ts.map +1 -0
  4. package/dist/application/__tests__/plan-exit-tool.test.js +12 -0
  5. package/dist/application/__tests__/plan-exit-tool.test.js.map +1 -0
  6. package/dist/application/agent-loader.d.ts +12 -0
  7. package/dist/application/agent-loader.d.ts.map +1 -0
  8. package/dist/application/agent-loader.js +114 -0
  9. package/dist/application/agent-loader.js.map +1 -0
  10. package/dist/application/agent-loader.test.d.ts +2 -0
  11. package/dist/application/agent-loader.test.d.ts.map +1 -0
  12. package/dist/application/agent-loader.test.js +151 -0
  13. package/dist/application/agent-loader.test.js.map +1 -0
  14. package/dist/application/context-artifact-keys.d.ts +5 -0
  15. package/dist/application/context-artifact-keys.d.ts.map +1 -0
  16. package/dist/application/context-artifact-keys.js +14 -0
  17. package/dist/application/context-artifact-keys.js.map +1 -0
  18. package/dist/application/context-governance.d.ts +6 -0
  19. package/dist/application/context-governance.d.ts.map +1 -0
  20. package/dist/application/context-governance.js +20 -0
  21. package/dist/application/context-governance.js.map +1 -0
  22. package/dist/application/context-governor.d.ts +26 -0
  23. package/dist/application/context-governor.d.ts.map +1 -0
  24. package/dist/application/context-governor.js +164 -0
  25. package/dist/application/context-governor.js.map +1 -0
  26. package/dist/application/context-types.d.ts +20 -0
  27. package/dist/application/context-types.d.ts.map +1 -0
  28. package/dist/application/context-types.js +21 -0
  29. package/dist/application/context-types.js.map +1 -0
  30. package/dist/application/plan-exit-tool.d.ts +19 -0
  31. package/dist/application/plan-exit-tool.d.ts.map +1 -0
  32. package/dist/application/plan-exit-tool.js +13 -0
  33. package/dist/application/plan-exit-tool.js.map +1 -0
  34. package/dist/application/repo-summary-cache.d.ts +5 -0
  35. package/dist/application/repo-summary-cache.d.ts.map +1 -0
  36. package/dist/application/repo-summary-cache.js +82 -0
  37. package/dist/application/repo-summary-cache.js.map +1 -0
  38. package/dist/application/resume-strategy-feedback.d.ts +5 -0
  39. package/dist/application/resume-strategy-feedback.d.ts.map +1 -0
  40. package/dist/application/resume-strategy-feedback.js +81 -0
  41. package/dist/application/resume-strategy-feedback.js.map +1 -0
  42. package/dist/application/resume-strategy-policy.d.ts +26 -0
  43. package/dist/application/resume-strategy-policy.d.ts.map +1 -0
  44. package/dist/application/resume-strategy-policy.js +35 -0
  45. package/dist/application/resume-strategy-policy.js.map +1 -0
  46. package/dist/application/run-session.d.ts +39 -0
  47. package/dist/application/run-session.d.ts.map +1 -0
  48. package/dist/application/run-session.js +349 -0
  49. package/dist/application/run-session.js.map +1 -0
  50. package/dist/application/session-hooks.d.ts +20 -0
  51. package/dist/application/session-hooks.d.ts.map +1 -0
  52. package/dist/application/session-hooks.js +55 -0
  53. package/dist/application/session-hooks.js.map +1 -0
  54. package/dist/application/session-ledger.d.ts +12 -0
  55. package/dist/application/session-ledger.d.ts.map +1 -0
  56. package/dist/application/session-ledger.js +29 -0
  57. package/dist/application/session-ledger.js.map +1 -0
  58. package/dist/application/session-report.d.ts +19 -0
  59. package/dist/application/session-report.d.ts.map +1 -0
  60. package/dist/application/session-report.js +207 -0
  61. package/dist/application/session-report.js.map +1 -0
  62. package/dist/application/session-resume.d.ts +3 -0
  63. package/dist/application/session-resume.d.ts.map +1 -0
  64. package/dist/application/session-resume.js +16 -0
  65. package/dist/application/session-resume.js.map +1 -0
  66. package/dist/commands/auth.d.ts +2 -0
  67. package/dist/commands/auth.d.ts.map +1 -0
  68. package/dist/commands/auth.js +138 -0
  69. package/dist/commands/auth.js.map +1 -0
  70. package/dist/commands/config.d.ts +1 -1
  71. package/dist/commands/config.d.ts.map +1 -1
  72. package/dist/commands/config.js +141 -27
  73. package/dist/commands/config.js.map +1 -1
  74. package/dist/commands/cron.d.ts +3 -0
  75. package/dist/commands/cron.d.ts.map +1 -0
  76. package/dist/commands/cron.js +201 -0
  77. package/dist/commands/cron.js.map +1 -0
  78. package/dist/commands/dev.js +2 -2
  79. package/dist/commands/dev.js.map +1 -1
  80. package/dist/commands/domain.js +17 -17
  81. package/dist/commands/domain.js.map +1 -1
  82. package/dist/commands/init.d.ts +2 -11
  83. package/dist/commands/init.d.ts.map +1 -1
  84. package/dist/commands/init.js +14 -25
  85. package/dist/commands/init.js.map +1 -1
  86. package/dist/commands/mcp-config.d.ts +7 -1
  87. package/dist/commands/mcp-config.d.ts.map +1 -1
  88. package/dist/commands/mcp-config.js +26 -10
  89. package/dist/commands/mcp-config.js.map +1 -1
  90. package/dist/commands/memory.d.ts +1 -1
  91. package/dist/commands/memory.d.ts.map +1 -1
  92. package/dist/commands/memory.js +10 -10
  93. package/dist/commands/memory.js.map +1 -1
  94. package/dist/commands/run.d.ts +15 -3
  95. package/dist/commands/run.d.ts.map +1 -1
  96. package/dist/commands/run.js +464 -85
  97. package/dist/commands/run.js.map +1 -1
  98. package/dist/commands/serve.d.ts +1 -1
  99. package/dist/commands/serve.d.ts.map +1 -1
  100. package/dist/commands/serve.js +3 -3
  101. package/dist/commands/serve.js.map +1 -1
  102. package/dist/commands/skill-capture.d.ts +13 -0
  103. package/dist/commands/skill-capture.d.ts.map +1 -0
  104. package/dist/commands/skill-capture.js +212 -0
  105. package/dist/commands/skill-capture.js.map +1 -0
  106. package/dist/commands/skill.d.ts.map +1 -1
  107. package/dist/commands/skill.js +7 -3
  108. package/dist/commands/skill.js.map +1 -1
  109. package/dist/commands/status.d.ts +1 -1
  110. package/dist/commands/status.d.ts.map +1 -1
  111. package/dist/commands/status.js +15 -16
  112. package/dist/commands/status.js.map +1 -1
  113. package/dist/commands/sync.d.ts +11 -0
  114. package/dist/commands/sync.d.ts.map +1 -0
  115. package/dist/commands/sync.js +114 -0
  116. package/dist/commands/sync.js.map +1 -0
  117. package/dist/commands/tools.d.ts +6 -0
  118. package/dist/commands/tools.d.ts.map +1 -0
  119. package/dist/commands/tools.js +28 -0
  120. package/dist/commands/tools.js.map +1 -0
  121. package/dist/commands/tui.d.ts +29 -0
  122. package/dist/commands/tui.d.ts.map +1 -0
  123. package/dist/commands/tui.js +444 -0
  124. package/dist/commands/tui.js.map +1 -0
  125. package/dist/config/config-merger.d.ts +5 -0
  126. package/dist/config/config-merger.d.ts.map +1 -0
  127. package/dist/config/config-merger.js +25 -0
  128. package/dist/config/config-merger.js.map +1 -0
  129. package/dist/config/config-merger.test.d.ts +2 -0
  130. package/dist/config/config-merger.test.d.ts.map +1 -0
  131. package/dist/config/config-merger.test.js +165 -0
  132. package/dist/config/config-merger.test.js.map +1 -0
  133. package/dist/config/env-config.d.ts +5 -0
  134. package/dist/config/env-config.d.ts.map +1 -0
  135. package/dist/config/env-config.js +24 -0
  136. package/dist/config/env-config.js.map +1 -0
  137. package/dist/config/env-config.test.d.ts +2 -0
  138. package/dist/config/env-config.test.d.ts.map +1 -0
  139. package/dist/config/env-config.test.js +79 -0
  140. package/dist/config/env-config.test.js.map +1 -0
  141. package/dist/config/global-config.d.ts +23 -0
  142. package/dist/config/global-config.d.ts.map +1 -0
  143. package/dist/config/global-config.js +48 -0
  144. package/dist/config/global-config.js.map +1 -0
  145. package/dist/config/global-config.test.d.ts +2 -0
  146. package/dist/config/global-config.test.d.ts.map +1 -0
  147. package/dist/config/global-config.test.js +96 -0
  148. package/dist/config/global-config.test.js.map +1 -0
  149. package/dist/config-resolver.d.ts +5 -0
  150. package/dist/config-resolver.d.ts.map +1 -0
  151. package/dist/config-resolver.js +19 -0
  152. package/dist/config-resolver.js.map +1 -0
  153. package/dist/config.d.ts +8 -7
  154. package/dist/config.d.ts.map +1 -1
  155. package/dist/config.js +14 -1
  156. package/dist/config.js.map +1 -1
  157. package/dist/index.d.ts.map +1 -1
  158. package/dist/index.js +193 -16
  159. package/dist/index.js.map +1 -1
  160. package/dist/kiln-yaml-types.d.ts +127 -0
  161. package/dist/kiln-yaml-types.d.ts.map +1 -0
  162. package/dist/kiln-yaml-types.js +42 -0
  163. package/dist/kiln-yaml-types.js.map +1 -0
  164. package/dist/kiln-yaml.d.ts +10 -0
  165. package/dist/kiln-yaml.d.ts.map +1 -0
  166. package/dist/kiln-yaml.js +109 -0
  167. package/dist/kiln-yaml.js.map +1 -0
  168. package/dist/mcp/config-generator.d.ts +9 -2
  169. package/dist/mcp/config-generator.d.ts.map +1 -1
  170. package/dist/mcp/config-generator.js +132 -2
  171. package/dist/mcp/config-generator.js.map +1 -1
  172. package/dist/mcp/index.d.ts.map +1 -1
  173. package/dist/mcp/index.js +2 -1
  174. package/dist/mcp/index.js.map +1 -1
  175. package/dist/mcp/server.d.ts.map +1 -1
  176. package/dist/mcp/server.js +2 -1
  177. package/dist/mcp/server.js.map +1 -1
  178. package/dist/mcp-entry.js +0 -5
  179. package/dist/mcp-entry.js.map +1 -1
  180. package/dist/sync/agent-sync.d.ts +13 -0
  181. package/dist/sync/agent-sync.d.ts.map +1 -0
  182. package/dist/sync/agent-sync.js +130 -0
  183. package/dist/sync/agent-sync.js.map +1 -0
  184. package/dist/sync/agent-sync.test.d.ts +2 -0
  185. package/dist/sync/agent-sync.test.d.ts.map +1 -0
  186. package/dist/sync/agent-sync.test.js +130 -0
  187. package/dist/sync/agent-sync.test.js.map +1 -0
  188. package/dist/sync/agents-md-sync.d.ts +7 -0
  189. package/dist/sync/agents-md-sync.d.ts.map +1 -0
  190. package/dist/sync/agents-md-sync.js +44 -0
  191. package/dist/sync/agents-md-sync.js.map +1 -0
  192. package/dist/sync/agents-md-sync.test.d.ts +2 -0
  193. package/dist/sync/agents-md-sync.test.d.ts.map +1 -0
  194. package/dist/sync/agents-md-sync.test.js +154 -0
  195. package/dist/sync/agents-md-sync.test.js.map +1 -0
  196. package/dist/sync/hook-sync.d.ts +8 -0
  197. package/dist/sync/hook-sync.d.ts.map +1 -0
  198. package/dist/sync/hook-sync.js +90 -0
  199. package/dist/sync/hook-sync.js.map +1 -0
  200. package/dist/sync/security-sync.d.ts +9 -0
  201. package/dist/sync/security-sync.d.ts.map +1 -0
  202. package/dist/sync/security-sync.js +161 -0
  203. package/dist/sync/security-sync.js.map +1 -0
  204. package/dist/sync/skill-sync.d.ts +10 -0
  205. package/dist/sync/skill-sync.d.ts.map +1 -0
  206. package/dist/sync/skill-sync.js +87 -0
  207. package/dist/sync/skill-sync.js.map +1 -0
  208. package/dist/sync/skill-sync.test.d.ts +2 -0
  209. package/dist/sync/skill-sync.test.d.ts.map +1 -0
  210. package/dist/sync/skill-sync.test.js +163 -0
  211. package/dist/sync/skill-sync.test.js.map +1 -0
  212. package/dist/wrapper/approval-memory-store.d.ts +40 -0
  213. package/dist/wrapper/approval-memory-store.d.ts.map +1 -0
  214. package/dist/wrapper/approval-memory-store.js +132 -0
  215. package/dist/wrapper/approval-memory-store.js.map +1 -0
  216. package/dist/wrapper/claude-code-process.d.ts +32 -34
  217. package/dist/wrapper/claude-code-process.d.ts.map +1 -1
  218. package/dist/wrapper/claude-code-process.js +181 -66
  219. package/dist/wrapper/claude-code-process.js.map +1 -1
  220. package/dist/wrapper/cleanup-registry.d.ts +7 -0
  221. package/dist/wrapper/cleanup-registry.d.ts.map +1 -0
  222. package/dist/wrapper/cleanup-registry.js +11 -0
  223. package/dist/wrapper/cleanup-registry.js.map +1 -0
  224. package/dist/wrapper/codex-session.d.ts +49 -0
  225. package/dist/wrapper/codex-session.d.ts.map +1 -0
  226. package/dist/wrapper/codex-session.js +470 -0
  227. package/dist/wrapper/codex-session.js.map +1 -0
  228. package/dist/wrapper/debug.d.ts +2 -0
  229. package/dist/wrapper/debug.d.ts.map +1 -0
  230. package/dist/wrapper/debug.js +7 -0
  231. package/dist/wrapper/debug.js.map +1 -0
  232. package/dist/wrapper/executable-provider-session.d.ts +26 -0
  233. package/dist/wrapper/executable-provider-session.d.ts.map +1 -0
  234. package/dist/wrapper/executable-provider-session.js +214 -0
  235. package/dist/wrapper/executable-provider-session.js.map +1 -0
  236. package/dist/wrapper/hook-executor.d.ts +21 -0
  237. package/dist/wrapper/hook-executor.d.ts.map +1 -0
  238. package/dist/wrapper/hook-executor.js +100 -0
  239. package/dist/wrapper/hook-executor.js.map +1 -0
  240. package/dist/wrapper/hook-registry.d.ts +10 -0
  241. package/dist/wrapper/hook-registry.d.ts.map +1 -0
  242. package/dist/wrapper/hook-registry.js +57 -0
  243. package/dist/wrapper/hook-registry.js.map +1 -0
  244. package/dist/wrapper/index.d.ts +83 -2
  245. package/dist/wrapper/index.d.ts.map +1 -1
  246. package/dist/wrapper/index.js +11 -0
  247. package/dist/wrapper/index.js.map +1 -1
  248. package/dist/wrapper/mcp-selector.d.ts +2 -0
  249. package/dist/wrapper/mcp-selector.d.ts.map +1 -0
  250. package/dist/wrapper/mcp-selector.js +8 -0
  251. package/dist/wrapper/mcp-selector.js.map +1 -0
  252. package/dist/wrapper/opencode-session.d.ts +69 -0
  253. package/dist/wrapper/opencode-session.d.ts.map +1 -0
  254. package/dist/wrapper/opencode-session.js +568 -0
  255. package/dist/wrapper/opencode-session.js.map +1 -0
  256. package/dist/wrapper/permission-evaluator.d.ts +57 -0
  257. package/dist/wrapper/permission-evaluator.d.ts.map +1 -0
  258. package/dist/wrapper/permission-evaluator.js +310 -0
  259. package/dist/wrapper/permission-evaluator.js.map +1 -0
  260. package/dist/wrapper/permission-normalizer.d.ts +13 -0
  261. package/dist/wrapper/permission-normalizer.d.ts.map +1 -0
  262. package/dist/wrapper/permission-normalizer.js +88 -0
  263. package/dist/wrapper/permission-normalizer.js.map +1 -0
  264. package/dist/wrapper/permission-policy-authorizer.d.ts +12 -0
  265. package/dist/wrapper/permission-policy-authorizer.d.ts.map +1 -0
  266. package/dist/wrapper/permission-policy-authorizer.js +64 -0
  267. package/dist/wrapper/permission-policy-authorizer.js.map +1 -0
  268. package/dist/wrapper/preamble-builder.d.ts +7 -0
  269. package/dist/wrapper/preamble-builder.d.ts.map +1 -0
  270. package/dist/wrapper/preamble-builder.js +103 -0
  271. package/dist/wrapper/preamble-builder.js.map +1 -0
  272. package/dist/wrapper/provider-context.d.ts +20 -0
  273. package/dist/wrapper/provider-context.d.ts.map +1 -0
  274. package/dist/wrapper/provider-context.js +58 -0
  275. package/dist/wrapper/provider-context.js.map +1 -0
  276. package/dist/wrapper/provider-session.d.ts +27 -0
  277. package/dist/wrapper/provider-session.d.ts.map +1 -0
  278. package/dist/wrapper/provider-session.js +208 -0
  279. package/dist/wrapper/provider-session.js.map +1 -0
  280. package/dist/wrapper/session-manager.d.ts +21 -4
  281. package/dist/wrapper/session-manager.d.ts.map +1 -1
  282. package/dist/wrapper/session-manager.js +173 -14
  283. package/dist/wrapper/session-manager.js.map +1 -1
  284. package/dist/wrapper/session-registry.d.ts +136 -0
  285. package/dist/wrapper/session-registry.d.ts.map +1 -0
  286. package/dist/wrapper/session-registry.js +977 -0
  287. package/dist/wrapper/session-registry.js.map +1 -0
  288. package/dist/wrapper/session-store.d.ts +63 -0
  289. package/dist/wrapper/session-store.d.ts.map +1 -0
  290. package/dist/wrapper/session-store.js +215 -0
  291. package/dist/wrapper/session-store.js.map +1 -0
  292. package/dist/wrapper/session.d.ts +127 -0
  293. package/dist/wrapper/session.d.ts.map +1 -0
  294. package/dist/wrapper/session.js +14 -0
  295. package/dist/wrapper/session.js.map +1 -0
  296. package/dist/wrapper/worktree-manager.d.ts +30 -0
  297. package/dist/wrapper/worktree-manager.d.ts.map +1 -0
  298. package/dist/wrapper/worktree-manager.js +139 -0
  299. package/dist/wrapper/worktree-manager.js.map +1 -0
  300. package/package.json +11 -7
@@ -0,0 +1,69 @@
1
+ import { spawn } from "node:child_process";
2
+ import type { SessionEvent, SessionCapabilities, SessionRunOptions, IKilnSession, KilnPermissionAction, KilnPermissionPolicy, KilnSandboxMode } from "./session.js";
3
+ interface McpServer {
4
+ name: string;
5
+ url: string;
6
+ }
7
+ interface TranslationRuleMetadata {
8
+ readonly category: string;
9
+ readonly selector: string;
10
+ readonly action: string;
11
+ readonly reason?: string;
12
+ }
13
+ interface OpenCodeNativeRuleMetadata {
14
+ readonly tools: readonly {
15
+ readonly tool: string;
16
+ readonly action: KilnPermissionAction;
17
+ }[];
18
+ readonly commands: readonly {
19
+ readonly pattern: string;
20
+ readonly shell?: "bash" | "sh" | "zsh" | "any";
21
+ readonly action: KilnPermissionAction;
22
+ }[];
23
+ readonly fileGovernance: {
24
+ readonly denyGlobs: readonly string[];
25
+ readonly askGlobs: readonly string[];
26
+ readonly allowGlobs: readonly string[];
27
+ };
28
+ }
29
+ export interface OpenCodeSessionConfig {
30
+ readonly task: string;
31
+ readonly cwd: string;
32
+ readonly env?: Record<string, string>;
33
+ readonly mcpServers?: McpServer[];
34
+ readonly mcpServerEntryPath?: string;
35
+ readonly model?: string;
36
+ readonly port?: number;
37
+ readonly baseUrl?: string;
38
+ readonly permissionDefault?: "ask" | "allow" | "deny";
39
+ readonly sandboxMode?: KilnSandboxMode;
40
+ readonly nativeRules?: OpenCodeNativeRuleMetadata;
41
+ readonly representableRules?: readonly TranslationRuleMetadata[];
42
+ readonly unsupportedRules?: readonly TranslationRuleMetadata[];
43
+ readonly constraintInstructions?: readonly string[];
44
+ readonly translationWarnings?: readonly string[];
45
+ readonly permissionPolicy?: KilnPermissionPolicy;
46
+ readonly resumeSessionId?: string;
47
+ }
48
+ export declare class OpenCodeSession implements IKilnSession {
49
+ readonly sessionId: string;
50
+ serveProcess: ReturnType<typeof spawn> | null;
51
+ private readonly _config;
52
+ private readonly _capabilities;
53
+ private _remoteSessionId;
54
+ private _resolvedBaseUrl;
55
+ private _lastCostUsd;
56
+ private _abortController;
57
+ private _eventAbortController;
58
+ private _disposed;
59
+ constructor(config: OpenCodeSessionConfig);
60
+ get capabilities(): SessionCapabilities;
61
+ get providerSessionId(): string | undefined;
62
+ run(options: SessionRunOptions): AsyncIterable<SessionEvent>;
63
+ spawnAndWaitForServe(port: number, cwd: string, env?: Record<string, string>): Promise<number>;
64
+ private _findOpencodePath;
65
+ private _killServeProcess;
66
+ dispose(): Promise<void>;
67
+ }
68
+ export {};
69
+ //# sourceMappingURL=opencode-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode-session.d.ts","sourceRoot":"","sources":["../../src/wrapper/opencode-session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,568 @@
1
+ import { spawn, execSync } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
3
+ import { existsSync } from "node:fs";
4
+ import { appendExecutionIdentity, resolveExecutionIdentity } from "@kilnai/core";
5
+ import { normalizeMcpSelector } from "./mcp-selector.js";
6
+ import { debug } from "./debug.js";
7
+ import { SessionStore } from "./session-store.js";
8
+ function asOpencodeClient(value) {
9
+ if (value === null || value === undefined) {
10
+ throw new Error("SDK client is null or undefined");
11
+ }
12
+ const v = value;
13
+ if (typeof v.session?.create !== "function") {
14
+ throw new Error("SDK client missing session.create");
15
+ }
16
+ if (typeof v.global?.event !== "function") {
17
+ throw new Error("SDK client missing global.event");
18
+ }
19
+ if (typeof v.config?.update !== "function") {
20
+ throw new Error("SDK client missing config.update");
21
+ }
22
+ return value;
23
+ }
24
+ const OPENCODE_SANDBOX_WARNING = "OpenCode does not natively enforce Kiln sandbox modes; Kiln maps sandbox intent to permission prompting semantics only.";
25
+ function derivePermissionPolicy(permissionDefault, sandboxMode, fallback) {
26
+ if (permissionDefault === "allow") {
27
+ return { approval: "never", sandbox: sandboxMode ?? "read-only" };
28
+ }
29
+ if (permissionDefault === "deny") {
30
+ return { approval: "untrusted", sandbox: "read-only" };
31
+ }
32
+ return fallback ?? { approval: "on-request", sandbox: "read-only" };
33
+ }
34
+ function collectRuntimeWarnings(config) {
35
+ const warnings = new Set(config.translationWarnings ?? []);
36
+ if (config.sandboxMode && config.sandboxMode !== "read-only") {
37
+ warnings.add(`OpenCode sandbox mode '${config.sandboxMode}' is not natively enforced; using permission prompting semantics only.`);
38
+ }
39
+ if (warnings.size === 0 && config.sandboxMode !== undefined) {
40
+ warnings.add(OPENCODE_SANDBOX_WARNING);
41
+ }
42
+ return [...warnings];
43
+ }
44
+ function toOpenCodePermissionValue(action) {
45
+ return action;
46
+ }
47
+ function mapToolToPermissionKey(tool) {
48
+ const normalized = tool.trim().toLowerCase();
49
+ if (normalized === "edit" || normalized === "write" || normalized === "multiedit" || normalized === "notebookedit") {
50
+ return "edit";
51
+ }
52
+ if (normalized === "bash" || normalized === "command_execution" || normalized === "command") {
53
+ return "bash";
54
+ }
55
+ if (normalized === "webfetch" || normalized === "web_fetch" || normalized === "fetch") {
56
+ return "webfetch";
57
+ }
58
+ return null;
59
+ }
60
+ function isGlobalCommandRule(pattern) {
61
+ const normalized = pattern.trim();
62
+ return normalized === "*" || normalized === "**" || normalized === "*:*";
63
+ }
64
+ function applyGranularPermissionOverrides(base, nativeRules) {
65
+ const next = { ...base };
66
+ if (!nativeRules)
67
+ return next;
68
+ for (const rule of nativeRules.tools) {
69
+ const permissionKey = mapToolToPermissionKey(rule.tool);
70
+ if (permissionKey !== null) {
71
+ next[permissionKey] = toOpenCodePermissionValue(rule.action);
72
+ }
73
+ }
74
+ for (const rule of nativeRules.commands) {
75
+ if (isGlobalCommandRule(rule.pattern)
76
+ && (rule.shell === undefined || rule.shell === "any" || rule.shell === "bash")) {
77
+ next.bash = toOpenCodePermissionValue(rule.action);
78
+ }
79
+ }
80
+ return next;
81
+ }
82
+ function buildFileGovernanceInstructions(nativeRules) {
83
+ if (!nativeRules)
84
+ return [];
85
+ const lines = [];
86
+ for (const glob of nativeRules.fileGovernance.denyGlobs) {
87
+ lines.push(`[file-governance] DENY ${glob}`);
88
+ }
89
+ for (const glob of nativeRules.fileGovernance.askGlobs) {
90
+ lines.push(`[file-governance] ASK ${glob}`);
91
+ }
92
+ for (const glob of nativeRules.fileGovernance.allowGlobs) {
93
+ lines.push(`[file-governance] ALLOW ${glob}`);
94
+ }
95
+ if (lines.length === 0)
96
+ return [];
97
+ return ["Kiln file governance constraints for opencode:", ...lines];
98
+ }
99
+ function appendConstraintInstructions(prompt, constraintInstructions, nativeRules) {
100
+ const sections = [prompt];
101
+ if (constraintInstructions && constraintInstructions.length > 0) {
102
+ sections.push(constraintInstructions.join("\n"));
103
+ }
104
+ const fileGovernanceInstructions = buildFileGovernanceInstructions(nativeRules);
105
+ if (fileGovernanceInstructions.length > 0) {
106
+ sections.push(fileGovernanceInstructions.join("\n"));
107
+ }
108
+ return sections.filter((section) => section.trim().length > 0).join("\n\n");
109
+ }
110
+ export class OpenCodeSession {
111
+ sessionId;
112
+ serveProcess = null;
113
+ _config;
114
+ _capabilities;
115
+ _remoteSessionId = null;
116
+ _resolvedBaseUrl = null;
117
+ _lastCostUsd = 0;
118
+ _abortController = null;
119
+ _eventAbortController = null;
120
+ _disposed = false;
121
+ constructor(config) {
122
+ this.sessionId = randomUUID();
123
+ this._config = config;
124
+ this._capabilities = {
125
+ mcp: true,
126
+ streaming: true,
127
+ resumable: config.resumeSessionId !== undefined,
128
+ resume: config.resumeSessionId !== undefined,
129
+ costTrackingMode: "native",
130
+ supportedTools: [],
131
+ maxContextTokens: null,
132
+ priority: 2,
133
+ fallbackTo: null,
134
+ permissionPolicy: derivePermissionPolicy(config.permissionDefault, config.sandboxMode, config.permissionPolicy),
135
+ };
136
+ for (const warning of collectRuntimeWarnings(config)) {
137
+ debug(`[opencode] ${warning}`);
138
+ }
139
+ }
140
+ get capabilities() {
141
+ return this._capabilities;
142
+ }
143
+ get providerSessionId() {
144
+ return this._remoteSessionId ?? undefined;
145
+ }
146
+ async *run(options) {
147
+ if (this._disposed)
148
+ return;
149
+ const startTime = Date.now();
150
+ const abortController = new AbortController();
151
+ this._abortController = abortController;
152
+ const knownMcpToolNames = new Set();
153
+ const partTypes = new Map(); // partID → part type ("text" | "reasoning" | "tool" | ...)
154
+ const cwd = options.cwd ?? this._config.cwd;
155
+ if (options.abortSignal) {
156
+ if (options.abortSignal.aborted) {
157
+ abortController.abort();
158
+ }
159
+ else {
160
+ options.abortSignal.addEventListener("abort", () => {
161
+ abortController.abort();
162
+ this._killServeProcess();
163
+ }, { once: true });
164
+ }
165
+ }
166
+ try {
167
+ let baseUrl;
168
+ const isResumingTurn = this._resolvedBaseUrl !== null && this._remoteSessionId !== null;
169
+ let storedRemoteSessionId;
170
+ if (!isResumingTurn) {
171
+ if (this._config.resumeSessionId !== undefined) {
172
+ try {
173
+ const store = new SessionStore(this._config.cwd);
174
+ const record = await store.find(this._config.resumeSessionId);
175
+ if (record?.providerSessionId) {
176
+ storedRemoteSessionId = record.providerSessionId;
177
+ }
178
+ }
179
+ catch {
180
+ console.error("[SessionStore] Resume lookup failed, continuing without resume");
181
+ }
182
+ }
183
+ if (this._config.baseUrl) {
184
+ baseUrl = this._config.baseUrl;
185
+ }
186
+ else {
187
+ const port = this._config.port ?? 0;
188
+ const actualPort = await this.spawnAndWaitForServe(port, cwd, this._config.env);
189
+ baseUrl = `http://127.0.0.1:${actualPort}`;
190
+ }
191
+ this._resolvedBaseUrl = baseUrl;
192
+ }
193
+ else {
194
+ baseUrl = this._resolvedBaseUrl;
195
+ }
196
+ const { createOpencodeClient } = await import("@opencode-ai/sdk/v2");
197
+ const client = asOpencodeClient(createOpencodeClient({ baseUrl }));
198
+ if (!isResumingTurn) {
199
+ const approval = this._capabilities.permissionPolicy.approval;
200
+ const permValue = approval === "never" ? "allow" : approval === "untrusted" ? "deny" : "ask";
201
+ const permissionPayload = applyGranularPermissionOverrides({
202
+ edit: permValue,
203
+ bash: permValue,
204
+ webfetch: permValue,
205
+ }, this._config.nativeRules);
206
+ await client.config
207
+ .update({
208
+ body: { permission: permissionPayload },
209
+ query: { directory: cwd },
210
+ })
211
+ .catch((err) => {
212
+ console.debug(`[opencode] config.update failed: ${err instanceof Error ? err.message : String(err)}`);
213
+ });
214
+ const mcpEntryPath = this._config.mcpServerEntryPath;
215
+ if (mcpEntryPath && existsSync(mcpEntryPath)) {
216
+ const mcpEntry = {
217
+ type: "local",
218
+ command: ["node", mcpEntryPath],
219
+ enabled: true,
220
+ };
221
+ await client.config
222
+ .update({
223
+ body: { mcp: { kiln: mcpEntry } },
224
+ query: { directory: cwd },
225
+ })
226
+ .catch((err) => {
227
+ console.debug(`[opencode] MCP config.update failed: ${err instanceof Error ? err.message : String(err)}`);
228
+ });
229
+ }
230
+ else if (mcpEntryPath) {
231
+ debug(`MCP server entry not found at ${mcpEntryPath}, skipping runtime MCP registration`);
232
+ }
233
+ await client.config
234
+ .update({
235
+ body: { experimental: { batch_tool: true } },
236
+ query: { directory: cwd },
237
+ })
238
+ .catch((err) => {
239
+ console.debug(`[opencode] batch_tool config.update failed: ${err instanceof Error ? err.message : String(err)}`);
240
+ });
241
+ if (this._config.model) {
242
+ await client.config
243
+ .update({
244
+ body: { model: this._config.model },
245
+ query: { directory: cwd },
246
+ })
247
+ .catch((err) => {
248
+ console.debug(`[opencode] model config.update failed: ${err instanceof Error ? err.message : String(err)}`);
249
+ });
250
+ }
251
+ }
252
+ if (!isResumingTurn) {
253
+ if (storedRemoteSessionId !== undefined) {
254
+ const getResult = await client.session.get({ sessionID: storedRemoteSessionId, directory: cwd }, { throwOnError: true });
255
+ this._remoteSessionId = getResult.data.id;
256
+ }
257
+ else {
258
+ const createResult = await client.session.create({ directory: cwd }, { throwOnError: true });
259
+ this._remoteSessionId = createResult.data.id;
260
+ }
261
+ }
262
+ this._eventAbortController = new AbortController();
263
+ const eventStreamPromise = client.global.event({
264
+ signal: this._eventAbortController.signal,
265
+ });
266
+ const eventQueue = [];
267
+ let eventYield = null;
268
+ const eventDone = new Set();
269
+ eventStreamPromise
270
+ .then(async ({ stream }) => {
271
+ try {
272
+ for await (const event of stream) {
273
+ if (this._eventAbortController?.signal.aborted)
274
+ break;
275
+ if (eventYield !== null) {
276
+ const yieldFn = eventYield;
277
+ eventYield = null;
278
+ yieldFn(event);
279
+ }
280
+ else {
281
+ eventQueue.push(event);
282
+ }
283
+ }
284
+ for (const doneFn of eventDone)
285
+ doneFn();
286
+ }
287
+ catch {
288
+ for (const doneFn of eventDone)
289
+ doneFn();
290
+ }
291
+ })
292
+ .catch(() => {
293
+ for (const doneFn of eventDone)
294
+ doneFn();
295
+ });
296
+ const waitForEvent = () => {
297
+ if (eventQueue.length > 0)
298
+ return Promise.resolve(eventQueue.shift());
299
+ return new Promise((resolve) => {
300
+ eventYield = resolve;
301
+ eventDone.add(() => resolve(null));
302
+ });
303
+ };
304
+ const promptWithExecutionIdentity = appendExecutionIdentity(options.prompt, resolveExecutionIdentity({
305
+ configuredProvider: "opencode",
306
+ configuredModel: this._config.model,
307
+ }));
308
+ const promptResult = await client.session
309
+ .prompt({
310
+ sessionID: this._remoteSessionId,
311
+ parts: [{
312
+ type: "text",
313
+ text: appendConstraintInstructions(promptWithExecutionIdentity, this._config.constraintInstructions, this._config.nativeRules),
314
+ }],
315
+ directory: cwd,
316
+ }, { throwOnError: false })
317
+ .catch(() => ({ data: undefined }));
318
+ this._lastCostUsd = promptResult?.data?.info?.cost ?? this._lastCostUsd;
319
+ while (true) {
320
+ const event = await waitForEvent();
321
+ if (!event)
322
+ break;
323
+ if (event.payload.type === "sessionUpdate") {
324
+ const props = event.payload.properties;
325
+ if (props?.sessionID !== this._remoteSessionId)
326
+ continue;
327
+ if (props?.type === "usage_update" && props?.cost?.amount !== undefined) {
328
+ this._lastCostUsd = props.cost.amount;
329
+ yield {
330
+ type: "cost_update",
331
+ usd: props.cost.amount,
332
+ mode: "native",
333
+ inputTokens: props.cost.inputTokens,
334
+ outputTokens: props.cost.outputTokens,
335
+ cacheReadTokens: props.cost.cacheReadTokens,
336
+ };
337
+ }
338
+ continue;
339
+ }
340
+ if (event.payload.type === "message.part.delta") {
341
+ const props = event.payload.properties;
342
+ if (props?.sessionID !== this._remoteSessionId)
343
+ continue;
344
+ if (props?.field === "text" && props?.delta) {
345
+ const partType = props.partID ? partTypes.get(props.partID) : undefined;
346
+ if (partType === "reasoning") {
347
+ yield { type: "text_delta", content: props.delta, isThinking: true };
348
+ }
349
+ else {
350
+ yield { type: "text_delta", content: props.delta };
351
+ }
352
+ }
353
+ continue;
354
+ }
355
+ if (event.payload.type === "message.part.updated") {
356
+ const props = event.payload.properties;
357
+ if (props?.sessionID !== this._remoteSessionId)
358
+ continue;
359
+ if (!props?.part)
360
+ continue;
361
+ const part = props.part;
362
+ if (part.id && part.type)
363
+ partTypes.set(part.id, part.type);
364
+ if (part.type === "tool") {
365
+ if (part.state?.status === "pending" || part.state?.status === "running") {
366
+ const toolName = part.tool ?? "unknown";
367
+ const isMcpTool = knownMcpToolNames.has(toolName);
368
+ yield {
369
+ type: "tool_use",
370
+ toolName,
371
+ input: part.state?.input ?? {},
372
+ ...(isMcpTool
373
+ ? { source: "mcp", mcpSelector: normalizeMcpSelector(toolName) }
374
+ : {}),
375
+ };
376
+ }
377
+ else if (part.state?.status === "completed") {
378
+ const output = part.state?.output;
379
+ const outputStr = output !== undefined
380
+ ? output
381
+ : part.state?.metadata?.output !== undefined
382
+ ? String(part.state.metadata.output)
383
+ : "";
384
+ yield {
385
+ type: "tool_result",
386
+ toolName: part.tool ?? "unknown",
387
+ output: outputStr,
388
+ };
389
+ }
390
+ else if (part.state?.status === "error") {
391
+ yield {
392
+ type: "tool_result",
393
+ toolName: part.tool ?? "unknown",
394
+ output: part.state.error ?? "Tool failed",
395
+ };
396
+ }
397
+ }
398
+ continue;
399
+ }
400
+ if (event.payload.type === "session.status") {
401
+ const props = event.payload.properties;
402
+ if (props?.sessionID !== this._remoteSessionId)
403
+ continue;
404
+ if (props?.status?.type === "idle") {
405
+ break;
406
+ }
407
+ }
408
+ if (event.payload.type === "session.compacted") {
409
+ const props = event.payload.properties;
410
+ if (props?.sessionID !== this._remoteSessionId)
411
+ continue;
412
+ const output = props?.tokens !== undefined
413
+ ? `Context compacted to approximately ${props.tokens} tokens`
414
+ : "Context compacted";
415
+ yield { type: "tool_result", toolName: "session.compacted", output };
416
+ continue;
417
+ }
418
+ if (event.payload.type === "question.asked") {
419
+ const props = event.payload.properties;
420
+ if (props?.sessionID !== this._remoteSessionId)
421
+ continue;
422
+ if (props?.question) {
423
+ yield { type: "text_delta", content: `[Question] ${props.question}` };
424
+ }
425
+ continue;
426
+ }
427
+ if (event.payload.type === "mcp.tools.changed") {
428
+ const props = event.payload.properties;
429
+ if (props?.sessionID !== this._remoteSessionId)
430
+ continue;
431
+ const nextMcpToolNames = new Set();
432
+ for (const tool of props?.tools ?? []) {
433
+ if (typeof tool?.name === "string" && tool.name.length > 0) {
434
+ nextMcpToolNames.add(tool.name);
435
+ }
436
+ }
437
+ knownMcpToolNames.clear();
438
+ for (const toolName of nextMcpToolNames) {
439
+ knownMcpToolNames.add(toolName);
440
+ }
441
+ const toolList = props?.tools?.map((t) => t.name).join(", ") ?? "";
442
+ yield { type: "tool_result", toolName: "mcp.tools.changed", output: toolList };
443
+ continue;
444
+ }
445
+ }
446
+ const stopReason = promptResult?.data?.info?.stopReason;
447
+ const isError = stopReason === "cancelled";
448
+ yield {
449
+ type: "completed",
450
+ totalUsd: this._lastCostUsd,
451
+ durationMs: Date.now() - startTime,
452
+ isError,
453
+ isPreflightCrash: false,
454
+ };
455
+ try {
456
+ const store = new SessionStore(this._config.cwd);
457
+ const completedAt = new Date().toISOString();
458
+ await store.append({
459
+ sessionId: this._remoteSessionId ?? this.sessionId,
460
+ provider: "opencode",
461
+ task: this._config.task,
462
+ completedAt,
463
+ cost: this._lastCostUsd,
464
+ projectPath: this._config.cwd,
465
+ providerSessionId: this._remoteSessionId ?? undefined,
466
+ });
467
+ }
468
+ catch (err) {
469
+ console.error("[SessionStore] Failed to append session record:", err instanceof Error ? err.message : String(err));
470
+ }
471
+ }
472
+ catch (err) {
473
+ yield {
474
+ type: "error",
475
+ code: "OPENCODE_ERROR",
476
+ message: err instanceof Error ? err.message : String(err),
477
+ isRetryable: false,
478
+ };
479
+ }
480
+ finally {
481
+ this._abortController = null;
482
+ this._eventAbortController?.abort();
483
+ this._eventAbortController = null;
484
+ }
485
+ }
486
+ async spawnAndWaitForServe(port, cwd, env) {
487
+ const opencodePath = this._findOpencodePath();
488
+ const args = ["serve", "--port", String(port)];
489
+ const spawnEnv = { ...process.env, ...env };
490
+ debug(`[opencode] spawning: ${opencodePath} ${args.join(" ")}`);
491
+ debug(`[opencode] cwd: ${cwd}`);
492
+ const proc = spawn(opencodePath, args, {
493
+ cwd,
494
+ env: spawnEnv,
495
+ stdio: ["ignore", "pipe", "pipe"],
496
+ detached: false,
497
+ });
498
+ this.serveProcess = proc;
499
+ let actualPort = port;
500
+ let stderrOutput = "";
501
+ const portRegex = /listening on (?:http:\/\/)?[^:]*:(\d+)/i;
502
+ const portPromise = new Promise((resolve, reject) => {
503
+ const timeout = setTimeout(() => {
504
+ reject(new Error(`opencode serve failed to start within 15 seconds\nStderr: ${stderrOutput}`));
505
+ }, 15_000);
506
+ const handleData = (data) => {
507
+ const line = data.toString();
508
+ debug(`[opencode] stdout/stderr: ${line.trim()}`);
509
+ const match = line.match(portRegex);
510
+ if (match?.[1]) {
511
+ actualPort = parseInt(match[1], 10);
512
+ clearTimeout(timeout);
513
+ proc.stdout?.removeListener("data", handleData);
514
+ resolve();
515
+ }
516
+ };
517
+ proc.stdout?.on("data", handleData);
518
+ proc.stderr?.on("data", (data) => {
519
+ stderrOutput += data.toString();
520
+ handleData(data);
521
+ });
522
+ proc.on("error", (err) => {
523
+ clearTimeout(timeout);
524
+ reject(new Error(`Failed to spawn opencode serve: ${err.message}\nStderr: ${stderrOutput}`));
525
+ });
526
+ proc.on("exit", (code) => {
527
+ clearTimeout(timeout);
528
+ debug(`[opencode] serve exited with code ${code}`);
529
+ if (code !== 0 && code !== null) {
530
+ reject(new Error(`opencode serve exited with code ${code}\nStderr: ${stderrOutput}`));
531
+ }
532
+ });
533
+ });
534
+ await portPromise;
535
+ return actualPort;
536
+ }
537
+ _findOpencodePath() {
538
+ const homedir = process.env.HOME ?? process.env.USERPROFILE ?? "";
539
+ const fallbackPaths = [
540
+ `${homedir}\\.bun\\bin\\opencode.exe`,
541
+ `${homedir}\\AppData\\Roaming\\npm\\opencode.cmd`,
542
+ ];
543
+ const candidates = ["opencode", "opencode.exe", ...fallbackPaths];
544
+ for (const candidate of candidates) {
545
+ try {
546
+ execSync(`"${candidate}" --version`, { stdio: "ignore" });
547
+ return candidate;
548
+ }
549
+ catch {
550
+ // try next
551
+ }
552
+ }
553
+ throw new Error("opencode binary not found in PATH. Ensure opencode is installed and accessible.");
554
+ }
555
+ _killServeProcess() {
556
+ if (this.serveProcess && !this.serveProcess.killed) {
557
+ this.serveProcess.kill("SIGTERM");
558
+ }
559
+ }
560
+ async dispose() {
561
+ this._disposed = true;
562
+ this._abortController?.abort();
563
+ this._eventAbortController?.abort();
564
+ this._killServeProcess();
565
+ this.serveProcess = null;
566
+ }
567
+ }
568
+ //# sourceMappingURL=opencode-session.js.map