@autohq/cli 0.1.293 → 0.1.295
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 +114 -22
- package/dist/index.js +131 -27
- package/package.json +1 -1
package/dist/agent-bridge.js
CHANGED
|
@@ -23399,7 +23399,7 @@ Object.assign(lookup, {
|
|
|
23399
23399
|
// package.json
|
|
23400
23400
|
var package_default = {
|
|
23401
23401
|
name: "@autohq/cli",
|
|
23402
|
-
version: "0.1.
|
|
23402
|
+
version: "0.1.295",
|
|
23403
23403
|
license: "SEE LICENSE IN README.md",
|
|
23404
23404
|
publishConfig: {
|
|
23405
23405
|
access: "public"
|
|
@@ -27210,7 +27210,16 @@ var SessionCheckTimeoutPhaseSchema = external_exports.enum(
|
|
|
27210
27210
|
);
|
|
27211
27211
|
var ManualSessionRequestSchema = external_exports.object({
|
|
27212
27212
|
message: external_exports.string().trim().min(1).max(2e4).optional(),
|
|
27213
|
-
interactive: external_exports.boolean().optional()
|
|
27213
|
+
interactive: external_exports.boolean().optional(),
|
|
27214
|
+
/**
|
|
27215
|
+
* Deliver the agent's configured `initialPrompt` as the session's kickoff
|
|
27216
|
+
* message so the agent opens the conversation. Agents without an
|
|
27217
|
+
* `initialPrompt` fall back to the default manual say-hello message, so a
|
|
27218
|
+
* kickoff always pings the agent. The kickoff is delivered through the
|
|
27219
|
+
* session's one idempotent start command and never renders as a user chat
|
|
27220
|
+
* bubble; the agent's greeting is the first visible content.
|
|
27221
|
+
*/
|
|
27222
|
+
kickoff: external_exports.boolean().optional()
|
|
27214
27223
|
});
|
|
27215
27224
|
var SessionArchiveRequestSchema = external_exports.object({
|
|
27216
27225
|
archived: external_exports.boolean()
|
|
@@ -28434,6 +28443,9 @@ var UsageEventSchema = external_exports.object({
|
|
|
28434
28443
|
});
|
|
28435
28444
|
var UsageEventRecordSchema = UsageEventSchema.extend({
|
|
28436
28445
|
id: external_exports.string().trim().min(1),
|
|
28446
|
+
// Set on append-only correction rows: this row replaces the pointed-at row's
|
|
28447
|
+
// amounts, and ledger readers exclude any row another row supersedes.
|
|
28448
|
+
supersedesUsageEventId: external_exports.string().trim().min(1).nullable().default(null),
|
|
28437
28449
|
createdAt: external_exports.string().datetime()
|
|
28438
28450
|
});
|
|
28439
28451
|
var UsageTotalsSchema = external_exports.object({
|
|
@@ -57686,6 +57698,7 @@ function withClaudeStderrDiagnosticsPointer(error51, capturedStderr) {
|
|
|
57686
57698
|
// src/commands/agent-bridge/harness/claude-code/session.ts
|
|
57687
57699
|
var CLAUDE_AGENT_STARTUP_TIMEOUT_MS = 3e4;
|
|
57688
57700
|
var CLAUDE_INTERRUPT_SETTLE_TIMEOUT_MS = 1e4;
|
|
57701
|
+
var CLAUDE_INTERRUPT_ACK_TIMEOUT_MS = 1e4;
|
|
57689
57702
|
var CLAUDE_MCP_REGISTRATION_TIMEOUT_MS = 3e3;
|
|
57690
57703
|
var CLAUDE_MCP_REGISTRATION_POLL_INTERVAL_MS = 100;
|
|
57691
57704
|
var CLAUDE_STARTUP_PROFILE_HOOK_EVENTS = [
|
|
@@ -57725,10 +57738,18 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
57725
57738
|
// decrements — so interleaved interrupt/result deliveries stay balanced.
|
|
57726
57739
|
activeTurnCount = 0;
|
|
57727
57740
|
interruptInFlight = null;
|
|
57741
|
+
// turnResultCount at the moment an interrupt ack timed out, or null when no
|
|
57742
|
+
// timeout is latched. While turnResultCount still equals this value the same
|
|
57743
|
+
// wedged turn is running, so follow-up messages defer immediately instead of
|
|
57744
|
+
// each re-racing a fresh ack timeout against the unresponsive control
|
|
57745
|
+
// channel (and stacking pending control requests). Any terminal result
|
|
57746
|
+
// advances turnResultCount and un-latches.
|
|
57747
|
+
interruptAckTimeoutTurn = null;
|
|
57728
57748
|
exitReported = false;
|
|
57729
|
-
// Messages
|
|
57730
|
-
//
|
|
57731
|
-
//
|
|
57749
|
+
// Messages held for the turn's terminal result instead of injected mid-turn
|
|
57750
|
+
// (which would reject a pending tool_use): "deferred"-mode deliveries, plus
|
|
57751
|
+
// interrupt-mode deliveries whose interrupt ack timed out. Flushed once the
|
|
57752
|
+
// turn ends and the session is idle.
|
|
57732
57753
|
deferredMessages = [];
|
|
57733
57754
|
// tool_use ids the assistant has emitted whose tool_result has not yet been
|
|
57734
57755
|
// observed. A non-empty set means a tool call is in flight, so an immediate
|
|
@@ -57808,7 +57829,19 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
57808
57829
|
});
|
|
57809
57830
|
return;
|
|
57810
57831
|
}
|
|
57811
|
-
await this.interruptActiveTurnBeforeMessage();
|
|
57832
|
+
const interruption = await this.interruptActiveTurnBeforeMessage();
|
|
57833
|
+
if (interruption === "defer" && this.hasInterruptibleTurn()) {
|
|
57834
|
+
this.deferredMessages.push(message);
|
|
57835
|
+
this.input.runtimeLogger?.info(
|
|
57836
|
+
"agent_bridge_claude_message_deferred_after_interrupt_timeout",
|
|
57837
|
+
{
|
|
57838
|
+
active_turn_count: this.activeTurnCount,
|
|
57839
|
+
pending_tool_use_count: this.pendingToolUseIds.size,
|
|
57840
|
+
deferred_count: this.deferredMessages.length
|
|
57841
|
+
}
|
|
57842
|
+
);
|
|
57843
|
+
return;
|
|
57844
|
+
}
|
|
57812
57845
|
await this.awaitMcpRegistration();
|
|
57813
57846
|
this.enqueueUserMessage(message);
|
|
57814
57847
|
this.input.runtimeLogger?.info("agent_bridge_claude_send_message_queued", {
|
|
@@ -58057,14 +58090,19 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
58057
58090
|
}
|
|
58058
58091
|
async interruptActiveTurnBeforeMessage() {
|
|
58059
58092
|
if (!this.hasInterruptibleTurn()) {
|
|
58060
|
-
return;
|
|
58093
|
+
return "proceed";
|
|
58061
58094
|
}
|
|
58062
58095
|
if (this.interruptInFlight) {
|
|
58063
|
-
await this.interruptInFlight;
|
|
58064
|
-
|
|
58096
|
+
return await this.interruptInFlight;
|
|
58097
|
+
}
|
|
58098
|
+
if (this.interruptAckTimeoutTurn !== null) {
|
|
58099
|
+
if (this.interruptAckTimeoutTurn === this.turnResultCount) {
|
|
58100
|
+
return "defer";
|
|
58101
|
+
}
|
|
58102
|
+
this.interruptAckTimeoutTurn = null;
|
|
58065
58103
|
}
|
|
58066
58104
|
if (this.state.kind !== "running") {
|
|
58067
|
-
return;
|
|
58105
|
+
return "proceed";
|
|
58068
58106
|
}
|
|
58069
58107
|
const query = this.state.query;
|
|
58070
58108
|
const hadInFlightToolUse = this.hasInFlightToolUse();
|
|
@@ -58074,29 +58112,79 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
58074
58112
|
`agent_bridge_claude_mid_turn_interrupt_started at=${new Date(startedAt).toISOString()} in_flight_tool_use=${hadInFlightToolUse}`
|
|
58075
58113
|
);
|
|
58076
58114
|
const interruptPromise = (async () => {
|
|
58077
|
-
|
|
58078
|
-
|
|
58079
|
-
this.
|
|
58080
|
-
|
|
58081
|
-
);
|
|
58082
|
-
} catch (error51) {
|
|
58083
|
-
this.input.writeOutput?.(
|
|
58084
|
-
`agent_bridge_claude_mid_turn_interrupt_failed duration_ms=${Date.now() - startedAt} error=${error51 instanceof Error ? error51.message : String(error51)}`
|
|
58085
|
-
);
|
|
58115
|
+
const ack = await this.requestInterruptAck(query, startedAt);
|
|
58116
|
+
if (ack === "timeout") {
|
|
58117
|
+
this.interruptAckTimeoutTurn = settlementBaseline;
|
|
58118
|
+
return "defer";
|
|
58086
58119
|
}
|
|
58087
58120
|
if (hadInFlightToolUse) {
|
|
58088
58121
|
await this.awaitTurnSettlement(settlementBaseline, startedAt);
|
|
58089
58122
|
}
|
|
58123
|
+
return "proceed";
|
|
58090
58124
|
})();
|
|
58091
58125
|
this.interruptInFlight = interruptPromise;
|
|
58092
58126
|
try {
|
|
58093
|
-
await interruptPromise;
|
|
58127
|
+
return await interruptPromise;
|
|
58094
58128
|
} finally {
|
|
58095
58129
|
if (this.interruptInFlight === interruptPromise) {
|
|
58096
58130
|
this.interruptInFlight = null;
|
|
58097
58131
|
}
|
|
58098
58132
|
}
|
|
58099
58133
|
}
|
|
58134
|
+
// Fire the interrupt control request, bounded by the ack timeout. "ready" =
|
|
58135
|
+
// the SDK acked; "failed" = the SDK rejected it (a steering message is still
|
|
58136
|
+
// useful, so the caller falls back to normal queued delivery); "timeout" =
|
|
58137
|
+
// no answer within CLAUDE_INTERRUPT_ACK_TIMEOUT_MS (FRA-3465). The abandoned
|
|
58138
|
+
// request stays attached to late-outcome logging so it can never surface as
|
|
58139
|
+
// an unhandled rejection after the timeout won.
|
|
58140
|
+
requestInterruptAck(query, startedAt) {
|
|
58141
|
+
return new Promise((resolve2) => {
|
|
58142
|
+
let done = false;
|
|
58143
|
+
const finish = (outcome, line) => {
|
|
58144
|
+
if (done) {
|
|
58145
|
+
return;
|
|
58146
|
+
}
|
|
58147
|
+
done = true;
|
|
58148
|
+
clearTimeout(timer);
|
|
58149
|
+
this.input.writeOutput?.(line);
|
|
58150
|
+
resolve2(outcome);
|
|
58151
|
+
};
|
|
58152
|
+
const timer = setTimeout(() => {
|
|
58153
|
+
finish(
|
|
58154
|
+
"timeout",
|
|
58155
|
+
`agent_bridge_claude_mid_turn_interrupt_ack_timeout duration_ms=${Date.now() - startedAt}`
|
|
58156
|
+
);
|
|
58157
|
+
}, CLAUDE_INTERRUPT_ACK_TIMEOUT_MS);
|
|
58158
|
+
timer.unref?.();
|
|
58159
|
+
query.interrupt().then(
|
|
58160
|
+
() => {
|
|
58161
|
+
if (done) {
|
|
58162
|
+
this.input.writeOutput?.(
|
|
58163
|
+
`agent_bridge_claude_mid_turn_interrupt_late_ready duration_ms=${Date.now() - startedAt}`
|
|
58164
|
+
);
|
|
58165
|
+
return;
|
|
58166
|
+
}
|
|
58167
|
+
finish(
|
|
58168
|
+
"ready",
|
|
58169
|
+
`agent_bridge_claude_mid_turn_interrupt_ready duration_ms=${Date.now() - startedAt}`
|
|
58170
|
+
);
|
|
58171
|
+
},
|
|
58172
|
+
(error51) => {
|
|
58173
|
+
const detail = `duration_ms=${Date.now() - startedAt} error=${error51 instanceof Error ? error51.message : String(error51)}`;
|
|
58174
|
+
if (done) {
|
|
58175
|
+
this.input.writeOutput?.(
|
|
58176
|
+
`agent_bridge_claude_mid_turn_interrupt_late_failed ${detail}`
|
|
58177
|
+
);
|
|
58178
|
+
return;
|
|
58179
|
+
}
|
|
58180
|
+
finish(
|
|
58181
|
+
"failed",
|
|
58182
|
+
`agent_bridge_claude_mid_turn_interrupt_failed ${detail}`
|
|
58183
|
+
);
|
|
58184
|
+
}
|
|
58185
|
+
);
|
|
58186
|
+
});
|
|
58187
|
+
}
|
|
58100
58188
|
// Why this gate is correct without a provider-backed test (it relies on real
|
|
58101
58189
|
// SDK mcpServerStatus() timing that fakes cannot prove):
|
|
58102
58190
|
// - Safe-degrade on uncertainty: pollMcpRegistration never rejects — on
|
|
@@ -60266,15 +60354,19 @@ function replaceErrors(_key, value2) {
|
|
|
60266
60354
|
async function runAgentBridgeProcess(input) {
|
|
60267
60355
|
const runAgentBridge = input.runAgentBridge ?? runAgentBridgeHarness;
|
|
60268
60356
|
const log = createRuntimeLogger(input.env);
|
|
60357
|
+
const writeOutput = (line) => {
|
|
60358
|
+
input.writeOutput?.(line);
|
|
60359
|
+
log.info(line);
|
|
60360
|
+
};
|
|
60269
60361
|
log.info("agent bridge process starting", {
|
|
60270
60362
|
bridge_url_host: bridgeUrlHost(input.env.AUTO_BRIDGE_URL),
|
|
60271
60363
|
log_level: log.level
|
|
60272
60364
|
});
|
|
60273
60365
|
try {
|
|
60274
60366
|
await runAgentBridge({
|
|
60275
|
-
...agentBridgeOptionsFromEnv(input),
|
|
60367
|
+
...agentBridgeOptionsFromEnv({ env: input.env, writeOutput }),
|
|
60276
60368
|
runtimeLogger: log,
|
|
60277
|
-
onBootstrap: createGitCredentialRelayStarter(
|
|
60369
|
+
onBootstrap: createGitCredentialRelayStarter(writeOutput, log)
|
|
60278
60370
|
});
|
|
60279
60371
|
} catch (error51) {
|
|
60280
60372
|
log.error("agent bridge process failed", { error: error51 });
|
package/dist/index.js
CHANGED
|
@@ -18941,7 +18941,16 @@ var init_sessions = __esm({
|
|
|
18941
18941
|
);
|
|
18942
18942
|
ManualSessionRequestSchema = external_exports.object({
|
|
18943
18943
|
message: external_exports.string().trim().min(1).max(2e4).optional(),
|
|
18944
|
-
interactive: external_exports.boolean().optional()
|
|
18944
|
+
interactive: external_exports.boolean().optional(),
|
|
18945
|
+
/**
|
|
18946
|
+
* Deliver the agent's configured `initialPrompt` as the session's kickoff
|
|
18947
|
+
* message so the agent opens the conversation. Agents without an
|
|
18948
|
+
* `initialPrompt` fall back to the default manual say-hello message, so a
|
|
18949
|
+
* kickoff always pings the agent. The kickoff is delivered through the
|
|
18950
|
+
* session's one idempotent start command and never renders as a user chat
|
|
18951
|
+
* bubble; the agent's greeting is the first visible content.
|
|
18952
|
+
*/
|
|
18953
|
+
kickoff: external_exports.boolean().optional()
|
|
18945
18954
|
});
|
|
18946
18955
|
SessionArchiveRequestSchema = external_exports.object({
|
|
18947
18956
|
archived: external_exports.boolean()
|
|
@@ -20336,6 +20345,9 @@ var init_usage = __esm({
|
|
|
20336
20345
|
});
|
|
20337
20346
|
UsageEventRecordSchema = UsageEventSchema.extend({
|
|
20338
20347
|
id: external_exports.string().trim().min(1),
|
|
20348
|
+
// Set on append-only correction rows: this row replaces the pointed-at row's
|
|
20349
|
+
// amounts, and ledger readers exclude any row another row supersedes.
|
|
20350
|
+
supersedesUsageEventId: external_exports.string().trim().min(1).nullable().default(null),
|
|
20339
20351
|
createdAt: external_exports.string().datetime()
|
|
20340
20352
|
});
|
|
20341
20353
|
UsageTotalsSchema = external_exports.object({
|
|
@@ -23182,7 +23194,7 @@ var init_package = __esm({
|
|
|
23182
23194
|
"package.json"() {
|
|
23183
23195
|
package_default = {
|
|
23184
23196
|
name: "@autohq/cli",
|
|
23185
|
-
version: "0.1.
|
|
23197
|
+
version: "0.1.295",
|
|
23186
23198
|
license: "SEE LICENSE IN README.md",
|
|
23187
23199
|
publishConfig: {
|
|
23188
23200
|
access: "public"
|
|
@@ -25118,7 +25130,10 @@ function readApplyAssets(resources, readAsset) {
|
|
|
25118
25130
|
`Invalid identity avatar asset for "${target.resourceName}": asset path must be under .auto/assets`
|
|
25119
25131
|
);
|
|
25120
25132
|
}
|
|
25121
|
-
const source = readAsset(
|
|
25133
|
+
const source = readAsset({
|
|
25134
|
+
asset: target.asset,
|
|
25135
|
+
resourceName: target.resourceName
|
|
25136
|
+
});
|
|
25122
25137
|
if (!source) {
|
|
25123
25138
|
throw new Error(
|
|
25124
25139
|
`Invalid identity avatar asset for "${target.resourceName}": ${target.asset} does not exist`
|
|
@@ -25158,12 +25173,20 @@ function projectApplyAssetFromSource(input) {
|
|
|
25158
25173
|
}
|
|
25159
25174
|
function avatarAssetTarget(resource) {
|
|
25160
25175
|
if (resource.kind === RESOURCE_KIND_IDENTITY) {
|
|
25161
|
-
const
|
|
25162
|
-
return
|
|
25176
|
+
const avatar = resource.spec.avatar;
|
|
25177
|
+
return avatar ? {
|
|
25178
|
+
asset: avatar.asset,
|
|
25179
|
+
sha256: avatar.sha256,
|
|
25180
|
+
resourceName: resource.metadata.name
|
|
25181
|
+
} : void 0;
|
|
25163
25182
|
}
|
|
25164
25183
|
if (resource.kind === RESOURCE_KIND_AGENT && typeof resource.spec.identity === "object" && resource.spec.identity !== null && !Array.isArray(resource.spec.identity)) {
|
|
25165
|
-
const
|
|
25166
|
-
return
|
|
25184
|
+
const avatar = resource.spec.identity.avatar;
|
|
25185
|
+
return avatar ? {
|
|
25186
|
+
asset: avatar.asset,
|
|
25187
|
+
sha256: avatar.sha256,
|
|
25188
|
+
resourceName: resource.metadata.name
|
|
25189
|
+
} : void 0;
|
|
25167
25190
|
}
|
|
25168
25191
|
return void 0;
|
|
25169
25192
|
}
|
|
@@ -25429,6 +25452,7 @@ var init_project_apply_files = __esm({
|
|
|
25429
25452
|
init_assets();
|
|
25430
25453
|
init_source();
|
|
25431
25454
|
init_template_injection();
|
|
25455
|
+
init_assets();
|
|
25432
25456
|
init_template_injection();
|
|
25433
25457
|
init_template_bump_diagnostics();
|
|
25434
25458
|
init_template_staleness();
|
|
@@ -35135,6 +35159,7 @@ function withClaudeStderrDiagnosticsPointer(error51, capturedStderr) {
|
|
|
35135
35159
|
// src/commands/agent-bridge/harness/claude-code/session.ts
|
|
35136
35160
|
var CLAUDE_AGENT_STARTUP_TIMEOUT_MS = 3e4;
|
|
35137
35161
|
var CLAUDE_INTERRUPT_SETTLE_TIMEOUT_MS = 1e4;
|
|
35162
|
+
var CLAUDE_INTERRUPT_ACK_TIMEOUT_MS = 1e4;
|
|
35138
35163
|
var CLAUDE_MCP_REGISTRATION_TIMEOUT_MS = 3e3;
|
|
35139
35164
|
var CLAUDE_MCP_REGISTRATION_POLL_INTERVAL_MS = 100;
|
|
35140
35165
|
var CLAUDE_STARTUP_PROFILE_HOOK_EVENTS = [
|
|
@@ -35174,10 +35199,18 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
35174
35199
|
// decrements — so interleaved interrupt/result deliveries stay balanced.
|
|
35175
35200
|
activeTurnCount = 0;
|
|
35176
35201
|
interruptInFlight = null;
|
|
35202
|
+
// turnResultCount at the moment an interrupt ack timed out, or null when no
|
|
35203
|
+
// timeout is latched. While turnResultCount still equals this value the same
|
|
35204
|
+
// wedged turn is running, so follow-up messages defer immediately instead of
|
|
35205
|
+
// each re-racing a fresh ack timeout against the unresponsive control
|
|
35206
|
+
// channel (and stacking pending control requests). Any terminal result
|
|
35207
|
+
// advances turnResultCount and un-latches.
|
|
35208
|
+
interruptAckTimeoutTurn = null;
|
|
35177
35209
|
exitReported = false;
|
|
35178
|
-
// Messages
|
|
35179
|
-
//
|
|
35180
|
-
//
|
|
35210
|
+
// Messages held for the turn's terminal result instead of injected mid-turn
|
|
35211
|
+
// (which would reject a pending tool_use): "deferred"-mode deliveries, plus
|
|
35212
|
+
// interrupt-mode deliveries whose interrupt ack timed out. Flushed once the
|
|
35213
|
+
// turn ends and the session is idle.
|
|
35181
35214
|
deferredMessages = [];
|
|
35182
35215
|
// tool_use ids the assistant has emitted whose tool_result has not yet been
|
|
35183
35216
|
// observed. A non-empty set means a tool call is in flight, so an immediate
|
|
@@ -35257,7 +35290,19 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
35257
35290
|
});
|
|
35258
35291
|
return;
|
|
35259
35292
|
}
|
|
35260
|
-
await this.interruptActiveTurnBeforeMessage();
|
|
35293
|
+
const interruption = await this.interruptActiveTurnBeforeMessage();
|
|
35294
|
+
if (interruption === "defer" && this.hasInterruptibleTurn()) {
|
|
35295
|
+
this.deferredMessages.push(message);
|
|
35296
|
+
this.input.runtimeLogger?.info(
|
|
35297
|
+
"agent_bridge_claude_message_deferred_after_interrupt_timeout",
|
|
35298
|
+
{
|
|
35299
|
+
active_turn_count: this.activeTurnCount,
|
|
35300
|
+
pending_tool_use_count: this.pendingToolUseIds.size,
|
|
35301
|
+
deferred_count: this.deferredMessages.length
|
|
35302
|
+
}
|
|
35303
|
+
);
|
|
35304
|
+
return;
|
|
35305
|
+
}
|
|
35261
35306
|
await this.awaitMcpRegistration();
|
|
35262
35307
|
this.enqueueUserMessage(message);
|
|
35263
35308
|
this.input.runtimeLogger?.info("agent_bridge_claude_send_message_queued", {
|
|
@@ -35506,14 +35551,19 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
35506
35551
|
}
|
|
35507
35552
|
async interruptActiveTurnBeforeMessage() {
|
|
35508
35553
|
if (!this.hasInterruptibleTurn()) {
|
|
35509
|
-
return;
|
|
35554
|
+
return "proceed";
|
|
35510
35555
|
}
|
|
35511
35556
|
if (this.interruptInFlight) {
|
|
35512
|
-
await this.interruptInFlight;
|
|
35513
|
-
|
|
35557
|
+
return await this.interruptInFlight;
|
|
35558
|
+
}
|
|
35559
|
+
if (this.interruptAckTimeoutTurn !== null) {
|
|
35560
|
+
if (this.interruptAckTimeoutTurn === this.turnResultCount) {
|
|
35561
|
+
return "defer";
|
|
35562
|
+
}
|
|
35563
|
+
this.interruptAckTimeoutTurn = null;
|
|
35514
35564
|
}
|
|
35515
35565
|
if (this.state.kind !== "running") {
|
|
35516
|
-
return;
|
|
35566
|
+
return "proceed";
|
|
35517
35567
|
}
|
|
35518
35568
|
const query = this.state.query;
|
|
35519
35569
|
const hadInFlightToolUse = this.hasInFlightToolUse();
|
|
@@ -35523,29 +35573,79 @@ var ClaudeAgentBridgeSessionImpl = class {
|
|
|
35523
35573
|
`agent_bridge_claude_mid_turn_interrupt_started at=${new Date(startedAt).toISOString()} in_flight_tool_use=${hadInFlightToolUse}`
|
|
35524
35574
|
);
|
|
35525
35575
|
const interruptPromise = (async () => {
|
|
35526
|
-
|
|
35527
|
-
|
|
35528
|
-
this.
|
|
35529
|
-
|
|
35530
|
-
);
|
|
35531
|
-
} catch (error51) {
|
|
35532
|
-
this.input.writeOutput?.(
|
|
35533
|
-
`agent_bridge_claude_mid_turn_interrupt_failed duration_ms=${Date.now() - startedAt} error=${error51 instanceof Error ? error51.message : String(error51)}`
|
|
35534
|
-
);
|
|
35576
|
+
const ack = await this.requestInterruptAck(query, startedAt);
|
|
35577
|
+
if (ack === "timeout") {
|
|
35578
|
+
this.interruptAckTimeoutTurn = settlementBaseline;
|
|
35579
|
+
return "defer";
|
|
35535
35580
|
}
|
|
35536
35581
|
if (hadInFlightToolUse) {
|
|
35537
35582
|
await this.awaitTurnSettlement(settlementBaseline, startedAt);
|
|
35538
35583
|
}
|
|
35584
|
+
return "proceed";
|
|
35539
35585
|
})();
|
|
35540
35586
|
this.interruptInFlight = interruptPromise;
|
|
35541
35587
|
try {
|
|
35542
|
-
await interruptPromise;
|
|
35588
|
+
return await interruptPromise;
|
|
35543
35589
|
} finally {
|
|
35544
35590
|
if (this.interruptInFlight === interruptPromise) {
|
|
35545
35591
|
this.interruptInFlight = null;
|
|
35546
35592
|
}
|
|
35547
35593
|
}
|
|
35548
35594
|
}
|
|
35595
|
+
// Fire the interrupt control request, bounded by the ack timeout. "ready" =
|
|
35596
|
+
// the SDK acked; "failed" = the SDK rejected it (a steering message is still
|
|
35597
|
+
// useful, so the caller falls back to normal queued delivery); "timeout" =
|
|
35598
|
+
// no answer within CLAUDE_INTERRUPT_ACK_TIMEOUT_MS (FRA-3465). The abandoned
|
|
35599
|
+
// request stays attached to late-outcome logging so it can never surface as
|
|
35600
|
+
// an unhandled rejection after the timeout won.
|
|
35601
|
+
requestInterruptAck(query, startedAt) {
|
|
35602
|
+
return new Promise((resolve4) => {
|
|
35603
|
+
let done = false;
|
|
35604
|
+
const finish = (outcome, line) => {
|
|
35605
|
+
if (done) {
|
|
35606
|
+
return;
|
|
35607
|
+
}
|
|
35608
|
+
done = true;
|
|
35609
|
+
clearTimeout(timer);
|
|
35610
|
+
this.input.writeOutput?.(line);
|
|
35611
|
+
resolve4(outcome);
|
|
35612
|
+
};
|
|
35613
|
+
const timer = setTimeout(() => {
|
|
35614
|
+
finish(
|
|
35615
|
+
"timeout",
|
|
35616
|
+
`agent_bridge_claude_mid_turn_interrupt_ack_timeout duration_ms=${Date.now() - startedAt}`
|
|
35617
|
+
);
|
|
35618
|
+
}, CLAUDE_INTERRUPT_ACK_TIMEOUT_MS);
|
|
35619
|
+
timer.unref?.();
|
|
35620
|
+
query.interrupt().then(
|
|
35621
|
+
() => {
|
|
35622
|
+
if (done) {
|
|
35623
|
+
this.input.writeOutput?.(
|
|
35624
|
+
`agent_bridge_claude_mid_turn_interrupt_late_ready duration_ms=${Date.now() - startedAt}`
|
|
35625
|
+
);
|
|
35626
|
+
return;
|
|
35627
|
+
}
|
|
35628
|
+
finish(
|
|
35629
|
+
"ready",
|
|
35630
|
+
`agent_bridge_claude_mid_turn_interrupt_ready duration_ms=${Date.now() - startedAt}`
|
|
35631
|
+
);
|
|
35632
|
+
},
|
|
35633
|
+
(error51) => {
|
|
35634
|
+
const detail = `duration_ms=${Date.now() - startedAt} error=${error51 instanceof Error ? error51.message : String(error51)}`;
|
|
35635
|
+
if (done) {
|
|
35636
|
+
this.input.writeOutput?.(
|
|
35637
|
+
`agent_bridge_claude_mid_turn_interrupt_late_failed ${detail}`
|
|
35638
|
+
);
|
|
35639
|
+
return;
|
|
35640
|
+
}
|
|
35641
|
+
finish(
|
|
35642
|
+
"failed",
|
|
35643
|
+
`agent_bridge_claude_mid_turn_interrupt_failed ${detail}`
|
|
35644
|
+
);
|
|
35645
|
+
}
|
|
35646
|
+
);
|
|
35647
|
+
});
|
|
35648
|
+
}
|
|
35549
35649
|
// Why this gate is correct without a provider-backed test (it relies on real
|
|
35550
35650
|
// SDK mcpServerStatus() timing that fakes cannot prove):
|
|
35551
35651
|
// - Safe-degrade on uncertainty: pollMcpRegistration never rejects — on
|
|
@@ -37721,15 +37821,19 @@ function replaceErrors(_key, value) {
|
|
|
37721
37821
|
async function runAgentBridgeProcess(input) {
|
|
37722
37822
|
const runAgentBridge = input.runAgentBridge ?? runAgentBridgeHarness;
|
|
37723
37823
|
const log = createRuntimeLogger(input.env);
|
|
37824
|
+
const writeOutput = (line) => {
|
|
37825
|
+
input.writeOutput?.(line);
|
|
37826
|
+
log.info(line);
|
|
37827
|
+
};
|
|
37724
37828
|
log.info("agent bridge process starting", {
|
|
37725
37829
|
bridge_url_host: bridgeUrlHost(input.env.AUTO_BRIDGE_URL),
|
|
37726
37830
|
log_level: log.level
|
|
37727
37831
|
});
|
|
37728
37832
|
try {
|
|
37729
37833
|
await runAgentBridge({
|
|
37730
|
-
...agentBridgeOptionsFromEnv(input),
|
|
37834
|
+
...agentBridgeOptionsFromEnv({ env: input.env, writeOutput }),
|
|
37731
37835
|
runtimeLogger: log,
|
|
37732
|
-
onBootstrap: createGitCredentialRelayStarter(
|
|
37836
|
+
onBootstrap: createGitCredentialRelayStarter(writeOutput, log)
|
|
37733
37837
|
});
|
|
37734
37838
|
} catch (error51) {
|
|
37735
37839
|
log.error("agent bridge process failed", { error: error51 });
|