@autohq/cli 0.1.258 → 0.1.260

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.
@@ -21270,11 +21270,11 @@ var SocketWithoutUpgrade = class _SocketWithoutUpgrade extends Emitter {
21270
21270
  */
21271
21271
  _resetPingTimeout() {
21272
21272
  this.clearTimeoutFn(this._pingTimeoutTimer);
21273
- const delay = this._pingInterval + this._pingTimeout;
21274
- this._pingTimeoutTime = Date.now() + delay;
21273
+ const delay2 = this._pingInterval + this._pingTimeout;
21274
+ this._pingTimeoutTime = Date.now() + delay2;
21275
21275
  this._pingTimeoutTimer = this.setTimeoutFn(() => {
21276
21276
  this._onClose("ping timeout");
21277
- }, delay);
21277
+ }, delay2);
21278
21278
  if (this.opts.autoUnref) {
21279
21279
  this._pingTimeoutTimer.unref();
21280
21280
  }
@@ -23255,8 +23255,8 @@ var Manager = class extends Emitter {
23255
23255
  this.emitReserved("reconnect_failed");
23256
23256
  this._reconnecting = false;
23257
23257
  } else {
23258
- const delay = this.backoff.duration();
23259
- debug10("will wait %dms before reconnect attempt", delay);
23258
+ const delay2 = this.backoff.duration();
23259
+ debug10("will wait %dms before reconnect attempt", delay2);
23260
23260
  this._reconnecting = true;
23261
23261
  const timer = this.setTimeoutFn(() => {
23262
23262
  if (self2.skipReconnect)
@@ -23276,7 +23276,7 @@ var Manager = class extends Emitter {
23276
23276
  self2.onreconnect();
23277
23277
  }
23278
23278
  });
23279
- }, delay);
23279
+ }, delay2);
23280
23280
  if (this.opts.autoUnref) {
23281
23281
  timer.unref();
23282
23282
  }
