@warlock.js/ai-cli 0.0.0-stage → 5.31.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 (139) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/LICENSE +21 -0
  3. package/README.md +69 -2
  4. package/cjs/index.cjs +4576 -0
  5. package/cjs/index.cjs.map +1 -0
  6. package/esm/adapters/aider.d.mts +41 -0
  7. package/esm/adapters/aider.d.mts.map +1 -0
  8. package/esm/adapters/aider.mjs +294 -0
  9. package/esm/adapters/aider.mjs.map +1 -0
  10. package/esm/adapters/amp.d.mts +33 -0
  11. package/esm/adapters/amp.d.mts.map +1 -0
  12. package/esm/adapters/amp.mjs +119 -0
  13. package/esm/adapters/amp.mjs.map +1 -0
  14. package/esm/adapters/antigravity.d.mts +37 -0
  15. package/esm/adapters/antigravity.d.mts.map +1 -0
  16. package/esm/adapters/antigravity.mjs +235 -0
  17. package/esm/adapters/antigravity.mjs.map +1 -0
  18. package/esm/adapters/claude-code.d.mts +30 -0
  19. package/esm/adapters/claude-code.d.mts.map +1 -0
  20. package/esm/adapters/claude-code.mjs +121 -0
  21. package/esm/adapters/claude-code.mjs.map +1 -0
  22. package/esm/adapters/claude-stream.d.mts +32 -0
  23. package/esm/adapters/claude-stream.d.mts.map +1 -0
  24. package/esm/adapters/claude-stream.mjs +140 -0
  25. package/esm/adapters/claude-stream.mjs.map +1 -0
  26. package/esm/adapters/codex.d.mts +37 -0
  27. package/esm/adapters/codex.d.mts.map +1 -0
  28. package/esm/adapters/codex.mjs +320 -0
  29. package/esm/adapters/codex.mjs.map +1 -0
  30. package/esm/adapters/copilot.d.mts +35 -0
  31. package/esm/adapters/copilot.d.mts.map +1 -0
  32. package/esm/adapters/copilot.mjs +244 -0
  33. package/esm/adapters/copilot.mjs.map +1 -0
  34. package/esm/adapters/cursor.d.mts +35 -0
  35. package/esm/adapters/cursor.d.mts.map +1 -0
  36. package/esm/adapters/cursor.mjs +203 -0
  37. package/esm/adapters/cursor.mjs.map +1 -0
  38. package/esm/adapters/droid.d.mts +32 -0
  39. package/esm/adapters/droid.d.mts.map +1 -0
  40. package/esm/adapters/droid.mjs +183 -0
  41. package/esm/adapters/droid.mjs.map +1 -0
  42. package/esm/adapters/grok.d.mts +32 -0
  43. package/esm/adapters/grok.d.mts.map +1 -0
  44. package/esm/adapters/grok.mjs +199 -0
  45. package/esm/adapters/grok.mjs.map +1 -0
  46. package/esm/adapters/index.d.mts +16 -0
  47. package/esm/adapters/index.mjs +18 -0
  48. package/esm/adapters/kimi.d.mts +34 -0
  49. package/esm/adapters/kimi.d.mts.map +1 -0
  50. package/esm/adapters/kimi.mjs +162 -0
  51. package/esm/adapters/kimi.mjs.map +1 -0
  52. package/esm/adapters/mistral-vibe.d.mts +32 -0
  53. package/esm/adapters/mistral-vibe.d.mts.map +1 -0
  54. package/esm/adapters/mistral-vibe.mjs +252 -0
  55. package/esm/adapters/mistral-vibe.mjs.map +1 -0
  56. package/esm/adapters/opencode.d.mts +36 -0
  57. package/esm/adapters/opencode.d.mts.map +1 -0
  58. package/esm/adapters/opencode.mjs +219 -0
  59. package/esm/adapters/opencode.mjs.map +1 -0
  60. package/esm/adapters/qwen-code.d.mts +30 -0
  61. package/esm/adapters/qwen-code.d.mts.map +1 -0
  62. package/esm/adapters/qwen-code.mjs +94 -0
  63. package/esm/adapters/qwen-code.mjs.map +1 -0
  64. package/esm/adapters/reasonix.d.mts +31 -0
  65. package/esm/adapters/reasonix.d.mts.map +1 -0
  66. package/esm/adapters/reasonix.mjs +250 -0
  67. package/esm/adapters/reasonix.mjs.map +1 -0
  68. package/esm/adapters/shared.d.mts +78 -0
  69. package/esm/adapters/shared.d.mts.map +1 -0
  70. package/esm/adapters/shared.mjs +161 -0
  71. package/esm/adapters/shared.mjs.map +1 -0
  72. package/esm/cli-agent.d.mts +40 -0
  73. package/esm/cli-agent.d.mts.map +1 -0
  74. package/esm/cli-agent.mjs +207 -0
  75. package/esm/cli-agent.mjs.map +1 -0
  76. package/esm/cli.d.mts +53 -0
  77. package/esm/cli.d.mts.map +1 -0
  78. package/esm/cli.mjs +61 -0
  79. package/esm/cli.mjs.map +1 -0
  80. package/esm/contracts/adapter.contract.d.mts +154 -0
  81. package/esm/contracts/adapter.contract.d.mts.map +1 -0
  82. package/esm/contracts/events.type.d.mts +111 -0
  83. package/esm/contracts/events.type.d.mts.map +1 -0
  84. package/esm/contracts/index.d.mts +4 -0
  85. package/esm/contracts/mcp.type.d.mts +29 -0
  86. package/esm/contracts/mcp.type.d.mts.map +1 -0
  87. package/esm/contracts/options.type.d.mts +87 -0
  88. package/esm/contracts/options.type.d.mts.map +1 -0
  89. package/esm/errors.d.mts +46 -0
  90. package/esm/errors.d.mts.map +1 -0
  91. package/esm/errors.mjs +32 -0
  92. package/esm/errors.mjs.map +1 -0
  93. package/esm/index.d.mts +31 -0
  94. package/esm/index.mjs +28 -0
  95. package/esm/model/cli-model.d.mts +48 -0
  96. package/esm/model/cli-model.d.mts.map +1 -0
  97. package/esm/model/cli-model.mjs +133 -0
  98. package/esm/model/cli-model.mjs.map +1 -0
  99. package/esm/probe/probe.d.mts +18 -0
  100. package/esm/probe/probe.d.mts.map +1 -0
  101. package/esm/probe/probe.mjs +148 -0
  102. package/esm/probe/probe.mjs.map +1 -0
  103. package/esm/session/aggregate.mjs +86 -0
  104. package/esm/session/aggregate.mjs.map +1 -0
  105. package/esm/session/environment.mjs +71 -0
  106. package/esm/session/environment.mjs.map +1 -0
  107. package/esm/session/run-directory.mjs +74 -0
  108. package/esm/session/run-directory.mjs.map +1 -0
  109. package/esm/session/session.mjs +188 -0
  110. package/esm/session/session.mjs.map +1 -0
  111. package/esm/spawn/invocation.d.mts +35 -0
  112. package/esm/spawn/invocation.d.mts.map +1 -0
  113. package/esm/spawn/invocation.mjs +76 -0
  114. package/esm/spawn/invocation.mjs.map +1 -0
  115. package/esm/spawn/process-tree.mjs +57 -0
  116. package/esm/spawn/process-tree.mjs.map +1 -0
  117. package/esm/spawn/resolve-command.d.mts +73 -0
  118. package/esm/spawn/resolve-command.d.mts.map +1 -0
  119. package/esm/spawn/resolve-command.mjs +109 -0
  120. package/esm/spawn/resolve-command.mjs.map +1 -0
  121. package/esm/stream/event-log.mjs +52 -0
  122. package/esm/stream/event-log.mjs.map +1 -0
  123. package/esm/stream/json-lines.d.mts +33 -0
  124. package/esm/stream/json-lines.d.mts.map +1 -0
  125. package/esm/stream/json-lines.mjs +65 -0
  126. package/esm/stream/json-lines.mjs.map +1 -0
  127. package/esm/stream/line-splitter.mjs +53 -0
  128. package/esm/stream/line-splitter.mjs.map +1 -0
  129. package/esm/utils/toml.d.mts +22 -0
  130. package/esm/utils/toml.d.mts.map +1 -0
  131. package/esm/utils/toml.mjs +53 -0
  132. package/esm/utils/toml.mjs.map +1 -0
  133. package/llms-full.txt +373 -0
  134. package/llms.txt +10 -0
  135. package/package.json +39 -4
  136. package/skills/README.md +13 -0
  137. package/skills/index.md +22 -0
  138. package/skills/run-cli-agents/SKILL.md +186 -0
  139. package/skills/write-a-cli-adapter/SKILL.md +173 -0
