@amalgm/chat 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 (183) hide show
  1. package/PURPOSE.md +107 -0
  2. package/README.md +41 -0
  3. package/dist/contract/chat-input.d.ts +144 -0
  4. package/dist/contract/chat-input.d.ts.map +1 -0
  5. package/dist/contract/chat-input.js +261 -0
  6. package/dist/contract/chat-input.js.map +1 -0
  7. package/dist/contract/create.d.ts +191 -0
  8. package/dist/contract/create.d.ts.map +1 -0
  9. package/dist/contract/create.js +249 -0
  10. package/dist/contract/create.js.map +1 -0
  11. package/dist/contract/freeze.d.ts +85 -0
  12. package/dist/contract/freeze.d.ts.map +1 -0
  13. package/dist/contract/freeze.js +151 -0
  14. package/dist/contract/freeze.js.map +1 -0
  15. package/dist/contract/harnesses.d.ts +21 -0
  16. package/dist/contract/harnesses.d.ts.map +1 -0
  17. package/dist/contract/harnesses.js +39 -0
  18. package/dist/contract/harnesses.js.map +1 -0
  19. package/dist/contract/index.d.ts +17 -0
  20. package/dist/contract/index.d.ts.map +1 -0
  21. package/dist/contract/index.js +17 -0
  22. package/dist/contract/index.js.map +1 -0
  23. package/dist/contract/input.d.ts +45 -0
  24. package/dist/contract/input.d.ts.map +1 -0
  25. package/dist/contract/input.js +78 -0
  26. package/dist/contract/input.js.map +1 -0
  27. package/dist/contract/models.d.ts +57 -0
  28. package/dist/contract/models.d.ts.map +1 -0
  29. package/dist/contract/models.js +380 -0
  30. package/dist/contract/models.js.map +1 -0
  31. package/dist/contract/payload-merge.d.ts +76 -0
  32. package/dist/contract/payload-merge.d.ts.map +1 -0
  33. package/dist/contract/payload-merge.js +215 -0
  34. package/dist/contract/payload-merge.js.map +1 -0
  35. package/dist/events/constructors.d.ts +53 -0
  36. package/dist/events/constructors.d.ts.map +1 -0
  37. package/dist/events/constructors.js +217 -0
  38. package/dist/events/constructors.js.map +1 -0
  39. package/dist/events/index.d.ts +12 -0
  40. package/dist/events/index.d.ts.map +1 -0
  41. package/dist/events/index.js +12 -0
  42. package/dist/events/index.js.map +1 -0
  43. package/dist/events/schema.d.ts +455 -0
  44. package/dist/events/schema.d.ts.map +1 -0
  45. package/dist/events/schema.js +228 -0
  46. package/dist/events/schema.js.map +1 -0
  47. package/dist/events/tool-display.d.ts +22 -0
  48. package/dist/events/tool-display.d.ts.map +1 -0
  49. package/dist/events/tool-display.js +60 -0
  50. package/dist/events/tool-display.js.map +1 -0
  51. package/dist/events/tool-shape.d.ts +48 -0
  52. package/dist/events/tool-shape.d.ts.map +1 -0
  53. package/dist/events/tool-shape.js +225 -0
  54. package/dist/events/tool-shape.js.map +1 -0
  55. package/dist/index.d.ts +27 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +26 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/normalizers/claude.d.ts +628 -0
  60. package/dist/normalizers/claude.d.ts.map +1 -0
  61. package/dist/normalizers/claude.js +603 -0
  62. package/dist/normalizers/claude.js.map +1 -0
  63. package/dist/normalizers/codex.d.ts +352 -0
  64. package/dist/normalizers/codex.d.ts.map +1 -0
  65. package/dist/normalizers/codex.js +485 -0
  66. package/dist/normalizers/codex.js.map +1 -0
  67. package/dist/normalizers/cursor.d.ts +34 -0
  68. package/dist/normalizers/cursor.d.ts.map +1 -0
  69. package/dist/normalizers/cursor.js +197 -0
  70. package/dist/normalizers/cursor.js.map +1 -0
  71. package/dist/normalizers/index.d.ts +13 -0
  72. package/dist/normalizers/index.d.ts.map +1 -0
  73. package/dist/normalizers/index.js +13 -0
  74. package/dist/normalizers/index.js.map +1 -0
  75. package/dist/normalizers/opencode.d.ts +81 -0
  76. package/dist/normalizers/opencode.d.ts.map +1 -0
  77. package/dist/normalizers/opencode.js +640 -0
  78. package/dist/normalizers/opencode.js.map +1 -0
  79. package/dist/normalizers/pi.d.ts +28 -0
  80. package/dist/normalizers/pi.d.ts.map +1 -0
  81. package/dist/normalizers/pi.js +160 -0
  82. package/dist/normalizers/pi.js.map +1 -0
  83. package/dist/ports.d.ts +14 -0
  84. package/dist/ports.d.ts.map +1 -0
  85. package/dist/ports.js +7 -0
  86. package/dist/ports.js.map +1 -0
  87. package/dist/transcript/index.d.ts +14 -0
  88. package/dist/transcript/index.d.ts.map +1 -0
  89. package/dist/transcript/index.js +12 -0
  90. package/dist/transcript/index.js.map +1 -0
  91. package/dist/transcript/message-reducer.d.ts +167 -0
  92. package/dist/transcript/message-reducer.d.ts.map +1 -0
  93. package/dist/transcript/message-reducer.js +400 -0
  94. package/dist/transcript/message-reducer.js.map +1 -0
  95. package/dist/transcript/part-accumulator.d.ts +24 -0
  96. package/dist/transcript/part-accumulator.d.ts.map +1 -0
  97. package/dist/transcript/part-accumulator.js +30 -0
  98. package/dist/transcript/part-accumulator.js.map +1 -0
  99. package/dist/transcript/tool-display.d.ts +14 -0
  100. package/dist/transcript/tool-display.d.ts.map +1 -0
  101. package/dist/transcript/tool-display.js +125 -0
  102. package/dist/transcript/tool-display.js.map +1 -0
  103. package/dist/transcript/tool-shape.d.ts +34 -0
  104. package/dist/transcript/tool-shape.d.ts.map +1 -0
  105. package/dist/transcript/tool-shape.js +216 -0
  106. package/dist/transcript/tool-shape.js.map +1 -0
  107. package/dist/turns/index.d.ts +11 -0
  108. package/dist/turns/index.d.ts.map +1 -0
  109. package/dist/turns/index.js +11 -0
  110. package/dist/turns/index.js.map +1 -0
  111. package/dist/turns/queries.d.ts +36 -0
  112. package/dist/turns/queries.d.ts.map +1 -0
  113. package/dist/turns/queries.js +37 -0
  114. package/dist/turns/queries.js.map +1 -0
  115. package/dist/turns/replay.d.ts +105 -0
  116. package/dist/turns/replay.d.ts.map +1 -0
  117. package/dist/turns/replay.js +163 -0
  118. package/dist/turns/replay.js.map +1 -0
  119. package/dist/turns/status.d.ts +49 -0
  120. package/dist/turns/status.d.ts.map +1 -0
  121. package/dist/turns/status.js +56 -0
  122. package/dist/turns/status.js.map +1 -0
  123. package/dist/turns/turn-store.d.ts +109 -0
  124. package/dist/turns/turn-store.d.ts.map +1 -0
  125. package/dist/turns/turn-store.js +169 -0
  126. package/dist/turns/turn-store.js.map +1 -0
  127. package/dist/usage/finalize.d.ts +57 -0
  128. package/dist/usage/finalize.d.ts.map +1 -0
  129. package/dist/usage/finalize.js +139 -0
  130. package/dist/usage/finalize.js.map +1 -0
  131. package/dist/usage/index.d.ts +11 -0
  132. package/dist/usage/index.d.ts.map +1 -0
  133. package/dist/usage/index.js +11 -0
  134. package/dist/usage/index.js.map +1 -0
  135. package/dist/usage/model-catalog.d.ts +50 -0
  136. package/dist/usage/model-catalog.d.ts.map +1 -0
  137. package/dist/usage/model-catalog.js +344 -0
  138. package/dist/usage/model-catalog.js.map +1 -0
  139. package/dist/usage/normalize.d.ts +103 -0
  140. package/dist/usage/normalize.d.ts.map +1 -0
  141. package/dist/usage/normalize.js +71 -0
  142. package/dist/usage/normalize.js.map +1 -0
  143. package/dist/usage/ports.d.ts +141 -0
  144. package/dist/usage/ports.d.ts.map +1 -0
  145. package/dist/usage/ports.js +113 -0
  146. package/dist/usage/ports.js.map +1 -0
  147. package/dist/usage/tokens.d.ts +79 -0
  148. package/dist/usage/tokens.d.ts.map +1 -0
  149. package/dist/usage/tokens.js +98 -0
  150. package/dist/usage/tokens.js.map +1 -0
  151. package/dist/warm/client-cache.d.ts +47 -0
  152. package/dist/warm/client-cache.d.ts.map +1 -0
  153. package/dist/warm/client-cache.js +48 -0
  154. package/dist/warm/client-cache.js.map +1 -0
  155. package/dist/warm/envelope-cache.d.ts +79 -0
  156. package/dist/warm/envelope-cache.d.ts.map +1 -0
  157. package/dist/warm/envelope-cache.js +85 -0
  158. package/dist/warm/envelope-cache.js.map +1 -0
  159. package/dist/warm/index.d.ts +16 -0
  160. package/dist/warm/index.d.ts.map +1 -0
  161. package/dist/warm/index.js +11 -0
  162. package/dist/warm/index.js.map +1 -0
  163. package/dist/warm/payload.d.ts +137 -0
  164. package/dist/warm/payload.d.ts.map +1 -0
  165. package/dist/warm/payload.js +112 -0
  166. package/dist/warm/payload.js.map +1 -0
  167. package/dist/warm/revision.d.ts +70 -0
  168. package/dist/warm/revision.d.ts.map +1 -0
  169. package/dist/warm/revision.js +63 -0
  170. package/dist/warm/revision.js.map +1 -0
  171. package/dist/warm/store-policy.d.ts +57 -0
  172. package/dist/warm/store-policy.d.ts.map +1 -0
  173. package/dist/warm/store-policy.js +89 -0
  174. package/dist/warm/store-policy.js.map +1 -0
  175. package/dist/wire/frames.d.ts +208 -0
  176. package/dist/wire/frames.d.ts.map +1 -0
  177. package/dist/wire/frames.js +245 -0
  178. package/dist/wire/frames.js.map +1 -0
  179. package/dist/wire/index.d.ts +7 -0
  180. package/dist/wire/index.d.ts.map +1 -0
  181. package/dist/wire/index.js +7 -0
  182. package/dist/wire/index.js.map +1 -0
  183. package/package.json +72 -0
