@autohq/cli 0.1.286 → 0.1.288

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.
@@ -23384,7 +23384,7 @@ Object.assign(lookup, {
23384
23384
  // package.json
23385
23385
  var package_default = {
23386
23386
  name: "@autohq/cli",
23387
- version: "0.1.286",
23387
+ version: "0.1.288",
23388
23388
  license: "SEE LICENSE IN README.md",
23389
23389
  publishConfig: {
23390
23390
  access: "public"
@@ -28362,6 +28362,9 @@ var ProjectTemplateSubscriptionSchema = external_exports.object({
28362
28362
  });
28363
28363
 
28364
28364
  // ../../packages/schemas/src/temporal.ts
28365
+ var SessionCheckTimeoutWorkflowInputSchema = external_exports.object({
28366
+ checkId: external_exports.string().trim().min(1)
28367
+ });
28365
28368
  var ResourceReconciliationScopeSchema = external_exports.object({
28366
28369
  organizationId: external_exports.string().trim().min(1),
28367
28370
  projectId: external_exports.string().trim().min(1).nullable()
@@ -28428,6 +28431,42 @@ var SessionUsageResponseSchema = external_exports.object({
28428
28431
  firstUsageAt: external_exports.string().datetime().nullable(),
28429
28432
  lastUsageAt: external_exports.string().datetime().nullable()
28430
28433
  });
28434
+ var PROJECT_USAGE_RANGE_KEYS = ["7d", "30d"];
28435
+ var ProjectUsageRangeKeySchema = external_exports.enum(PROJECT_USAGE_RANGE_KEYS);
28436
+ var UtcDateSchema = external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/);
28437
+ var ProjectUsageTotalsSchema = UsageTotalsSchema.extend({
28438
+ sessionCount: NonNegativeTokenCountSchema,
28439
+ // Share of all tokens served from prompt-cache reads (0..1); 0 when the
28440
+ // window has no tokens at all.
28441
+ cacheReadShare: external_exports.number().min(0).max(1)
28442
+ });
28443
+ var ProjectUsageAgentSummarySchema = UsageTotalsSchema.extend({
28444
+ agentResourceId: external_exports.string().trim().min(1),
28445
+ // Resolved from the agent resource (archived agents included); null only
28446
+ // when the agent row no longer exists at all — the ledger deliberately
28447
+ // outlives its attribution targets.
28448
+ agentName: external_exports.string().trim().min(1).nullable(),
28449
+ sessionCount: NonNegativeTokenCountSchema
28450
+ });
28451
+ var ProjectUsageModelSummarySchema = UsageTotalsSchema.extend({
28452
+ model: external_exports.string().trim().min(1)
28453
+ });
28454
+ var ProjectUsageDayPointSchema = UsageTotalsSchema.extend({
28455
+ date: UtcDateSchema
28456
+ });
28457
+ var ProjectUsageResponseSchema = external_exports.object({
28458
+ organizationId: OrganizationIdSchema,
28459
+ projectId: ProjectIdSchema,
28460
+ range: external_exports.object({
28461
+ key: ProjectUsageRangeKeySchema,
28462
+ since: external_exports.string().datetime(),
28463
+ until: external_exports.string().datetime()
28464
+ }),
28465
+ totals: ProjectUsageTotalsSchema,
28466
+ byAgent: external_exports.array(ProjectUsageAgentSummarySchema),
28467
+ byModel: external_exports.array(ProjectUsageModelSummarySchema),
28468
+ daily: external_exports.array(ProjectUsageDayPointSchema)
28469
+ });
28431
28470
 
28432
28471
  // ../../node_modules/ai/node_modules/@ai-sdk/provider/dist/index.mjs
28433
28472
  var marker = "vercel.ai.error";
@@ -57304,6 +57343,12 @@ var claudeAgentBridgeRuntime = {
57304
57343
  var claudeAgentSdkRuntime = {
57305
57344
  startup: (input) => aj$(input)
57306
57345
  };
57346
+ var ClaudeMcpRegistryEmptyError = class extends Error {
57347
+ constructor(detail) {
57348
+ super(`MCP tool registry came up empty at session start: ${detail}`);
57349
+ this.name = "ClaudeMcpRegistryEmptyError";
57350
+ }
57351
+ };
57307
57352
  var ClaudeAgentBridgeSessionImpl = class {
57308
57353
  input;
57309
57354
  inputQueue;
@@ -57693,17 +57738,20 @@ var ClaudeAgentBridgeSessionImpl = class {
57693
57738
  }
57694
57739
  // Why this gate is correct without a provider-backed test (it relies on real
57695
57740
  // SDK mcpServerStatus() timing that fakes cannot prove):
57696
- // - Safe-degrade: pollMcpRegistration never rejects — on timeout, a
57697
- // status-read failure, or a closed session it resolves, so the worst case
57698
- // is today's immediate injection. The gate can never regress below the
57699
- // status-quo race; it can only close it.
57741
+ // - Safe-degrade on uncertainty: pollMcpRegistration never rejects — on
57742
+ // timeout, a status-read failure, or a closed session it resolves, so an
57743
+ // *unconfirmed* registry still degrades to today's immediate injection.
57744
+ // The one fail-closed exit is the confirmed-empty registry: every
57745
+ // configured server settled and none is serving tools, which is exactly
57746
+ // the state that produced silent toolless runs in production (FRA-3458).
57700
57747
  // - Provider-path validation is deferred deliberately: the failure is the
57701
57748
  // intermittent live-prod race on the chief-of-staff singleton (a real
57702
57749
  // runtime restart dispatching turn-1 while real mcp__* servers reconnect),
57703
57750
  // which cannot be reproduced deterministically without live credentials
57704
57751
  // and a forced restart racing reconnect.
57705
57752
  // - Live confirmation comes from the agent_bridge_claude_mcp_gate_started/
57706
- // _ready/_timeout diagnostics on the next real restart, not a CI fake.
57753
+ // _ready/_timeout/_empty diagnostics on the next real restart, not a CI
57754
+ // fake.
57707
57755
  //
57708
57756
  // Block until the freshly-warmed query's MCP servers have left `pending`, so
57709
57757
  // the first turn's prompt is built with the mcp__* tools registered. Memoized
@@ -57727,15 +57775,22 @@ var ClaudeAgentBridgeSessionImpl = class {
57727
57775
  promise: this.pollMcpRegistration(query)
57728
57776
  };
57729
57777
  }
57730
- await this.mcpRegistrationGate.promise;
57778
+ const result = await this.mcpRegistrationGate.promise;
57779
+ if (result.outcome !== "empty") {
57780
+ return;
57781
+ }
57782
+ this.close();
57783
+ throw new ClaudeMcpRegistryEmptyError(result.detail);
57731
57784
  }
57732
57785
  // Poll the live query's MCP server status until no configured server is still
57733
57786
  // `pending`, bounded by CLAUDE_MCP_REGISTRATION_TIMEOUT_MS. Terminal states
57734
57787
  // (connected/failed/needs-auth/disabled) all count as settled, so a server
57735
57788
  // that cannot connect in this runtime (e.g. an interactively-authed server in
57736
- // a headless sandbox) never holds the gate to the timeout. Never rejects:
57789
+ // a headless sandbox) never holds the gate to the timeout. A settled state
57790
+ // where no configured server is serving tools resolves "empty" — the caller
57791
+ // fails the send rather than injecting a toolless turn. Never rejects:
57737
57792
  // status-read failure, timeout, or a closed session all resolve so the caller
57738
- // degrades to immediate injection.
57793
+ // degrades to immediate injection when the registry state is uncertain.
57739
57794
  async pollMcpRegistration(query) {
57740
57795
  const configuredNames = Object.keys(this.options.mcpServers ?? {});
57741
57796
  const startedAt = Date.now();
@@ -57745,7 +57800,7 @@ var ClaudeAgentBridgeSessionImpl = class {
57745
57800
  );
57746
57801
  while (true) {
57747
57802
  if (this.state.kind === "closed") {
57748
- return;
57803
+ return { outcome: "closed" };
57749
57804
  }
57750
57805
  let statuses;
57751
57806
  try {
@@ -57754,19 +57809,26 @@ var ClaudeAgentBridgeSessionImpl = class {
57754
57809
  this.input.writeOutput?.(
57755
57810
  `agent_bridge_claude_mcp_gate_status_failed duration_ms=${Date.now() - startedAt} error=${error51 instanceof Error ? error51.message : String(error51)}`
57756
57811
  );
57757
- return;
57812
+ return { outcome: "status_failed" };
57758
57813
  }
57759
57814
  if (mcpServersSettled(statuses, configuredNames)) {
57815
+ if (!mcpRegistryHasTools(statuses, configuredNames)) {
57816
+ const detail = mcpStatusDetailList(statuses);
57817
+ this.input.writeOutput?.(
57818
+ `agent_bridge_claude_mcp_gate_empty duration_ms=${Date.now() - startedAt} servers=${detail}`
57819
+ );
57820
+ return { outcome: "empty", detail };
57821
+ }
57760
57822
  this.input.writeOutput?.(
57761
57823
  `agent_bridge_claude_mcp_gate_ready duration_ms=${Date.now() - startedAt} servers=${mcpStatusList(statuses)}`
57762
57824
  );
57763
- return;
57825
+ return { outcome: "ready" };
57764
57826
  }
57765
57827
  if (Date.now() >= deadline) {
57766
57828
  this.input.writeOutput?.(
57767
57829
  `agent_bridge_claude_mcp_gate_timeout duration_ms=${Date.now() - startedAt} servers=${mcpStatusList(statuses)}`
57768
57830
  );
57769
- return;
57831
+ return { outcome: "timeout" };
57770
57832
  }
57771
57833
  await delay(CLAUDE_MCP_REGISTRATION_POLL_INTERVAL_MS);
57772
57834
  }
@@ -57909,9 +57971,20 @@ function mcpServersSettled(statuses, configuredNames) {
57909
57971
  return status !== void 0 && status !== "pending";
57910
57972
  });
57911
57973
  }
57974
+ function mcpRegistryHasTools(statuses, configuredNames) {
57975
+ const configured = new Set(configuredNames);
57976
+ return statuses.some(
57977
+ (server) => configured.has(server.name) && server.status === "connected" && (server.tools === void 0 || server.tools.length > 0)
57978
+ );
57979
+ }
57912
57980
  function mcpStatusList(statuses) {
57913
57981
  return statuses.map((server) => `${server.name}:${server.status}`).join(",");
57914
57982
  }
57983
+ function mcpStatusDetailList(statuses) {
57984
+ return statuses.map(
57985
+ (server) => server.error ? `${server.name}:${server.status}(${server.error})` : `${server.name}:${server.status}`
57986
+ ).join(",");
57987
+ }
57915
57988
  function delay(ms) {
57916
57989
  return new Promise((resolve2) => {
57917
57990
  const timer = setTimeout(resolve2, ms);
@@ -58019,6 +58092,13 @@ var ClaudeCodeCommandHandler = class {
58019
58092
  agentSession = null;
58020
58093
  persistedAgentId = null;
58021
58094
  injectedCommands = /* @__PURE__ */ new Set();
58095
+ // Message commands whose injection has started but not settled. A fresh SDK
58096
+ // warm inside sendMessage can outlast the bridge's command-ack window, so a
58097
+ // redelivery may arrive while the first attempt is still in flight; acking it
58098
+ // "duplicate" then would let the worker accept a command whose injection
58099
+ // later fails, silently stalling the turn (FRA-3458). The duplicate branch
58100
+ // awaits the in-flight outcome here and relays it instead.
58101
+ inFlightMessageCommands = /* @__PURE__ */ new Map();
58022
58102
  pendingQuestions = /* @__PURE__ */ new Map();
58023
58103
  outputBuffer;
58024
58104
  projector = new ClaudeCodeProjector();
@@ -58066,13 +58146,25 @@ var ClaudeCodeCommandHandler = class {
58066
58146
  });
58067
58147
  }
58068
58148
  if (this.injectedCommands.has(delivery.commandId)) {
58149
+ const inFlight = this.inFlightMessageCommands.get(delivery.commandId);
58150
+ const outcome2 = inFlight ? await inFlight : { status: "injected" };
58069
58151
  this.input.runtimeLogger?.info(
58070
58152
  "agent_bridge_claude_command_duplicate",
58071
58153
  commandLogContext(delivery, {
58072
58154
  socket_id: socketId,
58073
- duration_ms: Date.now() - startedAt
58155
+ duration_ms: Date.now() - startedAt,
58156
+ awaited_in_flight: Boolean(inFlight),
58157
+ in_flight_status: outcome2.status
58074
58158
  })
58075
58159
  );
58160
+ if (outcome2.status === "failed") {
58161
+ return commandAck({
58162
+ delivery,
58163
+ socketId,
58164
+ status: "failed",
58165
+ error: outcome2.error
58166
+ });
58167
+ }
58076
58168
  return commandAck({
58077
58169
  delivery,
58078
58170
  socketId,
@@ -58123,6 +58215,53 @@ var ClaudeCodeCommandHandler = class {
58123
58215
  });
58124
58216
  }
58125
58217
  this.injectedCommands.add(delivery.commandId);
58218
+ const injection = this.injectMessageCommand(
58219
+ activeContext,
58220
+ delivery,
58221
+ message,
58222
+ socketId
58223
+ );
58224
+ this.inFlightMessageCommands.set(delivery.commandId, injection);
58225
+ let outcome;
58226
+ try {
58227
+ outcome = await injection;
58228
+ } finally {
58229
+ this.inFlightMessageCommands.delete(delivery.commandId);
58230
+ }
58231
+ if (outcome.status === "failed") {
58232
+ this.injectedCommands.delete(delivery.commandId);
58233
+ this.input.runtimeLogger?.warn(
58234
+ "agent_bridge_claude_command_failed",
58235
+ commandLogContext(delivery, {
58236
+ socket_id: socketId,
58237
+ duration_ms: Date.now() - startedAt,
58238
+ error: outcome.error
58239
+ })
58240
+ );
58241
+ return commandAck({
58242
+ delivery,
58243
+ socketId,
58244
+ status: "failed",
58245
+ error: outcome.error
58246
+ });
58247
+ }
58248
+ this.input.runtimeLogger?.info(
58249
+ "agent_bridge_claude_command_injected",
58250
+ commandLogContext(delivery, {
58251
+ socket_id: socketId,
58252
+ duration_ms: Date.now() - startedAt
58253
+ })
58254
+ );
58255
+ return commandAck({
58256
+ delivery,
58257
+ socketId,
58258
+ status: "injected"
58259
+ });
58260
+ }
58261
+ // Runs one message command's injection to a terminal outcome. Never rejects:
58262
+ // the returned promise is stored in the in-flight map and awaited by both the
58263
+ // original delivery and any redeliveries, so failures travel as values.
58264
+ async injectMessageCommand(activeContext, delivery, message, socketId) {
58126
58265
  try {
58127
58266
  const emitStartedAt = Date.now();
58128
58267
  this.input.runtimeLogger?.info(
@@ -58168,35 +58307,13 @@ var ClaudeCodeCommandHandler = class {
58168
58307
  duration_ms: Date.now() - sendStartedAt
58169
58308
  })
58170
58309
  );
58310
+ return { status: "injected" };
58171
58311
  } catch (error51) {
58172
- this.injectedCommands.delete(delivery.commandId);
58173
- this.input.runtimeLogger?.warn(
58174
- "agent_bridge_claude_command_failed",
58175
- commandLogContext(delivery, {
58176
- socket_id: socketId,
58177
- duration_ms: Date.now() - startedAt,
58178
- error: error51 instanceof Error ? error51.message : String(error51)
58179
- })
58180
- );
58181
- return commandAck({
58182
- delivery,
58183
- socketId,
58312
+ return {
58184
58313
  status: "failed",
58185
58314
  error: error51 instanceof Error ? error51.message : String(error51)
58186
- });
58315
+ };
58187
58316
  }
58188
- this.input.runtimeLogger?.info(
58189
- "agent_bridge_claude_command_injected",
58190
- commandLogContext(delivery, {
58191
- socket_id: socketId,
58192
- duration_ms: Date.now() - startedAt
58193
- })
58194
- );
58195
- return commandAck({
58196
- delivery,
58197
- socketId,
58198
- status: "injected"
58199
- });
58200
58317
  }
58201
58318
  // -----------------------------------------------------------------------------
58202
58319
  // Question answering
package/dist/index.js CHANGED
@@ -20265,11 +20265,14 @@ var init_templates = __esm({
20265
20265
  });
20266
20266
 
20267
20267
  // ../../packages/schemas/src/temporal.ts
20268
- var ResourceReconciliationScopeSchema;
20268
+ var SessionCheckTimeoutWorkflowInputSchema, ResourceReconciliationScopeSchema;
20269
20269
  var init_temporal = __esm({
20270
20270
  "../../packages/schemas/src/temporal.ts"() {
20271
20271
  "use strict";
20272
20272
  init_zod();
20273
+ SessionCheckTimeoutWorkflowInputSchema = external_exports.object({
20274
+ checkId: external_exports.string().trim().min(1)
20275
+ });
20273
20276
  ResourceReconciliationScopeSchema = external_exports.object({
20274
20277
  organizationId: external_exports.string().trim().min(1),
20275
20278
  projectId: external_exports.string().trim().min(1).nullable()
@@ -20278,7 +20281,7 @@ var init_temporal = __esm({
20278
20281
  });
20279
20282
 
20280
20283
  // ../../packages/schemas/src/usage.ts
20281
- var UsageHarnessSchema, ModelCredentialFundingSourceSchema, NonNegativeTokenCountSchema, NonNegativeUsdSchema, UsageEventSchema, UsageEventRecordSchema, UsageTotalsSchema, SessionUsageModelSummarySchema, SessionUsageResponseSchema;
20284
+ var UsageHarnessSchema, ModelCredentialFundingSourceSchema, NonNegativeTokenCountSchema, NonNegativeUsdSchema, UsageEventSchema, UsageEventRecordSchema, UsageTotalsSchema, SessionUsageModelSummarySchema, SessionUsageResponseSchema, PROJECT_USAGE_RANGE_KEYS, ProjectUsageRangeKeySchema, UtcDateSchema, ProjectUsageTotalsSchema, ProjectUsageAgentSummarySchema, ProjectUsageModelSummarySchema, ProjectUsageDayPointSchema, ProjectUsageResponseSchema;
20282
20285
  var init_usage = __esm({
20283
20286
  "../../packages/schemas/src/usage.ts"() {
20284
20287
  "use strict";
@@ -20345,6 +20348,42 @@ var init_usage = __esm({
20345
20348
  firstUsageAt: external_exports.string().datetime().nullable(),
20346
20349
  lastUsageAt: external_exports.string().datetime().nullable()
20347
20350
  });
20351
+ PROJECT_USAGE_RANGE_KEYS = ["7d", "30d"];
20352
+ ProjectUsageRangeKeySchema = external_exports.enum(PROJECT_USAGE_RANGE_KEYS);
20353
+ UtcDateSchema = external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/);
20354
+ ProjectUsageTotalsSchema = UsageTotalsSchema.extend({
20355
+ sessionCount: NonNegativeTokenCountSchema,
20356
+ // Share of all tokens served from prompt-cache reads (0..1); 0 when the
20357
+ // window has no tokens at all.
20358
+ cacheReadShare: external_exports.number().min(0).max(1)
20359
+ });
20360
+ ProjectUsageAgentSummarySchema = UsageTotalsSchema.extend({
20361
+ agentResourceId: external_exports.string().trim().min(1),
20362
+ // Resolved from the agent resource (archived agents included); null only
20363
+ // when the agent row no longer exists at all — the ledger deliberately
20364
+ // outlives its attribution targets.
20365
+ agentName: external_exports.string().trim().min(1).nullable(),
20366
+ sessionCount: NonNegativeTokenCountSchema
20367
+ });
20368
+ ProjectUsageModelSummarySchema = UsageTotalsSchema.extend({
20369
+ model: external_exports.string().trim().min(1)
20370
+ });
20371
+ ProjectUsageDayPointSchema = UsageTotalsSchema.extend({
20372
+ date: UtcDateSchema
20373
+ });
20374
+ ProjectUsageResponseSchema = external_exports.object({
20375
+ organizationId: OrganizationIdSchema,
20376
+ projectId: ProjectIdSchema,
20377
+ range: external_exports.object({
20378
+ key: ProjectUsageRangeKeySchema,
20379
+ since: external_exports.string().datetime(),
20380
+ until: external_exports.string().datetime()
20381
+ }),
20382
+ totals: ProjectUsageTotalsSchema,
20383
+ byAgent: external_exports.array(ProjectUsageAgentSummarySchema),
20384
+ byModel: external_exports.array(ProjectUsageModelSummarySchema),
20385
+ daily: external_exports.array(ProjectUsageDayPointSchema)
20386
+ });
20348
20387
  }
20349
20388
  });
20350
20389
 
@@ -23134,7 +23173,7 @@ var init_package = __esm({
23134
23173
  "package.json"() {
23135
23174
  package_default = {
23136
23175
  name: "@autohq/cli",
23137
- version: "0.1.286",
23176
+ version: "0.1.288",
23138
23177
  license: "SEE LICENSE IN README.md",
23139
23178
  publishConfig: {
23140
23179
  access: "public"
@@ -34750,6 +34789,12 @@ var claudeAgentBridgeRuntime = {
34750
34789
  var claudeAgentSdkRuntime = {
34751
34790
  startup: (input) => startupClaudeAgent(input)
34752
34791
  };
34792
+ var ClaudeMcpRegistryEmptyError = class extends Error {
34793
+ constructor(detail) {
34794
+ super(`MCP tool registry came up empty at session start: ${detail}`);
34795
+ this.name = "ClaudeMcpRegistryEmptyError";
34796
+ }
34797
+ };
34753
34798
  var ClaudeAgentBridgeSessionImpl = class {
34754
34799
  input;
34755
34800
  inputQueue;
@@ -35139,17 +35184,20 @@ var ClaudeAgentBridgeSessionImpl = class {
35139
35184
  }
35140
35185
  // Why this gate is correct without a provider-backed test (it relies on real
35141
35186
  // SDK mcpServerStatus() timing that fakes cannot prove):
35142
- // - Safe-degrade: pollMcpRegistration never rejects — on timeout, a
35143
- // status-read failure, or a closed session it resolves, so the worst case
35144
- // is today's immediate injection. The gate can never regress below the
35145
- // status-quo race; it can only close it.
35187
+ // - Safe-degrade on uncertainty: pollMcpRegistration never rejects — on
35188
+ // timeout, a status-read failure, or a closed session it resolves, so an
35189
+ // *unconfirmed* registry still degrades to today's immediate injection.
35190
+ // The one fail-closed exit is the confirmed-empty registry: every
35191
+ // configured server settled and none is serving tools, which is exactly
35192
+ // the state that produced silent toolless runs in production (FRA-3458).
35146
35193
  // - Provider-path validation is deferred deliberately: the failure is the
35147
35194
  // intermittent live-prod race on the chief-of-staff singleton (a real
35148
35195
  // runtime restart dispatching turn-1 while real mcp__* servers reconnect),
35149
35196
  // which cannot be reproduced deterministically without live credentials
35150
35197
  // and a forced restart racing reconnect.
35151
35198
  // - Live confirmation comes from the agent_bridge_claude_mcp_gate_started/
35152
- // _ready/_timeout diagnostics on the next real restart, not a CI fake.
35199
+ // _ready/_timeout/_empty diagnostics on the next real restart, not a CI
35200
+ // fake.
35153
35201
  //
35154
35202
  // Block until the freshly-warmed query's MCP servers have left `pending`, so
35155
35203
  // the first turn's prompt is built with the mcp__* tools registered. Memoized
@@ -35173,15 +35221,22 @@ var ClaudeAgentBridgeSessionImpl = class {
35173
35221
  promise: this.pollMcpRegistration(query)
35174
35222
  };
35175
35223
  }
35176
- await this.mcpRegistrationGate.promise;
35224
+ const result = await this.mcpRegistrationGate.promise;
35225
+ if (result.outcome !== "empty") {
35226
+ return;
35227
+ }
35228
+ this.close();
35229
+ throw new ClaudeMcpRegistryEmptyError(result.detail);
35177
35230
  }
35178
35231
  // Poll the live query's MCP server status until no configured server is still
35179
35232
  // `pending`, bounded by CLAUDE_MCP_REGISTRATION_TIMEOUT_MS. Terminal states
35180
35233
  // (connected/failed/needs-auth/disabled) all count as settled, so a server
35181
35234
  // that cannot connect in this runtime (e.g. an interactively-authed server in
35182
- // a headless sandbox) never holds the gate to the timeout. Never rejects:
35235
+ // a headless sandbox) never holds the gate to the timeout. A settled state
35236
+ // where no configured server is serving tools resolves "empty" — the caller
35237
+ // fails the send rather than injecting a toolless turn. Never rejects:
35183
35238
  // status-read failure, timeout, or a closed session all resolve so the caller
35184
- // degrades to immediate injection.
35239
+ // degrades to immediate injection when the registry state is uncertain.
35185
35240
  async pollMcpRegistration(query) {
35186
35241
  const configuredNames = Object.keys(this.options.mcpServers ?? {});
35187
35242
  const startedAt = Date.now();
@@ -35191,7 +35246,7 @@ var ClaudeAgentBridgeSessionImpl = class {
35191
35246
  );
35192
35247
  while (true) {
35193
35248
  if (this.state.kind === "closed") {
35194
- return;
35249
+ return { outcome: "closed" };
35195
35250
  }
35196
35251
  let statuses;
35197
35252
  try {
@@ -35200,19 +35255,26 @@ var ClaudeAgentBridgeSessionImpl = class {
35200
35255
  this.input.writeOutput?.(
35201
35256
  `agent_bridge_claude_mcp_gate_status_failed duration_ms=${Date.now() - startedAt} error=${error51 instanceof Error ? error51.message : String(error51)}`
35202
35257
  );
35203
- return;
35258
+ return { outcome: "status_failed" };
35204
35259
  }
35205
35260
  if (mcpServersSettled(statuses, configuredNames)) {
35261
+ if (!mcpRegistryHasTools(statuses, configuredNames)) {
35262
+ const detail = mcpStatusDetailList(statuses);
35263
+ this.input.writeOutput?.(
35264
+ `agent_bridge_claude_mcp_gate_empty duration_ms=${Date.now() - startedAt} servers=${detail}`
35265
+ );
35266
+ return { outcome: "empty", detail };
35267
+ }
35206
35268
  this.input.writeOutput?.(
35207
35269
  `agent_bridge_claude_mcp_gate_ready duration_ms=${Date.now() - startedAt} servers=${mcpStatusList(statuses)}`
35208
35270
  );
35209
- return;
35271
+ return { outcome: "ready" };
35210
35272
  }
35211
35273
  if (Date.now() >= deadline) {
35212
35274
  this.input.writeOutput?.(
35213
35275
  `agent_bridge_claude_mcp_gate_timeout duration_ms=${Date.now() - startedAt} servers=${mcpStatusList(statuses)}`
35214
35276
  );
35215
- return;
35277
+ return { outcome: "timeout" };
35216
35278
  }
35217
35279
  await delay(CLAUDE_MCP_REGISTRATION_POLL_INTERVAL_MS);
35218
35280
  }
@@ -35355,9 +35417,20 @@ function mcpServersSettled(statuses, configuredNames) {
35355
35417
  return status !== void 0 && status !== "pending";
35356
35418
  });
35357
35419
  }
35420
+ function mcpRegistryHasTools(statuses, configuredNames) {
35421
+ const configured = new Set(configuredNames);
35422
+ return statuses.some(
35423
+ (server) => configured.has(server.name) && server.status === "connected" && (server.tools === void 0 || server.tools.length > 0)
35424
+ );
35425
+ }
35358
35426
  function mcpStatusList(statuses) {
35359
35427
  return statuses.map((server) => `${server.name}:${server.status}`).join(",");
35360
35428
  }
35429
+ function mcpStatusDetailList(statuses) {
35430
+ return statuses.map(
35431
+ (server) => server.error ? `${server.name}:${server.status}(${server.error})` : `${server.name}:${server.status}`
35432
+ ).join(",");
35433
+ }
35361
35434
  function delay(ms) {
35362
35435
  return new Promise((resolve4) => {
35363
35436
  const timer = setTimeout(resolve4, ms);
@@ -35465,6 +35538,13 @@ var ClaudeCodeCommandHandler = class {
35465
35538
  agentSession = null;
35466
35539
  persistedAgentId = null;
35467
35540
  injectedCommands = /* @__PURE__ */ new Set();
35541
+ // Message commands whose injection has started but not settled. A fresh SDK
35542
+ // warm inside sendMessage can outlast the bridge's command-ack window, so a
35543
+ // redelivery may arrive while the first attempt is still in flight; acking it
35544
+ // "duplicate" then would let the worker accept a command whose injection
35545
+ // later fails, silently stalling the turn (FRA-3458). The duplicate branch
35546
+ // awaits the in-flight outcome here and relays it instead.
35547
+ inFlightMessageCommands = /* @__PURE__ */ new Map();
35468
35548
  pendingQuestions = /* @__PURE__ */ new Map();
35469
35549
  outputBuffer;
35470
35550
  projector = new ClaudeCodeProjector();
@@ -35512,13 +35592,25 @@ var ClaudeCodeCommandHandler = class {
35512
35592
  });
35513
35593
  }
35514
35594
  if (this.injectedCommands.has(delivery.commandId)) {
35595
+ const inFlight = this.inFlightMessageCommands.get(delivery.commandId);
35596
+ const outcome2 = inFlight ? await inFlight : { status: "injected" };
35515
35597
  this.input.runtimeLogger?.info(
35516
35598
  "agent_bridge_claude_command_duplicate",
35517
35599
  commandLogContext(delivery, {
35518
35600
  socket_id: socketId,
35519
- duration_ms: Date.now() - startedAt
35601
+ duration_ms: Date.now() - startedAt,
35602
+ awaited_in_flight: Boolean(inFlight),
35603
+ in_flight_status: outcome2.status
35520
35604
  })
35521
35605
  );
35606
+ if (outcome2.status === "failed") {
35607
+ return commandAck({
35608
+ delivery,
35609
+ socketId,
35610
+ status: "failed",
35611
+ error: outcome2.error
35612
+ });
35613
+ }
35522
35614
  return commandAck({
35523
35615
  delivery,
35524
35616
  socketId,
@@ -35569,6 +35661,53 @@ var ClaudeCodeCommandHandler = class {
35569
35661
  });
35570
35662
  }
35571
35663
  this.injectedCommands.add(delivery.commandId);
35664
+ const injection = this.injectMessageCommand(
35665
+ activeContext,
35666
+ delivery,
35667
+ message,
35668
+ socketId
35669
+ );
35670
+ this.inFlightMessageCommands.set(delivery.commandId, injection);
35671
+ let outcome;
35672
+ try {
35673
+ outcome = await injection;
35674
+ } finally {
35675
+ this.inFlightMessageCommands.delete(delivery.commandId);
35676
+ }
35677
+ if (outcome.status === "failed") {
35678
+ this.injectedCommands.delete(delivery.commandId);
35679
+ this.input.runtimeLogger?.warn(
35680
+ "agent_bridge_claude_command_failed",
35681
+ commandLogContext(delivery, {
35682
+ socket_id: socketId,
35683
+ duration_ms: Date.now() - startedAt,
35684
+ error: outcome.error
35685
+ })
35686
+ );
35687
+ return commandAck({
35688
+ delivery,
35689
+ socketId,
35690
+ status: "failed",
35691
+ error: outcome.error
35692
+ });
35693
+ }
35694
+ this.input.runtimeLogger?.info(
35695
+ "agent_bridge_claude_command_injected",
35696
+ commandLogContext(delivery, {
35697
+ socket_id: socketId,
35698
+ duration_ms: Date.now() - startedAt
35699
+ })
35700
+ );
35701
+ return commandAck({
35702
+ delivery,
35703
+ socketId,
35704
+ status: "injected"
35705
+ });
35706
+ }
35707
+ // Runs one message command's injection to a terminal outcome. Never rejects:
35708
+ // the returned promise is stored in the in-flight map and awaited by both the
35709
+ // original delivery and any redeliveries, so failures travel as values.
35710
+ async injectMessageCommand(activeContext, delivery, message, socketId) {
35572
35711
  try {
35573
35712
  const emitStartedAt = Date.now();
35574
35713
  this.input.runtimeLogger?.info(
@@ -35614,35 +35753,13 @@ var ClaudeCodeCommandHandler = class {
35614
35753
  duration_ms: Date.now() - sendStartedAt
35615
35754
  })
35616
35755
  );
35756
+ return { status: "injected" };
35617
35757
  } catch (error51) {
35618
- this.injectedCommands.delete(delivery.commandId);
35619
- this.input.runtimeLogger?.warn(
35620
- "agent_bridge_claude_command_failed",
35621
- commandLogContext(delivery, {
35622
- socket_id: socketId,
35623
- duration_ms: Date.now() - startedAt,
35624
- error: error51 instanceof Error ? error51.message : String(error51)
35625
- })
35626
- );
35627
- return commandAck({
35628
- delivery,
35629
- socketId,
35758
+ return {
35630
35759
  status: "failed",
35631
35760
  error: error51 instanceof Error ? error51.message : String(error51)
35632
- });
35761
+ };
35633
35762
  }
35634
- this.input.runtimeLogger?.info(
35635
- "agent_bridge_claude_command_injected",
35636
- commandLogContext(delivery, {
35637
- socket_id: socketId,
35638
- duration_ms: Date.now() - startedAt
35639
- })
35640
- );
35641
- return commandAck({
35642
- delivery,
35643
- socketId,
35644
- status: "injected"
35645
- });
35646
35763
  }
35647
35764
  // -----------------------------------------------------------------------------
35648
35765
  // Question answering
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autohq/cli",
3
- "version": "0.1.286",
3
+ "version": "0.1.288",
4
4
  "license": "SEE LICENSE IN README.md",
5
5
  "publishConfig": {
6
6
  "access": "public"