@@ -0,0 +1,219 @@
1
+ import { asRecord, jsonLinesParser, numberField, recordField, stringField } from "../stream/json-lines.mjs";
2
+ import { argvPrompt, jsonFile, usageOf } from "./shared.mjs";
3
+ import path from "node:path";
4
+
5
+ //#region ../../packages/ai-cli/src/adapters/opencode.ts
6
+ /**
7
+ * OpenCode (`opencode`, npm `opencode-ai`).
8
+ *
9
+ * Verified against the real `opencode --help` / `opencode run --help` of
10
+ * 1.19.3, the CLI source at that tag (`packages/opencode/src/cli/cmd/run.ts`)
11
+ * and the docs (`cli.mdx`, `config.mdx`, `mcp-servers.mdx`,
12
+ * `permissions.mdx`), 2026-10-17.
13
+ *
14
+ * - Headless: `opencode run --format json <message..>`; with no message,
15
+ * piped stdin is the prompt.
16
+ * - Stream: `{"type","timestamp","sessionID","part"|"error"}` lines of six
17
+ * types: `step_start`, `text` (whole text blocks, no deltas), `reasoning`,
18
+ * `tool_use` (emitted once the tool finished — call and result together,
19
+ * `state.status` `completed`/`error`), `step_finish` (per-step `cost` in
20
+ * USD and `tokens{input,output,reasoning,cache{read,write}}`, input net of
21
+ * cache) and `error{error:{name,data:{message}}}`. No init or final record.
22
+ * - Permissions: `standard` keeps OpenCode's own rules (permissive by
23
+ * default; anything that would ask is auto-rejected in `run`); bypass is
24
+ * `--auto`.
25
+ * - MCP: the `mcp` key of a config file named by `OPENCODE_CONFIG`, written
26
+ * to the run directory — works with any home.
27
+ * - Home: OpenCode follows XDG; a private/owned home sets `XDG_CONFIG_HOME`,
28
+ * `XDG_DATA_HOME` (auth lives here), `XDG_STATE_HOME` and `XDG_CACHE_HOME`
29
+ * beneath it. Provider keys use the provider's own variable
30
+ * (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, …) — pass them through `env`.
31
+ * - The working directory is passed with `--dir`, because OpenCode prefers a
32
+ * (possibly stale) `PWD` over the process cwd.
33
+ */
34
+ const opencodeAdapter = {
35
+ name: "opencode",
36
+ title: "OpenCode",
37
+ command: "opencode",
38
+ apiKeyEnv: [],
39
+ homeEnv: [
40
+ "XDG_CONFIG_HOME",
41
+ "XDG_DATA_HOME",
42
+ "XDG_STATE_HOME",
43
+ "XDG_CACHE_HOME"
44
+ ],
45
+ transports: ["argv", "stdin"],
46
+ effort: true,
47
+ mcp: true,
48
+ probe: {
49
+ help: [["run", "--help"]],
50
+ version: ["--version"]
51
+ },
52
+ verification: {
53
+ version: "1.19.3",
54
+ verifiedBy: "help",
55
+ sources: [
56
+ "https://opencode.ai/docs/cli/",
57
+ "https://opencode.ai/docs/mcp-servers/",
58
+ "https://opencode.ai/docs/permissions/",
59
+ "https://github.com/anomalyco/opencode/blob/v1.19.3/packages/opencode/src/cli/cmd/run.ts"
60
+ ],
61
+ checkedAt: "2026-10-17"
62
+ },
63
+ plan(context) {
64
+ const args = [
65
+ "run",
66
+ "--format",
67
+ "json",
68
+ "--dir",
69
+ context.cwd
70
+ ];
71
+ const requires = ["--format", "--dir"];
72
+ const files = [];
73
+ const env = { OPENCODE_DISABLE_AUTOUPDATE: "1" };
74
+ if (context.model !== void 0) {
75
+ args.push("--model", context.model);
76
+ requires.push("--model");
77
+ }
78
+ if (context.effort !== void 0) {
79
+ args.push("--variant", context.effort);
80
+ requires.push("--variant");
81
+ }
82
+ if (context.resume !== void 0) {
83
+ args.push("--session", context.resume);
84
+ requires.push("--session");
85
+ }
86
+ if (context.permission === "bypass") {
87
+ args.push("--auto");
88
+ requires.push("--auto");
89
+ }
90
+ if (context.mcp !== void 0) {
91
+ const file = path.join(context.runDir, "opencode.json");
92
+ files.push({
93
+ path: file,
94
+ content: jsonFile({
95
+ $schema: "https://opencode.ai/config.json",
96
+ mcp: opencodeMcp(context.mcp)
97
+ })
98
+ });
99
+ env.OPENCODE_CONFIG = file;
100
+ }
101
+ if (context.home !== void 0) {
102
+ env.XDG_CONFIG_HOME = path.join(context.home, "config");
103
+ env.XDG_DATA_HOME = path.join(context.home, "data");
104
+ env.XDG_STATE_HOME = path.join(context.home, "state");
105
+ env.XDG_CACHE_HOME = path.join(context.home, "cache");
106
+ }
107
+ return {
108
+ args,
109
+ tail: context.transport === "argv" ? [argvPrompt(context.prompt)] : [],
110
+ stdin: context.transport === "stdin" ? context.prompt : void 0,
111
+ env,
112
+ files,
113
+ requires
114
+ };
115
+ },
116
+ parser: createOpencodeParser
117
+ };
118
+ function opencodeMcp(servers) {
119
+ const rendered = {};
120
+ for (const [name, server] of Object.entries(servers)) rendered[name] = server.type === "stdio" ? {
121
+ type: "local",
122
+ command: [server.command, ...server.args ?? []],
123
+ ...server.env !== void 0 ? { environment: server.env } : {},
124
+ enabled: true
125
+ } : {
126
+ type: "remote",
127
+ url: server.url,
128
+ ...server.headers !== void 0 ? { headers: server.headers } : {},
129
+ enabled: true
130
+ };
131
+ return rendered;
132
+ }
133
+ /** Parser for `opencode run --format json`. */
134
+ function createOpencodeParser() {
135
+ let started = false;
136
+ return jsonLinesParser((record) => {
137
+ const events = [];
138
+ const part = recordField(record, "part");
139
+ if (!started) {
140
+ started = true;
141
+ events.push({
142
+ type: "session.started",
143
+ sessionRef: stringField(record, "sessionID"),
144
+ raw: record
145
+ });
146
+ }
147
+ switch (stringField(record, "type")) {
148
+ case "text": {
149
+ const text = stringField(part, "text") ?? "";
150
+ if (text.length > 0) events.push({
151
+ type: "message",
152
+ text,
153
+ delta: false,
154
+ raw: record
155
+ });
156
+ break;
157
+ }
158
+ case "tool_use": {
159
+ const state = recordField(part, "state");
160
+ const id = stringField(part, "callID") ?? stringField(part, "id") ?? "";
161
+ const failed = stringField(state, "status") === "error";
162
+ events.push({
163
+ type: "tool.call",
164
+ id,
165
+ name: stringField(part, "tool") ?? "",
166
+ input: state?.input,
167
+ raw: record
168
+ }, {
169
+ type: "tool.result",
170
+ id,
171
+ output: (failed ? stringField(state, "error") : stringField(state, "output")) ?? "",
172
+ isError: failed,
173
+ raw: record
174
+ });
175
+ break;
176
+ }
177
+ case "step_finish": {
178
+ const tokens = recordField(part, "tokens");
179
+ const cache = recordField(tokens, "cache");
180
+ if (tokens !== void 0) {
181
+ const uncached = numberField(tokens, "input") ?? 0;
182
+ const cacheRead = numberField(cache, "read") ?? 0;
183
+ const cacheWrite = numberField(cache, "write") ?? 0;
184
+ events.push({
185
+ type: "usage",
186
+ cumulative: false,
187
+ usage: usageOf({
188
+ input: uncached + cacheRead + cacheWrite,
189
+ output: numberField(tokens, "output"),
190
+ cachedTokens: cacheRead > 0 ? cacheRead : void 0,
191
+ cacheWriteTokens: cacheWrite > 0 ? cacheWrite : void 0,
192
+ reasoningTokens: numberField(tokens, "reasoning") || void 0,
193
+ costUSD: numberField(part, "cost")
194
+ }),
195
+ raw: record
196
+ });
197
+ }
198
+ break;
199
+ }
200
+ case "error": {
201
+ const error = asRecord(record.error);
202
+ const name = stringField(error, "name") ?? "Error";
203
+ const message = stringField(recordField(error, "data"), "message") ?? name;
204
+ events.push({
205
+ type: "error",
206
+ message: `${name}: ${message}`,
207
+ fatal: true,
208
+ raw: record
209
+ });
210
+ break;
211
+ }
212
+ }
213
+ return events;
214
+ });
215
+ }
216
+
217
+ //#endregion
218
+ export { createOpencodeParser, opencodeAdapter };
219
+ //# sourceMappingURL=opencode.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode.mjs","names":[],"sources":["../../../../../../../../packages/ai-cli/src/adapters/opencode.ts"],"sourcesContent":["import path from \"node:path\";\nimport type {\n CliAdapter,\n CliLaunchFile,\n CliMcpServers,\n CliParsedEvent,\n CliStreamParser,\n} from \"../contracts\";\nimport {\n asRecord,\n jsonLinesParser,\n numberField,\n recordField,\n stringField,\n} from \"../stream/json-lines\";\nimport { argvPrompt, jsonFile, usageOf } from \"./shared\";\n\n/**\n * OpenCode (`opencode`, npm `opencode-ai`).\n *\n * Verified against the real `opencode --help` / `opencode run --help` of\n * 1.19.3, the CLI source at that tag (`packages/opencode/src/cli/cmd/run.ts`)\n * and the docs (`cli.mdx`, `config.mdx`, `mcp-servers.mdx`,\n * `permissions.mdx`), 2026-10-17.\n *\n * - Headless: `opencode run --format json <message..>`; with no message,\n * piped stdin is the prompt.\n * - Stream: `{\"type\",\"timestamp\",\"sessionID\",\"part\"|\"error\"}` lines of six\n * types: `step_start`, `text` (whole text blocks, no deltas), `reasoning`,\n * `tool_use` (emitted once the tool finished — call and result together,\n * `state.status` `completed`/`error`), `step_finish` (per-step `cost` in\n * USD and `tokens{input,output,reasoning,cache{read,write}}`, input net of\n * cache) and `error{error:{name,data:{message}}}`. No init or final record.\n * - Permissions: `standard` keeps OpenCode's own rules (permissive by\n * default; anything that would ask is auto-rejected in `run`); bypass is\n * `--auto`.\n * - MCP: the `mcp` key of a config file named by `OPENCODE_CONFIG`, written\n * to the run directory — works with any home.\n * - Home: OpenCode follows XDG; a private/owned home sets `XDG_CONFIG_HOME`,\n * `XDG_DATA_HOME` (auth lives here), `XDG_STATE_HOME` and `XDG_CACHE_HOME`\n * beneath it. Provider keys use the provider's own variable\n * (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, …) — pass them through `env`.\n * - The working directory is passed with `--dir`, because OpenCode prefers a\n * (possibly stale) `PWD` over the process cwd.\n */\nexport const opencodeAdapter: CliAdapter = {\n name: \"opencode\",\n title: \"OpenCode\",\n command: \"opencode\",\n apiKeyEnv: [],\n homeEnv: [\"XDG_CONFIG_HOME\", \"XDG_DATA_HOME\", \"XDG_STATE_HOME\", \"XDG_CACHE_HOME\"],\n transports: [\"argv\", \"stdin\"],\n effort: true,\n mcp: true,\n probe: { help: [[\"run\", \"--help\"]], version: [\"--version\"] },\n verification: {\n version: \"1.19.3\",\n verifiedBy: \"help\",\n sources: [\n \"https://opencode.ai/docs/cli/\",\n \"https://opencode.ai/docs/mcp-servers/\",\n \"https://opencode.ai/docs/permissions/\",\n \"https://github.com/anomalyco/opencode/blob/v1.19.3/packages/opencode/src/cli/cmd/run.ts\",\n ],\n checkedAt: \"2026-10-17\",\n },\n plan(context) {\n const args = [\"run\", \"--format\", \"json\", \"--dir\", context.cwd];\n const requires = [\"--format\", \"--dir\"];\n const files: CliLaunchFile[] = [];\n const env: Record<string, string> = { OPENCODE_DISABLE_AUTOUPDATE: \"1\" };\n\n if (context.model !== undefined) {\n args.push(\"--model\", context.model);\n requires.push(\"--model\");\n }\n\n if (context.effort !== undefined) {\n // `--variant` is provider-specific (\"high, max, minimal\"); the level is passed as-is.\n args.push(\"--variant\", context.effort);\n requires.push(\"--variant\");\n }\n\n if (context.resume !== undefined) {\n args.push(\"--session\", context.resume);\n requires.push(\"--session\");\n }\n\n if (context.permission === \"bypass\") {\n args.push(\"--auto\");\n requires.push(\"--auto\");\n }\n\n if (context.mcp !== undefined) {\n const file = path.join(context.runDir, \"opencode.json\");\n\n files.push({\n path: file,\n content: jsonFile({\n $schema: \"https://opencode.ai/config.json\",\n mcp: opencodeMcp(context.mcp),\n }),\n });\n env.OPENCODE_CONFIG = file;\n }\n\n if (context.home !== undefined) {\n env.XDG_CONFIG_HOME = path.join(context.home, \"config\");\n env.XDG_DATA_HOME = path.join(context.home, \"data\");\n env.XDG_STATE_HOME = path.join(context.home, \"state\");\n env.XDG_CACHE_HOME = path.join(context.home, \"cache\");\n }\n\n return {\n args,\n tail: context.transport === \"argv\" ? [argvPrompt(context.prompt)] : [],\n stdin: context.transport === \"stdin\" ? context.prompt : undefined,\n env,\n files,\n requires,\n };\n },\n parser: createOpencodeParser,\n};\n\nfunction opencodeMcp(servers: CliMcpServers): Record<string, unknown> {\n const rendered: Record<string, unknown> = {};\n\n for (const [name, server] of Object.entries(servers)) {\n rendered[name] =\n server.type === \"stdio\"\n ? {\n type: \"local\",\n command: [server.command, ...(server.args ?? [])],\n ...(server.env !== undefined ? { environment: server.env } : {}),\n enabled: true,\n }\n : {\n type: \"remote\",\n url: server.url,\n ...(server.headers !== undefined ? { headers: server.headers } : {}),\n enabled: true,\n };\n }\n\n return rendered;\n}\n\n/** Parser for `opencode run --format json`. */\nexport function createOpencodeParser(): CliStreamParser {\n let started = false;\n\n return jsonLinesParser((record): CliParsedEvent[] => {\n const events: CliParsedEvent[] = [];\n const part = recordField(record, \"part\");\n\n if (!started) {\n started = true;\n events.push({\n type: \"session.started\",\n sessionRef: stringField(record, \"sessionID\"),\n raw: record,\n });\n }\n\n switch (stringField(record, \"type\")) {\n case \"text\": {\n const text = stringField(part, \"text\") ?? \"\";\n\n if (text.length > 0) {\n events.push({ type: \"message\", text, delta: false, raw: record });\n }\n break;\n }\n case \"tool_use\": {\n const state = recordField(part, \"state\");\n const id = stringField(part, \"callID\") ?? stringField(part, \"id\") ?? \"\";\n const failed = stringField(state, \"status\") === \"error\";\n\n events.push(\n {\n type: \"tool.call\",\n id,\n name: stringField(part, \"tool\") ?? \"\",\n input: state?.input,\n raw: record,\n },\n {\n type: \"tool.result\",\n id,\n output: (failed ? stringField(state, \"error\") : stringField(state, \"output\")) ?? \"\",\n isError: failed,\n raw: record,\n },\n );\n break;\n }\n case \"step_finish\": {\n const tokens = recordField(part, \"tokens\");\n const cache = recordField(tokens, \"cache\");\n\n if (tokens !== undefined) {\n const uncached = numberField(tokens, \"input\") ?? 0;\n const cacheRead = numberField(cache, \"read\") ?? 0;\n const cacheWrite = numberField(cache, \"write\") ?? 0;\n\n events.push({\n type: \"usage\",\n cumulative: false,\n usage: usageOf({\n input: uncached + cacheRead + cacheWrite,\n output: numberField(tokens, \"output\"),\n cachedTokens: cacheRead > 0 ? cacheRead : undefined,\n cacheWriteTokens: cacheWrite > 0 ? cacheWrite : undefined,\n reasoningTokens: numberField(tokens, \"reasoning\") || undefined,\n costUSD: numberField(part, \"cost\"),\n }),\n raw: record,\n });\n }\n break;\n }\n case \"error\": {\n const error = asRecord(record.error);\n const name = stringField(error, \"name\") ?? \"Error\";\n const message = stringField(recordField(error, \"data\"), \"message\") ?? name;\n\n events.push({ type: \"error\", message: `${name}: ${message}`, fatal: true, raw: record });\n break;\n }\n }\n\n return events;\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { CliAdapter } from "../contracts/adapter.contract.mjs";
2
+ //#region ../../packages/ai-cli/src/adapters/qwen-code.d.ts
3
+ /**
4
+ * Qwen Code (`qwen`, npm `@qwen-code/qwen-code`), a Gemini CLI fork.
5
+ *
6
+ * Verified against the real `qwen --help` of 0.25.2 and the docs/source at
7
+ * tag v0.25.2 (`docs/users/features/headless.md`, `nonInteractive/types.ts`),
8
+ * 2026-10-17.
9
+ *
10
+ * - Headless: positional prompt (`-p` is deprecated) with
11
+ * `--output-format stream-json`; piped stdin is used as the prompt when no
12
+ * positional prompt is given.
13
+ * - Stream: the Claude Code `stream-json` shape (`system/init`, `assistant`,
14
+ * `user` tool results, `result`), with `input_tokens` that already include
15
+ * cached tokens and no cost field.
16
+ * - Permissions: headless auto-denies tools that need confirmation (the tool
17
+ * result says so and `permission_denials` lists it); bypass is `--yolo`.
18
+ * - MCP: `--mcp-config <file>` with `{"mcpServers":…}` — written to the run
19
+ * directory, so it works with any home. Remote servers use `httpUrl`
20
+ * (streamable HTTP) or `url` (SSE).
21
+ * - Home: `QWEN_HOME`. Auth depends on the auth type: `OPENAI_API_KEY`
22
+ * (default for `apiKey`), or `ANTHROPIC_API_KEY` / `GEMINI_API_KEY` with
23
+ * `--auth-type` in `args`.
24
+ * - Effort has no flag (it is the `model.reasoningEffort` setting), so
25
+ * `effort` is refused.
26
+ */
27
+ declare const qwenCodeAdapter: CliAdapter;
28
+ //#endregion
29
+ export { qwenCodeAdapter };
30
+ //# sourceMappingURL=qwen-code.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qwen-code.d.mts","names":[],"sources":["../../../../../../../../packages/ai-cli/src/adapters/qwen-code.ts"],"mappings":";;;;;AA6BA;;;;AA+DC;;;;;;;;;;;;;;;;;cA/DY,eAAA,EAAiB,UA+D7B"}
@@ -0,0 +1,94 @@
1
+ import { argvPrompt, jsonFile, mcpServersJson } from "./shared.mjs";
2
+ import { createClaudeStreamParser } from "./claude-stream.mjs";
3
+ import path from "node:path";
4
+
5
+ //#region ../../packages/ai-cli/src/adapters/qwen-code.ts
6
+ /**
7
+ * Qwen Code (`qwen`, npm `@qwen-code/qwen-code`), a Gemini CLI fork.
8
+ *
9
+ * Verified against the real `qwen --help` of 0.25.2 and the docs/source at
10
+ * tag v0.25.2 (`docs/users/features/headless.md`, `nonInteractive/types.ts`),
11
+ * 2026-10-17.
12
+ *
13
+ * - Headless: positional prompt (`-p` is deprecated) with
14
+ * `--output-format stream-json`; piped stdin is used as the prompt when no
15
+ * positional prompt is given.
16
+ * - Stream: the Claude Code `stream-json` shape (`system/init`, `assistant`,
17
+ * `user` tool results, `result`), with `input_tokens` that already include
18
+ * cached tokens and no cost field.
19
+ * - Permissions: headless auto-denies tools that need confirmation (the tool
20
+ * result says so and `permission_denials` lists it); bypass is `--yolo`.
21
+ * - MCP: `--mcp-config <file>` with `{"mcpServers":…}` — written to the run
22
+ * directory, so it works with any home. Remote servers use `httpUrl`
23
+ * (streamable HTTP) or `url` (SSE).
24
+ * - Home: `QWEN_HOME`. Auth depends on the auth type: `OPENAI_API_KEY`
25
+ * (default for `apiKey`), or `ANTHROPIC_API_KEY` / `GEMINI_API_KEY` with
26
+ * `--auth-type` in `args`.
27
+ * - Effort has no flag (it is the `model.reasoningEffort` setting), so
28
+ * `effort` is refused.
29
+ */
30
+ const qwenCodeAdapter = {
31
+ name: "qwen-code",
32
+ title: "Qwen Code",
33
+ command: "qwen",
34
+ apiKeyEnv: ["OPENAI_API_KEY"],
35
+ homeEnv: ["QWEN_HOME"],
36
+ transports: ["argv", "stdin"],
37
+ effort: false,
38
+ mcp: true,
39
+ probe: {
40
+ help: [["--help"]],
41
+ version: ["--version"]
42
+ },
43
+ verification: {
44
+ version: "0.25.2",
45
+ verifiedBy: "help",
46
+ sources: ["https://qwenlm.github.io/qwen-code-docs/", "https://github.com/QwenLM/qwen-code/blob/v0.25.2/docs/users/features/headless.md"],
47
+ checkedAt: "2026-10-17"
48
+ },
49
+ plan(context) {
50
+ const args = ["--output-format", "stream-json"];
51
+ const requires = ["--output-format", "stream-json"];
52
+ const files = [];
53
+ if (context.model !== void 0) {
54
+ args.push("--model", context.model);
55
+ requires.push("--model");
56
+ }
57
+ if (context.resume !== void 0) {
58
+ args.push("--resume", context.resume);
59
+ requires.push("--resume");
60
+ }
61
+ if (context.permission === "bypass") {
62
+ args.push("--yolo");
63
+ requires.push("--yolo");
64
+ }
65
+ if (context.mcp !== void 0) {
66
+ const file = path.join(context.runDir, "mcp.json");
67
+ files.push({
68
+ path: file,
69
+ content: jsonFile({ mcpServers: mcpServersJson(context.mcp, { httpUrlKey: "httpUrl" }) })
70
+ });
71
+ args.push("--mcp-config", file);
72
+ requires.push("--mcp-config");
73
+ }
74
+ return {
75
+ args,
76
+ tail: context.transport === "argv" ? [argvPrompt(context.prompt)] : [],
77
+ stdin: context.transport === "stdin" ? context.prompt : void 0,
78
+ env: {
79
+ ...context.home !== void 0 ? { QWEN_HOME: context.home } : {},
80
+ QWEN_CODE_SUPPRESS_YOLO_WARNING: "1"
81
+ },
82
+ files,
83
+ requires
84
+ };
85
+ },
86
+ parser: () => createClaudeStreamParser({
87
+ usage: "inclusive",
88
+ vendor: "Qwen Code"
89
+ })
90
+ };
91
+
92
+ //#endregion
93
+ export { qwenCodeAdapter };
94
+ //# sourceMappingURL=qwen-code.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qwen-code.mjs","names":[],"sources":["../../../../../../../../packages/ai-cli/src/adapters/qwen-code.ts"],"sourcesContent":["import path from \"node:path\";\nimport type { CliAdapter, CliLaunchFile } from \"../contracts\";\nimport { createClaudeStreamParser } from \"./claude-stream\";\nimport { argvPrompt, jsonFile, mcpServersJson } from \"./shared\";\n\n/**\n * Qwen Code (`qwen`, npm `@qwen-code/qwen-code`), a Gemini CLI fork.\n *\n * Verified against the real `qwen --help` of 0.25.2 and the docs/source at\n * tag v0.25.2 (`docs/users/features/headless.md`, `nonInteractive/types.ts`),\n * 2026-10-17.\n *\n * - Headless: positional prompt (`-p` is deprecated) with\n * `--output-format stream-json`; piped stdin is used as the prompt when no\n * positional prompt is given.\n * - Stream: the Claude Code `stream-json` shape (`system/init`, `assistant`,\n * `user` tool results, `result`), with `input_tokens` that already include\n * cached tokens and no cost field.\n * - Permissions: headless auto-denies tools that need confirmation (the tool\n * result says so and `permission_denials` lists it); bypass is `--yolo`.\n * - MCP: `--mcp-config <file>` with `{\"mcpServers\":…}` — written to the run\n * directory, so it works with any home. Remote servers use `httpUrl`\n * (streamable HTTP) or `url` (SSE).\n * - Home: `QWEN_HOME`. Auth depends on the auth type: `OPENAI_API_KEY`\n * (default for `apiKey`), or `ANTHROPIC_API_KEY` / `GEMINI_API_KEY` with\n * `--auth-type` in `args`.\n * - Effort has no flag (it is the `model.reasoningEffort` setting), so\n * `effort` is refused.\n */\nexport const qwenCodeAdapter: CliAdapter = {\n name: \"qwen-code\",\n title: \"Qwen Code\",\n command: \"qwen\",\n apiKeyEnv: [\"OPENAI_API_KEY\"],\n homeEnv: [\"QWEN_HOME\"],\n transports: [\"argv\", \"stdin\"],\n effort: false,\n mcp: true,\n probe: { help: [[\"--help\"]], version: [\"--version\"] },\n verification: {\n version: \"0.25.2\",\n verifiedBy: \"help\",\n sources: [\n \"https://qwenlm.github.io/qwen-code-docs/\",\n \"https://github.com/QwenLM/qwen-code/blob/v0.25.2/docs/users/features/headless.md\",\n ],\n checkedAt: \"2026-10-17\",\n },\n plan(context) {\n const args = [\"--output-format\", \"stream-json\"];\n const requires = [\"--output-format\", \"stream-json\"];\n const files: CliLaunchFile[] = [];\n\n if (context.model !== undefined) {\n args.push(\"--model\", context.model);\n requires.push(\"--model\");\n }\n\n if (context.resume !== undefined) {\n args.push(\"--resume\", context.resume);\n requires.push(\"--resume\");\n }\n\n if (context.permission === \"bypass\") {\n args.push(\"--yolo\");\n requires.push(\"--yolo\");\n }\n\n if (context.mcp !== undefined) {\n const file = path.join(context.runDir, \"mcp.json\");\n\n files.push({\n path: file,\n content: jsonFile({ mcpServers: mcpServersJson(context.mcp, { httpUrlKey: \"httpUrl\" }) }),\n });\n args.push(\"--mcp-config\", file);\n requires.push(\"--mcp-config\");\n }\n\n return {\n args,\n tail: context.transport === \"argv\" ? [argvPrompt(context.prompt)] : [],\n stdin: context.transport === \"stdin\" ? context.prompt : undefined,\n env: {\n ...(context.home !== undefined ? { QWEN_HOME: context.home } : {}),\n QWEN_CODE_SUPPRESS_YOLO_WARNING: \"1\",\n },\n files,\n requires,\n };\n },\n parser: () => createClaudeStreamParser({ usage: \"inclusive\", vendor: \"Qwen Code\" }),\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAa,kBAA8B;CACzC,MAAM;CACN,OAAO;CACP,SAAS;CACT,WAAW,CAAC,gBAAgB;CAC5B,SAAS,CAAC,WAAW;CACrB,YAAY,CAAC,QAAQ,OAAO;CAC5B,QAAQ;CACR,KAAK;CACL,OAAO;EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC;EAAG,SAAS,CAAC,WAAW;CAAE;CACpD,cAAc;EACZ,SAAS;EACT,YAAY;EACZ,SAAS,CACP,4CACA,kFACF;EACA,WAAW;CACb;CACA,KAAK,SAAS;EACZ,MAAM,OAAO,CAAC,mBAAmB,aAAa;EAC9C,MAAM,WAAW,CAAC,mBAAmB,aAAa;EAClD,MAAM,QAAyB,CAAC;EAEhC,IAAI,QAAQ,UAAU,QAAW;GAC/B,KAAK,KAAK,WAAW,QAAQ,KAAK;GAClC,SAAS,KAAK,SAAS;EACzB;EAEA,IAAI,QAAQ,WAAW,QAAW;GAChC,KAAK,KAAK,YAAY,QAAQ,MAAM;GACpC,SAAS,KAAK,UAAU;EAC1B;EAEA,IAAI,QAAQ,eAAe,UAAU;GACnC,KAAK,KAAK,QAAQ;GAClB,SAAS,KAAK,QAAQ;EACxB;EAEA,IAAI,QAAQ,QAAQ,QAAW;GAC7B,MAAM,OAAO,KAAK,KAAK,QAAQ,QAAQ,UAAU;GAEjD,MAAM,KAAK;IACT,MAAM;IACN,SAAS,SAAS,EAAE,YAAY,eAAe,QAAQ,KAAK,EAAE,YAAY,UAAU,CAAC,EAAE,CAAC;GAC1F,CAAC;GACD,KAAK,KAAK,gBAAgB,IAAI;GAC9B,SAAS,KAAK,cAAc;EAC9B;EAEA,OAAO;GACL;GACA,MAAM,QAAQ,cAAc,SAAS,CAAC,WAAW,QAAQ,MAAM,CAAC,IAAI,CAAC;GACrE,OAAO,QAAQ,cAAc,UAAU,QAAQ,SAAS;GACxD,KAAK;IACH,GAAI,QAAQ,SAAS,SAAY,EAAE,WAAW,QAAQ,KAAK,IAAI,CAAC;IAChE,iCAAiC;GACnC;GACA;GACA;EACF;CACF;CACA,cAAc,yBAAyB;EAAE,OAAO;EAAa,QAAQ;CAAY,CAAC;AACpF"}
@@ -0,0 +1,31 @@
1
+ import { CliAdapter, CliStreamParser } from "../contracts/adapter.contract.mjs";
2
+ //#region ../../packages/ai-cli/src/adapters/reasonix.d.ts
3
+ /**
4
+ * Reasonix (`reasonix`, npm `reasonix`) — a cache-first coding agent built
5
+ * around DeepSeek (now multi-provider); the npm package launches a prebuilt
6
+ * Go binary.
7
+ *
8
+ * Verified against the real `reasonix run --help` / `--version` of 2.43.0,
9
+ * real stream-json output (captured against a local fake model server) and
10
+ * the docs at that tag (`docs/CLI.md`, `docs/CONFIG_PATHS.md`), 2026-10-17.
11
+ *
12
+ * - Headless: `reasonix run --output-format stream-json <task>`; with no
13
+ * positional task, piped stdin is the prompt.
14
+ * - Stream: every line has `kind` (`text` deltas, `message` full text,
15
+ * `tool_dispatch`/`tool_started`/`tool_result` with a `tool` object whose
16
+ * non-empty `err` marks a failure, `usage` per model call, `turn_done`) plus
17
+ * `sessionId`; the final line is `{"type":"result",…}` with totals.
18
+ * - Permissions: headless "ask" mode refuses writes (listed in
19
+ * `permission_denials`; exit 4 in an untrusted folder); bypass is `--yolo`.
20
+ * - MCP: no flag; `[[plugins]]` in `$REASONIX_HOME/config.toml`.
21
+ * - Auth: provider keys are read only from `$REASONIX_HOME/.env`, never the
22
+ * shell environment, so `apiKey` is written there (0600) as
23
+ * `DEEPSEEK_API_KEY` — this needs a private home. For another provider,
24
+ * put its variable in that file yourself via a caller-owned home.
25
+ */
26
+ declare const reasonixAdapter: CliAdapter;
27
+ /** Parser for `reasonix run --output-format stream-json`. */
28
+ declare function createReasonixParser(): CliStreamParser;
29
+ //#endregion
30
+ export { createReasonixParser, reasonixAdapter };
31
+ //# sourceMappingURL=reasonix.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reasonix.d.mts","names":[],"sources":["../../../../../../../../packages/ai-cli/src/adapters/reasonix.ts"],"mappings":";;;;;AAkDA;;;;AAgFC;AA6BD;;;;AAAuD;;;;;;;;;;;cA7G1C,eAAA,EAAiB,UAgF7B;;iBA6Be,oBAAA,IAAwB,eAAe"}
@@ -0,0 +1,250 @@
1
+ import { asRecord, jsonLinesParser, numberField, recordField, stringField } from "../stream/json-lines.mjs";
2
+ import { toToml } from "../utils/toml.mjs";
3
+ import { argvPrompt, dotenvFile, mapEffort, parseArguments, requirePrivateHome, usageOf } from "./shared.mjs";
4
+ import path from "node:path";
5
+
6
+ //#region ../../packages/ai-cli/src/adapters/reasonix.ts
7
+ /**
8
+ * Reasonix (`reasonix`, npm `reasonix`) — a cache-first coding agent built
9
+ * around DeepSeek (now multi-provider); the npm package launches a prebuilt
10
+ * Go binary.
11
+ *
12
+ * Verified against the real `reasonix run --help` / `--version` of 2.43.0,
13
+ * real stream-json output (captured against a local fake model server) and
14
+ * the docs at that tag (`docs/CLI.md`, `docs/CONFIG_PATHS.md`), 2026-10-17.
15
+ *
16
+ * - Headless: `reasonix run --output-format stream-json <task>`; with no
17
+ * positional task, piped stdin is the prompt.
18
+ * - Stream: every line has `kind` (`text` deltas, `message` full text,
19
+ * `tool_dispatch`/`tool_started`/`tool_result` with a `tool` object whose
20
+ * non-empty `err` marks a failure, `usage` per model call, `turn_done`) plus
21
+ * `sessionId`; the final line is `{"type":"result",…}` with totals.
22
+ * - Permissions: headless "ask" mode refuses writes (listed in
23
+ * `permission_denials`; exit 4 in an untrusted folder); bypass is `--yolo`.
24
+ * - MCP: no flag; `[[plugins]]` in `$REASONIX_HOME/config.toml`.
25
+ * - Auth: provider keys are read only from `$REASONIX_HOME/.env`, never the
26
+ * shell environment, so `apiKey` is written there (0600) as
27
+ * `DEEPSEEK_API_KEY` — this needs a private home. For another provider,
28
+ * put its variable in that file yourself via a caller-owned home.
29
+ */
30
+ const reasonixAdapter = {
31
+ name: "reasonix",
32
+ title: "Reasonix",
33
+ command: "reasonix",
34
+ apiKeyEnv: ["DEEPSEEK_API_KEY"],
35
+ homeEnv: ["REASONIX_HOME"],
36
+ transports: ["argv", "stdin"],
37
+ effort: true,
38
+ mcp: true,
39
+ probe: {
40
+ help: [["run", "--help"]],
41
+ version: ["--version"]
42
+ },
43
+ verification: {
44
+ version: "2.43.0",
45
+ verifiedBy: "help",
46
+ sources: [
47
+ "https://github.com/esengine/DeepSeek-Reasonix/blob/v2.43.0/docs/CLI.md",
48
+ "https://github.com/esengine/DeepSeek-Reasonix/blob/v2.43.0/docs/CONFIG_PATHS.md",
49
+ "https://www.npmjs.com/package/reasonix"
50
+ ],
51
+ checkedAt: "2026-10-17"
52
+ },
53
+ plan(context) {
54
+ const args = [
55
+ "run",
56
+ "--output-format",
57
+ "stream-json"
58
+ ];
59
+ const requires = ["--output-format", "stream-json"];
60
+ const files = [];
61
+ if (context.model !== void 0) {
62
+ args.push("--model", context.model);
63
+ requires.push("--model");
64
+ }
65
+ if (context.effort !== void 0) {
66
+ args.push("--effort", mapEffort(context.effort, {
67
+ none: "disabled",
68
+ low: "low",
69
+ high: "high",
70
+ max: "max"
71
+ }));
72
+ requires.push("--effort");
73
+ }
74
+ if (context.resume !== void 0) {
75
+ args.push("--resume", context.resume);
76
+ requires.push("--resume");
77
+ }
78
+ if (context.permission === "bypass") {
79
+ args.push("--yolo");
80
+ requires.push("--yolo");
81
+ }
82
+ if (context.mcp !== void 0) {
83
+ const home = requirePrivateHome(context, reasonixAdapter, "MCP servers");
84
+ files.push({
85
+ path: path.join(home, "config.toml"),
86
+ content: toToml({ plugins: reasonixPlugins(context.mcp) })
87
+ });
88
+ }
89
+ if (context.apiKey !== void 0) {
90
+ const home = requirePrivateHome(context, reasonixAdapter, "API keys (its .env file)");
91
+ files.push({
92
+ path: path.join(home, ".env"),
93
+ content: dotenvFile({ DEEPSEEK_API_KEY: context.apiKey })
94
+ });
95
+ }
96
+ return {
97
+ args,
98
+ tail: context.transport === "argv" ? [argvPrompt(context.prompt)] : [],
99
+ stdin: context.transport === "stdin" ? context.prompt : void 0,
100
+ env: {
101
+ ...context.home !== void 0 ? { REASONIX_HOME: context.home } : {},
102
+ REASONIX_TELEMETRY: "off"
103
+ },
104
+ files,
105
+ requires
106
+ };
107
+ },
108
+ parser: createReasonixParser
109
+ };
110
+ function reasonixPlugins(servers) {
111
+ return Object.entries(servers).map(([name, server]) => server.type === "stdio" ? {
112
+ name,
113
+ type: "stdio",
114
+ command: server.command,
115
+ args: server.args ?? [],
116
+ env: server.env
117
+ } : {
118
+ name,
119
+ type: server.type,
120
+ url: server.url,
121
+ headers: server.headers
122
+ });
123
+ }
124
+ /** Reasonix per-call usage (`promptTokens` includes cache hits). */
125
+ function callUsage(usage) {
126
+ if (usage === void 0) return;
127
+ const cacheHit = numberField(usage, "cacheHitTokens");
128
+ return usageOf({
129
+ input: numberField(usage, "promptTokens"),
130
+ output: numberField(usage, "completionTokens"),
131
+ total: numberField(usage, "totalTokens"),
132
+ cachedTokens: cacheHit !== void 0 && cacheHit > 0 ? cacheHit : void 0,
133
+ reasoningTokens: numberField(usage, "reasoningTokens"),
134
+ costUSD: numberField(usage, "costUsd")
135
+ });
136
+ }
137
+ /** Parser for `reasonix run --output-format stream-json`. */
138
+ function createReasonixParser() {
139
+ let started = false;
140
+ let sessionRef;
141
+ return {
142
+ ...jsonLinesParser((record) => {
143
+ const events = [];
144
+ const recordSession = stringField(record, "sessionId") ?? stringField(record, "session_id");
145
+ if (recordSession !== void 0) sessionRef = recordSession;
146
+ if (!started && recordSession !== void 0) {
147
+ started = true;
148
+ events.push({
149
+ type: "session.started",
150
+ sessionRef: recordSession,
151
+ raw: record
152
+ });
153
+ }
154
+ if (stringField(record, "type") === "result") {
155
+ const usage = recordField(record, "usage");
156
+ if (usage !== void 0) events.push({
157
+ type: "usage",
158
+ cumulative: true,
159
+ usage: usageOf({
160
+ input: numberField(usage, "input_tokens"),
161
+ output: numberField(usage, "output_tokens"),
162
+ cachedTokens: numberField(usage, "cache_read_input_tokens") || void 0,
163
+ costUSD: numberField(record, "total_cost_usd")
164
+ }),
165
+ raw: record
166
+ });
167
+ const result = stringField(record, "result") ?? "";
168
+ if (record.is_error === true) events.push({
169
+ type: "error",
170
+ message: result || "Reasonix run failed",
171
+ fatal: true,
172
+ raw: record
173
+ });
174
+ else if (result.length > 0) events.push({
175
+ type: "message",
176
+ text: result,
177
+ delta: false,
178
+ final: true,
179
+ raw: record
180
+ });
181
+ return events;
182
+ }
183
+ const tool = asRecord(record.tool);
184
+ switch (stringField(record, "kind")) {
185
+ case "text": {
186
+ const text = stringField(record, "text") ?? "";
187
+ if (text.length > 0) events.push({
188
+ type: "message",
189
+ text,
190
+ delta: true,
191
+ raw: record
192
+ });
193
+ break;
194
+ }
195
+ case "message":
196
+ events.push({
197
+ type: "message",
198
+ text: stringField(record, "text") ?? "",
199
+ delta: false,
200
+ raw: record
201
+ });
202
+ break;
203
+ case "tool_dispatch":
204
+ if (tool !== void 0 && tool.partial !== true) events.push({
205
+ type: "tool.call",
206
+ id: stringField(tool, "id") ?? "",
207
+ name: stringField(tool, "name") ?? "",
208
+ input: parseArguments(tool.args),
209
+ raw: record
210
+ });
211
+ break;
212
+ case "tool_result": {
213
+ const error = stringField(tool, "err") ?? "";
214
+ events.push({
215
+ type: "tool.result",
216
+ id: stringField(tool, "id") ?? "",
217
+ output: error.length > 0 ? error : stringField(tool, "output") ?? "",
218
+ isError: error.length > 0,
219
+ raw: record
220
+ });
221
+ break;
222
+ }
223
+ case "usage": {
224
+ const usage = callUsage(recordField(record, "usage"));
225
+ if (usage !== void 0) events.push({
226
+ type: "usage",
227
+ usage,
228
+ cumulative: false,
229
+ raw: record
230
+ });
231
+ break;
232
+ }
233
+ case "turn_done":
234
+ if (stringField(record, "status") === "failed") events.push({
235
+ type: "error",
236
+ message: stringField(record, "err") ?? "Reasonix turn failed",
237
+ fatal: true,
238
+ raw: record
239
+ });
240
+ break;
241
+ }
242
+ return events;
243
+ }),
244
+ sessionRef: () => sessionRef
245
+ };
246
+ }
247
+
248
+ //#endregion
249
+ export { createReasonixParser, reasonixAdapter };
250
+ //# sourceMappingURL=reasonix.mjs.map