@deepstrike/sdk 0.2.65 → 0.2.68

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.
@@ -4,7 +4,6 @@ export interface SessionMessage {
4
4
  content: string;
5
5
  /** Structured multimodal parts. Preserved for round-trip fidelity (e.g. tool result messages). */
6
6
  contentParts?: ContentPart[];
7
- tokenCount?: number;
8
7
  toolCalls?: Array<{
9
8
  id: string;
10
9
  name: string;
@@ -325,13 +325,11 @@ export class AnthropicMessagesAdapter {
325
325
  }
326
326
  if (hasTextualToolCall(content, decodeInput.input))
327
327
  throw textualToolCallError();
328
- const usage = this.normalizeUsage(raw.usage);
329
328
  const blocks = raw.content;
330
329
  return {
331
330
  message: {
332
331
  role: "assistant",
333
332
  content,
334
- ...(usage ? { tokenCount: usage.outputTokens } : {}),
335
333
  toolCalls,
336
334
  },
337
335
  ...(blocks?.length ? { replay: { protocol: "anthropic-messages", native_blocks: blocks } } : {}),
@@ -134,10 +134,11 @@ export function toAnthropicContent(msg) {
134
134
  if (p.type === "text")
135
135
  return { type: "text", text: p.text };
136
136
  if (p.type === "image") {
137
- if (p.data) {
138
- return { type: "image", source: { type: "base64", media_type: p.mediaType ?? "image/png", data: p.data } };
139
- }
140
- return { type: "image", source: { type: "url", url: p.url } };
137
+ if (p.source.kind === "base64")
138
+ return { type: "image", source: { type: "base64", media_type: p.mediaType ?? "image/png", data: p.source.data } };
139
+ if (p.source.kind === "url")
140
+ return { type: "image", source: { type: "url", url: p.source.url } };
141
+ return { type: "text", text: "[image]" };
141
142
  }
142
143
  if (p.type === "audio") {
143
144
  throw new UnsupportedModalityError("audio", "anthropic");
@@ -254,11 +255,13 @@ export function toOpenAIContent(msg) {
254
255
  if (p.type === "text")
255
256
  return { type: "text", text: p.text };
256
257
  if (p.type === "image") {
257
- const url = p.data ? `data:${p.mediaType ?? "image/png"};base64,${p.data}` : p.url;
258
+ const url = p.source.kind === "base64" ? `data:${p.mediaType ?? "image/png"};base64,${p.source.data}` : p.source.kind === "url" ? p.source.url : "";
258
259
  return { type: "image_url", image_url: { url, ...(p.detail ? { detail: p.detail } : {}) } };
259
260
  }
260
261
  if (p.type === "audio") {
261
- return { type: "input_audio", input_audio: { data: p.data, format: openaiAudioFormat(p.mediaType) } };
262
+ if (p.source.kind !== "base64")
263
+ return { type: "text", text: "[audio]" };
264
+ return { type: "input_audio", input_audio: { data: p.source.data, format: openaiAudioFormat(p.mediaType) } };
262
265
  }
263
266
  if (p.type === "tool_result") {
264
267
  return { type: "text", text: p.output };
@@ -21,7 +21,6 @@ export interface CanonicalMessage {
21
21
  /** `blocks` remains authoritative; this only preserves an observable wire-shape distinction. */
22
22
  readonly contentForm?: "text" | "blocks";
23
23
  readonly toolCalls?: readonly ToolCall[];
24
- readonly tokenCount?: number;
25
24
  readonly providerReplay?: ProviderReplay;
26
25
  }
27
26
  export interface CanonicalRenderedContext {
@@ -145,14 +145,9 @@ function normalizeMessage(message, replayForMessage) {
145
145
  if (part.type === "text")
146
146
  return { type: "text", text: part.text };
147
147
  if (part.type === "image") {
148
- if ((part.url === undefined) === (part.data === undefined)) {
149
- throw new ContentValidationError("image requires exactly one of url or data");
150
- }
151
148
  return {
152
149
  type: "image",
153
- source: part.data !== undefined
154
- ? { kind: "base64", data: part.data }
155
- : { kind: "url", url: part.url },
150
+ source: part.source,
156
151
  ...(part.mediaType ? { mediaType: part.mediaType } : {}),
157
152
  ...(part.detail ? { providerOptions: { openai_detail: part.detail } } : {}),
158
153
  };
@@ -160,7 +155,7 @@ function normalizeMessage(message, replayForMessage) {
160
155
  if (part.type === "audio") {
161
156
  return {
162
157
  type: "audio",
163
- source: { kind: "base64", data: part.data },
158
+ source: part.source,
164
159
  mediaType: part.mediaType,
165
160
  };
166
161
  }
@@ -171,7 +166,6 @@ function normalizeMessage(message, replayForMessage) {
171
166
  blocks,
172
167
  contentForm: message.contentParts === undefined ? "text" : "blocks",
173
168
  ...(message.toolCalls ? { toolCalls: message.toolCalls } : {}),
174
- ...(message.tokenCount !== undefined ? { tokenCount: message.tokenCount } : {}),
175
169
  ...(providerReplay ? { providerReplay } : {}),
176
170
  };
177
171
  }
@@ -171,15 +171,10 @@ export class GeminiAdapter {
171
171
  }
172
172
  decodeComplete(raw, _input) {
173
173
  const decoded = decodeParts(raw);
174
- const usage = this.normalizeUsage(raw.usageMetadata);
175
- const rawUsage = raw.usageMetadata;
176
- const tokenCount = usage?.outputTokens
177
- ?? (rawUsage ? numberField(rawUsage, "totalTokenCount") : undefined);
178
174
  return {
179
175
  message: {
180
176
  role: "assistant",
181
177
  content: decoded.content,
182
- ...(tokenCount !== undefined ? { tokenCount } : {}),
183
178
  toolCalls: decoded.toolCalls,
184
179
  },
185
180
  };
@@ -235,9 +235,6 @@ export class OpenAIChatAdapter {
235
235
  const message = {
236
236
  role: "assistant",
237
237
  content,
238
- ...(usage ? {
239
- tokenCount: numberField(usage, "completion_tokens") ?? numberField(usage, "total_tokens"),
240
- } : {}),
241
238
  toolCalls,
242
239
  };
243
240
  const replay = replayForTurn(dialect, "complete", input.input.resolved.identity.modelId, content, toolCalls, {
@@ -191,15 +191,11 @@ export class OpenAIResponsesAdapter {
191
191
  : undefined;
192
192
  if (usage)
193
193
  this.normalizeUsage(usage);
194
- const tokenCount = usage
195
- ? numberField(usage, "output_tokens") ?? numberField(usage, "total_tokens")
196
- : undefined;
197
194
  return {
198
195
  message: {
199
196
  role: "assistant",
200
197
  content: decoded.content,
201
198
  toolCalls: decoded.toolCalls,
202
- ...(tokenCount !== undefined ? { tokenCount } : {}),
203
199
  },
204
200
  };
205
201
  }
@@ -77,9 +77,6 @@ export function messageToKernelMessage(message) {
77
77
  arguments: tryParseJson(tc.arguments) ?? {},
78
78
  })),
79
79
  };
80
- if (message.tokenCount !== undefined) {
81
- out.token_count = message.tokenCount;
82
- }
83
80
  if (message.contentParts && message.contentParts.length > 0) {
84
81
  out.content = message.contentParts.map(part => {
85
82
  if (part.type === "text")
@@ -95,14 +92,13 @@ export function messageToKernelMessage(message) {
95
92
  if (part.type === "image") {
96
93
  return {
97
94
  type: "image",
98
- url: part.url,
99
- data: part.data,
95
+ source: part.source,
100
96
  media_type: part.mediaType,
101
97
  detail: part.detail,
102
98
  };
103
99
  }
104
100
  if (part.type === "audio") {
105
- return { type: "audio", data: part.data, media_type: part.mediaType };
101
+ return { type: "audio", source: part.source, media_type: part.mediaType };
106
102
  }
107
103
  return { type: "text", text: message.content };
108
104
  });
@@ -113,12 +109,12 @@ export function messageToKernelMessage(message) {
113
109
  return out;
114
110
  }
115
111
  export function toolResultToKernel(result) {
112
+ // Usage evidence enters through the host event contract; content alone cannot establish usage.
116
113
  const out = {
117
114
  call_id: result.callId,
118
115
  output: result.output,
119
116
  is_error: result.isError,
120
117
  is_fatal: result.isFatal ?? false,
121
- token_count: result.tokenCount ?? null,
122
118
  };
123
119
  if (result.errorKind !== undefined) {
124
120
  out.error_kind = result.errorKind;
@@ -212,9 +208,6 @@ export function kernelMessageToSdk(raw) {
212
208
  arguments: JSON.stringify(tc.arguments ?? {}),
213
209
  })),
214
210
  };
215
- if (typeof (raw.tokens ?? raw.token_count) === "number") {
216
- message.tokenCount = Number(raw.tokens ?? raw.token_count);
217
- }
218
211
  if (structuredContent) {
219
212
  message.contentParts = structuredContent
220
213
  .filter((part) => typeof part === "object" && part !== null)
@@ -233,8 +226,7 @@ export function kernelMessageToSdk(raw) {
233
226
  if (part.type === "image") {
234
227
  return {
235
228
  type: "image",
236
- url: part.url,
237
- data: part.data,
229
+ source: part.source,
238
230
  mediaType: part.media_type,
239
231
  detail: part.detail,
240
232
  };
@@ -242,7 +234,7 @@ export function kernelMessageToSdk(raw) {
242
234
  if (part.type === "audio") {
243
235
  return {
244
236
  type: "audio",
245
- data: String(part.data ?? ""),
237
+ source: part.source,
246
238
  mediaType: String(part.media_type ?? "audio/wav"),
247
239
  };
248
240
  }
@@ -6,6 +6,15 @@ import type { CredentialVault } from "./credential-vault.js";
6
6
  export interface McpContentBlock {
7
7
  type: string;
8
8
  text?: string;
9
+ source?: {
10
+ kind?: string;
11
+ url?: string;
12
+ data?: string;
13
+ id?: string;
14
+ handle?: string;
15
+ owner?: string;
16
+ payloadRef?: string;
17
+ };
9
18
  data?: string;
10
19
  mimeType?: string;
11
20
  }
@@ -29,10 +29,16 @@ export function mcpResultToToolOutput(result) {
29
29
  if (c.type === "text")
30
30
  return { type: "text", text: c.text ?? "" };
31
31
  if (c.type === "image") {
32
- return { type: "image", source: { kind: "base64", data: c.data ?? "" }, mediaType: c.mimeType };
32
+ const source = c.source?.kind === "url" && c.source.url
33
+ ? { kind: "url", url: c.source.url }
34
+ : { kind: "base64", data: c.source?.data ?? c.data ?? "" };
35
+ return { type: "image", source, mediaType: c.mimeType };
33
36
  }
34
37
  if (c.type === "audio") {
35
- return { type: "audio", source: { kind: "base64", data: c.data ?? "" }, mediaType: c.mimeType ?? "audio/wav" };
38
+ const source = c.source?.kind === "url" && c.source.url
39
+ ? { kind: "url", url: c.source.url }
40
+ : { kind: "base64", data: c.source?.data ?? c.data ?? "" };
41
+ return { type: "audio", source, mediaType: c.mimeType ?? "audio/wav" };
36
42
  }
37
43
  return { type: "text", text: JSON.stringify(c) };
38
44
  });
@@ -15,8 +15,8 @@ import type { SessionEvent } from "./session-log.js";
15
15
  * one Message per event. Pass the result directly to `new ReplayProvider(messages)`.
16
16
  *
17
17
  * Accepts both wire shapes the SDK uses interchangeably:
18
- * - in-memory: `{ toolCalls, tokenCount, providerReplay }` (camelCase)
19
- * - serialised session-log: `{ tool_calls, token_count, provider_replay }` (snake_case)
18
+ * - in-memory: `{ toolCalls, providerReplay }` (camelCase)
19
+ * - serialised session-log: `{ tool_calls, token_count, provider_replay }` (snake_case; token_count is wire evidence)
20
20
  *
21
21
  * @param events Session events, in original order. Accepts both `{ event, seq }` (the shape
22
22
  * `SessionLog.read()` returns) and a bare `SessionEvent[]`.
@@ -13,8 +13,8 @@
13
13
  * one Message per event. Pass the result directly to `new ReplayProvider(messages)`.
14
14
  *
15
15
  * Accepts both wire shapes the SDK uses interchangeably:
16
- * - in-memory: `{ toolCalls, tokenCount, providerReplay }` (camelCase)
17
- * - serialised session-log: `{ tool_calls, token_count, provider_replay }` (snake_case)
16
+ * - in-memory: `{ toolCalls, providerReplay }` (camelCase)
17
+ * - serialised session-log: `{ tool_calls, token_count, provider_replay }` (snake_case; token_count is wire evidence)
18
18
  *
19
19
  * @param events Session events, in original order. Accepts both `{ event, seq }` (the shape
20
20
  * `SessionLog.read()` returns) and a bare `SessionEvent[]`.
@@ -27,14 +27,12 @@ export function extractRecordedMessages(events) {
27
27
  continue;
28
28
  const e = event;
29
29
  const tcRaw = (e.toolCalls ?? e.tool_calls);
30
- const tokenCount = (e.tokenCount ?? e.token_count);
31
30
  out.push({
32
31
  role: "assistant",
33
32
  content: typeof e.content === "string" ? e.content : "",
34
33
  ...(Array.isArray(tcRaw) && tcRaw.length > 0
35
34
  ? { toolCalls: normalizeToolCalls(tcRaw) }
36
35
  : {}),
37
- ...(tokenCount !== undefined ? { tokenCount } : {}),
38
36
  });
39
37
  }
40
38
  return out;
@@ -16,8 +16,8 @@
16
16
  * - `inputTokens` is ESTIMATED from the rendered context this call carries (NOT a recorded value
17
17
  * from the original run). That's the point of replay-for-benchmarking: prompt may differ across
18
18
  * variants, response is pinned, so a cost Δ purely reflects the prompt change.
19
- * - `outputTokens` is taken from `message.tokenCount` when present; otherwise estimated from
20
- * `message.content.length / 4`.
19
+ * - `outputTokens` is estimated from `message.content.length / 4`; provider usage belongs to
20
+ * the session measurement plane, never to the public Message mirror.
21
21
  * - `cacheReadInputTokens` / `cacheCreationInputTokens` are emitted as 0 — replay has no real
22
22
  * cache state. Mechanisms whose Δ depends on cache behavior must validate with a live A/B too.
23
23
  *
@@ -16,8 +16,8 @@
16
16
  * - `inputTokens` is ESTIMATED from the rendered context this call carries (NOT a recorded value
17
17
  * from the original run). That's the point of replay-for-benchmarking: prompt may differ across
18
18
  * variants, response is pinned, so a cost Δ purely reflects the prompt change.
19
- * - `outputTokens` is taken from `message.tokenCount` when present; otherwise estimated from
20
- * `message.content.length / 4`.
19
+ * - `outputTokens` is estimated from `message.content.length / 4`; provider usage belongs to
20
+ * the session measurement plane, never to the public Message mirror.
21
21
  * - `cacheReadInputTokens` / `cacheCreationInputTokens` are emitted as 0 — replay has no real
22
22
  * cache state. Mechanisms whose Δ depends on cache behavior must validate with a live A/B too.
23
23
  *
@@ -68,13 +68,12 @@ export class ReplayProvider {
68
68
  role: "assistant",
69
69
  content: msg.content,
70
70
  ...(msg.toolCalls ? { toolCalls: msg.toolCalls } : {}),
71
- ...(msg.tokenCount !== undefined ? { tokenCount: msg.tokenCount } : {}),
72
71
  };
73
72
  }
74
73
  async *stream(context, tools, _extensions, _state, _signal) {
75
74
  const msg = this.pull();
76
75
  const inputTokens = this.estimateInputTokens(context, tools);
77
- const outputTokens = msg.tokenCount !== undefined ? msg.tokenCount : this.tokenizer(msg.content || "");
76
+ const outputTokens = this.tokenizer(msg.content || "");
78
77
  const usage = {
79
78
  type: "usage",
80
79
  totalTokens: inputTokens + outputTokens,
@@ -1869,7 +1869,6 @@ export class RuntimeRunner {
1869
1869
  role: "assistant",
1870
1870
  content: finalText,
1871
1871
  toolCalls: canonicalToolCalls,
1872
- tokenCount: turnOutputTokens || turnTokens || undefined,
1873
1872
  };
1874
1873
  // P4 §2: assemble the measurement from the exact numbers that cross the boundary today
1875
1874
  // (inputTokens/outputTokens turn counters), enriched with the raw provider frame's cache
@@ -2325,7 +2324,6 @@ export class RuntimeRunner {
2325
2324
  call_id: r.callId,
2326
2325
  output: r.output,
2327
2326
  is_error: r.isError,
2328
- token_count: r.tokenCount,
2329
2327
  content: { blocks: toolOutputBlocksToDurable(r.contentParts?.length ? r.contentParts : [{ type: "text", text: r.output }]) },
2330
2328
  })),
2331
2329
  effect_id: toolEffectId,
@@ -2520,7 +2518,6 @@ export class RuntimeRunner {
2520
2518
  role: m.role,
2521
2519
  content: m.content,
2522
2520
  contentParts: m.contentParts,
2523
- tokenCount: m.tokenCount,
2524
2521
  toolCalls: m.toolCalls?.length ? m.toolCalls : undefined,
2525
2522
  }));
2526
2523
  if (newMsgs.length > 0) {
@@ -2815,14 +2812,13 @@ function attachmentsToKernelMessage(parts) {
2815
2812
  if (p.type === "image") {
2816
2813
  return {
2817
2814
  type: "image",
2818
- ...(p.url ? { url: p.url } : {}),
2819
- ...(p.data ? { data: p.data } : {}),
2815
+ source: p.source,
2820
2816
  ...(p.mediaType ? { media_type: p.mediaType } : {}),
2821
2817
  ...(p.detail ? { detail: p.detail } : {}),
2822
2818
  };
2823
2819
  }
2824
2820
  if (p.type === "audio")
2825
- return { type: "audio", data: p.data, media_type: p.mediaType };
2821
+ return { type: "audio", source: p.source, media_type: p.mediaType };
2826
2822
  if (p.type === "text")
2827
2823
  return { type: "text", text: p.text };
2828
2824
  return { type: "text", text: "" };
@@ -2886,7 +2882,6 @@ export function pairOrphanToolCalls(messages) {
2886
2882
  content: "",
2887
2883
  toolCalls: [],
2888
2884
  contentParts: [{ type: "tool_result", callId: c.id, output: `[${c.name} handled by kernel]`, isError: false }],
2889
- tokenCount: 1,
2890
2885
  });
2891
2886
  }
2892
2887
  }
@@ -2917,7 +2912,6 @@ export function replayMessages(events, maxBytes) {
2917
2912
  content: userText,
2918
2913
  ...(contentParts ? { contentParts } : {}),
2919
2914
  toolCalls: [],
2920
- tokenCount: Math.max(1, Math.ceil(userText.length / 4)),
2921
2915
  });
2922
2916
  }
2923
2917
  else if (e.kind === "compressed") {
@@ -2928,7 +2922,6 @@ export function replayMessages(events, maxBytes) {
2928
2922
  role: "system",
2929
2923
  content: systemText,
2930
2924
  toolCalls: [],
2931
- tokenCount: Math.max(1, Math.ceil(systemText.length / 4)),
2932
2925
  });
2933
2926
  }
2934
2927
  }
@@ -2937,7 +2930,6 @@ export function replayMessages(events, maxBytes) {
2937
2930
  role: "assistant",
2938
2931
  content: sanitizeReplayText(e.content, maxBytes),
2939
2932
  toolCalls: e.tool_calls ?? [],
2940
- tokenCount: e.token_count,
2941
2933
  });
2942
2934
  }
2943
2935
  else if (e.kind === "tool_completed") {
@@ -2952,7 +2944,6 @@ export function replayMessages(events, maxBytes) {
2952
2944
  content: "",
2953
2945
  toolCalls: [],
2954
2946
  contentParts: [{ type: "tool_result", callId: durable.call_id, output: sanitizeReplayText(r.output, maxBytes), isError: durable.is_error, ...(durable.blocks.length ? { contentParts: durableBlocksToToolOutput(durable.blocks) } : {}) }],
2955
- tokenCount: r.token_count,
2956
2947
  });
2957
2948
  }
2958
2949
  }
@@ -2990,7 +2981,6 @@ export async function replayMessagesAsync(events, maxBytes, loadArchive) {
2990
2981
  content: userText,
2991
2982
  ...(contentParts ? { contentParts } : {}),
2992
2983
  toolCalls: [],
2993
- tokenCount: Math.max(1, Math.ceil(userText.length / 4)),
2994
2984
  });
2995
2985
  }
2996
2986
  else if (e.kind === "compressed") {
@@ -3006,7 +2996,6 @@ export async function replayMessagesAsync(events, maxBytes, loadArchive) {
3006
2996
  role: "system",
3007
2997
  content: systemText,
3008
2998
  toolCalls: [],
3009
- tokenCount: Math.max(1, Math.ceil(systemText.length / 4)),
3010
2999
  });
3011
3000
  }
