@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.
- package/dist/agent-bridge.js +173 -3
- package/dist/index.js +175 -3
- package/package.json +1 -1
package/dist/agent-bridge.js
CHANGED
|
@@ -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.
|
|
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
|
|
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.
|
|
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
|
|
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);
|