@witqq/agent-sdk 0.1.1 → 0.3.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.
- package/README.md +55 -3
- package/dist/backends/claude.cjs +163 -8
- package/dist/backends/claude.cjs.map +1 -1
- package/dist/backends/claude.d.cts +1 -1
- package/dist/backends/claude.d.ts +1 -1
- package/dist/backends/claude.js +163 -8
- package/dist/backends/claude.js.map +1 -1
- package/dist/backends/copilot.cjs +119 -5
- package/dist/backends/copilot.cjs.map +1 -1
- package/dist/backends/copilot.d.cts +1 -1
- package/dist/backends/copilot.d.ts +1 -1
- package/dist/backends/copilot.js +119 -5
- package/dist/backends/copilot.js.map +1 -1
- package/dist/backends/vercel-ai.cjs +120 -5
- package/dist/backends/vercel-ai.cjs.map +1 -1
- package/dist/backends/vercel-ai.d.cts +1 -1
- package/dist/backends/vercel-ai.d.ts +1 -1
- package/dist/backends/vercel-ai.js +120 -5
- package/dist/backends/vercel-ai.js.map +1 -1
- package/dist/index.cjs +116 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -6
- package/dist/index.d.ts +47 -6
- package/dist/index.js +116 -4
- package/dist/index.js.map +1 -1
- package/dist/{types-DxstXhLL.d.cts → types-CBzhRrN9.d.cts} +33 -4
- package/dist/{types-DxstXhLL.d.ts → types-CBzhRrN9.d.ts} +33 -4
- package/package.json +5 -5
package/dist/backends/claude.js
CHANGED
|
@@ -52,7 +52,9 @@ var BaseAgent = class {
|
|
|
52
52
|
this.state = "running";
|
|
53
53
|
try {
|
|
54
54
|
const messages = [{ role: "user", content: prompt }];
|
|
55
|
-
|
|
55
|
+
const result = await this.executeRun(messages, options, ac.signal);
|
|
56
|
+
this.enrichAndNotifyUsage(result);
|
|
57
|
+
return result;
|
|
56
58
|
} finally {
|
|
57
59
|
this.state = "idle";
|
|
58
60
|
this.abortController = null;
|
|
@@ -64,7 +66,9 @@ var BaseAgent = class {
|
|
|
64
66
|
const ac = this.createAbortController(options?.signal);
|
|
65
67
|
this.state = "running";
|
|
66
68
|
try {
|
|
67
|
-
|
|
69
|
+
const result = await this.executeRun(messages, options, ac.signal);
|
|
70
|
+
this.enrichAndNotifyUsage(result);
|
|
71
|
+
return result;
|
|
68
72
|
} finally {
|
|
69
73
|
this.state = "idle";
|
|
70
74
|
this.abortController = null;
|
|
@@ -77,12 +81,14 @@ var BaseAgent = class {
|
|
|
77
81
|
this.state = "running";
|
|
78
82
|
try {
|
|
79
83
|
const messages = [{ role: "user", content: prompt }];
|
|
80
|
-
|
|
84
|
+
const result = await this.executeRunStructured(
|
|
81
85
|
messages,
|
|
82
86
|
schema,
|
|
83
87
|
options,
|
|
84
88
|
ac.signal
|
|
85
89
|
);
|
|
90
|
+
this.enrichAndNotifyUsage(result);
|
|
91
|
+
return result;
|
|
86
92
|
} finally {
|
|
87
93
|
this.state = "idle";
|
|
88
94
|
this.abortController = null;
|
|
@@ -95,7 +101,21 @@ var BaseAgent = class {
|
|
|
95
101
|
this.state = "streaming";
|
|
96
102
|
try {
|
|
97
103
|
const messages = [{ role: "user", content: prompt }];
|
|
98
|
-
|
|
104
|
+
const enriched = this.enrichStream(this.executeStream(messages, options, ac.signal));
|
|
105
|
+
yield* this.heartbeatStream(enriched);
|
|
106
|
+
} finally {
|
|
107
|
+
this.state = "idle";
|
|
108
|
+
this.abortController = null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async *streamWithContext(messages, options) {
|
|
112
|
+
this.guardReentrancy();
|
|
113
|
+
this.guardDisposed();
|
|
114
|
+
const ac = this.createAbortController(options?.signal);
|
|
115
|
+
this.state = "streaming";
|
|
116
|
+
try {
|
|
117
|
+
const enriched = this.enrichStream(this.executeStream(messages, options, ac.signal));
|
|
118
|
+
yield* this.heartbeatStream(enriched);
|
|
99
119
|
} finally {
|
|
100
120
|
this.state = "idle";
|
|
101
121
|
this.abortController = null;
|
|
@@ -117,6 +137,95 @@ var BaseAgent = class {
|
|
|
117
137
|
this.abort();
|
|
118
138
|
this.state = "disposed";
|
|
119
139
|
}
|
|
140
|
+
// ─── Usage Enrichment ───────────────────────────────────────────
|
|
141
|
+
/** Enrich result usage with model/backend and fire onUsage callback */
|
|
142
|
+
enrichAndNotifyUsage(result) {
|
|
143
|
+
if (result.usage) {
|
|
144
|
+
result.usage = {
|
|
145
|
+
...result.usage,
|
|
146
|
+
model: this.config.model,
|
|
147
|
+
backend: this.backendName
|
|
148
|
+
};
|
|
149
|
+
this.callOnUsage(result.usage);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/** Wrap a stream to enrich usage_update events and fire onUsage callback */
|
|
153
|
+
async *enrichStream(source) {
|
|
154
|
+
for await (const event of source) {
|
|
155
|
+
if (event.type === "usage_update") {
|
|
156
|
+
const usage = {
|
|
157
|
+
promptTokens: event.promptTokens,
|
|
158
|
+
completionTokens: event.completionTokens,
|
|
159
|
+
model: this.config.model,
|
|
160
|
+
backend: this.backendName
|
|
161
|
+
};
|
|
162
|
+
this.callOnUsage(usage);
|
|
163
|
+
yield { type: "usage_update", ...usage };
|
|
164
|
+
} else {
|
|
165
|
+
yield event;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/** Fire onUsage callback (fire-and-forget: errors logged, not propagated) */
|
|
170
|
+
callOnUsage(usage) {
|
|
171
|
+
if (!this.config.onUsage) return;
|
|
172
|
+
try {
|
|
173
|
+
this.config.onUsage(usage);
|
|
174
|
+
} catch (e) {
|
|
175
|
+
console.warn(
|
|
176
|
+
"[agent-sdk] onUsage callback error:",
|
|
177
|
+
e instanceof Error ? e.message : String(e)
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// ─── Heartbeat ───────────────────────────────────────────────
|
|
182
|
+
/** Wrap a stream to emit heartbeat events at configured intervals.
|
|
183
|
+
* When heartbeatInterval is not set, passes through directly. */
|
|
184
|
+
async *heartbeatStream(source) {
|
|
185
|
+
const interval = this.config.heartbeatInterval;
|
|
186
|
+
if (!interval || interval <= 0) {
|
|
187
|
+
yield* source;
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
191
|
+
let pendingEvent = null;
|
|
192
|
+
let heartbeatResolve = null;
|
|
193
|
+
const timer = setInterval(() => {
|
|
194
|
+
if (heartbeatResolve) {
|
|
195
|
+
const resolve = heartbeatResolve;
|
|
196
|
+
heartbeatResolve = null;
|
|
197
|
+
resolve();
|
|
198
|
+
}
|
|
199
|
+
}, interval);
|
|
200
|
+
try {
|
|
201
|
+
while (true) {
|
|
202
|
+
if (!pendingEvent) {
|
|
203
|
+
pendingEvent = iterator.next();
|
|
204
|
+
}
|
|
205
|
+
const heartbeatPromise = new Promise((resolve) => {
|
|
206
|
+
heartbeatResolve = resolve;
|
|
207
|
+
});
|
|
208
|
+
const eventDone = pendingEvent.then(
|
|
209
|
+
(r) => ({ kind: "event", result: r })
|
|
210
|
+
);
|
|
211
|
+
const heartbeatDone = heartbeatPromise.then(
|
|
212
|
+
() => ({ kind: "heartbeat" })
|
|
213
|
+
);
|
|
214
|
+
const winner = await Promise.race([eventDone, heartbeatDone]);
|
|
215
|
+
if (winner.kind === "heartbeat") {
|
|
216
|
+
yield { type: "heartbeat" };
|
|
217
|
+
} else {
|
|
218
|
+
pendingEvent = null;
|
|
219
|
+
heartbeatResolve = null;
|
|
220
|
+
if (winner.result.done) break;
|
|
221
|
+
yield winner.result.value;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
} finally {
|
|
225
|
+
clearInterval(timer);
|
|
226
|
+
heartbeatResolve = null;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
120
229
|
// ─── Guards ───────────────────────────────────────────────────
|
|
121
230
|
guardReentrancy() {
|
|
122
231
|
if (this.state === "running" || this.state === "streaming") {
|
|
@@ -154,6 +263,9 @@ var BaseAgent = class {
|
|
|
154
263
|
// src/utils/schema.ts
|
|
155
264
|
function zodToJsonSchema(schema) {
|
|
156
265
|
const schemaAny = schema;
|
|
266
|
+
if ("toJSONSchema" in schema && typeof schemaAny.toJSONSchema === "function") {
|
|
267
|
+
return schemaAny.toJSONSchema();
|
|
268
|
+
}
|
|
157
269
|
if ("jsonSchema" in schema && typeof schemaAny.jsonSchema === "function") {
|
|
158
270
|
return schemaAny.jsonSchema();
|
|
159
271
|
}
|
|
@@ -342,7 +454,31 @@ function aggregateUsage(modelUsage) {
|
|
|
342
454
|
}
|
|
343
455
|
return { promptTokens, completionTokens };
|
|
344
456
|
}
|
|
345
|
-
|
|
457
|
+
var ClaudeToolCallTracker = class {
|
|
458
|
+
queues = /* @__PURE__ */ new Map();
|
|
459
|
+
trackStart(toolCallId, toolName) {
|
|
460
|
+
if (!this.queues.has(toolName)) {
|
|
461
|
+
this.queues.set(toolName, []);
|
|
462
|
+
}
|
|
463
|
+
this.queues.get(toolName).push(toolCallId);
|
|
464
|
+
}
|
|
465
|
+
/** Peek at the current tool call ID for a tool name (does not consume) */
|
|
466
|
+
peekToolCallId(toolName) {
|
|
467
|
+
const queue = this.queues.get(toolName);
|
|
468
|
+
if (!queue || queue.length === 0) return "";
|
|
469
|
+
return queue[0];
|
|
470
|
+
}
|
|
471
|
+
/** Consume and return the first tool call ID for a tool name */
|
|
472
|
+
consumeToolCallId(toolName) {
|
|
473
|
+
const queue = this.queues.get(toolName);
|
|
474
|
+
if (!queue || queue.length === 0) return "";
|
|
475
|
+
return queue.shift();
|
|
476
|
+
}
|
|
477
|
+
clear() {
|
|
478
|
+
this.queues.clear();
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
|
|
346
482
|
switch (msg.type) {
|
|
347
483
|
case "assistant": {
|
|
348
484
|
const betaMessage = msg.message;
|
|
@@ -354,9 +490,15 @@ function mapSDKMessage(msg, thinkingBlockIndices) {
|
|
|
354
490
|
}
|
|
355
491
|
for (const block of betaMessage.content) {
|
|
356
492
|
if (block.type === "tool_use") {
|
|
493
|
+
const toolCallId = String(block.id ?? "");
|
|
494
|
+
const toolName = block.name ?? "unknown";
|
|
495
|
+
if (toolCallTracker) {
|
|
496
|
+
toolCallTracker.trackStart(toolCallId, toolName);
|
|
497
|
+
}
|
|
357
498
|
events.push({
|
|
358
499
|
type: "tool_call_start",
|
|
359
|
-
|
|
500
|
+
toolCallId,
|
|
501
|
+
toolName,
|
|
360
502
|
args: block.input ?? {}
|
|
361
503
|
});
|
|
362
504
|
}
|
|
@@ -373,9 +515,18 @@ function mapSDKMessage(msg, thinkingBlockIndices) {
|
|
|
373
515
|
case "tool_use_summary": {
|
|
374
516
|
const summary = msg.summary;
|
|
375
517
|
const toolName = msg.tool_name ?? "unknown";
|
|
518
|
+
const precedingIds = msg.preceding_tool_use_ids;
|
|
519
|
+
let toolCallId = "";
|
|
520
|
+
if (precedingIds && precedingIds.length > 0) {
|
|
521
|
+
toolCallId = precedingIds[0];
|
|
522
|
+
if (toolCallTracker) toolCallTracker.consumeToolCallId(toolName);
|
|
523
|
+
} else if (toolCallTracker) {
|
|
524
|
+
toolCallId = toolCallTracker.consumeToolCallId(toolName);
|
|
525
|
+
}
|
|
376
526
|
if (summary) {
|
|
377
527
|
return {
|
|
378
528
|
type: "tool_call_end",
|
|
529
|
+
toolCallId,
|
|
379
530
|
toolName,
|
|
380
531
|
result: summary
|
|
381
532
|
};
|
|
@@ -402,7 +553,9 @@ function mapSDKMessage(msg, thinkingBlockIndices) {
|
|
|
402
553
|
}
|
|
403
554
|
case "tool_progress": {
|
|
404
555
|
const toolName = msg.tool_name;
|
|
405
|
-
|
|
556
|
+
if (!toolName) return null;
|
|
557
|
+
const toolCallId = toolCallTracker?.peekToolCallId(toolName) ?? "";
|
|
558
|
+
return { type: "tool_call_start", toolCallId, toolName, args: {} };
|
|
406
559
|
}
|
|
407
560
|
case "result": {
|
|
408
561
|
if (msg.subtype === "success") {
|
|
@@ -427,6 +580,7 @@ function mapSDKMessage(msg, thinkingBlockIndices) {
|
|
|
427
580
|
}
|
|
428
581
|
}
|
|
429
582
|
var ClaudeAgent = class extends BaseAgent {
|
|
583
|
+
backendName = "claude";
|
|
430
584
|
options;
|
|
431
585
|
tools;
|
|
432
586
|
canUseTool;
|
|
@@ -607,10 +761,11 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
607
761
|
opts = await this.buildMcpConfig(opts);
|
|
608
762
|
const q = sdk.query({ prompt, options: opts });
|
|
609
763
|
const thinkingBlockIndices = /* @__PURE__ */ new Set();
|
|
764
|
+
const toolCallTracker = new ClaudeToolCallTracker();
|
|
610
765
|
try {
|
|
611
766
|
for await (const msg of q) {
|
|
612
767
|
if (signal.aborted) throw new AbortError();
|
|
613
|
-
const event = mapSDKMessage(msg, thinkingBlockIndices);
|
|
768
|
+
const event = mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker);
|
|
614
769
|
if (event) {
|
|
615
770
|
if (Array.isArray(event)) {
|
|
616
771
|
for (const e of event) yield e;
|