@autohq/cli 0.1.341 → 0.1.343

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.
@@ -19426,6 +19426,49 @@ function date4(params) {
19426
19426
  // ../../node_modules/zod/v4/classic/external.js
19427
19427
  config(en_default());
19428
19428
 
19429
+ // ../../packages/protocol/src/content-sanitizer.ts
19430
+ function sanitizeUnstorableStrings(value2) {
19431
+ return sanitizeValue(value2);
19432
+ }
19433
+ var UNSTORABLE_STRING_CONTENT = (
19434
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: matching NUL is the point
19435
+ /\u0000|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g
19436
+ );
19437
+ var REPLACEMENT_CHARACTER = "\uFFFD";
19438
+ function sanitizeString(value2) {
19439
+ UNSTORABLE_STRING_CONTENT.lastIndex = 0;
19440
+ if (!UNSTORABLE_STRING_CONTENT.test(value2)) {
19441
+ return value2;
19442
+ }
19443
+ return value2.replace(UNSTORABLE_STRING_CONTENT, REPLACEMENT_CHARACTER);
19444
+ }
19445
+ function sanitizeValue(value2) {
19446
+ if (typeof value2 === "string") {
19447
+ return sanitizeString(value2);
19448
+ }
19449
+ if (Array.isArray(value2)) {
19450
+ let changed = false;
19451
+ const next = value2.map((item) => {
19452
+ const sanitized = sanitizeValue(item);
19453
+ changed ||= sanitized !== item;
19454
+ return sanitized;
19455
+ });
19456
+ return changed ? next : value2;
19457
+ }
19458
+ if (typeof value2 === "object" && value2 !== null) {
19459
+ let changed = false;
19460
+ const next = {};
19461
+ for (const [key, item] of Object.entries(value2)) {
19462
+ const sanitizedKey = sanitizeString(key);
19463
+ const sanitizedItem = sanitizeValue(item);
19464
+ changed ||= sanitizedKey !== key || sanitizedItem !== item;
19465
+ next[sanitizedKey] = sanitizedItem;
19466
+ }
19467
+ return changed ? next : value2;
19468
+ }
19469
+ return value2;
19470
+ }
19471
+
19429
19472
  // ../../packages/protocol/src/index.ts
19430
19473
  var OpaqueIdSchema = external_exports.string().trim().min(1);
19431
19474
  var SessionIdSchema = OpaqueIdSchema.brand();
@@ -19790,13 +19833,28 @@ var RuntimeBridgeOutputUiMessageCompletedEnvelopeSchema = external_exports.objec
19790
19833
  createdAt: external_exports.string().datetime(),
19791
19834
  completedAt: external_exports.string().datetime().nullable()
19792
19835
  });
19836
+ var RuntimeBridgeOutputLivenessEnvelopeSchema = external_exports.object({
19837
+ type: external_exports.literal("runtime.liveness"),
19838
+ sessionId: SessionIdSchema,
19839
+ runtimeId: RuntimeIdSchema,
19840
+ bridgeLeaseId: RuntimeBridgeLeaseIdSchema,
19841
+ outputSeq: external_exports.number().int().positive(),
19842
+ // The emitter's beat cadence, so watchdog thresholds stay interpretable
19843
+ // against the runtime that produced them.
19844
+ intervalMs: external_exports.number().int().positive(),
19845
+ createdAt: external_exports.string().datetime()
19846
+ });
19793
19847
  var RuntimeBridgeOutputEnvelopeSchema = external_exports.union([
19794
19848
  RuntimeBridgeOutputEntryEnvelopeSchema,
19795
19849
  RuntimeBridgeOutputDeltaEnvelopeSchema,
19796
19850
  RuntimeBridgeOutputUiMessageChunkEnvelopeSchema,
19797
19851
  RuntimeBridgeOutputUiMessagePartEnvelopeSchema,
19798
- RuntimeBridgeOutputUiMessageCompletedEnvelopeSchema
19852
+ RuntimeBridgeOutputUiMessageCompletedEnvelopeSchema,
19853
+ RuntimeBridgeOutputLivenessEnvelopeSchema
19799
19854
  ]);