@@ -23340,7 +23340,7 @@ Object.assign(lookup, {
23340
23340
  // package.json
23341
23341
  var package_default = {
23342
23342
  name: "@autohq/cli",
23343
- version: "0.1.258",
23343
+ version: "0.1.260",
23344
23344
  license: "SEE LICENSE IN README.md",
23345
23345
  publishConfig: {
23346
23346
  access: "public"
@@ -27176,6 +27176,8 @@ var SessionListItemSchema = external_exports.object({
27176
27176
  displayTitle: RunDisplayTitleSchema,
27177
27177
  summary: external_exports.string(),
27178
27178
  agent: SessionListItemAgentSchema,
27179
+ createdAt: external_exports.string().datetime(),
27180
+ updatedAt: external_exports.string().datetime(),
27179
27181
  archivedAt: external_exports.string().datetime().nullable(),
27180
27182
  lastActivityAt: external_exports.string().datetime()
27181
27183
  });
@@ -48190,6 +48192,8 @@ function withClaudeStderrDiagnosticsPointer(error51, capturedStderr) {
48190
48192
  // src/commands/agent-bridge/harness/claude-code/session.ts
48191
48193
  var CLAUDE_AGENT_STARTUP_TIMEOUT_MS = 3e4;
48192
48194
  var CLAUDE_INTERRUPT_SETTLE_TIMEOUT_MS = 1e4;
48195
+ var CLAUDE_MCP_REGISTRATION_TIMEOUT_MS = 3e3;
48196
+ var CLAUDE_MCP_REGISTRATION_POLL_INTERVAL_MS = 100;
48193
48197
  var CLAUDE_STARTUP_PROFILE_HOOK_EVENTS = [
48194
48198
  "Setup",
48195
48199
  "SessionStart",
@@ -48237,6 +48241,12 @@ var ClaudeAgentBridgeSessionImpl = class {
48237
48241
  // message lands only once the interrupted turn has settled its tool_use.
48238
48242
  turnResultCount = 0;
48239
48243
  turnSettlementWaiters = /* @__PURE__ */ new Set();
48244
+ // Memoized MCP-registration gate, keyed by the attached query. A runtime
48245
+ // restart warms a brand-new session + query, so keying off the query object
48246
+ // (rather than a session-lifetime flag) re-arms the gate on every restart:
48247
+ // each fresh warm gates its own first turn, while later turns on the same
48248
+ // query share the resolved promise and never re-poll.
48249
+ mcpRegistrationGate = null;
48240
48250
  constructor(input) {
48241
48251
  const optionsStartedAt = Date.now();
48242
48252
  this.input = input;
@@ -48299,6 +48309,7 @@ var ClaudeAgentBridgeSessionImpl = class {
48299
48309
  return;
48300
48310
  }
48301
48311
  await this.interruptActiveTurnBeforeMessage();
48312
+ await this.awaitMcpRegistration();
48302
48313
  this.enqueueUserMessage(message);
48303
48314
  this.input.runtimeLogger?.info("agent_bridge_claude_send_message_queued", {
48304
48315
  delivery_mode: mode,
@@ -48586,6 +48597,86 @@ var ClaudeAgentBridgeSessionImpl = class {
48586
48597
  }
48587
48598
  }
48588
48599
  }
48600
+ // Why this gate is correct without a provider-backed test (it relies on real
48601
+ // SDK mcpServerStatus() timing that fakes cannot prove):
48602
+ // - Safe-degrade: pollMcpRegistration never rejects — on timeout, a
48603
+ // status-read failure, or a closed session it resolves, so the worst case
48604
+ // is today's immediate injection. The gate can never regress below the
48605
+ // status-quo race; it can only close it.
48606
+ // - Provider-path validation is deferred deliberately: the failure is the
48607
+ // intermittent live-prod race on the chief-of-staff singleton (a real
48608
+ // runtime restart dispatching turn-1 while real mcp__* servers reconnect),
48609
+ // which cannot be reproduced deterministically without live credentials
48610
+ // and a forced restart racing reconnect.
48611
+ // - Live confirmation comes from the agent_bridge_claude_mcp_gate_started/
48612
+ // _ready/_timeout diagnostics on the next real restart, not a CI fake.
48613
+ //
48614
+ // Block until the freshly-warmed query's MCP servers have left `pending`, so
48615
+ // the first turn's prompt is built with the mcp__* tools registered. Memoized
48616
+ // per query: the first send pays the wait, concurrent first sends share it,
48617
+ // and a later turn on the same query returns immediately. Sessions without
48618
+ // configured MCP servers, or whose query failed to attach, skip the gate and
48619
+ // fall through to the existing enqueue/error path unchanged.
48620
+ async awaitMcpRegistration() {
48621
+ if (Object.keys(this.options.mcpServers ?? {}).length === 0) {
48622
+ return;
48623
+ }
48624
+ let query;
48625
+ try {
48626
+ query = await this.ensureRunningQuery();
48627
+ } catch {
48628
+ return;
48629
+ }
48630
+ if (this.mcpRegistrationGate?.query !== query) {
48631
+ this.mcpRegistrationGate = {
48632
+ query,
48633
+ promise: this.pollMcpRegistration(query)
48634
+ };
48635
+ }
48636
+ await this.mcpRegistrationGate.promise;
48637
+ }
48638
+ // Poll the live query's MCP server status until no configured server is still
48639
+ // `pending`, bounded by CLAUDE_MCP_REGISTRATION_TIMEOUT_MS. Terminal states
48640
+ // (connected/failed/needs-auth/disabled) all count as settled, so a server
48641
+ // that cannot connect in this runtime (e.g. an interactively-authed server in
48642
+ // a headless sandbox) never holds the gate to the timeout. Never rejects:
48643
+ // status-read failure, timeout, or a closed session all resolve so the caller
48644
+ // degrades to immediate injection.
48645
+ async pollMcpRegistration(query) {
48646
+ const configuredNames = Object.keys(this.options.mcpServers ?? {});
48647
+ const startedAt = Date.now();
48648
+ const deadline = startedAt + CLAUDE_MCP_REGISTRATION_TIMEOUT_MS;
48649
+ this.input.writeOutput?.(
48650
+ `agent_bridge_claude_mcp_gate_started servers=${configuredNames.length}`
48651
+ );
48652
+ while (true) {
48653
+ if (this.state.kind === "closed") {
48654
+ return;
48655
+ }
48656
+ let statuses;
48657
+ try {
48658
+ statuses = await query.mcpServerStatus();
48659
+ } catch (error51) {
48660
+ this.input.writeOutput?.(
48661
+ `agent_bridge_claude_mcp_gate_status_failed duration_ms=${Date.now() - startedAt} error=${error51 instanceof Error ? error51.message : String(error51)}`
48662
+ );
48663
+ return;
48664
+ }
48665
+ if (mcpServersSettled(statuses, configuredNames)) {
48666
+ this.input.writeOutput?.(
48667
+ `agent_bridge_claude_mcp_gate_ready duration_ms=${Date.now() - startedAt} servers=${mcpStatusList(statuses)}`
48668
+ );
48669
+ return;
48670
+ }
48671
+ if (Date.now() >= deadline) {
48672
+ this.input.writeOutput?.(
48673
+ `agent_bridge_claude_mcp_gate_timeout duration_ms=${Date.now() - startedAt} servers=${mcpStatusList(statuses)}`
48674
+ );
48675
+ return;
48676
+ }
48677
+ await delay(CLAUDE_MCP_REGISTRATION_POLL_INTERVAL_MS);
48678
+ }
48679
+ }
48589
48680
  messageProbeContext() {
48590
48681
  return {
48591
48682
  configuredMcpServerNames: Object.keys(this.options.mcpServers ?? {}),
@@ -48715,6 +48806,24 @@ var AsyncMessageQueue = class {
48715
48806
  });
48716
48807
  }
48717
48808
  };
48809
+ function mcpServersSettled(statuses, configuredNames) {
48810
+ const statusByName = new Map(
48811
+ statuses.map((server) => [server.name, server.status])
48812
+ );
48813
+ return configuredNames.every((name) => {
48814
+ const status = statusByName.get(name);
48815
+ return status !== void 0 && status !== "pending";
48816
+ });
48817
+ }
48818
+ function mcpStatusList(statuses) {
48819
+ return statuses.map((server) => `${server.name}:${server.status}`).join(",");
48820
+ }
48821
+ function delay(ms) {
48822
+ return new Promise((resolve) => {
48823
+ const timer = setTimeout(resolve, ms);
48824
+ timer.unref?.();
48825
+ });
48826
+ }
48718
48827
  function claudeAgentUserMessage(message) {
48719
48828
  return {
48720
48829
  type: "user",
package/dist/index.js CHANGED
@@ -18956,6 +18956,8 @@ var init_sessions = __esm({
18956
18956
  displayTitle: RunDisplayTitleSchema,
18957
18957
  summary: external_exports.string(),
18958
18958
  agent: SessionListItemAgentSchema,
18959
+ createdAt: external_exports.string().datetime(),
18960
+ updatedAt: external_exports.string().datetime(),
18959
18961
  archivedAt: external_exports.string().datetime().nullable(),
18960
18962
  lastActivityAt: external_exports.string().datetime()
18961
18963
  });
@@ -22780,7 +22782,7 @@ var init_package = __esm({
22780
22782
  "package.json"() {
22781
22783
  package_default = {
22782
22784
  name: "@autohq/cli",
22783
- version: "0.1.258",
22785
+ version: "0.1.260",
22784
22786
  license: "SEE LICENSE IN README.md",
22785
22787
  publishConfig: {
22786
22788
  access: "public"
@@ -33653,6 +33655,8 @@ function withClaudeStderrDiagnosticsPointer(error51, capturedStderr) {
33653
33655
  // src/commands/agent-bridge/harness/claude-code/session.ts
33654
33656
  var CLAUDE_AGENT_STARTUP_TIMEOUT_MS = 3e4;
33655
33657
  var CLAUDE_INTERRUPT_SETTLE_TIMEOUT_MS = 1e4;
33658
+ var CLAUDE_MCP_REGISTRATION_TIMEOUT_MS = 3e3;
33659
+ var CLAUDE_MCP_REGISTRATION_POLL_INTERVAL_MS = 100;
33656
33660
  var CLAUDE_STARTUP_PROFILE_HOOK_EVENTS = [
33657
33661
  "Setup",
33658
33662
  "SessionStart",
@@ -33700,6 +33704,12 @@ var ClaudeAgentBridgeSessionImpl = class {
33700
33704
  // message lands only once the interrupted turn has settled its tool_use.
33701
33705
  turnResultCount = 0;
33702
33706
  turnSettlementWaiters = /* @__PURE__ */ new Set();
33707
+ // Memoized MCP-registration gate, keyed by the attached query. A runtime
33708
+ // restart warms a brand-new session + query, so keying off the query object
33709
+ // (rather than a session-lifetime flag) re-arms the gate on every restart:
33710
+ // each fresh warm gates its own first turn, while later turns on the same
33711
+ // query share the resolved promise and never re-poll.
33712
+ mcpRegistrationGate = null;
33703
33713
  constructor(input) {
33704
33714
  const optionsStartedAt = Date.now();
33705
33715
  this.input = input;
@@ -33762,6 +33772,7 @@ var ClaudeAgentBridgeSessionImpl = class {
33762
33772
  return;
33763
33773
  }
33764
33774
  await this.interruptActiveTurnBeforeMessage();
33775
+ await this.awaitMcpRegistration();
33765
33776
  this.enqueueUserMessage(message);
33766
33777
  this.input.runtimeLogger?.info("agent_bridge_claude_send_message_queued", {
33767
33778
  delivery_mode: mode,
@@ -34049,6 +34060,86 @@ var ClaudeAgentBridgeSessionImpl = class {
34049
34060
  }
34050
34061
  }
34051
34062
  }
34063
+ // Why this gate is correct without a provider-backed test (it relies on real
34064
+ // SDK mcpServerStatus() timing that fakes cannot prove):
34065
+ // - Safe-degrade: pollMcpRegistration never rejects — on timeout, a
34066
+ // status-read failure, or a closed session it resolves, so the worst case
34067
+ // is today's immediate injection. The gate can never regress below the
34068
+ // status-quo race; it can only close it.
34069
+ // - Provider-path validation is deferred deliberately: the failure is the
34070
+ // intermittent live-prod race on the chief-of-staff singleton (a real
34071
+ // runtime restart dispatching turn-1 while real mcp__* servers reconnect),
34072
+ // which cannot be reproduced deterministically without live credentials
34073
+ // and a forced restart racing reconnect.
34074
+ // - Live confirmation comes from the agent_bridge_claude_mcp_gate_started/
34075
+ // _ready/_timeout diagnostics on the next real restart, not a CI fake.
34076
+ //
34077
+ // Block until the freshly-warmed query's MCP servers have left `pending`, so
34078
+ // the first turn's prompt is built with the mcp__* tools registered. Memoized
34079
+ // per query: the first send pays the wait, concurrent first sends share it,
34080
+ // and a later turn on the same query returns immediately. Sessions without
34081
+ // configured MCP servers, or whose query failed to attach, skip the gate and
34082
+ // fall through to the existing enqueue/error path unchanged.
34083
+ async awaitMcpRegistration() {
34084
+ if (Object.keys(this.options.mcpServers ?? {}).length === 0) {
34085
+ return;
34086
+ }
34087
+ let query;
34088
+ try {
34089
+ query = await this.ensureRunningQuery();
34090
+ } catch {
34091
+ return;
34092
+ }
34093
+ if (this.mcpRegistrationGate?.query !== query) {
34094
+ this.mcpRegistrationGate = {
34095
+ query,
34096
+ promise: this.pollMcpRegistration(query)
34097
+ };
34098
+ }
34099
+ await this.mcpRegistrationGate.promise;
34100
+ }
34101
+ // Poll the live query's MCP server status until no configured server is still
34102
+ // `pending`, bounded by CLAUDE_MCP_REGISTRATION_TIMEOUT_MS. Terminal states
34103
+ // (connected/failed/needs-auth/disabled) all count as settled, so a server
34104
+ // that cannot connect in this runtime (e.g. an interactively-authed server in
34105
+ // a headless sandbox) never holds the gate to the timeout. Never rejects:
34106
+ // status-read failure, timeout, or a closed session all resolve so the caller
34107
+ // degrades to immediate injection.
34108
+ async pollMcpRegistration(query) {
34109
+ const configuredNames = Object.keys(this.options.mcpServers ?? {});
34110
+ const startedAt = Date.now();
34111
+ const deadline = startedAt + CLAUDE_MCP_REGISTRATION_TIMEOUT_MS;
34112
+ this.input.writeOutput?.(
34113
+ `agent_bridge_claude_mcp_gate_started servers=${configuredNames.length}`
34114
+ );
34115
+ while (true) {
34116
+ if (this.state.kind === "closed") {
34117
+ return;
34118
+ }
34119
+ let statuses;
34120
+ try {
34121
+ statuses = await query.mcpServerStatus();
34122
+ } catch (error51) {
34123
+ this.input.writeOutput?.(
34124
+ `agent_bridge_claude_mcp_gate_status_failed duration_ms=${Date.now() - startedAt} error=${error51 instanceof Error ? error51.message : String(error51)}`
34125
+ );
34126
+ return;
34127
+ }
34128
+ if (mcpServersSettled(statuses, configuredNames)) {
34129
+ this.input.writeOutput?.(
34130
+ `agent_bridge_claude_mcp_gate_ready duration_ms=${Date.now() - startedAt} servers=${mcpStatusList(statuses)}`
34131
+ );
34132
+ return;
34133
+ }
34134
+ if (Date.now() >= deadline) {
34135
+ this.input.writeOutput?.(
34136
+ `agent_bridge_claude_mcp_gate_timeout duration_ms=${Date.now() - startedAt} servers=${mcpStatusList(statuses)}`
34137
+ );
34138
+ return;
34139
+ }
34140
+ await delay(CLAUDE_MCP_REGISTRATION_POLL_INTERVAL_MS);
34141
+ }
34142
+ }
34052
34143
  messageProbeContext() {
34053
34144
  return {
34054
34145
  configuredMcpServerNames: Object.keys(this.options.mcpServers ?? {}),
@@ -34178,6 +34269,24 @@ var AsyncMessageQueue = class {
34178
34269
  });
34179
34270
  }
34180
34271
  };
34272
+ function mcpServersSettled(statuses, configuredNames) {
34273
+ const statusByName = new Map(
34274
+ statuses.map((server) => [server.name, server.status])
34275
+ );
34276
+ return configuredNames.every((name) => {
34277
+ const status = statusByName.get(name);
34278
+ return status !== void 0 && status !== "pending";
34279
+ });
34280
+ }
34281
+ function mcpStatusList(statuses) {
34282
+ return statuses.map((server) => `${server.name}:${server.status}`).join(",");
34283
+ }
34284
+ function delay(ms) {
34285
+ return new Promise((resolve4) => {
34286
+ const timer = setTimeout(resolve4, ms);
34287
+ timer.unref?.();
34288
+ });
34289
+ }
34181
34290
  function claudeAgentUserMessage(message) {
34182
34291
  return {
34183
34292
  type: "user",
@@ -37817,7 +37926,7 @@ function selectLastLines(content, count) {
37817
37926
  async function followRuntimeLog(input) {
37818
37927
  let offset = input.fromByte;
37819
37928
  while (!input.signal?.aborted) {
37820
- await delay(input.pollIntervalMs, input.signal);
37929
+ await delay2(input.pollIntervalMs, input.signal);
37821
37930
  if (input.signal?.aborted) {
37822
37931
  return;
37823
37932
  }
@@ -37852,7 +37961,7 @@ function readByteRange(path2, offset, length) {
37852
37961
  }
37853
37962
  return buffer.toString("utf8");
37854
37963
  }
37855
- function delay(ms, signal) {
37964
+ function delay2(ms, signal) {
37856
37965
  return new Promise((resolve4) => {
37857
37966
  const timer = setTimeout(resolve4, ms);
37858
37967
  signal?.addEventListener(
@@ -38798,10 +38907,10 @@ async function runConsole(input) {
38798
38907
  input.writeOutput(
38799
38908
  `stream interrupted: ${error51 instanceof Error ? error51.message : String(error51)}`
38800
38909
  );
38801
- await delay2(1e3, abort.signal);
38910
+ await delay3(1e3, abort.signal);
38802
38911
  continue;
38803
38912
  }
38804
- await delay2(250, abort.signal);
38913
+ await delay3(250, abort.signal);
38805
38914
  }
38806
38915
  })();
38807
38916
  const consoleInput = createInteractiveInputController({
@@ -38881,7 +38990,7 @@ function createInteractiveInputController(input) {
38881
38990
  })();
38882
38991
  return { done, close };
38883
38992
  }
38884
- function delay2(ms, signal) {
38993
+ function delay3(ms, signal) {
38885
38994
  if (signal.aborted) {
38886
38995
  return Promise.resolve();
38887
38996
  }
@@ -39202,7 +39311,7 @@ async function pollUntilAwaiting(input) {
39202
39311
  if (firstPoll) {
39203
39312
  firstPoll = false;
39204
39313
  } else {
39205
- await delay3(
39314
+ await delay4(
39206
39315
  Math.min(input.pollIntervalMs, Math.max(deadline - Date.now(), 0))
39207
39316
  );
39208
39317
  }
@@ -39216,7 +39325,7 @@ async function pollUntilAwaiting(input) {
39216
39325
  status = session.status;
39217
39326
  }
39218
39327
  }
39219
- function delay3(ms) {
39328
+ function delay4(ms) {
39220
39329
  if (ms <= 0) {
39221
39330
  return Promise.resolve();
39222
39331
  }
@@ -39276,7 +39385,7 @@ async function streamDiagnosticsBestEffort(input) {
39276
39385
  }
39277
39386
  }
39278
39387
  async function settleDiagnostics(done) {
39279
- await Promise.race([done, delay3(DIAGNOSTICS_SHUTDOWN_WAIT_MS)]);
39388
+ await Promise.race([done, delay4(DIAGNOSTICS_SHUTDOWN_WAIT_MS)]);
39280
39389
  }
39281
39390
  function writeBenchmarkResult(context, result, json2) {
39282
39391
  if (json2) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autohq/cli",
3
- "version": "0.1.258",
3
+ "version": "0.1.260",
4
4
  "license": "SEE LICENSE IN README.md",
5
5
  "publishConfig": {
6
6
  "access": "public"