3012
3001
  }
@@ -3019,7 +3008,6 @@ export async function replayMessagesAsync(events, maxBytes, loadArchive) {
3019
3008
  role: msg.role,
3020
3009
  content: sanitizeReplayText(msg.content, maxBytes),
3021
3010
  toolCalls: msg.toolCalls ?? [],
3022
- tokenCount: msg.tokenCount,
3023
3011
  });
3024
3012
  }
3025
3013
  }
@@ -3030,7 +3018,6 @@ export async function replayMessagesAsync(events, maxBytes, loadArchive) {
3030
3018
  role: "system",
3031
3019
  content: systemText,
3032
3020
  toolCalls: [],
3033
- tokenCount: Math.max(1, Math.ceil(systemText.length / 4)),
3034
3021
  });
3035
3022
  }
3036
3023
  }
@@ -3040,7 +3027,6 @@ export async function replayMessagesAsync(events, maxBytes, loadArchive) {
3040
3027
  role: "assistant",
3041
3028
  content: sanitizeReplayText(e.content, maxBytes),
3042
3029
  toolCalls: e.tool_calls ?? [],
3043
- tokenCount: e.token_count,
3044
3030
  });
3045
3031
  }
3046
3032
  else if (e.kind === "tool_completed") {
@@ -3055,7 +3041,6 @@ export async function replayMessagesAsync(events, maxBytes, loadArchive) {
3055
3041
  content: "",
3056
3042
  toolCalls: [],
3057
3043
  contentParts: [{ type: "tool_result", callId: durable.call_id, output: sanitizeReplayText(r.output, maxBytes), isError: durable.is_error, ...(durable.blocks.length ? { contentParts: durableBlocksToToolOutput(durable.blocks) } : {}) }],
3058
- tokenCount: r.token_count,
3059
3044
  });