19855
+ function isLiveOnlyRuntimeOutputEnvelope(envelope) {
19856
+ return envelope.type === "ui.message.chunk" || envelope.type === "conversation.delta";
19857
+ }
19800
19858
  function legacyRuntimeTurnStatus(entry, sessionStatusAfter) {
19801
19859
  if (sessionStatusAfter === "failed" || entry.status === "failed") {
19802
19860
  return "failed";
@@ -23434,7 +23492,7 @@ Object.assign(lookup, {
23434
23492
  // package.json
23435
23493
  var package_default = {
23436
23494
  name: "@autohq/cli",
23437
- version: "0.1.341",
23495
+ version: "0.1.343",
23438
23496
  license: "SEE LICENSE IN README.md",
23439
23497
  publishConfig: {
23440
23498
  access: "public"
@@ -28460,6 +28518,10 @@ var SessionTurnRecordSchema = external_exports.object({
28460
28518
  acceptedAt: external_exports.string().datetime().nullable(),
28461
28519
  startedAt: external_exports.string().datetime().nullable(),
28462
28520
  lastProgressAt: external_exports.string().datetime().nullable(),
28521
+ // Last runtime liveness beat that vouched for this open turn (null before
28522
+ // the first beat, and always null on runtimes that predate liveness).
28523
+ // Distinct from lastProgressAt: pipeline liveness, not durable progress.
28524
+ livenessAt: external_exports.string().datetime().nullable().default(null),
28463
28525
  completedAt: external_exports.string().datetime().nullable(),
28464
28526
  failedAt: external_exports.string().datetime().nullable(),
28465
28527
  staleAt: external_exports.string().datetime().nullable(),
@@ -35817,6 +35879,23 @@ var ProjectUsageResponseSchema = external_exports.object({
35817
35879
  daily: external_exports.array(ProjectUsageDayPointSchema)
35818
35880
  });
35819
35881
 
35882
+ // src/commands/agent-bridge/harness/liveness-ticker.ts
35883
+ var RUNTIME_LIVENESS_INTERVAL_MS = 2e4;
35884
+ function startRuntimeLivenessTicker(input) {
35885
+ const intervalMs = input.intervalMs ?? RUNTIME_LIVENESS_INTERVAL_MS;
35886
+ const timer = setInterval(() => {
35887
+ void input.emitBeat().catch((error51) => {
35888
+ input.writeOutput?.(
35889
+ `agent_bridge_liveness_beat_failed error=${error51 instanceof Error ? error51.message : String(error51)}`
35890
+ );
35891
+ });
35892
+ }, intervalMs);
35893
+ timer.unref?.();
35894
+ return {
35895
+ stop: () => clearInterval(timer)
35896
+ };
35897
+ }
35898
+
35820
35899
  // src/commands/agent-bridge/harness/output-buffer.ts
35821
35900
  import { setTimeout as sleep } from "timers/promises";
35822
35901
 
@@ -44468,6 +44547,9 @@ var AgentBridgeOutputBuffer = class {
44468
44547
  uiMessagePartTracker = new UiMessagePartTracker();
44469
44548
  drainBlocked = false;
44470
44549
  drainPromise = null;
44550
+ // Sequence of the most recent liveness beat, for the skip-while-pending
44551
+ // gate in emitLivenessBeat.
44552
+ pendingLivenessSeq = null;
44471
44553
  // Ack-retry-ladder exhausts per pending output sequence, for the poison
44472
44554
  // quarantine; entries clear when the sequence finally acks.
44473
44555
  ackExhaustsBySeq = /* @__PURE__ */ new Map();
@@ -44498,6 +44580,29 @@ var AgentBridgeOutputBuffer = class {
44498
44580
  await this.flushPendingDelta({ force: true });
44499
44581
  await this.drainPendingOutputs({ force: true });
44500
44582
  }
44583
+ // Emits one runtime liveness beat through the same ordered, acked drain as
44584
+ // content envelopes — deliberately, so a beat landing at the bridge proves
44585
+ // the pipeline is healthy end to end. While a previous beat is still
44586
+ // pending (stalled drain, retry ladder, reconnect) new beats are skipped
44587
+ // rather than queued: an unhealthy pipeline must show up as beats
44588
+ // *stopping*, and a backlog of parked beats replaying after recovery would
44589
+ // stamp liveness for exactly the window the runtime was mute.
44590
+ async emitLivenessBeat(context, intervalMs) {
44591
+ if (this.pendingLivenessSeq !== null && this.pendingOutputs.has(this.pendingLivenessSeq)) {
44592
+ return;
44593
+ }
44594
+ this.outputSeq += 1;
44595
+ this.pendingLivenessSeq = this.outputSeq;
44596
+ this.enqueueOutput(
44597
+ buildLivenessOutputEnvelope({
44598
+ context,
44599
+ outputSeq: this.outputSeq,
44600
+ intervalMs,
44601
+ createdAt: now2()
44602
+ })
44603
+ );
44604
+ await this.drainPendingOutputs();
44605
+ }
44501
44606
  async emitUiMessageChunk(context, projection) {
44502
44607
  await this.flushPendingDelta();
44503
44608
  await this.emitLiveUiMessageChunk(context, projection.chunk);
@@ -44776,6 +44881,16 @@ var AgentBridgeOutputBuffer = class {
44776
44881
  return;
44777
44882
  } catch (error51) {
44778
44883
  if (!(error51 instanceof AgentBridgeOutputAckTimeoutError)) {
44884
+ this.input.runtimeLogger?.error(
44885
+ "agent_bridge_output_buffer_emit_rejected_definitively",
44886
+ this.outputLogContext(output, {
44887
+ error: error51 instanceof Error ? error51.message : String(error51),
44888
+ socket_cycle: this.input.cycleSocket ? "requested" : "unavailable",
44889
+ pending_count: this.pendingOutputs.size
44890
+ })
44891
+ );
44892
+ this.quarantineAfterRepeatedExhausts(output);
44893
+ this.input.cycleSocket?.();
44779
44894
  throw error51;
44780
44895
  }
44781
44896
  const backoffMs = AGENT_BRIDGE_OUTPUT_ACK_RETRY_BACKOFF_MS[attempt];
@@ -44851,7 +44966,16 @@ var AgentBridgeOutputBuffer = class {
44851
44966
  await this.drainPendingOutputs();
44852
44967
  }
44853
44968
  enqueueOutput(output) {
44854
- const bounded = boundOutputEnvelope(output);
44969
+ const sanitized = isLiveOnlyRuntimeOutputEnvelope(output) ? output : sanitizeUnstorableStrings(output);
44970
+ if (sanitized !== output) {
44971
+ this.input.runtimeLogger?.warn(
44972
+ "agent_bridge_output_envelope_sanitized",
44973
+ this.outputLogContext(sanitized, {
44974
+ pending_count: this.pendingOutputs.size
44975
+ })
44976
+ );
44977
+ }
44978
+ const bounded = boundOutputEnvelope(sanitized);
44855
44979
  if (bounded.truncated) {
44856
44980
  this.input.runtimeLogger?.warn(
44857
44981
  "agent_bridge_output_envelope_truncated",
@@ -45175,6 +45299,18 @@ function buildUiMessageChunkOutputEnvelope(input) {
45175
45299
  createdAt: input.createdAt
45176
45300
  });
45177
45301
  }
45302
+ function buildLivenessOutputEnvelope(input) {
45303
+ const { context } = input;
45304
+ return RuntimeBridgeOutputEnvelopeSchema.parse({
45305
+ type: "runtime.liveness",
45306
+ sessionId: context.sessionId,
45307
+ runtimeId: context.runtimeId,
45308
+ bridgeLeaseId: context.bridgeLeaseId,
45309
+ outputSeq: input.outputSeq,
45310
+ intervalMs: input.intervalMs,
45311
+ createdAt: input.createdAt
45312
+ });
45313
+ }
45178
45314
  function buildUiMessagePartOutputEnvelope(input) {
45179
45315
  const { context, snapshot } = input;
45180
45316
  return RuntimeBridgeOutputEnvelopeSchema.parse({
@@ -66431,11 +66567,16 @@ var ClaudeCodeCommandHandler = class {
66431
66567
  pendingQuestions = /* @__PURE__ */ new Map();
66432
66568
  outputBuffer;
66433
66569
  projector = new ClaudeCodeProjector();
66570
+ livenessTicker = null;
66434
66571
  // -----------------------------------------------------------------------------
66435
66572
  // Lifecycle (public API)
66436
66573
  // -----------------------------------------------------------------------------
66437
66574
  setContext(nextContext) {
66438
66575
  this.context = RuntimeBridgeConnectedPayloadSchema.parse(nextContext);
66576
+ this.livenessTicker ??= startRuntimeLivenessTicker({
66577
+ emitBeat: () => this.emitLivenessBeat(),
66578
+ writeOutput: this.input.writeOutput
66579
+ });
66439
66580
  }
66440
66581
  async replayPendingOutputs() {
66441
66582
  await this.outputBuffer.replayPendingOutputs();
@@ -66444,6 +66585,8 @@ var ClaudeCodeCommandHandler = class {
66444
66585
  await this.ensureAgentSession().prepare();
66445
66586
  }
66446
66587
  shutdown() {
66588
+ this.livenessTicker?.stop();
66589
+ this.livenessTicker = null;
66447
66590
  this.agentSession?.close();
66448
66591
  this.agentSession = null;
66449
66592
  this.settlePendingQuestions("Runtime is shutting down");
@@ -66775,6 +66918,16 @@ var ClaudeCodeCommandHandler = class {
66775
66918
  // -----------------------------------------------------------------------------
66776
66919
  // SDK callbacks
66777
66920
  // -----------------------------------------------------------------------------
66921
+ async emitLivenessBeat() {
66922
+ const activeContext = this.context;
66923
+ if (!activeContext) {
66924
+ return;
66925
+ }
66926
+ await this.outputBuffer.emitLivenessBeat(
66927
+ activeContext,
66928
+ RUNTIME_LIVENESS_INTERVAL_MS
66929
+ );
66930
+ }
66778
66931
  async emitBridgeOutput(activeContext, projection) {
66779
66932
  try {
66780
66933
  await this.outputBuffer.emitProjection(activeContext, projection);
@@ -68236,12 +68389,17 @@ var CodexCommandHandler = class {
68236
68389
  pendingApprovals = /* @__PURE__ */ new Map();
68237
68390
  outputBuffer;
68238
68391
  projector = new CodexProjector();
68392
+ livenessTicker = null;
68239
68393
  skipResumeForNextSession = false;
68240
68394
  // ---------------------------------------------------------------------------
68241
68395
  // Lifecycle (public API)
68242
68396
  // ---------------------------------------------------------------------------
68243
68397
  setContext(nextContext) {
68244
68398
  this.context = RuntimeBridgeConnectedPayloadSchema.parse(nextContext);
68399
+ this.livenessTicker ??= startRuntimeLivenessTicker({
68400
+ emitBeat: () => this.emitLivenessBeat(),
68401
+ writeOutput: this.input.writeOutput
68402
+ });
68245
68403
  }
68246
68404
  async replayPendingOutputs() {
68247
68405
  await this.outputBuffer.replayPendingOutputs();
@@ -68250,6 +68408,8 @@ var CodexCommandHandler = class {
68250
68408
  await this.ensureSession().prepare();
68251
68409
  }
68252
68410
  shutdown() {
68411
+ this.livenessTicker?.stop();
68412
+ this.livenessTicker = null;
68253
68413
  this.session?.close();
68254
68414
  this.session = null;
68255
68415
  this.pendingApprovals.clear();
@@ -68417,6 +68577,16 @@ var CodexCommandHandler = class {
68417
68577
  this.projector.projectSessionFailure(errorMessage3(error51))
68418
68578
  );
68419
68579
  }
68580
+ async emitLivenessBeat() {
68581
+ const activeContext = this.context;
68582
+ if (!activeContext) {
68583
+ return;
68584
+ }
68585
+ await this.outputBuffer.emitLivenessBeat(
68586
+ activeContext,
68587
+ RUNTIME_LIVENESS_INTERVAL_MS
68588
+ );
68589
+ }
68420
68590
  async emit(activeContext, projection) {
68421
68591
  try {
68422
68592
  await this.outputBuffer.emitProjection(activeContext, projection);
package/dist/index.js CHANGED
@@ -20227,6 +20227,10 @@ var init_session_turns = __esm({
20227
20227
  acceptedAt: external_exports.string().datetime().nullable(),
20228
20228
  startedAt: external_exports.string().datetime().nullable(),
20229
20229
  lastProgressAt: external_exports.string().datetime().nullable(),
20230
+ // Last runtime liveness beat that vouched for this open turn (null before
20231
+ // the first beat, and always null on runtimes that predate liveness).
20232
+ // Distinct from lastProgressAt: pipeline liveness, not durable progress.
20233
+ livenessAt: external_exports.string().datetime().nullable().default(null),
20230
20234
  completedAt: external_exports.string().datetime().nullable(),
20231
20235
  failedAt: external_exports.string().datetime().nullable(),
20232
20236
  staleAt: external_exports.string().datetime().nullable(),
@@ -30499,7 +30503,7 @@ var init_package = __esm({
30499
30503
  "package.json"() {
30500
30504
  package_default = {
30501
30505
  name: "@autohq/cli",
30502
- version: "0.1.341",
30506
+ version: "0.1.343",
30503
30507
  license: "SEE LICENSE IN README.md",
30504
30508
  publishConfig: {
30505
30509
  access: "public"
@@ -40350,6 +40354,51 @@ async function readStream(stream) {
40350
40354
 
40351
40355
  // ../../packages/protocol/src/index.ts
40352
40356
  init_zod();
40357
+
40358
+ // ../../packages/protocol/src/content-sanitizer.ts
40359
+ function sanitizeUnstorableStrings(value) {
40360
+ return sanitizeValue(value);
40361
+ }
40362
+ var UNSTORABLE_STRING_CONTENT = (
40363
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: matching NUL is the point
40364
+ /\u0000|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g
40365
+ );
40366
+ var REPLACEMENT_CHARACTER = "\uFFFD";
40367
+ function sanitizeString(value) {
40368
+ UNSTORABLE_STRING_CONTENT.lastIndex = 0;
40369
+ if (!UNSTORABLE_STRING_CONTENT.test(value)) {
40370
+ return value;
40371
+ }
40372
+ return value.replace(UNSTORABLE_STRING_CONTENT, REPLACEMENT_CHARACTER);
40373
+ }
40374
+ function sanitizeValue(value) {
40375
+ if (typeof value === "string") {
40376
+ return sanitizeString(value);
40377
+ }
40378
+ if (Array.isArray(value)) {
40379
+ let changed = false;
40380
+ const next = value.map((item) => {
40381
+ const sanitized = sanitizeValue(item);
40382
+ changed ||= sanitized !== item;
40383
+ return sanitized;
40384
+ });
40385
+ return changed ? next : value;
40386
+ }
40387
+ if (typeof value === "object" && value !== null) {
40388
+ let changed = false;
40389
+ const next = {};
40390
+ for (const [key, item] of Object.entries(value)) {
40391
+ const sanitizedKey = sanitizeString(key);
40392
+ const sanitizedItem = sanitizeValue(item);
40393
+ changed ||= sanitizedKey !== key || sanitizedItem !== item;
40394
+ next[sanitizedKey] = sanitizedItem;
40395
+ }
40396
+ return changed ? next : value;
40397
+ }
40398
+ return value;
40399
+ }
40400
+
40401
+ // ../../packages/protocol/src/index.ts
40353
40402
  var OpaqueIdSchema2 = external_exports.string().trim().min(1);
40354
40403
  var SessionIdSchema2 = OpaqueIdSchema2.brand();
40355
40404
  var SessionCommandIdSchema2 = OpaqueIdSchema2.brand();
@@ -40713,13 +40762,28 @@ var RuntimeBridgeOutputUiMessageCompletedEnvelopeSchema = external_exports.objec
40713
40762
  createdAt: external_exports.string().datetime(),
40714
40763
  completedAt: external_exports.string().datetime().nullable()
40715
40764
  });
40765
+ var RuntimeBridgeOutputLivenessEnvelopeSchema = external_exports.object({
40766
+ type: external_exports.literal("runtime.liveness"),
40767
+ sessionId: SessionIdSchema2,
40768
+ runtimeId: RuntimeIdSchema2,
40769
+ bridgeLeaseId: RuntimeBridgeLeaseIdSchema2,
40770
+ outputSeq: external_exports.number().int().positive(),
40771
+ // The emitter's beat cadence, so watchdog thresholds stay interpretable
40772
+ // against the runtime that produced them.
40773
+ intervalMs: external_exports.number().int().positive(),
40774
+ createdAt: external_exports.string().datetime()
40775
+ });
40716
40776
  var RuntimeBridgeOutputEnvelopeSchema = external_exports.union([
40717
40777
  RuntimeBridgeOutputEntryEnvelopeSchema,
40718
40778
  RuntimeBridgeOutputDeltaEnvelopeSchema,
40719
40779
  RuntimeBridgeOutputUiMessageChunkEnvelopeSchema,
40720
40780
  RuntimeBridgeOutputUiMessagePartEnvelopeSchema,
40721
- RuntimeBridgeOutputUiMessageCompletedEnvelopeSchema
40781
+ RuntimeBridgeOutputUiMessageCompletedEnvelopeSchema,
40782
+ RuntimeBridgeOutputLivenessEnvelopeSchema
40722
40783
  ]);
40784
+ function isLiveOnlyRuntimeOutputEnvelope(envelope) {
40785
+ return envelope.type === "ui.message.chunk" || envelope.type === "conversation.delta";
40786
+ }
40723
40787
  function legacyRuntimeTurnStatus(entry, sessionStatusAfter) {
40724
40788
  if (sessionStatusAfter === "failed" || entry.status === "failed") {
40725
40789
  return "failed";
@@ -41230,6 +41294,23 @@ function jsonRecordString(value, key) {
41230
41294
  // src/commands/agent-bridge/harness/claude-code/index.ts
41231
41295
  init_src();
41232
41296
 
41297
+ // src/commands/agent-bridge/harness/liveness-ticker.ts
41298
+ var RUNTIME_LIVENESS_INTERVAL_MS = 2e4;
41299
+ function startRuntimeLivenessTicker(input) {
41300
+ const intervalMs = input.intervalMs ?? RUNTIME_LIVENESS_INTERVAL_MS;
41301
+ const timer = setInterval(() => {
41302
+ void input.emitBeat().catch((error51) => {
41303
+ input.writeOutput?.(
41304
+ `agent_bridge_liveness_beat_failed error=${error51 instanceof Error ? error51.message : String(error51)}`
41305
+ );
41306
+ });
41307
+ }, intervalMs);
41308
+ timer.unref?.();
41309
+ return {
41310
+ stop: () => clearInterval(timer)
41311
+ };
41312
+ }
41313
+
41233
41314
  // src/commands/agent-bridge/harness/output-buffer.ts
41234
41315
  import { setTimeout as sleep2 } from "timers/promises";
41235
41316
  init_src();
@@ -41682,6 +41763,9 @@ var AgentBridgeOutputBuffer = class {
41682
41763
  uiMessagePartTracker = new UiMessagePartTracker();
41683
41764
  drainBlocked = false;
41684
41765
  drainPromise = null;
41766
+ // Sequence of the most recent liveness beat, for the skip-while-pending
41767
+ // gate in emitLivenessBeat.
41768
+ pendingLivenessSeq = null;
41685
41769
  // Ack-retry-ladder exhausts per pending output sequence, for the poison
41686
41770
  // quarantine; entries clear when the sequence finally acks.
41687
41771
  ackExhaustsBySeq = /* @__PURE__ */ new Map();
@@ -41712,6 +41796,29 @@ var AgentBridgeOutputBuffer = class {
41712
41796
  await this.flushPendingDelta({ force: true });
41713
41797
  await this.drainPendingOutputs({ force: true });
41714
41798
  }
41799
+ // Emits one runtime liveness beat through the same ordered, acked drain as
41800
+ // content envelopes — deliberately, so a beat landing at the bridge proves
41801
+ // the pipeline is healthy end to end. While a previous beat is still
41802
+ // pending (stalled drain, retry ladder, reconnect) new beats are skipped
41803
+ // rather than queued: an unhealthy pipeline must show up as beats
41804
+ // *stopping*, and a backlog of parked beats replaying after recovery would
41805
+ // stamp liveness for exactly the window the runtime was mute.
41806
+ async emitLivenessBeat(context, intervalMs) {
41807
+ if (this.pendingLivenessSeq !== null && this.pendingOutputs.has(this.pendingLivenessSeq)) {
41808
+ return;
41809
+ }
41810
+ this.outputSeq += 1;
41811
+ this.pendingLivenessSeq = this.outputSeq;
41812
+ this.enqueueOutput(
41813
+ buildLivenessOutputEnvelope({
41814
+ context,
41815
+ outputSeq: this.outputSeq,
41816
+ intervalMs,
41817
+ createdAt: now2()
41818
+ })
41819
+ );
41820
+ await this.drainPendingOutputs();
41821
+ }
41715
41822
  async emitUiMessageChunk(context, projection) {
41716
41823
  await this.flushPendingDelta();
41717
41824
  await this.emitLiveUiMessageChunk(context, projection.chunk);
@@ -41990,6 +42097,16 @@ var AgentBridgeOutputBuffer = class {
41990
42097
  return;
41991
42098
  } catch (error51) {
41992
42099
  if (!(error51 instanceof AgentBridgeOutputAckTimeoutError)) {
42100
+ this.input.runtimeLogger?.error(
42101
+ "agent_bridge_output_buffer_emit_rejected_definitively",
42102
+ this.outputLogContext(output, {
42103
+ error: error51 instanceof Error ? error51.message : String(error51),
42104
+ socket_cycle: this.input.cycleSocket ? "requested" : "unavailable",
42105
+ pending_count: this.pendingOutputs.size
42106
+ })
42107
+ );
42108
+ this.quarantineAfterRepeatedExhausts(output);
42109
+ this.input.cycleSocket?.();
41993
42110
  throw error51;
41994
42111
  }
41995
42112
  const backoffMs = AGENT_BRIDGE_OUTPUT_ACK_RETRY_BACKOFF_MS[attempt];
@@ -42065,7 +42182,16 @@ var AgentBridgeOutputBuffer = class {
42065
42182
  await this.drainPendingOutputs();
42066
42183
  }
42067
42184
  enqueueOutput(output) {
42068
- const bounded = boundOutputEnvelope(output);
42185
+ const sanitized = isLiveOnlyRuntimeOutputEnvelope(output) ? output : sanitizeUnstorableStrings(output);
42186
+ if (sanitized !== output) {
42187
+ this.input.runtimeLogger?.warn(
42188
+ "agent_bridge_output_envelope_sanitized",
42189
+ this.outputLogContext(sanitized, {
42190
+ pending_count: this.pendingOutputs.size
42191
+ })
42192
+ );
42193
+ }
42194
+ const bounded = boundOutputEnvelope(sanitized);
42069
42195
  if (bounded.truncated) {
42070
42196
  this.input.runtimeLogger?.warn(
42071
42197
  "agent_bridge_output_envelope_truncated",
@@ -42389,6 +42515,18 @@ function buildUiMessageChunkOutputEnvelope(input) {
42389
42515
  createdAt: input.createdAt
42390
42516
  });
42391
42517
  }
42518
+ function buildLivenessOutputEnvelope(input) {
42519
+ const { context } = input;
42520
+ return RuntimeBridgeOutputEnvelopeSchema.parse({
42521
+ type: "runtime.liveness",
42522
+ sessionId: context.sessionId,
42523
+ runtimeId: context.runtimeId,
42524
+ bridgeLeaseId: context.bridgeLeaseId,
42525
+ outputSeq: input.outputSeq,
42526
+ intervalMs: input.intervalMs,
42527
+ createdAt: input.createdAt
42528
+ });
42529
+ }
42392
42530
  function buildUiMessagePartOutputEnvelope(input) {
42393
42531
  const { context, snapshot } = input;
42394
42532
  return RuntimeBridgeOutputEnvelopeSchema.parse({
@@ -44074,11 +44212,16 @@ var ClaudeCodeCommandHandler = class {
44074
44212
  pendingQuestions = /* @__PURE__ */ new Map();
44075
44213
  outputBuffer;
44076
44214
  projector = new ClaudeCodeProjector();
44215
+ livenessTicker = null;
44077
44216
  // -----------------------------------------------------------------------------
44078
44217
  // Lifecycle (public API)
44079
44218
  // -----------------------------------------------------------------------------
44080
44219
  setContext(nextContext) {
44081
44220
  this.context = RuntimeBridgeConnectedPayloadSchema.parse(nextContext);
44221
+ this.livenessTicker ??= startRuntimeLivenessTicker({
44222
+ emitBeat: () => this.emitLivenessBeat(),
44223
+ writeOutput: this.input.writeOutput
44224
+ });
44082
44225
  }
44083
44226
  async replayPendingOutputs() {
44084
44227
  await this.outputBuffer.replayPendingOutputs();
@@ -44087,6 +44230,8 @@ var ClaudeCodeCommandHandler = class {
44087
44230
  await this.ensureAgentSession().prepare();
44088
44231
  }
44089
44232
  shutdown() {
44233
+ this.livenessTicker?.stop();
44234
+ this.livenessTicker = null;
44090
44235
  this.agentSession?.close();
44091
44236
  this.agentSession = null;
44092
44237
  this.settlePendingQuestions("Runtime is shutting down");
@@ -44418,6 +44563,16 @@ var ClaudeCodeCommandHandler = class {
44418
44563
  // -----------------------------------------------------------------------------
44419
44564
  // SDK callbacks
44420
44565
  // -----------------------------------------------------------------------------
44566
+ async emitLivenessBeat() {
44567
+ const activeContext = this.context;
44568
+ if (!activeContext) {
44569
+ return;
44570
+ }
44571
+ await this.outputBuffer.emitLivenessBeat(
44572
+ activeContext,
44573
+ RUNTIME_LIVENESS_INTERVAL_MS
44574
+ );
44575
+ }
44421
44576
  async emitBridgeOutput(activeContext, projection) {
44422
44577
  try {
44423
44578
  await this.outputBuffer.emitProjection(activeContext, projection);
@@ -45884,12 +46039,17 @@ var CodexCommandHandler = class {
45884
46039
  pendingApprovals = /* @__PURE__ */ new Map();
45885
46040
  outputBuffer;
45886
46041
  projector = new CodexProjector();
46042
+ livenessTicker = null;
45887
46043
  skipResumeForNextSession = false;
45888
46044
  // ---------------------------------------------------------------------------
45889
46045
  // Lifecycle (public API)
45890
46046
  // ---------------------------------------------------------------------------
45891
46047
  setContext(nextContext) {
45892
46048
  this.context = RuntimeBridgeConnectedPayloadSchema.parse(nextContext);
46049
+ this.livenessTicker ??= startRuntimeLivenessTicker({
46050
+ emitBeat: () => this.emitLivenessBeat(),
46051
+ writeOutput: this.input.writeOutput
46052
+ });
45893
46053
  }
45894
46054
  async replayPendingOutputs() {
45895
46055
  await this.outputBuffer.replayPendingOutputs();
@@ -45898,6 +46058,8 @@ var CodexCommandHandler = class {
45898
46058
  await this.ensureSession().prepare();
45899
46059
  }
45900
46060
  shutdown() {
46061
+ this.livenessTicker?.stop();
46062
+ this.livenessTicker = null;
45901
46063
  this.session?.close();
45902
46064
  this.session = null;
45903
46065
  this.pendingApprovals.clear();
@@ -46065,6 +46227,16 @@ var CodexCommandHandler = class {
46065
46227
  this.projector.projectSessionFailure(errorMessage3(error51))
46066
46228
  );
46067
46229
  }
46230
+ async emitLivenessBeat() {
46231
+ const activeContext = this.context;
46232
+ if (!activeContext) {
46233
+ return;
46234
+ }
46235
+ await this.outputBuffer.emitLivenessBeat(
46236
+ activeContext,
46237
+ RUNTIME_LIVENESS_INTERVAL_MS
46238
+ );
46239
+ }
46068
46240
  async emit(activeContext, projection) {
46069
46241
  try {
46070
46242
  await this.outputBuffer.emitProjection(activeContext, projection);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autohq/cli",
3
- "version": "0.1.341",
3
+ "version": "0.1.343",
4
4
  "license": "SEE LICENSE IN README.md",
5
5
  "publishConfig": {
6
6
  "access": "public"