ai-sdk-provider-claude-code 3.3.3 → 3.3.5
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 +117 -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 +117 -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
|
}
|
|
@@ -1328,7 +1420,8 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
|
|
|
1328
1420
|
messagesPrompt,
|
|
1329
1421
|
outputStreamEnded,
|
|
1330
1422
|
effectiveResume,
|
|
1331
|
-
streamingContentParts
|
|
1423
|
+
streamingContentParts,
|
|
1424
|
+
this.settings.onStreamStart
|
|
1332
1425
|
) : messagesPrompt;
|
|
1333
1426
|
this.logger.debug(
|
|
1334
1427
|
`[claude-code] Executing query with streamingInput: ${wantsStreamInput}, session: ${effectiveResume ?? "new"}`
|
|
@@ -1563,7 +1656,8 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
|
|
|
1563
1656
|
messagesPrompt,
|
|
1564
1657
|
outputStreamEnded,
|
|
1565
1658
|
effectiveResume,
|
|
1566
|
-
streamingContentParts
|
|
1659
|
+
streamingContentParts,
|
|
1660
|
+
this.settings.onStreamStart
|
|
1567
1661
|
) : messagesPrompt;
|
|
1568
1662
|
this.logger.debug(
|
|
1569
1663
|
`[claude-code] Starting stream query with streamingInput: ${wantsStreamInput}, session: ${effectiveResume ?? "new"}`
|
|
@@ -1938,6 +2032,23 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
|
|
|
1938
2032
|
);
|
|
1939
2033
|
continue;
|
|
1940
2034
|
}
|
|
2035
|
+
if (textPartId) {
|
|
2036
|
+
const closedTextId = textPartId;
|
|
2037
|
+
controller.enqueue({
|
|
2038
|
+
type: "text-end",
|
|
2039
|
+
id: closedTextId
|
|
2040
|
+
});
|
|
2041
|
+
textPartId = void 0;
|
|
2042
|
+
for (const [blockIndex, blockTextId] of textBlocksByIndex) {
|
|
2043
|
+
if (blockTextId === closedTextId) {
|
|
2044
|
+
textBlocksByIndex.delete(blockIndex);
|
|
2045
|
+
break;
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
accumulatedText = "";
|
|
2049
|
+
streamedTextLength = 0;
|
|
2050
|
+
this.logger.debug("[claude-code] Closed text part due to user message");
|
|
2051
|
+
}
|
|
1941
2052
|
const sdkParentToolUseIdForResults = message.parent_tool_use_id;
|
|
1942
2053
|
const content = message.message.content;
|
|
1943
2054
|
for (const result of this.extractToolResults(content)) {
|