@vincemakes/kiso-provider-anthropic 0.15.12 → 0.16.1

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 (2) hide show
  1. package/dist/index.js +86 -4
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -39,6 +39,13 @@ export function createAnthropicAdapter(client, adapterOpts = {}) {
39
39
  try {
40
40
  stream = client.messages.stream({
41
41
  model: options.model,
42
+ // XP-1: the resolved effort (the effort doc, read
43
+ // 2026-08-26): output_config.effort. Absent adds NO
44
+ // key. The thinking-mode WIRE is deliberately not
45
+ // serialized yet — the registry carries no sourced
46
+ // mode list for the entered models, so no valid
47
+ // selection can produce one; nothing is guessed.
48
+ ...(options.reasoning?.effort !== undefined ? { output_config: { effort: options.reasoning.effort } } : {}),
42
49
  ...(options.systemPrompt !== undefined
43
50
  ? {
44
51
  system: caching
@@ -47,7 +54,7 @@ export function createAnthropicAdapter(client, adapterOpts = {}) {
47
54
  }
48
55
  : {}),
49
56
  max_tokens: options.maxTokens ?? 4096,
50
- messages: caching ? withCacheBreakpoint(toAnthropicMessages(options.messages)) : toAnthropicMessages(options.messages),
57
+ messages: caching ? withCacheBreakpoint(toAnthropicMessages(options.messages, options.model)) : toAnthropicMessages(options.messages, options.model),
51
58
  ...(options.tools?.length ? { tools: toAnthropicTools(options.tools) } : {}),
52
59
  ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),
53
60
  },
@@ -70,6 +77,14 @@ export function createAnthropicAdapter(client, adapterOpts = {}) {
70
77
  let stopReasonSeen = false;
71
78
  let stopReason = "end_turn";
72
79
  const toolBuffer = new Map();
80
+ // MG-1 (A5): thinking-family blocks are captured WHOLE — one
81
+ // opaque entry per block, closed at its content_block_stop so
82
+ // interleaving order is preserved verbatim. The provider
83
+ // requires the complete blocks back on a tool-use continuation;
84
+ // an aggregated reasoning string cannot reconstruct them.
85
+ const thinkBuffer = new Map();
86
+ const contEntries = [];
87
+ let sawToolUse = false;
73
88
  try {
74
89
  for await (const event of stream) {
75
90
  switch (event.type) {
@@ -88,6 +103,7 @@ export function createAnthropicAdapter(client, adapterOpts = {}) {
88
103
  yield { seq: 0, type: "text_start" };
89
104
  }
90
105
  else if (block.type === "tool_use") {
106
+ sawToolUse = true;
91
107
  toolBuffer.set(event.index, { id: block.id, name: block.name, json: "" });
92
108
  yield {
93
109
  seq: 0,
@@ -96,6 +112,12 @@ export function createAnthropicAdapter(client, adapterOpts = {}) {
96
112
  name: block.name,
97
113
  };
98
114
  }
115
+ else if (block.type === "thinking") {
116
+ thinkBuffer.set(event.index, { type: "thinking", thinking: "", signature: "" });
117
+ }
118
+ else if (block.type === "redacted_thinking") {
119
+ thinkBuffer.set(event.index, { type: "redacted_thinking", data: block.data ?? "" });
120
+ }
99
121
  break;
100
122
  }
101
123
  case "content_block_delta": {
@@ -116,8 +138,18 @@ export function createAnthropicAdapter(client, adapterOpts = {}) {
116
138
  }
117
139
  }
118
140
  else if (delta.type === "thinking_delta") {
141
+ const t = thinkBuffer.get(event.index);
142
+ if (t?.type === "thinking")
143
+ t.thinking += delta.thinking;
119
144
  yield { seq: 0, type: "thinking", text: delta.thinking };
120
145
  }
146
+ else if (delta.type === "signature_delta") {
147
+ // A5: the signature is continuation material only —
148
+ // never a projection event.
149
+ const t = thinkBuffer.get(event.index);
150
+ if (t?.type === "thinking")
151
+ t.signature += delta.signature ?? "";
152
+ }
121
153
  break;
122
154
  }
123
155
  case "content_block_stop": {
@@ -139,6 +171,13 @@ export function createAnthropicAdapter(client, adapterOpts = {}) {
139
171
  input,
140
172
  };
141
173
  }
174
+ // A5: a thinking-family block closes HERE — pushed at
175
+ // its stop so interleaving order lands verbatim.
176
+ const think = thinkBuffer.get(event.index);
177
+ if (think) {
178
+ thinkBuffer.delete(event.index);
179
+ contEntries.push({ kind: "anthropic.content_block", data: JSON.stringify(think) });
180
+ }
142
181
  // Text blocks end implicitly: the next TextStart (or
143
182
  // the terminal) closes the previous block (ADR-0003).
144
183
  break;
@@ -182,7 +221,25 @@ export function createAnthropicAdapter(client, adapterOpts = {}) {
182
221
  // D3: a message_stop with NO delta means the
183
222
  // provider never reported a stop reason — that is
184
223
  // an error, never a default end_turn.
185
- yield { seq: 0, type: "stop", reason: stopReasonSeen ? stopReason : "error" };
224
+ // A5: the captured blocks ride the stop as opaque
225
+ // entries (self-reported scope; the kernel
226
+ // re-stamps it — adapters are not trusted).
227
+ // `required` follows the turn: a tool-use
228
+ // continuation is INVALID without its thinking
229
+ // blocks; elsewhere omission degrades, not 400s.
230
+ yield {
231
+ seq: 0,
232
+ type: "stop",
233
+ reason: stopReasonSeen ? stopReason : "error",
234
+ ...(contEntries.length > 0
235
+ ? {
236
+ continuation: {
237
+ scope: { providerId: "anthropic", apiId: "anthropic-messages", modelId: options.model },
238
+ entries: contEntries.map((e) => ({ ...e, required: sawToolUse })),
239
+ },
240
+ }
241
+ : {}),
242
+ };
186
243
  break;
187
244
  }
188
245
  }
@@ -240,7 +297,32 @@ function toAnthropicContent(content) {
240
297
  };
241
298
  });
242
299
  }
243
- function toAnthropicMessages(messages) {
300
+ /** A5: the stored blocks replay ONLY to the scope that produced them —
301
+ * provider AND api AND model (thinking blocks bind to the model that
302
+ * produced them; the provider requires foreign ones stripped). A
303
+ * mismatched or absent envelope replays nothing; an unparseable stored
304
+ * entry is skipped, never a crash. */
305
+ function continuationBlocks(msg, model) {
306
+ const c = msg.continuation;
307
+ if (c === undefined)
308
+ return [];
309
+ const s = c.scope;
310
+ if (s.providerId !== "anthropic" || s.apiId !== "anthropic-messages" || s.modelId !== model)
311
+ return [];
312
+ const out = [];
313
+ for (const e of c.entries) {
314
+ if (e.kind !== "anthropic.content_block")
315
+ continue;
316
+ try {
317
+ out.push(JSON.parse(e.data));
318
+ }
319
+ catch {
320
+ // skipped — opaque bytes that no longer parse must not kill the request
321
+ }
322
+ }
323
+ return out;
324
+ }
325
+ function toAnthropicMessages(messages, model) {
244
326
  const out = [];
245
327
  for (const msg of messages) {
246
328
  if (msg.role === "user") {
@@ -249,7 +331,7 @@ function toAnthropicMessages(messages) {
249
331
  else if (msg.role === "assistant") {
250
332
  out.push({
251
333
  role: "assistant",
252
- content: msg.blocks.map(toAnthropicBlock),
334
+ content: [...continuationBlocks(msg, model), ...msg.blocks.map(toAnthropicBlock)],
253
335
  });
254
336
  }
255
337
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vincemakes/kiso-provider-anthropic",
3
- "version": "0.15.12",
3
+ "version": "0.16.1",
4
4
  "description": "kiso Anthropic adapter \u2014 official SDK wire events mapped to the kiso event union.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -22,7 +22,7 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@anthropic-ai/sdk": "^0.115.0",
25
- "@vincemakes/kiso-core": "0.15.12"
25
+ "@vincemakes/kiso-core": "0.16.1"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^26.1.2",