ai-sdk-provider-claude-code 3.3.2 → 3.3.4
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.
- package/README.md +62 -0
- package/dist/index.cjs +106 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +58 -1
- package/dist/index.d.ts +58 -1
- package/dist/index.js +106 -6
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -310,6 +310,68 @@ const model = claudeCode('sonnet', {
|
|
|
310
310
|
});
|
|
311
311
|
```
|
|
312
312
|
|
|
313
|
+
## Mid-Session Message Injection
|
|
314
|
+
|
|
315
|
+
This provider supports **mid-session message injection** for supervisor patterns, allowing you to interrupt, redirect, or provide feedback to an agent during execution.
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
import { streamText } from 'ai';
|
|
319
|
+
import { claudeCode, type MessageInjector } from 'ai-sdk-provider-claude-code';
|
|
320
|
+
|
|
321
|
+
let injector: MessageInjector | null = null;
|
|
322
|
+
|
|
323
|
+
const result = streamText({
|
|
324
|
+
model: claudeCode('haiku', {
|
|
325
|
+
streamingInput: 'always', // Required for injection
|
|
326
|
+
onStreamStart: (inj) => {
|
|
327
|
+
injector = inj;
|
|
328
|
+
|
|
329
|
+
// Example: Inject after 5 seconds
|
|
330
|
+
setTimeout(() => {
|
|
331
|
+
injector?.inject('STOP! Change of plans - do something else.');
|
|
332
|
+
}, 5000);
|
|
333
|
+
},
|
|
334
|
+
}),
|
|
335
|
+
prompt: 'Write 10 files with poems...',
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
for await (const chunk of result.textStream) {
|
|
339
|
+
process.stdout.write(chunk);
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
**Requirements:**
|
|
344
|
+
|
|
345
|
+
- `streamingInput: 'always'` or `'auto'` with `canUseTool` set
|
|
346
|
+
- Messages injected via `inject(content)` are delivered to the agent mid-turn
|
|
347
|
+
|
|
348
|
+
**Important:** Injection works between tool calls, not during continuous text generation. Use tasks that involve tool usage (file operations, bash commands, etc.) for effective mid-turn interruption.
|
|
349
|
+
|
|
350
|
+
**Use Cases:**
|
|
351
|
+
|
|
352
|
+
- Stop an agent mid-task
|
|
353
|
+
- Redirect to a different goal
|
|
354
|
+
- Provide real-time feedback
|
|
355
|
+
- Implement human-in-the-loop approval workflows
|
|
356
|
+
|
|
357
|
+
**API:**
|
|
358
|
+
|
|
359
|
+
- `inject(content: string, onResult?: (delivered: boolean) => void)` - Inject a user message. Optional callback reports delivery status.
|
|
360
|
+
- `close()` - Signal no more messages will be injected
|
|
361
|
+
|
|
362
|
+
**Delivery Tracking:**
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
injector.inject('STOP!', (delivered) => {
|
|
366
|
+
if (!delivered) {
|
|
367
|
+
// Session ended before message was delivered
|
|
368
|
+
// Handle retry via session resume, etc.
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
See [examples/message-injection.ts](examples/message-injection.ts) for complete examples including conditional injection and supervisor approval patterns.
|
|
374
|
+
|
|
313
375
|
## Image Inputs (Streaming Only)
|
|
314
376
|
|
|
315
377
|
- Enable streaming input (`streamingInput: 'always'` or provide `canUseTool`) before sending images.
|
package/dist/index.cjs
CHANGED
|
@@ -572,6 +572,9 @@ var claudeCodeSettingsSchema = import_zod.z.object({
|
|
|
572
572
|
// Callback invoked when Query object is created - for mid-stream injection via streamInput()
|
|
573
573
|
onQueryCreated: import_zod.z.any().refine((val) => val === void 0 || typeof val === "function", {
|
|
574
574
|
message: "onQueryCreated must be a function"
|
|
575
|
+
}).optional(),
|
|
576
|
+
onStreamStart: import_zod.z.any().refine((val) => val === void 0 || typeof val === "function", {
|
|
577
|
+
message: "onStreamStart must be a function"
|
|
575
578
|
}).optional()
|
|
576
579
|
}).strict();
|
|
577
580
|
function validateModelId(modelId) {
|
|
@@ -806,9 +809,66 @@ function convertClaudeCodeUsage(usage) {
|
|
|
806
809
|
raw: usage
|
|
807
810
|
};
|
|
808
811
|
}
|
|
809
|
-
function
|
|
812
|
+
function createMessageInjector() {
|
|
813
|
+
const queue = [];
|
|
814
|
+
let closed = false;
|
|
815
|
+
let resolver = null;
|
|
816
|
+
const injector = {
|
|
817
|
+
inject(content, onResult) {
|
|
818
|
+
if (closed) {
|
|
819
|
+
onResult?.(false);
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
const item = { content, onResult };
|
|
823
|
+
if (resolver) {
|
|
824
|
+
const r = resolver;
|
|
825
|
+
resolver = null;
|
|
826
|
+
r(item);
|
|
827
|
+
} else {
|
|
828
|
+
queue.push(item);
|
|
829
|
+
}
|
|
830
|
+
},
|
|
831
|
+
close() {
|
|
832
|
+
closed = true;
|
|
833
|
+
if (resolver && queue.length === 0) {
|
|
834
|
+
resolver(null);
|
|
835
|
+
resolver = null;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
const getNextItem = () => {
|
|
840
|
+
if (queue.length > 0) {
|
|
841
|
+
const item = queue.shift();
|
|
842
|
+
if (!item) {
|
|
843
|
+
return Promise.resolve(null);
|
|
844
|
+
}
|
|
845
|
+
return Promise.resolve(item);
|
|
846
|
+
}
|
|
847
|
+
if (closed) {
|
|
848
|
+
return Promise.resolve(null);
|
|
849
|
+
}
|
|
850
|
+
return new Promise((resolve) => {
|
|
851
|
+
resolver = (item) => {
|
|
852
|
+
resolve(item);
|
|
853
|
+
};
|
|
854
|
+
});
|
|
855
|
+
};
|
|
856
|
+
const notifySessionEnded = () => {
|
|
857
|
+
for (const item of queue) {
|
|
858
|
+
item.onResult?.(false);
|
|
859
|
+
}
|
|
860
|
+
queue.length = 0;
|
|
861
|
+
closed = true;
|
|
862
|
+
if (resolver) {
|
|
863
|
+
resolver(null);
|
|
864
|
+
resolver = null;
|
|
865
|
+
}
|
|
866
|
+
};
|
|
867
|
+
return { injector, getNextItem, notifySessionEnded };
|
|
868
|
+
}
|
|
869
|
+
function toAsyncIterablePrompt(messagesPrompt, outputStreamEnded, sessionId, contentParts, onStreamStart) {
|
|
810
870
|
const content = contentParts && contentParts.length > 0 ? contentParts : [{ type: "text", text: messagesPrompt }];
|
|
811
|
-
const
|
|
871
|
+
const initialMsg = {
|
|
812
872
|
type: "user",
|
|
813
873
|
message: {
|
|
814
874
|
role: "user",
|
|
@@ -817,10 +877,42 @@ function toAsyncIterablePrompt(messagesPrompt, outputStreamEnded, sessionId, con
|
|
|
817
877
|
parent_tool_use_id: null,
|
|
818
878
|
session_id: sessionId ?? ""
|
|
819
879
|
};
|
|
880
|
+
if (!onStreamStart) {
|
|
881
|
+
return {
|
|
882
|
+
async *[Symbol.asyncIterator]() {
|
|
883
|
+
yield initialMsg;
|
|
884
|
+
await outputStreamEnded;
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
const { injector, getNextItem, notifySessionEnded } = createMessageInjector();
|
|
820
889
|
return {
|
|
821
890
|
async *[Symbol.asyncIterator]() {
|
|
822
|
-
yield
|
|
823
|
-
|
|
891
|
+
yield initialMsg;
|
|
892
|
+
onStreamStart(injector);
|
|
893
|
+
let streamEnded = false;
|
|
894
|
+
void outputStreamEnded.then(() => {
|
|
895
|
+
streamEnded = true;
|
|
896
|
+
notifySessionEnded();
|
|
897
|
+
});
|
|
898
|
+
while (!streamEnded) {
|
|
899
|
+
const item = await Promise.race([getNextItem(), outputStreamEnded.then(() => null)]);
|
|
900
|
+
if (item === null) {
|
|
901
|
+
await outputStreamEnded;
|
|
902
|
+
break;
|
|
903
|
+
}
|
|
904
|
+
const sdkMsg = {
|
|
905
|
+
type: "user",
|
|
906
|
+
message: {
|
|
907
|
+
role: "user",
|
|
908
|
+
content: [{ type: "text", text: item.content }]
|
|
909
|
+
},
|
|
910
|
+
parent_tool_use_id: null,
|
|
911
|
+
session_id: sessionId ?? ""
|
|
912
|
+
};
|
|
913
|
+
yield sdkMsg;
|
|
914
|
+
item.onResult?.(true);
|
|
915
|
+
}
|
|
824
916
|
}
|
|
825
917
|
};
|
|
826
918
|
}
|
|
@@ -1293,6 +1385,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
|
|
|
1293
1385
|
let wasTruncated = false;
|
|
1294
1386
|
let costUsd;
|
|
1295
1387
|
let durationMs;
|
|
1388
|
+
let modelUsage;
|
|
1296
1389
|
const warnings = this.generateAllWarnings(options, messagesPrompt);
|
|
1297
1390
|
if (messageWarnings) {
|
|
1298
1391
|
messageWarnings.forEach((warning) => {
|
|
@@ -1327,7 +1420,8 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
|
|
|
1327
1420
|
messagesPrompt,
|
|
1328
1421
|
outputStreamEnded,
|
|
1329
1422
|
effectiveResume,
|
|
1330
|
-
streamingContentParts
|
|
1423
|
+
streamingContentParts,
|
|
1424
|
+
this.settings.onStreamStart
|
|
1331
1425
|
) : messagesPrompt;
|
|
1332
1426
|
this.logger.debug(
|
|
1333
1427
|
`[claude-code] Executing query with streamingInput: ${wantsStreamInput}, session: ${effectiveResume ?? "new"}`
|
|
@@ -1346,6 +1440,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
|
|
|
1346
1440
|
this.setSessionId(message.session_id);
|
|
1347
1441
|
costUsd = message.total_cost_usd;
|
|
1348
1442
|
durationMs = message.duration_ms;
|
|
1443
|
+
modelUsage = message.modelUsage;
|
|
1349
1444
|
if ("is_error" in message && message.is_error === true) {
|
|
1350
1445
|
const errorMessage = "result" in message && typeof message.result === "string" ? message.result : "Claude Code CLI returned an error";
|
|
1351
1446
|
throw Object.assign(new Error(errorMessage), { exitCode: 1 });
|
|
@@ -1421,6 +1516,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
|
|
|
1421
1516
|
...this.sessionId !== void 0 && { sessionId: this.sessionId },
|
|
1422
1517
|
...costUsd !== void 0 && { costUsd },
|
|
1423
1518
|
...durationMs !== void 0 && { durationMs },
|
|
1519
|
+
...modelUsage !== void 0 && { modelUsage },
|
|
1424
1520
|
...wasTruncated && { truncated: true }
|
|
1425
1521
|
}
|
|
1426
1522
|
}
|
|
@@ -1560,7 +1656,8 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
|
|
|
1560
1656
|
messagesPrompt,
|
|
1561
1657
|
outputStreamEnded,
|
|
1562
1658
|
effectiveResume,
|
|
1563
|
-
streamingContentParts
|
|
1659
|
+
streamingContentParts,
|
|
1660
|
+
this.settings.onStreamStart
|
|
1564
1661
|
) : messagesPrompt;
|
|
1565
1662
|
this.logger.debug(
|
|
1566
1663
|
`[claude-code] Starting stream query with streamingInput: ${wantsStreamInput}, session: ${effectiveResume ?? "new"}`
|
|
@@ -2155,6 +2252,9 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
|
|
|
2155
2252
|
costUsd: message.total_cost_usd
|
|
2156
2253
|
},
|
|
2157
2254
|
...message.duration_ms !== void 0 && { durationMs: message.duration_ms },
|
|
2255
|
+
...message.modelUsage !== void 0 && {
|
|
2256
|
+
modelUsage: message.modelUsage
|
|
2257
|
+
},
|
|
2158
2258
|
// JSON validation warnings are collected during streaming and included
|
|
2159
2259
|
// in providerMetadata since the AI SDK's finish event doesn't support
|
|
2160
2260
|
// a top-level warnings field (unlike stream-start which was already emitted)
|