3060
3045
  }
3061
3046
  }
@@ -5,7 +5,7 @@ export { REPLAY_CONTENT_MAX_BYTES as RECOVERY_CONTENT_MAX_BYTES } from "./replay
5
5
  /**
6
6
  * Normalize a persisted llm_completed event for recovery.
7
7
  *
8
- * Content is sanitized and token_count backfilled, but the stored
8
+ * Content is sanitized while any existing token_count remains raw evidence, but the stored
9
9
  * `provider_replay` envelope is passed through verbatim — this layer is
10
10
  * provider-neutral and must never synthesize protocol-specific replay shapes
11
11
  * (e.g. Anthropic `native_blocks`). Canonical replay seeding for a given protocol
@@ -1,12 +1,9 @@
1
1
  import { sanitizeReplayText } from "./replay-sanitize.js";
2
2
  export { REPLAY_CONTENT_MAX_BYTES as RECOVERY_CONTENT_MAX_BYTES } from "./replay-sanitize.js";
3
- function estimateTokenCount(text) {
4
- return Math.max(1, Math.ceil(text.length / 4));
5
- }
6
3
  /**
7
4
  * Normalize a persisted llm_completed event for recovery.
8
5
  *
9
- * Content is sanitized and token_count backfilled, but the stored
6
+ * Content is sanitized while any existing token_count remains raw evidence, but the stored
10
7
  * `provider_replay` envelope is passed through verbatim — this layer is
11
8
  * provider-neutral and must never synthesize protocol-specific replay shapes
12
9
  * (e.g. Anthropic `native_blocks`). Canonical replay seeding for a given protocol
@@ -25,7 +22,7 @@ export function normalizeLlmCompleted(event, maxBytes) {
25
22
  turn: event.turn,
26
23
  content,
27
24
  tool_calls: toolCalls,
28
- token_count: event.token_count ?? estimateTokenCount(content),
25
+ ...(event.token_count !== undefined ? { token_count: event.token_count } : {}),
29
26
  ...(providerReplay ? { provider_replay: providerReplay } : {}),
30
27
  ...(event.effect_id !== undefined ? { effect_id: event.effect_id } : {}),
31
28
  ...(event.invocation_id !== undefined ? { invocation_id: event.invocation_id } : {}),
@@ -235,7 +235,6 @@ export interface KernelWorkflowNodeOutcome {
235
235
  name: string;
236
236
  arguments?: Record<string, unknown>;
237
237
  }>;
238
- token_count?: number;
239
238
  };
240
239
  }
241
240
  export interface WorkflowNodeOutcome {
@@ -93,7 +93,6 @@ export function subAgentResultToKernel(result) {
93
93
  name: tc.name,
94
94
  arguments: safeParseToolArgs(tc.arguments),
95
95
  })),
96
- ...(finalMessage.tokenCount !== undefined ? { token_count: finalMessage.tokenCount } : {}),
97
96
  }
98
97
  : null,
99
98
  turns_used: result.result.turnsUsed,
@@ -171,7 +170,6 @@ export function workflowNodeOutcomeFromKernel(raw) {
171
170
  name: call.name,
172
171
  arguments: JSON.stringify(call.arguments ?? {}),
173
172
  })),
174
- ...(output.token_count != null ? { tokenCount: output.token_count } : {}),
175
173
  },
176
174
  }
177
175
  : {}),
package/dist/types.d.ts CHANGED
@@ -5,20 +5,14 @@ export interface TextPart {
5
5
  }
6
6
  export interface ImagePart {
7
7
  type: "image";
8
- /** Remote image URL (mutually exclusive with `data`). */
9
- url?: string;
10
- /** Raw base64-encoded image bytes (mutually exclusive with `url`). */
11
- data?: string;
12
- /** MIME type, e.g. `"image/png"`. Required when `data` is set. */
8
+ source: MediaSource;
13
9
  mediaType?: string;