@@ -0,0 +1,640 @@
1
+ /**
2
+ * OpenCode normalizer — engine `chat-core/normalizers/opencode.js` (552
3
+ * lines) ported byte-exact. The engine module calls the event constructors
4
+ * directly; here the pure helpers (`suffixPrefixOverlap`,
5
+ * `openCodeErrorMessage`, `usageFromOpenCodeTokens`) are module-level
6
+ * exports and everything that constructs events lives behind
7
+ * `createOpenCodeNormalizer(events)` (axiom 7: the host injects the clock
8
+ * via {@link EventConstructors}).
9
+ *
10
+ * Streaming laws preserved verbatim:
11
+ * - per-part delta dedup via suffix-prefix overlap (`appendOpenCodeDelta`);
12
+ * - snapshot merges keep the longer prefix-compatible text
13
+ * (`mergeOpenCodeSnapshot`);
14
+ * - cross-part assistant-text dedup with the 40/80-char thresholds and the
15
+ * 80% overlap ratio (`dedupeOpenCodeAssistantText`);
16
+ * - per-step usage from `step-finish` parts and internal
17
+ * compaction/continuation usage from `message.updated`.
18
+ */
19
+ import { mcpMetadataFromName, normalizeError, stringifyResult, summarizeToolOutput, summarizeToolRequest, titleForTool, toolKind, } from '../transcript/index.js';
20
+ import { normalizeUsage } from '../usage/index.js';
21
+ /** Engine opencode.js:8-9 — cross-part dedup thresholds. */
22
+ const CROSS_PART_DEDUPE_MIN_CHARS = 40;
23
+ const CROSS_PART_OVERLAP_MIN_CHARS = 80;
24
+ /**
25
+ * Engine opencode.js:11-29. OpenCode step tokens
26
+ * (`{input, output, reasoning, cache:{read, write}}`) -> normalized usage.
27
+ * `output` includes reasoning (thought tokens ride inside output), `exact`
28
+ * is always true, source `'opencode_sdk'`.
29
+ */
30
+ export function usageFromOpenCodeTokens(tokens, cost, contextLimit, operation = 'chat_step') {
31
+ const inputTokens = Number(tokens?.['input'] || 0);
32
+ const cacheReadTokens = Number(tokens?.['cache']?.read || 0);
33
+ const cacheWriteTokens = Number(tokens?.['cache']?.write || 0);
34
+ const thoughtTokens = Number(tokens?.['reasoning'] || 0);
35
+ const outputTokens = Number(tokens?.['output'] || 0) + thoughtTokens;
36
+ return normalizeUsage({
37
+ inputTokens,
38
+ outputTokens,
39
+ thoughtTokens,
40
+ cacheReadTokens,
41
+ cacheWriteTokens,
42
+ contextLimit,
43
+ costUsd: cost,
44
+ operation,
45
+ source: 'opencode_sdk',
46
+ exact: true,
47
+ });
48
+ }
49
+ /** Engine opencode.js:31-44. Nested-error unwrap chain, JSON fallback. */
50
+ export function openCodeErrorMessage(err, fallback = 'OpenCode session error') {
51
+ const e = err;
52
+ if (!e)
53
+ return fallback;
54
+ if (typeof e === 'string')
55
+ return e;
56
+ if (e['data']?.message)
57
+ return e['data'].message;
58
+ if (e['data']?.error?.message)
59
+ return e['data'].error.message;
60
+ if (e['cause']?.message)
61
+ return e['cause'].message;
62
+ if (e['message'])
63
+ return e['message'];
64
+ if (e['name'])
65
+ return e['name'];
66
+ try {
67
+ return JSON.stringify(e);
68
+ }
69
+ catch {
70
+ return fallback;
71
+ }
72
+ }
73
+ /** Engine opencode.js:46-48. */
74
+ function openCodeRequestId(prefix, id) {
75
+ return `${prefix}_${String(id || 'unknown')}`;
76
+ }
77
+ /** Engine opencode.js:50-52. */
78
+ function eventRaw(type, payload) {
79
+ return { raw: { source: 'opencode.sdk.event', method: type || 'unknown', payload } };
80
+ }
81
+ /** Engine opencode.js:54-59. */
82
+ function commonPrefixLength(a = '', b = '') {
83
+ const max = Math.min(a.length, b.length);
84
+ let index = 0;
85
+ while (index < max && a[index] === b[index])
86
+ index += 1;
87
+ return index;
88
+ }
89
+ /**
90
+ * Engine opencode.js:61-67. Longest length such that `text` ends with the
91
+ * first `length` chars of `delta` — the overlap-dedup primitive.
92
+ */
93
+ export function suffixPrefixOverlap(text = '', delta = '') {
94
+ const max = Math.min(text.length, delta.length);
95
+ for (let length = max; length > 0; length -= 1) {
96
+ if (text.endsWith(delta.slice(0, length)))
97
+ return length;
98
+ }
99
+ return 0;
100
+ }
101
+ /** Engine opencode.js:69-77. Snapshot merge: longer prefix-compatible text wins. */
102
+ function mergeOpenCodeSnapshot(previousText = '', nextText = '') {
103
+ const latestText = previousText.length > nextText.length && previousText.startsWith(nextText)
104
+ ? previousText
105
+ : nextText;
106
+ return {
107
+ latestText,
108
+ deltaToEmit: latestText.slice(commonPrefixLength(previousText, latestText)),
109
+ };
110
+ }
111
+ /** Engine opencode.js:79-85. Delta append with suffix-prefix overlap trim. */
112
+ function appendOpenCodeDelta(previousText = '', delta = '') {
113
+ const deltaToEmit = delta.slice(suffixPrefixOverlap(previousText, delta));
114
+ return {
115
+ nextText: `${previousText}${deltaToEmit}`,
116
+ deltaToEmit,
117
+ };
118
+ }
119
+ /** Engine opencode.js:87-105. Cross-part dedup: 40-char full-tail drop, 80-char / 80% overlap trim. */
120
+ function dedupeOpenCodeAssistantText(state, text = '') {
121
+ if (!text)
122
+ return '';
123
+ const aggregate = String(state.assistantTextAggregate || '');
124
+ let emitText = text;
125
+ if (emitText.length >= CROSS_PART_DEDUPE_MIN_CHARS && aggregate.endsWith(emitText)) {
126
+ emitText = '';
127
+ }
128
+ else {
129
+ const overlap = suffixPrefixOverlap(aggregate, emitText);
130
+ const mostlyDuplicate = emitText.length >= CROSS_PART_DEDUPE_MIN_CHARS &&
131
+ overlap > 0 &&
132
+ (overlap >= CROSS_PART_OVERLAP_MIN_CHARS || overlap / emitText.length >= 0.8);
133
+ if (mostlyDuplicate)
134
+ emitText = emitText.slice(overlap);
135
+ }
136
+ if (emitText)
137
+ state.assistantTextAggregate = `${aggregate}${emitText}`;
138
+ return emitText;
139
+ }
140
+ /** Engine opencode.js:107-114. */
141
+ function openCodeToolDetail(part) {
142
+ const toolName = part?.['tool'] || 'unknown';
143
+ const status = part?.['state']?.status;
144
+ if (status === 'completed' || status === 'error') {
145
+ return summarizeToolOutput(part?.['state']?.output ?? part?.['state']?.error ?? '');
146
+ }
147
+ return summarizeToolRequest(toolName, part?.['state']?.input || {}, part?.['state']?.title || '');
148
+ }
149
+ /** Engine opencode.js:116-121. */
150
+ function openCodeToolData(part) {
151
+ return {
152
+ tool: part?.['tool'] || 'unknown',
153
+ state: part?.['state'] || {},
154
+ };
155
+ }
156
+ /** Engine opencode.js:123-141. `state.metadata.sessionId` marks subagent tools; `tool === 'task'` always does. */
157
+ function openCodeToolMetadata(part) {
158
+ const toolName = part?.['tool'] || 'unknown';
159
+ const metadata = {
160
+ providerTool: toolName,
161
+ providerStatus: part?.['state']?.status,
162
+ ...mcpMetadataFromName(toolName),
163
+ };
164
+ const stateMetadata = part?.['state']?.metadata;
165
+ if (stateMetadata && typeof stateMetadata === 'object') {
166
+ Object.assign(metadata, stateMetadata);
167
+ if (stateMetadata.sessionId) {
168
+ metadata['childSessionId'] = stateMetadata.sessionId;
169
+ metadata['subagent'] = true;
170
+ }
171
+ }
172
+ if (part?.['tool'] === 'task')
173
+ metadata['subagent'] = true;
174
+ if (part?.['state']?.title)
175
+ metadata['title'] = part['state'].title;
176
+ return metadata;
177
+ }
178
+ /** Engine opencode.js:143-146. */
179
+ function openCodeToolError(part) {
180
+ if (part?.['state']?.status !== 'error')
181
+ return undefined;
182
+ return normalizeError(part?.['state']?.error || part?.['state']?.output || 'Tool failed');
183
+ }
184
+ /** Engine opencode.js:148-160. */
185
+ function openCodeToolTitle(part) {
186
+ const toolName = part?.['tool'] || 'unknown';
187
+ const input = part?.['state']?.input || {};
188
+ const metadata = openCodeToolMetadata(part);
189
+ return titleForTool({
190
+ name: toolName,
191
+ input,
192
+ output: part?.['state']?.output,
193
+ error: part?.['state']?.error,
194
+ metadata,
195
+ fallback: part?.['state']?.title || toolName,
196
+ });
197
+ }
198
+ /** Engine opencode.js:162-168. */
199
+ function stopReasonFromOpenCodeFinish(finish) {
200
+ if (finish === 'length')
201
+ return 'max_tokens';
202
+ if (finish === 'content_filter')
203
+ return 'content_filter';
204
+ if (finish === 'error')
205
+ return 'error';
206
+ if (finish === 'stop')
207
+ return 'end_turn';
208
+ return null;
209
+ }
210
+ /** Engine opencode.js:170-173. */
211
+ function pendingParts(state) {
212
+ if (!state.pendingParts)
213
+ state.pendingParts = new Map();
214
+ return state.pendingParts;
215
+ }
216
+ /** Engine opencode.js:175-178. */
217
+ function messageRoles(state) {
218
+ if (!state.messageRoles)
219
+ state.messageRoles = new Map();
220
+ return state.messageRoles;
221
+ }
222
+ /** Engine opencode.js:180-183. */
223
+ function compactionContinuationMessages(state) {
224
+ if (!state.compactionContinuationMessageIds)
225
+ state.compactionContinuationMessageIds = new Set();
226
+ return state.compactionContinuationMessageIds;
227
+ }
228
+ /** Engine opencode.js:185-187. */
229
+ function rememberCompactionContinuation(part, state) {
230
+ if (part?.['messageID'])
231
+ compactionContinuationMessages(state).add(part['messageID']);
232
+ }
233
+ /** Engine opencode.js:189-193. Compaction runs + their continuation prompts bill internally. */
234
+ function isOpenCodeInternalUsage(info, state) {
235
+ if (!info || info['sessionID'] !== state.providerSessionId || info['role'] !== 'assistant')
236
+ return false;
237
+ if (info['mode'] === 'compaction' || info['agent'] === 'compaction')
238
+ return true;
239
+ return Boolean(info['parentID'] && state.compactionContinuationMessageIds?.has(info['parentID']));
240
+ }
241
+ /** Engine opencode.js:195-206. Text/reasoning deltas fold into the cached part snapshot. */
242
+ function cachePendingPart(part, state, delta) {
243
+ if (!part?.['id'])
244
+ return;
245
+ const cache = pendingParts(state);
246
+ const previous = cache.get(part['id'])?.part || part;
247
+ const next = { ...previous, ...part };
248
+ if (typeof delta === 'string' && (part['type'] === 'text' || part['type'] === 'reasoning')) {
249
+ const base = String(previous['text'] || part['text'] || '');
250
+ const merged = appendOpenCodeDelta(base, delta);
251
+ next['text'] = merged.nextText;
252
+ }
253
+ cache.set(part['id'], { part: next, delta: undefined });
254
+ }
255
+ /** Engine opencode.js:208-213. */
256
+ function dropPendingParts(messageId, state) {
257
+ const cache = pendingParts(state);
258
+ for (const [partId, entry] of [...cache.entries()]) {
259
+ if (entry.part?.['messageID'] === messageId)
260
+ cache.delete(partId);
261
+ }
262
+ }
263
+ /** Engine opencode.js:338-344. Current-turn assistant filter: parent + creation-time gates. */
264
+ function isOurAssistant(info, state) {
265
+ if (!info || info['sessionID'] !== state.providerSessionId || info['role'] !== 'assistant')
266
+ return false;
267
+ if (state.userMessageId && info['parentID'] && info['parentID'] !== state.userMessageId)
268
+ return false;
269
+ const created = Number(info['time']?.created || 0);
270
+ if (state.turnStartedAt && created && created < state.turnStartedAt)
271
+ return false;
272
+ return true;
273
+ }
274
+ /**
275
+ * Build the OpenCode normalizer over injected event constructors — the
276
+ * engine functions verbatim, closed over `events` instead of the ambient
277
+ * `require('../events')` module.
278
+ */
279
+ export function createOpenCodeNormalizer(events) {
280
+ const { compactionFinished, compactionStarted, done, errorEvent, reasoningDelta, textDelta, toolCompleted, toolStarted, toolUpdated, usageFinal, warningEvent, } = events;
281
+ /** Engine opencode.js:215-224. */
282
+ function flushPendingParts(messageId, state) {
283
+ const cache = pendingParts(state);
284
+ const flushed = [];
285
+ for (const [partId, entry] of [...cache.entries()]) {
286
+ if (entry.part?.['messageID'] !== messageId)
287
+ continue;
288
+ cache.delete(partId);
289
+ flushed.push(...normalizeOpenCodePart(entry.part, state, entry.delta));
290
+ }
291
+ return flushed;
292
+ }
293
+ /** Engine opencode.js:226-336. One part (streamed or snapshot) -> amalgm events. */
294
+ function normalizeOpenCodePart(partInput, state, delta) {
295
+ const part = partInput;
296
+ if (!part || part['sessionID'] !== state.providerSessionId)
297
+ return [];
298
+ if (part['type'] === 'compaction') {
299
+ if (part['tail_start_id'])
300
+ return [];
301
+ if (!state.compactionsStarted)
302
+ state.compactionsStarted = new Set();
303
+ const compactionId = part['id'] || part['messageID'] || 'opencode-compaction';
304
+ if (state.compactionsStarted.has(compactionId))
305
+ return [];
306
+ state.compactionsStarted.add(compactionId);
307
+ return [compactionStarted({
308
+ providerSessionId: state.providerSessionId,
309
+ trigger: part['auto'] === false ? 'manual' : 'auto',
310
+ ...eventRaw('part/compaction', part),
311
+ })];
312
+ }
313
+ if (!state.assistantMessageIds.has(part['messageID']))
314
+ return [];
315
+ if (part['id'] && part['type']) {
316
+ if (!state.partTypes)
317
+ state.partTypes = new Map();
318
+ state.partTypes.set(part['id'], part['type']);
319
+ }
320
+ if (part['metadata']?.compaction_continue) {
321
+ rememberCompactionContinuation(part, state);
322
+ return [];
323
+ }
324
+ const seen = state.partText.get(part['id']) || '';
325
+ if (part['type'] === 'text') {
326
+ const merged = typeof delta === 'string'
327
+ ? appendOpenCodeDelta(seen, delta)
328
+ : mergeOpenCodeSnapshot(seen, String(part['text'] || ''));
329
+ const text = dedupeOpenCodeAssistantText(state, merged['deltaToEmit']);
330
+ state.partText.set(part['id'], merged['latestText'] || merged['nextText'] || `${seen}${text}`);
331
+ return text ? [textDelta(text, { providerSessionId: state.providerSessionId, ts: part['time']?.start, ...eventRaw(`part/${part['type']}`, part) })] : [];
332
+ }
333
+ if (part['type'] === 'reasoning') {
334
+ const merged = typeof delta === 'string'
335
+ ? appendOpenCodeDelta(seen, delta)
336
+ : mergeOpenCodeSnapshot(seen, String(part['text'] || ''));
337
+ const text = merged['deltaToEmit'];
338
+ state.partText.set(part['id'], merged['latestText'] || merged['nextText'] || `${seen}${text}`);
339
+ return text ? [reasoningDelta(text, { providerSessionId: state.providerSessionId, ts: part['time']?.start, ...eventRaw(`part/${part['type']}`, part) })] : [];
340
+ }
341
+ if (part['type'] === 'tool') {
342
+ const toolId = part['callID'] || part['id'];
343
+ const status = part['state']?.status;
344
+ const toolName = part['tool'] || 'unknown';
345
+ const input = part['state']?.input || {};
346
+ const metadata = openCodeToolMetadata(part);
347
+ const title = openCodeToolTitle(part);
348
+ const kind = toolKind(toolName, input, metadata);
349
+ const timing = part['state']?.time || part['time'];
350
+ if (status === 'pending' || status === 'running') {
351
+ const detail = openCodeToolDetail(part);
352
+ if (state.toolsStarted.has(toolId)) {
353
+ return [toolUpdated(toolId, {
354
+ providerSessionId: state.providerSessionId,
355
+ toolName,
356
+ title,
357
+ kind,
358
+ detail,
359
+ inputDelta: input,
360
+ status: 'in_progress',
361
+ metadata,
362
+ timing,
363
+ data: openCodeToolData(part),
364
+ ...eventRaw('part/tool', part),
365
+ })];
366
+ }
367
+ state.toolsStarted.add(toolId);
368
+ return [toolStarted(toolId, {
369
+ providerSessionId: state.providerSessionId,
370
+ toolName,
371
+ title,
372
+ kind,
373
+ detail,
374
+ input,
375
+ ts: part['state']?.time?.start,
376
+ metadata,
377
+ timing,
378
+ data: openCodeToolData(part),
379
+ ...eventRaw('part/tool', part),
380
+ })];
381
+ }
382
+ if ((status === 'completed' || status === 'error') && !state.toolsCompleted.has(toolId)) {
383
+ state.toolsCompleted.add(toolId);
384
+ const output = part['state']?.output ?? part['state']?.error ?? '';
385
+ const detail = openCodeToolDetail(part);
386
+ return [toolCompleted(toolId, {
387
+ providerSessionId: state.providerSessionId,
388
+ toolName,
389
+ title,
390
+ kind,
391
+ detail,
392
+ input,
393
+ output,
394
+ result: stringifyResult(output),
395
+ isError: status === 'error',
396
+ error: openCodeToolError(part),
397
+ ts: part['state']?.time?.end,
398
+ metadata,
399
+ timing,
400
+ data: openCodeToolData(part),
401
+ ...eventRaw('part/tool', part),
402
+ })];
403
+ }
404
+ }
405
+ if (part['type'] === 'step-finish') {
406
+ const stopReason = stopReasonFromOpenCodeFinish(part['reason']);
407
+ if (stopReason)
408
+ state.pendingStopReason = stopReason;
409
+ return [usageFinal(usageFromOpenCodeTokens(part['tokens'], part['cost'], state.contextLimit), { providerSessionId: state.providerSessionId, ...eventRaw('part/step-finish', part) })];
410
+ }
411
+ return [];
412
+ }
413
+ /** Engine opencode.js:346-509. One SDK event -> amalgm events. */
414
+ function normalizeOpenCodeEvent(event, state) {
415
+ const payload = (event?.['payload'] || event);
416
+ if (!payload || typeof payload !== 'object')
417
+ return [];
418
+ const raw = eventRaw(payload['type'], event);
419
+ if (payload['type'] === 'message.updated') {
420
+ const info = payload['properties']?.info;
421
+ if (info?.id && info.sessionID === state.providerSessionId && info.role) {
422
+ messageRoles(state).set(info.id, info.role);
423
+ if (info.role === 'user') {
424
+ const created = Number(info.time?.created || 0);
425
+ const isCurrentTurnUser = !state.turnStartedAt || !created || created >= state.turnStartedAt;
426
+ if (!state.userMessageId && isCurrentTurnUser)
427
+ state.userMessageId = info.id;
428
+ dropPendingParts(info.id, state);
429
+ return [];
430
+ }
431
+ }
432
+ }
433
+ if (payload['type'] === 'message.part.delta') {
434
+ const props = payload['properties'] || {};
435
+ const delta = typeof props.delta === 'string' ? props.delta : '';
436
+ if (!delta)
437
+ return [];
438
+ if (!state.assistantMessageIds.has(props.messageID)) {
439
+ const pending = state.pendingParts?.get(props.partID);
440
+ if (pending?.part)
441
+ cachePendingPart(pending.part, state, delta);
442
+ return [];
443
+ }
444
+ const knownType = state.partTypes?.get(props.partID);
445
+ const previous = state.partText.get(props.partID) || '';
446
+ const merged = appendOpenCodeDelta(previous, delta);
447
+ state.partText.set(props.partID, merged.nextText);
448
+ if (!merged.deltaToEmit)
449
+ return [];
450
+ if (knownType === 'text' || (!knownType && props.field === 'text')) {
451
+ const text = dedupeOpenCodeAssistantText(state, merged.deltaToEmit);
452
+ return text ? [textDelta(text, { providerSessionId: state.providerSessionId, ts: payload['time'] || payload['properties']?.time, ...raw })] : [];
453
+ }
454
+ if (knownType === 'reasoning' || props.field === 'reasoning' || props.field === 'thinking') {
455
+ return [reasoningDelta(merged.deltaToEmit, { providerSessionId: state.providerSessionId, ...raw })];
456
+ }
457
+ return [];
458
+ }
459
+ if (payload['type'] === 'message.part.updated') {
460
+ const part = payload['properties']?.part;
461
+ if (part?.id && part?.type) {
462
+ if (!state.partTypes)
463
+ state.partTypes = new Map();
464
+ state.partTypes.set(part.id, part.type);
465
+ }
466
+ if (part?.metadata?.compaction_continue) {
467
+ rememberCompactionContinuation(part, state);
468
+ return [];
469
+ }
470
+ const role = part?.messageID ? state.messageRoles?.get(part.messageID) : undefined;
471
+ if (role && role !== 'assistant')
472
+ return [];
473
+ if (part?.type === 'compaction')
474
+ return normalizeOpenCodePart(part, state, payload['properties']?.delta);
475
+ if (part?.messageID && !state.assistantMessageIds.has(part.messageID)) {
476
+ cachePendingPart(part, state, payload['properties']?.delta);
477
+ return [];
478
+ }
479
+ const partEvents = normalizeOpenCodePart(part, state, payload['properties']?.delta);
480
+ if (part?.type === 'step-finish' && part.messageID)
481
+ state.usageEmittedForAssistant.add(part.messageID);
482
+ return partEvents;
483
+ }
484
+ if (payload['type'] === 'message.updated') {
485
+ const info = payload['properties']?.info;
486
+ if (isOpenCodeInternalUsage(info, state)) {
487
+ if (info.id && info.tokens && (info.finish || info.time?.completed)) {
488
+ if (state.finishedAssistantIds.has(info.id))
489
+ return [];
490
+ state.finishedAssistantIds.add(info.id);
491
+ const operation = info.mode === 'compaction' || info.agent === 'compaction' ? 'compaction' : 'chat_step';
492
+ return [usageFinal(usageFromOpenCodeTokens(info.tokens, info.cost, state.contextLimit, operation), { providerSessionId: state.providerSessionId, ...raw })];
493
+ }
494
+ return [];
495
+ }
496
+ if (!isOurAssistant(info, state))
497
+ return [];
498
+ if (info.id) {
499
+ state.assistantMessageIds.add(info.id);
500
+ state.sawTurnActivity = true;
501
+ }
502
+ const flushed = info.id ? flushPendingParts(info.id, state) : [];
503
+ if (info.tokens && (info.finish || info.time?.completed)) {
504
+ if (state.finishedAssistantIds.has(info.id))
505
+ return [];
506
+ state.finishedAssistantIds.add(info.id);
507
+ const finishEvents = [...flushed];
508
+ if (!state.usageEmittedForAssistant.has(info.id)) {
509
+ state.usageEmittedForAssistant.add(info.id);
510
+ finishEvents.push(usageFinal(usageFromOpenCodeTokens(info.tokens, info.cost, state.contextLimit), { providerSessionId: state.providerSessionId, ...raw }));
511
+ }
512
+ if (!state.doneEmitted && (info.finish === 'stop' || info.finish === 'length' || info.finish === 'content_filter')) {
513
+ state.pendingStopReason = stopReasonFromOpenCodeFinish(info.finish) || 'end_turn';
514
+ }
515
+ return finishEvents;
516
+ }
517
+ return flushed;
518
+ }
519
+ if (payload['type'] === 'session.compacted' && payload['properties']?.sessionID === state.providerSessionId) {
520
+ return [compactionFinished({
521
+ providerSessionId: state.providerSessionId,
522
+ trigger: 'auto',
523
+ ...raw,
524
+ })];
525
+ }
526
+ if (payload['type'] === 'session.error' && payload['properties']?.sessionID === state.providerSessionId) {
527
+ const err = payload['properties']?.error;
528
+ const message = openCodeErrorMessage(err);
529
+ if (state.doneEmitted)
530
+ return [errorEvent(message, { providerSessionId: state.providerSessionId, ...raw })];
531
+ state.doneEmitted = true;
532
+ return [errorEvent(message, { providerSessionId: state.providerSessionId, ...raw }), done({ providerSessionId: state.providerSessionId, stopReason: 'error', ...raw })];
533
+ }
534
+ if (payload['type'] === 'permission.asked' || payload['type'] === 'permission.replied')
535
+ return [];
536
+ if (payload['type'] === 'question.asked') {
537
+ const props = payload['properties'] || {};
538
+ if (props.sessionID && props.sessionID !== state.providerSessionId)
539
+ return [];
540
+ const id = openCodeRequestId('question', props.id);
541
+ return [toolStarted(id, {
542
+ providerSessionId: state.providerSessionId,
543
+ toolName: 'AskUserQuestion',
544
+ title: 'Question',
545
+ kind: 'question',
546
+ detail: 'Question',
547
+ input: {
548
+ questions: props.questions || props.question || [],
549
+ },
550
+ metadata: { providerEvent: payload['type'], question: true },
551
+ data: { question: props },
552
+ ts: payload['time'],
553
+ ...raw,
554
+ })];
555
+ }
556
+ if (payload['type'] === 'question.replied' || payload['type'] === 'question.rejected') {
557
+ const props = payload['properties'] || {};
558
+ if (props.sessionID && props.sessionID !== state.providerSessionId)
559
+ return [];
560
+ const id = openCodeRequestId('question', props.requestID || props.id);
561
+ const rejected = payload['type'] === 'question.rejected';
562
+ return [toolCompleted(id, {
563
+ providerSessionId: state.providerSessionId,
564
+ toolName: 'AskUserQuestion',
565
+ title: 'Question',
566
+ kind: 'question',
567
+ detail: rejected ? 'Question rejected' : 'Question answered',
568
+ result: rejected ? 'Question rejected' : JSON.stringify(props.answers || {}),
569
+ output: props,
570
+ isError: rejected,
571
+ error: rejected ? normalizeError('Question rejected') : undefined,
572
+ metadata: { providerEvent: payload['type'], question: true },
573
+ data: { question: props },
574
+ ts: payload['time'],
575
+ ...raw,
576
+ })];
577
+ }
578
+ if ((payload['type'] === 'session.idle' || payload['type'] === 'session.status') && payload['properties']?.sessionID === state.providerSessionId) {
579
+ const status = payload['properties']?.status;
580
+ if (status?.type === 'busy' || status === 'busy')
581
+ state.sawBusy = true;
582
+ if (status?.type === 'retry') {
583
+ const attempt = status.attempt ?? status.count ?? status.retry ?? '?';
584
+ const reason = status.message || openCodeErrorMessage(status.error, 'unknown upstream error');
585
+ const message = `OpenCode upstream retrying: ${reason} (attempt ${attempt})`;
586
+ return [warningEvent(message, { providerSessionId: state.providerSessionId, ...raw })];
587
+ }
588
+ if ((!status || status === 'idle' || status?.type === 'idle') && state.sawTurnActivity && !state.doneEmitted) {
589
+ state.doneEmitted = true;
590
+ return [done({ providerSessionId: state.providerSessionId, stopReason: state.pendingStopReason, ...raw })];
591
+ }
592
+ }
593
+ return [];
594
+ }
595
+ /** Engine opencode.js:511-543. Prompt-RPC resolution -> parts + usage + done. */
596
+ function normalizeOpenCodePromptResult(dataInput, state) {
597
+ const data = dataInput;
598
+ const raw = eventRaw('prompt.result', data);
599
+ const resultError = data?.['error'] || data?.['info']?.error;
600
+ if (resultError) {
601
+ const message = openCodeErrorMessage(resultError);
602
+ if (state.doneEmitted)
603
+ return [errorEvent(message, { providerSessionId: state.providerSessionId, ...raw })];
604
+ state.doneEmitted = true;
605
+ return [errorEvent(message, { providerSessionId: state.providerSessionId, ...raw }), done({ providerSessionId: state.providerSessionId, stopReason: 'error', ...raw })];
606
+ }
607
+ const info = data?.['info'];
608
+ if (!info?.id)
609
+ return [];
610
+ if (!isOurAssistant(info, state))
611
+ return [];
612
+ state.assistantMessageIds.add(info.id);
613
+ state.sawTurnActivity = true;
614
+ const resultEvents = [];
615
+ for (const part of data?.['parts'] || []) {
616
+ resultEvents.push(...normalizeOpenCodePart(part, state));
617
+ }
618
+ const finished = Boolean(info.finish || info.time?.completed);
619
+ if (!finished)
620
+ return resultEvents;
621
+ if (info.tokens && !state.usageEmittedForAssistant.has(info.id)) {
622
+ state.usageEmittedForAssistant.add(info.id);
623
+ resultEvents.push(usageFinal(usageFromOpenCodeTokens(info.tokens, info.cost, state.contextLimit), { providerSessionId: state.providerSessionId, ...raw }));
624
+ }
625
+ if (!state.doneEmitted) {
626
+ state.doneEmitted = true;
627
+ const stopReason = info.finish === 'length' ? 'max_tokens'
628
+ : info.finish === 'content_filter' ? 'content_filter'
629
+ : 'end_turn';
630
+ resultEvents.push(done({ providerSessionId: state.providerSessionId, stopReason, ...raw }));
631
+ }
632
+ return resultEvents;
633
+ }
634
+ return {
635
+ normalizeOpenCodeEvent,
636
+ normalizeOpenCodePart,
637
+ normalizeOpenCodePromptResult,
638
+ };
639
+ }
640
+ //# sourceMappingURL=opencode.js.map