@autohq/cli 0.1.195 → 0.1.196
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/dist/agent-bridge.js +109 -2
- package/dist/index.js +109 -2
- package/package.json +1 -1
package/dist/agent-bridge.js
CHANGED
|
@@ -26640,7 +26640,7 @@ Object.assign(lookup, {
|
|
|
26640
26640
|
// package.json
|
|
26641
26641
|
var package_default = {
|
|
26642
26642
|
name: "@autohq/cli",
|
|
26643
|
-
version: "0.1.
|
|
26643
|
+
version: "0.1.196",
|
|
26644
26644
|
license: "SEE LICENSE IN README.md",
|
|
26645
26645
|
publishConfig: {
|
|
26646
26646
|
access: "public"
|
|
@@ -46955,6 +46955,7 @@ function withClaudeStderrDiagnosticsPointer(error51, capturedStderr) {
|
|
|
46955
46955
|
|
|
46956
46956
|
// src/commands/agent-bridge/harness/claude-code/session.ts
|
|
46957
46957
|
var CLAUDE_AGENT_STARTUP_TIMEOUT_MS = 3e4;
|
|
46958
|
+
var CLAUDE_INTERRUPT_SETTLE_TIMEOUT_MS = 1e4;
|
|
46958
46959
|
var CLAUDE_STARTUP_PROFILE_HOOK_EVENTS = [
|
|
46959
46960
|
"Setup",
|
|
46960
46961
|
"SessionStart",
|
|
@@ -46991,6 +46992,17 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
46991
46992
|
// held here instead of injected mid-turn (which would reject a pending
|
|
46992
46993
|
// tool_use) and flushed once the turn ends and the session is idle.
|
|
46993
46994
|
deferredMessages = [];
|
|
46995
|
+
// tool_use ids the assistant has emitted whose tool_result has not yet been
|
|
46996
|
+
// observed. A non-empty set means a tool call is in flight, so an immediate
|
|
46997
|
+
// interrupt would append the new user message after an unresolved
|
|
46998
|
+
// stop_reason=tool_use — the invalid transcript that trips Claude Code's
|
|
46999
|
+
// ede_diagnostic and auto-restarts the session (FRA-3049).
|
|
47000
|
+
pendingToolUseIds = /* @__PURE__ */ new Set();
|
|
47001
|
+
// Incremented on every turn-terminal `result`. An interrupt that aborts an
|
|
47002
|
+
// in-flight tool call waits for this to advance before injecting, so the
|
|
47003
|
+
// message lands only once the interrupted turn has settled its tool_use.
|
|
47004
|
+
turnResultCount = 0;
|
|
47005
|
+
turnSettlementWaiters = /* @__PURE__ */ new Set();
|
|
46994
47006
|
constructor(input) {
|
|
46995
47007
|
const optionsStartedAt = Date.now();
|
|
46996
47008
|
this.input = input;
|
|
@@ -47052,6 +47064,8 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
47052
47064
|
}
|
|
47053
47065
|
this.state = { kind: "closed" };
|
|
47054
47066
|
this.activeTurnCount = 0;
|
|
47067
|
+
this.pendingToolUseIds.clear();
|
|
47068
|
+
this.resolveTurnSettlement(false);
|
|
47055
47069
|
if (this.deferredMessages.length > 0) {
|
|
47056
47070
|
this.input.writeOutput?.(
|
|
47057
47071
|
`agent_bridge_claude_deferred_dropped count=${this.deferredMessages.length} reason=session_closed`
|
|
@@ -47140,8 +47154,12 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
47140
47154
|
void (async () => {
|
|
47141
47155
|
try {
|
|
47142
47156
|
for await (const message of query) {
|
|
47157
|
+
this.trackToolUseLifecycle(message);
|
|
47143
47158
|
if (isClaudeAgentTurnResult(message)) {
|
|
47144
47159
|
this.activeTurnCount = Math.max(0, this.activeTurnCount - 1);
|
|
47160
|
+
this.pendingToolUseIds.clear();
|
|
47161
|
+
this.turnResultCount += 1;
|
|
47162
|
+
this.resolveTurnSettlement(true);
|
|
47145
47163
|
if (this.activeTurnCount === 0) {
|
|
47146
47164
|
this.flushDeferredMessages();
|
|
47147
47165
|
}
|
|
@@ -47168,6 +47186,8 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
47168
47186
|
}
|
|
47169
47187
|
} finally {
|
|
47170
47188
|
this.activeTurnCount = 0;
|
|
47189
|
+
this.pendingToolUseIds.clear();
|
|
47190
|
+
this.resolveTurnSettlement(false);
|
|
47171
47191
|
this.state = { kind: "closed" };
|
|
47172
47192
|
this.reportExit();
|
|
47173
47193
|
}
|
|
@@ -47189,6 +47209,64 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
47189
47209
|
hasInterruptibleTurn() {
|
|
47190
47210
|
return this.activeTurnCount > 0 && this.state.kind === "running";
|
|
47191
47211
|
}
|
|
47212
|
+
// True while the assistant has an emitted tool_use with no matching
|
|
47213
|
+
// tool_result yet — the window where an immediate interrupt would strand an
|
|
47214
|
+
// unresolved stop_reason=tool_use ahead of the injected user message.
|
|
47215
|
+
hasInFlightToolUse() {
|
|
47216
|
+
return this.pendingToolUseIds.size > 0;
|
|
47217
|
+
}
|
|
47218
|
+
// Track tool_use/tool_result pairs as they stream so hasInFlightToolUse()
|
|
47219
|
+
// reflects the live transcript. tool_use opens a call; the matching
|
|
47220
|
+
// tool_result closes it; the turn's terminal result clears any remainder.
|
|
47221
|
+
trackToolUseLifecycle(message) {
|
|
47222
|
+
for (const id of toolUseIds(message)) {
|
|
47223
|
+
this.pendingToolUseIds.add(id);
|
|
47224
|
+
}
|
|
47225
|
+
for (const id of toolResultIds(message)) {
|
|
47226
|
+
this.pendingToolUseIds.delete(id);
|
|
47227
|
+
}
|
|
47228
|
+
}
|
|
47229
|
+
// Resolve once the interrupted turn emits its terminal `result` (turnResultCount
|
|
47230
|
+
// advances past the pre-interrupt baseline), bounded so a tool that never
|
|
47231
|
+
// returns after interrupt cannot wedge delivery. On timeout the caller falls
|
|
47232
|
+
// back to immediate injection rather than hanging or dropping the message.
|
|
47233
|
+
awaitTurnSettlement(baseline, startedAt) {
|
|
47234
|
+
if (this.turnResultCount > baseline) {
|
|
47235
|
+
return Promise.resolve();
|
|
47236
|
+
}
|
|
47237
|
+
return new Promise((resolve) => {
|
|
47238
|
+
let done = false;
|
|
47239
|
+
const finish = (outcome) => {
|
|
47240
|
+
if (done) {
|
|
47241
|
+
return;
|
|
47242
|
+
}
|
|
47243
|
+
done = true;
|
|
47244
|
+
clearTimeout(timer);
|
|
47245
|
+
this.turnSettlementWaiters.delete(waiter);
|
|
47246
|
+
this.input.writeOutput?.(
|
|
47247
|
+
`agent_bridge_claude_interrupt_settle_${outcome} duration_ms=${Date.now() - startedAt}`
|
|
47248
|
+
);
|
|
47249
|
+
resolve();
|
|
47250
|
+
};
|
|
47251
|
+
const waiter = (settled) => finish(settled ? "ready" : "aborted");
|
|
47252
|
+
const timer = setTimeout(
|
|
47253
|
+
() => finish("timeout"),
|
|
47254
|
+
CLAUDE_INTERRUPT_SETTLE_TIMEOUT_MS
|
|
47255
|
+
);
|
|
47256
|
+
timer.unref?.();
|
|
47257
|
+
this.turnSettlementWaiters.add(waiter);
|
|
47258
|
+
});
|
|
47259
|
+
}
|
|
47260
|
+
resolveTurnSettlement(settled) {
|
|
47261
|
+
if (this.turnSettlementWaiters.size === 0) {
|
|
47262
|
+
return;
|
|
47263
|
+
}
|
|
47264
|
+
const waiters = [...this.turnSettlementWaiters];
|
|
47265
|
+
this.turnSettlementWaiters.clear();
|
|
47266
|
+
for (const waiter of waiters) {
|
|
47267
|
+
waiter(settled);
|
|
47268
|
+
}
|
|
47269
|
+
}
|
|
47192
47270
|
flushDeferredMessages() {
|
|
47193
47271
|
if (this.deferredMessages.length === 0) {
|
|
47194
47272
|
return;
|
|
@@ -47213,9 +47291,11 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
47213
47291
|
return;
|
|
47214
47292
|
}
|
|
47215
47293
|
const query = this.state.query;
|
|
47294
|
+
const hadInFlightToolUse = this.hasInFlightToolUse();
|
|
47295
|
+
const settlementBaseline = this.turnResultCount;
|
|
47216
47296
|
const startedAt = Date.now();
|
|
47217
47297
|
this.input.writeOutput?.(
|
|
47218
|
-
`agent_bridge_claude_mid_turn_interrupt_started at=${new Date(startedAt).toISOString()}`
|
|
47298
|
+
`agent_bridge_claude_mid_turn_interrupt_started at=${new Date(startedAt).toISOString()} in_flight_tool_use=${hadInFlightToolUse}`
|
|
47219
47299
|
);
|
|
47220
47300
|
const interruptPromise = (async () => {
|
|
47221
47301
|
try {
|
|
@@ -47228,6 +47308,9 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
47228
47308
|
`agent_bridge_claude_mid_turn_interrupt_failed duration_ms=${Date.now() - startedAt} error=${error51 instanceof Error ? error51.message : String(error51)}`
|
|
47229
47309
|
);
|
|
47230
47310
|
}
|
|
47311
|
+
if (hadInFlightToolUse) {
|
|
47312
|
+
await this.awaitTurnSettlement(settlementBaseline, startedAt);
|
|
47313
|
+
}
|
|
47231
47314
|
})();
|
|
47232
47315
|
this.interruptInFlight = interruptPromise;
|
|
47233
47316
|
try {
|
|
@@ -47385,6 +47468,30 @@ function claudeAgentUserMessage(message) {
|
|
|
47385
47468
|
function isClaudeAgentTurnResult(message) {
|
|
47386
47469
|
return message.type === "result";
|
|
47387
47470
|
}
|
|
47471
|
+
function toolUseIds(message) {
|
|
47472
|
+
if (message.type !== "assistant") {
|
|
47473
|
+
return [];
|
|
47474
|
+
}
|
|
47475
|
+
return contentBlocks(message.message.content).flatMap(
|
|
47476
|
+
(block) => block.type === "tool_use" && typeof block.id === "string" ? [block.id] : []
|
|
47477
|
+
);
|
|
47478
|
+
}
|
|
47479
|
+
function toolResultIds(message) {
|
|
47480
|
+
if (message.type !== "user") {
|
|
47481
|
+
return [];
|
|
47482
|
+
}
|
|
47483
|
+
return contentBlocks(message.message.content).flatMap(
|
|
47484
|
+
(block) => block.type === "tool_result" && typeof block.tool_use_id === "string" ? [block.tool_use_id] : []
|
|
47485
|
+
);
|
|
47486
|
+
}
|
|
47487
|
+
function contentBlocks(content) {
|
|
47488
|
+
if (!Array.isArray(content)) {
|
|
47489
|
+
return [];
|
|
47490
|
+
}
|
|
47491
|
+
return content.filter(
|
|
47492
|
+
(block) => typeof block === "object" && block !== null
|
|
47493
|
+
);
|
|
47494
|
+
}
|
|
47388
47495
|
|
|
47389
47496
|
// src/commands/agent-bridge/harness/claude-code/index.ts
|
|
47390
47497
|
async function runAgentBridgeClaudeCode(options) {
|
package/dist/index.js
CHANGED
|
@@ -21623,7 +21623,7 @@ var init_package = __esm({
|
|
|
21623
21623
|
"package.json"() {
|
|
21624
21624
|
package_default = {
|
|
21625
21625
|
name: "@autohq/cli",
|
|
21626
|
-
version: "0.1.
|
|
21626
|
+
version: "0.1.196",
|
|
21627
21627
|
license: "SEE LICENSE IN README.md",
|
|
21628
21628
|
publishConfig: {
|
|
21629
21629
|
access: "public"
|
|
@@ -31975,6 +31975,7 @@ function withClaudeStderrDiagnosticsPointer(error51, capturedStderr) {
|
|
|
31975
31975
|
|
|
31976
31976
|
// src/commands/agent-bridge/harness/claude-code/session.ts
|
|
31977
31977
|
var CLAUDE_AGENT_STARTUP_TIMEOUT_MS = 3e4;
|
|
31978
|
+
var CLAUDE_INTERRUPT_SETTLE_TIMEOUT_MS = 1e4;
|
|
31978
31979
|
var CLAUDE_STARTUP_PROFILE_HOOK_EVENTS = [
|
|
31979
31980
|
"Setup",
|
|
31980
31981
|
"SessionStart",
|
|
@@ -32011,6 +32012,17 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
32011
32012
|
// held here instead of injected mid-turn (which would reject a pending
|
|
32012
32013
|
// tool_use) and flushed once the turn ends and the session is idle.
|
|
32013
32014
|
deferredMessages = [];
|
|
32015
|
+
// tool_use ids the assistant has emitted whose tool_result has not yet been
|
|
32016
|
+
// observed. A non-empty set means a tool call is in flight, so an immediate
|
|
32017
|
+
// interrupt would append the new user message after an unresolved
|
|
32018
|
+
// stop_reason=tool_use — the invalid transcript that trips Claude Code's
|
|
32019
|
+
// ede_diagnostic and auto-restarts the session (FRA-3049).
|
|
32020
|
+
pendingToolUseIds = /* @__PURE__ */ new Set();
|
|
32021
|
+
// Incremented on every turn-terminal `result`. An interrupt that aborts an
|
|
32022
|
+
// in-flight tool call waits for this to advance before injecting, so the
|
|
32023
|
+
// message lands only once the interrupted turn has settled its tool_use.
|
|
32024
|
+
turnResultCount = 0;
|
|
32025
|
+
turnSettlementWaiters = /* @__PURE__ */ new Set();
|
|
32014
32026
|
constructor(input) {
|
|
32015
32027
|
const optionsStartedAt = Date.now();
|
|
32016
32028
|
this.input = input;
|
|
@@ -32072,6 +32084,8 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
32072
32084
|
}
|
|
32073
32085
|
this.state = { kind: "closed" };
|
|
32074
32086
|
this.activeTurnCount = 0;
|
|
32087
|
+
this.pendingToolUseIds.clear();
|
|
32088
|
+
this.resolveTurnSettlement(false);
|
|
32075
32089
|
if (this.deferredMessages.length > 0) {
|
|
32076
32090
|
this.input.writeOutput?.(
|
|
32077
32091
|
`agent_bridge_claude_deferred_dropped count=${this.deferredMessages.length} reason=session_closed`
|
|
@@ -32160,8 +32174,12 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
32160
32174
|
void (async () => {
|
|
32161
32175
|
try {
|
|
32162
32176
|
for await (const message of query) {
|
|
32177
|
+
this.trackToolUseLifecycle(message);
|
|
32163
32178
|
if (isClaudeAgentTurnResult(message)) {
|
|
32164
32179
|
this.activeTurnCount = Math.max(0, this.activeTurnCount - 1);
|
|
32180
|
+
this.pendingToolUseIds.clear();
|
|
32181
|
+
this.turnResultCount += 1;
|
|
32182
|
+
this.resolveTurnSettlement(true);
|
|
32165
32183
|
if (this.activeTurnCount === 0) {
|
|
32166
32184
|
this.flushDeferredMessages();
|
|
32167
32185
|
}
|
|
@@ -32188,6 +32206,8 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
32188
32206
|
}
|
|
32189
32207
|
} finally {
|
|
32190
32208
|
this.activeTurnCount = 0;
|
|
32209
|
+
this.pendingToolUseIds.clear();
|
|
32210
|
+
this.resolveTurnSettlement(false);
|
|
32191
32211
|
this.state = { kind: "closed" };
|
|
32192
32212
|
this.reportExit();
|
|
32193
32213
|
}
|
|
@@ -32209,6 +32229,64 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
32209
32229
|
hasInterruptibleTurn() {
|
|
32210
32230
|
return this.activeTurnCount > 0 && this.state.kind === "running";
|
|
32211
32231
|
}
|
|
32232
|
+
// True while the assistant has an emitted tool_use with no matching
|
|
32233
|
+
// tool_result yet — the window where an immediate interrupt would strand an
|
|
32234
|
+
// unresolved stop_reason=tool_use ahead of the injected user message.
|
|
32235
|
+
hasInFlightToolUse() {
|
|
32236
|
+
return this.pendingToolUseIds.size > 0;
|
|
32237
|
+
}
|
|
32238
|
+
// Track tool_use/tool_result pairs as they stream so hasInFlightToolUse()
|
|
32239
|
+
// reflects the live transcript. tool_use opens a call; the matching
|
|
32240
|
+
// tool_result closes it; the turn's terminal result clears any remainder.
|
|
32241
|
+
trackToolUseLifecycle(message) {
|
|
32242
|
+
for (const id of toolUseIds(message)) {
|
|
32243
|
+
this.pendingToolUseIds.add(id);
|
|
32244
|
+
}
|
|
32245
|
+
for (const id of toolResultIds(message)) {
|
|
32246
|
+
this.pendingToolUseIds.delete(id);
|
|
32247
|
+
}
|
|
32248
|
+
}
|
|
32249
|
+
// Resolve once the interrupted turn emits its terminal `result` (turnResultCount
|
|
32250
|
+
// advances past the pre-interrupt baseline), bounded so a tool that never
|
|
32251
|
+
// returns after interrupt cannot wedge delivery. On timeout the caller falls
|
|
32252
|
+
// back to immediate injection rather than hanging or dropping the message.
|
|
32253
|
+
awaitTurnSettlement(baseline, startedAt) {
|
|
32254
|
+
if (this.turnResultCount > baseline) {
|
|
32255
|
+
return Promise.resolve();
|
|
32256
|
+
}
|
|
32257
|
+
return new Promise((resolve4) => {
|
|
32258
|
+
let done = false;
|
|
32259
|
+
const finish = (outcome) => {
|
|
32260
|
+
if (done) {
|
|
32261
|
+
return;
|
|
32262
|
+
}
|
|
32263
|
+
done = true;
|
|
32264
|
+
clearTimeout(timer);
|
|
32265
|
+
this.turnSettlementWaiters.delete(waiter);
|
|
32266
|
+
this.input.writeOutput?.(
|
|
32267
|
+
`agent_bridge_claude_interrupt_settle_${outcome} duration_ms=${Date.now() - startedAt}`
|
|
32268
|
+
);
|
|
32269
|
+
resolve4();
|
|
32270
|
+
};
|
|
32271
|
+
const waiter = (settled) => finish(settled ? "ready" : "aborted");
|
|
32272
|
+
const timer = setTimeout(
|
|
32273
|
+
() => finish("timeout"),
|
|
32274
|
+
CLAUDE_INTERRUPT_SETTLE_TIMEOUT_MS
|
|
32275
|
+
);
|
|
32276
|
+
timer.unref?.();
|
|
32277
|
+
this.turnSettlementWaiters.add(waiter);
|
|
32278
|
+
});
|
|
32279
|
+
}
|
|
32280
|
+
resolveTurnSettlement(settled) {
|
|
32281
|
+
if (this.turnSettlementWaiters.size === 0) {
|
|
32282
|
+
return;
|
|
32283
|
+
}
|
|
32284
|
+
const waiters = [...this.turnSettlementWaiters];
|
|
32285
|
+
this.turnSettlementWaiters.clear();
|
|
32286
|
+
for (const waiter of waiters) {
|
|
32287
|
+
waiter(settled);
|
|
32288
|
+
}
|
|
32289
|
+
}
|
|
32212
32290
|
flushDeferredMessages() {
|
|
32213
32291
|
if (this.deferredMessages.length === 0) {
|
|
32214
32292
|
return;
|
|
@@ -32233,9 +32311,11 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
32233
32311
|
return;
|
|
32234
32312
|
}
|
|
32235
32313
|
const query = this.state.query;
|
|
32314
|
+
const hadInFlightToolUse = this.hasInFlightToolUse();
|
|
32315
|
+
const settlementBaseline = this.turnResultCount;
|
|
32236
32316
|
const startedAt = Date.now();
|
|
32237
32317
|
this.input.writeOutput?.(
|
|
32238
|
-
`agent_bridge_claude_mid_turn_interrupt_started at=${new Date(startedAt).toISOString()}`
|
|
32318
|
+
`agent_bridge_claude_mid_turn_interrupt_started at=${new Date(startedAt).toISOString()} in_flight_tool_use=${hadInFlightToolUse}`
|
|
32239
32319
|
);
|
|
32240
32320
|
const interruptPromise = (async () => {
|
|
32241
32321
|
try {
|
|
@@ -32248,6 +32328,9 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
32248
32328
|
`agent_bridge_claude_mid_turn_interrupt_failed duration_ms=${Date.now() - startedAt} error=${error51 instanceof Error ? error51.message : String(error51)}`
|
|
32249
32329
|
);
|
|
32250
32330
|
}
|
|
32331
|
+
if (hadInFlightToolUse) {
|
|
32332
|
+
await this.awaitTurnSettlement(settlementBaseline, startedAt);
|
|
32333
|
+
}
|
|
32251
32334
|
})();
|
|
32252
32335
|
this.interruptInFlight = interruptPromise;
|
|
32253
32336
|
try {
|
|
@@ -32405,6 +32488,30 @@ function claudeAgentUserMessage(message) {
|
|
|
32405
32488
|
function isClaudeAgentTurnResult(message) {
|
|
32406
32489
|
return message.type === "result";
|
|
32407
32490
|
}
|
|
32491
|
+
function toolUseIds(message) {
|
|
32492
|
+
if (message.type !== "assistant") {
|
|
32493
|
+
return [];
|
|
32494
|
+
}
|
|
32495
|
+
return contentBlocks(message.message.content).flatMap(
|
|
32496
|
+
(block) => block.type === "tool_use" && typeof block.id === "string" ? [block.id] : []
|
|
32497
|
+
);
|
|
32498
|
+
}
|
|
32499
|
+
function toolResultIds(message) {
|
|
32500
|
+
if (message.type !== "user") {
|
|
32501
|
+
return [];
|
|
32502
|
+
}
|
|
32503
|
+
return contentBlocks(message.message.content).flatMap(
|
|
32504
|
+
(block) => block.type === "tool_result" && typeof block.tool_use_id === "string" ? [block.tool_use_id] : []
|
|
32505
|
+
);
|
|
32506
|
+
}
|
|
32507
|
+
function contentBlocks(content) {
|
|
32508
|
+
if (!Array.isArray(content)) {
|
|
32509
|
+
return [];
|
|
32510
|
+
}
|
|
32511
|
+
return content.filter(
|
|
32512
|
+
(block) => typeof block === "object" && block !== null
|
|
32513
|
+
);
|
|
32514
|
+
}
|
|
32408
32515
|
|
|
32409
32516
|
// src/commands/agent-bridge/harness/claude-code/index.ts
|
|
32410
32517
|
async function runAgentBridgeClaudeCode(options) {
|