@zvada/agent-server 0.3.10 → 0.3.11
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,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.11
|
|
4
|
+
|
|
5
|
+
- Report a Claude turn stopped while a tool runs as `cancelled`. Claude Code
|
|
6
|
+
ends it with `error_during_execution` and the aborted step's stop reason
|
|
7
|
+
(`tool_use`), which was reported as an error; an interrupted turn that did
|
|
8
|
+
not succeed is now a cancellation whatever its stop reason.
|
|
9
|
+
- Confirm a Claude cancel at once while the CLI is still starting: terminate
|
|
10
|
+
the process instead of waiting for it to acknowledge an interrupt. A slow
|
|
11
|
+
sandbox startup reported `confirmed: false` after the 2-second interrupt
|
|
12
|
+
timeout, and a prompt the CLI had not yet dequeued could survive the
|
|
13
|
+
interrupt and run anyway. The next turn starts a new process.
|
|
14
|
+
|
|
3
15
|
## 0.3.10
|
|
4
16
|
|
|
5
17
|
- 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.11",
|
|
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",
|
|
@@ -240,7 +240,7 @@ export class ClaudeCodeTransformer implements EventTransformer<unknown> {
|
|
|
240
240
|
private error?: string;
|
|
241
241
|
private interrupted = false;
|
|
242
242
|
private execFailure = false;
|
|
243
|
-
private
|
|
243
|
+
private succeeded = false;
|
|
244
244
|
|
|
245
245
|
constructor(ctx: { sessionId: string }) {
|
|
246
246
|
this.ctx = { sessionId: ctx.sessionId, messageId: "" };
|
|
@@ -281,17 +281,19 @@ export class ClaudeCodeTransformer implements EventTransformer<unknown> {
|
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
finish(): TransformResult {
|
|
284
|
-
//
|
|
285
|
-
//
|
|
286
|
-
// "
|
|
287
|
-
//
|
|
288
|
-
//
|
|
289
|
-
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
284
|
+
// An interrupted turn that did not succeed was cancelled. Claude ends it with
|
|
285
|
+
// error_during_execution and the stop reason of the step it aborted (null
|
|
286
|
+
// while streaming, "tool_use" while a tool runs), or with no result when
|
|
287
|
+
// the process was stopped first. Without an interrupt, error_during_execution
|
|
288
|
+
// is a real failure (a silent "cancelled" here is how a bad resume id used
|
|
289
|
+
// to vanish without a trace).
|
|
290
|
+
const cancelled = this.interrupted && !this.succeeded;
|
|
291
|
+
const error = cancelled
|
|
292
|
+
? undefined
|
|
293
|
+
: (this.error ??
|
|
294
|
+
(this.execFailure
|
|
295
|
+
? "Claude turn failed during execution (error_during_execution — e.g. an invalid resumeSessionId fails this way)"
|
|
296
|
+
: undefined));
|
|
295
297
|
return {
|
|
296
298
|
usage: this.usage,
|
|
297
299
|
...(this.reportedModels.size && { reportedModels: [...this.reportedModels] }),
|
|
@@ -503,7 +505,7 @@ export class ClaudeCodeTransformer implements EventTransformer<unknown> {
|
|
|
503
505
|
}
|
|
504
506
|
|
|
505
507
|
private captureResult(msg: Extract<ClaudeMessage, { type: "result" }>): AdapterEvent[] {
|
|
506
|
-
this.
|
|
508
|
+
this.succeeded = msg.subtype === "success";
|
|
507
509
|
if (msg.usage) {
|
|
508
510
|
const creation = msg.usage.cache_creation;
|
|
509
511
|
this.usage = {
|
|
@@ -528,7 +530,8 @@ export class ClaudeCodeTransformer implements EventTransformer<unknown> {
|
|
|
528
530
|
if (msg.subtype === "error_during_execution" && msg.stop_reason === null) {
|
|
529
531
|
// Ambiguous shape: an interrupt AND an execution failure (e.g. a bad
|
|
530
532
|
// resume id) both surface this way. The agent's `turn_interrupted`
|
|
531
|
-
// marker (which arrives after this message) disambiguates in finish()
|
|
533
|
+
// marker (which arrives after this message) disambiguates in finish(),
|
|
534
|
+
// as it does for an interrupt that aborted a tool (stop reason "tool_use").
|
|
532
535
|
this.execFailure = true;
|
|
533
536
|
} else if (msg.subtype === "error_max_turns") {
|
|
534
537
|
// Hitting the turn budget is a stop condition, not a failure.
|
|
@@ -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
|
}
|