@zvada/agent-server 0.3.10 → 0.3.12
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.12
|
|
4
|
+
|
|
5
|
+
- Report a Claude turn whose API call failed as an error. Claude Code ends it
|
|
6
|
+
as a `success` result flagged `is_error` (a rejected key, an exhausted quota,
|
|
7
|
+
an overloaded provider), with the failure as its text; it was reported as a
|
|
8
|
+
completed `end_turn` whose answer quoted the failure. The failure's text now
|
|
9
|
+
classifies the error (a 401 is `auth`).
|
|
10
|
+
|
|
11
|
+
## 0.3.11
|
|
12
|
+
|
|
13
|
+
- Report a Claude turn stopped while a tool runs as `cancelled`. Claude Code
|
|
14
|
+
ends it with `error_during_execution` and the aborted step's stop reason
|
|
15
|
+
(`tool_use`), which was reported as an error; an interrupted turn that did
|
|
16
|
+
not succeed is now a cancellation whatever its stop reason.
|
|
17
|
+
- Confirm a Claude cancel at once while the CLI is still starting: terminate
|
|
18
|
+
the process instead of waiting for it to acknowledge an interrupt. A slow
|
|
19
|
+
sandbox startup reported `confirmed: false` after the 2-second interrupt
|
|
20
|
+
timeout, and a prompt the CLI had not yet dequeued could survive the
|
|
21
|
+
interrupt and run anyway. The next turn starts a new process.
|
|
22
|
+
|
|
3
23
|
## 0.3.10
|
|
4
24
|
|
|
5
25
|
- Support Codex app-server per-turn stdio and Streamable HTTP MCP configuration
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zvada/agent-server",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.12",
|
|
4
4
|
"description": "Harness-agnostic agent execution engine: run Claude Code, Codex (SDK/CLI + app-server), and any ACP agent behind one interface with a normalized event stream, multi-turn sessions, and resume. Root export is the wire contract; /core, /server, /client are the seats.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -97,6 +97,8 @@ type ClaudeMessage =
|
|
|
97
97
|
turn_cost_usd?: number | null;
|
|
98
98
|
stop_reason?: string | null;
|
|
99
99
|
is_error?: boolean;
|
|
100
|
+
/** On an `is_error` success, the API failure's text. */
|
|
101
|
+
result?: string;
|
|
100
102
|
};
|
|
101
103
|
|
|
102
104
|
type BlockKind = "text" | "reasoning" | "tool";
|
|
@@ -240,7 +242,7 @@ export class ClaudeCodeTransformer implements EventTransformer<unknown> {
|
|
|
240
242
|
private error?: string;
|
|
241
243
|
private interrupted = false;
|
|
242
244
|
private execFailure = false;
|
|
243
|
-
private
|
|
245
|
+
private succeeded = false;
|
|
244
246
|
|
|
245
247
|
constructor(ctx: { sessionId: string }) {
|
|
246
248
|
this.ctx = { sessionId: ctx.sessionId, messageId: "" };
|
|
@@ -281,17 +283,19 @@ export class ClaudeCodeTransformer implements EventTransformer<unknown> {
|
|
|
281
283
|
}
|
|
282
284
|
|
|
283
285
|
finish(): TransformResult {
|
|
284
|
-
//
|
|
285
|
-
//
|
|
286
|
-
// "
|
|
287
|
-
//
|
|
288
|
-
//
|
|
289
|
-
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
286
|
+
// An interrupted turn that did not succeed was cancelled. Claude ends it with
|
|
287
|
+
// error_during_execution and the stop reason of the step it aborted (null
|
|
288
|
+
// while streaming, "tool_use" while a tool runs), or with no result when
|
|
289
|
+
// the process was stopped first. Without an interrupt, error_during_execution
|
|
290
|
+
// is a real failure (a silent "cancelled" here is how a bad resume id used
|
|
291
|
+
// to vanish without a trace).
|
|
292
|
+
const cancelled = this.interrupted && !this.succeeded;
|
|
293
|
+
const error = cancelled
|
|
294
|
+
? undefined
|
|
295
|
+
: (this.error ??
|
|
296
|
+
(this.execFailure
|
|
297
|
+
? "Claude turn failed during execution (error_during_execution — e.g. an invalid resumeSessionId fails this way)"
|
|
298
|
+
: undefined));
|
|
295
299
|
return {
|
|
296
300
|
usage: this.usage,
|
|
297
301
|
...(this.reportedModels.size && { reportedModels: [...this.reportedModels] }),
|
|
@@ -503,7 +507,7 @@ export class ClaudeCodeTransformer implements EventTransformer<unknown> {
|
|
|
503
507
|
}
|
|
504
508
|
|
|
505
509
|
private captureResult(msg: Extract<ClaudeMessage, { type: "result" }>): AdapterEvent[] {
|
|
506
|
-
this.
|
|
510
|
+
this.succeeded = msg.subtype === "success" && !msg.is_error;
|
|
507
511
|
if (msg.usage) {
|
|
508
512
|
const creation = msg.usage.cache_creation;
|
|
509
513
|
this.usage = {
|
|
@@ -528,13 +532,19 @@ export class ClaudeCodeTransformer implements EventTransformer<unknown> {
|
|
|
528
532
|
if (msg.subtype === "error_during_execution" && msg.stop_reason === null) {
|
|
529
533
|
// Ambiguous shape: an interrupt AND an execution failure (e.g. a bad
|
|
530
534
|
// resume id) both surface this way. The agent's `turn_interrupted`
|
|
531
|
-
// marker (which arrives after this message) disambiguates in finish()
|
|
535
|
+
// marker (which arrives after this message) disambiguates in finish(),
|
|
536
|
+
// as it does for an interrupt that aborted a tool (stop reason "tool_use").
|
|
532
537
|
this.execFailure = true;
|
|
533
538
|
} else if (msg.subtype === "error_max_turns") {
|
|
534
539
|
// Hitting the turn budget is a stop condition, not a failure.
|
|
535
540
|
} else {
|
|
536
541
|
this.error = `Claude turn ended: ${msg.subtype}`;
|
|
537
542
|
}
|
|
543
|
+
} else if (msg.is_error) {
|
|
544
|
+
// A failed API call (a rejected key, an exhausted quota, an overloaded
|
|
545
|
+
// provider) ends the turn as a "success" whose text is the error. The
|
|
546
|
+
// text names the failure, so the runtime classifies it (401 -> auth).
|
|
547
|
+
this.error = msg.result || "Claude turn ended with an API error";
|
|
538
548
|
}
|
|
539
549
|
// Final authoritative gauge: the result knows the context-window size.
|
|
540
550
|
if (!msg.usage) return [];
|
|
@@ -122,6 +122,8 @@ export class ClaudeGeneratorSession {
|
|
|
122
122
|
private readonly failedMcpServers = new Set<string>();
|
|
123
123
|
private processInvalidated = false;
|
|
124
124
|
private nativeSessionId: string | null = null;
|
|
125
|
+
/** Set by the CLI's first message; until then it has begun no turn. */
|
|
126
|
+
private cliStarted = false;
|
|
125
127
|
private startupError: unknown;
|
|
126
128
|
/** SDK cost is cumulative within this query; token usage is already per turn. */
|
|
127
129
|
private cumulativeCost: number | undefined = 0;
|
|
@@ -375,9 +377,17 @@ export class ClaudeGeneratorSession {
|
|
|
375
377
|
* SDK acknowledged it; false when the round-trip timed out or failed — the
|
|
376
378
|
* subprocess may still be running its turn and the caller must report that
|
|
377
379
|
* honestly instead of assuming a clean cancel.
|
|
380
|
+
*
|
|
381
|
+
* A session still starting, or whose CLI has sent nothing yet, is terminated
|
|
382
|
+
* instead: it has begun no turn, an interrupt would wait out its startup, and
|
|
383
|
+
* a prompt the CLI has not dequeued survives an interrupt and runs anyway.
|
|
378
384
|
*/
|
|
379
385
|
async interruptTurn(timeoutMs = 2000): Promise<boolean> {
|
|
380
|
-
if (
|
|
386
|
+
if (this.state === "idle" || this.state === "terminated") return true;
|
|
387
|
+
if (this.state === "starting" || !this.cliStarted || !this.query) {
|
|
388
|
+
await this.terminate();
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
381
391
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
382
392
|
try {
|
|
383
393
|
await Promise.race([
|
|
@@ -422,6 +432,7 @@ export class ClaudeGeneratorSession {
|
|
|
422
432
|
if (!this.query) return;
|
|
423
433
|
try {
|
|
424
434
|
for await (const event of this.query) {
|
|
435
|
+
this.cliStarted = true;
|
|
425
436
|
if (event.type === "system" && event.subtype === "init" && event.session_id) {
|
|
426
437
|
this.nativeSessionId = event.session_id;
|
|
427
438
|
}
|