14
10
  /** OpenAI vision detail level. */
15
11
  detail?: "auto" | "low" | "high";
16
12
  }
17
13
  export interface AudioPart {
18
14
  type: "audio";
19
- /** Raw base64-encoded audio bytes. */
20
- data: string;
21
- /** MIME type, e.g. `"audio/wav"`. */
15
+ source: MediaSource;
22
16
  mediaType: string;
23
17
  }
24
18
  export interface ToolResultPart {
@@ -34,8 +28,8 @@ export type ContentPart = TextPart | ImagePart | AudioPart | ToolResultPart;
34
28
  /**
35
29
  * spc_011-B-05: canonical multimodal content, additive alongside `ContentPart` during the
36
30
  * migration. `ContentBlockImage`/`ContentBlockAudio`/etc.
37
- * are distinctly named (not reusing `ImagePart`/`AudioPart`) since those names are already taken
38
- * by `ContentPart`'s variants with a different shape (`url?/data?` inline vs `source: MediaSource`).
31
+ * are distinctly named (not reusing `ImagePart`/`AudioPart`) because they are tool-output blocks
32
+ * with provider options and a durable `source` mirror.
39
33
  */
40
34
  export type MediaSource = {
41
35
  kind: "url";
@@ -96,7 +90,6 @@ export interface Message {
96
90
  content: string;
97
91
  /** Structured multimodal content. When present, takes precedence over `content` for provider calls. */
98
92
  contentParts?: ContentPart[];
99
- tokenCount?: number;
100
93
  toolCalls?: ToolCall[];
101
94
  }
102
95
  export interface ToolCall {
@@ -111,7 +104,6 @@ export interface ToolResult {
111
104
  isError: boolean;
112
105
  isFatal?: boolean;
113
106
  errorKind?: ToolErrorKind;
114
- tokenCount?: number;
115
107
  /** spc_012-N-01: same additive contract as `ToolResultPart.contentParts` (see there). */
116
108
  contentParts?: ToolOutputBlock[];
117
109
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepstrike/sdk",
3
- "version": "0.2.65",
3
+ "version": "0.2.68",
4
4
  "description": "DeepStrike Node.js SDK",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",
@@ -73,7 +73,7 @@
73
73
  },
74
74
  "dependencies": {
75
75
  "@anthropic-ai/sdk": "^0.99.0",
76
- "@deepstrike/core": "0.2.65",
76
+ "@deepstrike/core": "0.2.68",
77
77
  "@google/generative-ai": "^0.24.1",
78
78
  "openai": "^7.5.0"
79
79
  },