@witqq/agent-sdk 0.1.0 → 0.2.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 +91 -8
- package/dist/backends/claude.cjs +160 -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 +160 -8
- package/dist/backends/claude.js.map +1 -1
- package/dist/backends/copilot.cjs +140 -10
- package/dist/backends/copilot.cjs.map +1 -1
- package/dist/backends/copilot.d.cts +3 -1
- package/dist/backends/copilot.d.ts +3 -1
- package/dist/backends/copilot.js +140 -10
- package/dist/backends/copilot.js.map +1 -1
- package/dist/backends/vercel-ai.cjs +117 -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 +117 -5
- package/dist/backends/vercel-ai.js.map +1 -1
- package/dist/index.cjs +113 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -5
- package/dist/index.d.ts +54 -5
- package/dist/index.js +113 -4
- package/dist/index.js.map +1 -1
- package/dist/{types-JVBEqeDw.d.cts → types-CBzhRrN9.d.cts} +41 -4
- package/dist/{types-JVBEqeDw.d.ts → types-CBzhRrN9.d.ts} +41 -4
- package/package.json +1 -1
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") {
|
|
@@ -342,7 +451,31 @@ function aggregateUsage(modelUsage) {
|
|
|
342
451
|
}
|
|
343
452
|
return { promptTokens, completionTokens };
|
|
344
453
|
}
|
|
345
|
-
|
|
454
|
+
var ClaudeToolCallTracker = class {
|
|
455
|
+
queues = /* @__PURE__ */ new Map();
|
|
456
|
+
trackStart(toolCallId, toolName) {
|
|
457
|
+
if (!this.queues.has(toolName)) {
|
|
458
|
+
this.queues.set(toolName, []);
|
|
459
|
+
}
|
|
460
|
+
this.queues.get(toolName).push(toolCallId);
|
|
461
|
+
}
|
|
462
|
+
/** Peek at the current tool call ID for a tool name (does not consume) */
|
|
463
|
+
peekToolCallId(toolName) {
|
|
464
|
+
const queue = this.queues.get(toolName);
|
|
465
|
+
if (!queue || queue.length === 0) return "";
|
|
466
|
+
return queue[0];
|
|
467
|
+
}
|
|
468
|
+
/** Consume and return the first tool call ID for a tool name */
|
|
469
|
+
consumeToolCallId(toolName) {
|
|
470
|
+
const queue = this.queues.get(toolName);
|
|
471
|
+
if (!queue || queue.length === 0) return "";
|
|
472
|
+
return queue.shift();
|
|
473
|
+
}
|
|
474
|
+
clear() {
|
|
475
|
+
this.queues.clear();
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
function mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker) {
|
|
346
479
|
switch (msg.type) {
|
|
347
480
|
case "assistant": {
|
|
348
481
|
const betaMessage = msg.message;
|
|
@@ -354,9 +487,15 @@ function mapSDKMessage(msg, thinkingBlockIndices) {
|
|
|
354
487
|
}
|
|
355
488
|
for (const block of betaMessage.content) {
|
|
356
489
|
if (block.type === "tool_use") {
|
|
490
|
+
const toolCallId = String(block.id ?? "");
|
|
491
|
+
const toolName = block.name ?? "unknown";
|
|
492
|
+
if (toolCallTracker) {
|
|
493
|
+
toolCallTracker.trackStart(toolCallId, toolName);
|
|
494
|
+
}
|
|
357
495
|
events.push({
|
|
358
496
|
type: "tool_call_start",
|
|
359
|
-
|
|
497
|
+
toolCallId,
|
|
498
|
+
toolName,
|
|
360
499
|
args: block.input ?? {}
|
|
361
500
|
});
|
|
362
501
|
}
|
|
@@ -373,9 +512,18 @@ function mapSDKMessage(msg, thinkingBlockIndices) {
|
|
|
373
512
|
case "tool_use_summary": {
|
|
374
513
|
const summary = msg.summary;
|
|
375
514
|
const toolName = msg.tool_name ?? "unknown";
|
|
515
|
+
const precedingIds = msg.preceding_tool_use_ids;
|
|
516
|
+
let toolCallId = "";
|
|
517
|
+
if (precedingIds && precedingIds.length > 0) {
|
|
518
|
+
toolCallId = precedingIds[0];
|
|
519
|
+
if (toolCallTracker) toolCallTracker.consumeToolCallId(toolName);
|
|
520
|
+
} else if (toolCallTracker) {
|
|
521
|
+
toolCallId = toolCallTracker.consumeToolCallId(toolName);
|
|
522
|
+
}
|
|
376
523
|
if (summary) {
|
|
377
524
|
return {
|
|
378
525
|
type: "tool_call_end",
|
|
526
|
+
toolCallId,
|
|
379
527
|
toolName,
|
|
380
528
|
result: summary
|
|
381
529
|
};
|
|
@@ -402,7 +550,9 @@ function mapSDKMessage(msg, thinkingBlockIndices) {
|
|
|
402
550
|
}
|
|
403
551
|
case "tool_progress": {
|
|
404
552
|
const toolName = msg.tool_name;
|
|
405
|
-
|
|
553
|
+
if (!toolName) return null;
|
|
554
|
+
const toolCallId = toolCallTracker?.peekToolCallId(toolName) ?? "";
|
|
555
|
+
return { type: "tool_call_start", toolCallId, toolName, args: {} };
|
|
406
556
|
}
|
|
407
557
|
case "result": {
|
|
408
558
|
if (msg.subtype === "success") {
|
|
@@ -427,6 +577,7 @@ function mapSDKMessage(msg, thinkingBlockIndices) {
|
|
|
427
577
|
}
|
|
428
578
|
}
|
|
429
579
|
var ClaudeAgent = class extends BaseAgent {
|
|
580
|
+
backendName = "claude";
|
|
430
581
|
options;
|
|
431
582
|
tools;
|
|
432
583
|
canUseTool;
|
|
@@ -607,10 +758,11 @@ var ClaudeAgent = class extends BaseAgent {
|
|
|
607
758
|
opts = await this.buildMcpConfig(opts);
|
|
608
759
|
const q = sdk.query({ prompt, options: opts });
|
|
609
760
|
const thinkingBlockIndices = /* @__PURE__ */ new Set();
|
|
761
|
+
const toolCallTracker = new ClaudeToolCallTracker();
|
|
610
762
|
try {
|
|
611
763
|
for await (const msg of q) {
|
|
612
764
|
if (signal.aborted) throw new AbortError();
|
|
613
|
-
const event = mapSDKMessage(msg, thinkingBlockIndices);
|
|
765
|
+
const event = mapSDKMessage(msg, thinkingBlockIndices, toolCallTracker);
|
|
614
766
|
if (event) {
|
|
615
767
|
if (Array.isArray(event)) {
|
|
616
768
|
for (const e of event) yield e;
|