@adhdev/daemon-standalone 1.0.58-rc.51 → 1.0.58-rc.53
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/index.js +255 -34
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -36911,10 +36911,10 @@ var require_dist3 = __commonJS({
|
|
|
36911
36911
|
}
|
|
36912
36912
|
function getDaemonBuildInfo() {
|
|
36913
36913
|
if (cached2) return cached2;
|
|
36914
|
-
const commit = readInjected(true ? "
|
|
36915
|
-
const commitShort = readInjected(true ? "
|
|
36916
|
-
const version2 = readInjected(true ? "1.0.58-rc.
|
|
36917
|
-
const builtAt = readInjected(true ? "2026-08-
|
|
36914
|
+
const commit = readInjected(true ? "7335576b38c7efe574df77aa232271dca8221f88" : void 0) ?? "unknown";
|
|
36915
|
+
const commitShort = readInjected(true ? "7335576b" : void 0) ?? (commit !== "unknown" ? commit.slice(0, 7) : "unknown");
|
|
36916
|
+
const version2 = readInjected(true ? "1.0.58-rc.53" : void 0) ?? readInjected(typeof process !== "undefined" ? process.env?.ADHDEV_PKG_VERSION : void 0) ?? "unknown";
|
|
36917
|
+
const builtAt = readInjected(true ? "2026-08-30T19:29:33.228Z" : void 0);
|
|
36918
36918
|
cached2 = builtAt ? { commit, commitShort, version: version2, builtAt } : { commit, commitShort, version: version2 };
|
|
36919
36919
|
return cached2;
|
|
36920
36920
|
}
|
|
@@ -75127,6 +75127,43 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
75127
75127
|
const reasonText = reason === "probe_disabled" ? "quota probing is disabled for this provider on that node (quotaEnabled: false \u2014 an intended opt-out, not a fault)" : reason === "provider_disabled" ? "this provider is not enabled on that node" : reason === "not_measured" ? "no snapshot has been measured yet" : "that node reported no provider-enablement facts (a daemon predating them, or no loader injected here) \u2014 cannot tell not-yet-measured from an opt-out; check the owning node's config directly";
|
|
75128
75128
|
LOG2.info("MeshQuota", `QUOTA GATE: provider '${providerType}' on node ${nodeId} fails open \u2014 no quota entry (${reason}: ${reasonText})`);
|
|
75129
75129
|
}
|
|
75130
|
+
function antigravityPoolFromLeadingLabel(value) {
|
|
75131
|
+
const label = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
75132
|
+
if (/^gemini(?:[\s/_-]|$)/.test(label)) return "gemini";
|
|
75133
|
+
if (/^(?:claude|gpt)(?:[\s/_-]|$)/.test(label)) return "claude-gpt";
|
|
75134
|
+
return void 0;
|
|
75135
|
+
}
|
|
75136
|
+
function worstBucketWindow(buckets, targetMinutes) {
|
|
75137
|
+
const tolerance = targetMinutes * 0.1;
|
|
75138
|
+
const matches = buckets.filter((bucket2) => {
|
|
75139
|
+
const windowMinutes3 = Number(bucket2.windowMinutes);
|
|
75140
|
+
return Number.isFinite(windowMinutes3) && windowMinutes3 > 0 && Math.abs(windowMinutes3 - targetMinutes) <= tolerance;
|
|
75141
|
+
});
|
|
75142
|
+
if (!matches.length) return null;
|
|
75143
|
+
return matches.reduce((worst, bucket2) => Number(bucket2.usedPercent) > Number(worst.usedPercent) ? bucket2 : worst);
|
|
75144
|
+
}
|
|
75145
|
+
function quotaWindowsForGateTarget(providerType, quota, target) {
|
|
75146
|
+
if (providerType !== "antigravity-cli" || quota.status !== "ok") return void 0;
|
|
75147
|
+
const selectedPool = antigravityPoolFromLeadingLabel(target?.model);
|
|
75148
|
+
if (!selectedPool || !Array.isArray(quota.buckets) || !quota.buckets.length) return void 0;
|
|
75149
|
+
const classified = [];
|
|
75150
|
+
for (const bucket2 of quota.buckets) {
|
|
75151
|
+
const pool = antigravityPoolFromLeadingLabel(bucket2?.name);
|
|
75152
|
+
const usedPercent = Number(bucket2?.usedPercent);
|
|
75153
|
+
const windowMinutes3 = Number(bucket2?.windowMinutes);
|
|
75154
|
+
if (!pool || !Number.isFinite(usedPercent) || !Number.isFinite(windowMinutes3)) return void 0;
|
|
75155
|
+
classified.push({
|
|
75156
|
+
pool,
|
|
75157
|
+
bucket: { usedPercent, windowMinutes: windowMinutes3, resetsAt: bucket2.resetsAt }
|
|
75158
|
+
});
|
|
75159
|
+
}
|
|
75160
|
+
const selectedBuckets = classified.filter((entry) => entry.pool === selectedPool).map((entry) => entry.bucket);
|
|
75161
|
+
if (!selectedBuckets.length) return void 0;
|
|
75162
|
+
const session = worstBucketWindow(selectedBuckets, DEFAULT_SESSION_WINDOW_MINUTES);
|
|
75163
|
+
const weekly = worstBucketWindow(selectedBuckets, DEFAULT_WEEKLY_WINDOW_MINUTES);
|
|
75164
|
+
if (quota.session && !session || quota.weekly && !weekly) return void 0;
|
|
75165
|
+
return { session, weekly };
|
|
75166
|
+
}
|
|
75130
75167
|
function isSessionResetImminent(session, facts, quota, imminentMs, now) {
|
|
75131
75168
|
const resetsAt = Number(session?.resetsAt);
|
|
75132
75169
|
if (!Number.isFinite(resetsAt) || resetsAt <= 0) return false;
|
|
@@ -75154,13 +75191,16 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
75154
75191
|
if (!Number.isFinite(reporterNowMs)) return false;
|
|
75155
75192
|
return reporterNowMs >= resetsAt;
|
|
75156
75193
|
}
|
|
75157
|
-
function evaluateProviderQuotaGate(node, providerType, policy, now = Date.now(), context) {
|
|
75194
|
+
function evaluateProviderQuotaGate(node, providerType, policy, now = Date.now(), context, target) {
|
|
75158
75195
|
const entry = quotaEntryFor(node, providerType, context, now);
|
|
75159
75196
|
if (!entry) {
|
|
75160
75197
|
logAbsentQuotaFailOpen(node, providerType, policy, now, context);
|
|
75161
75198
|
return null;
|
|
75162
75199
|
}
|
|
75163
75200
|
const { facts, quota } = entry;
|
|
75201
|
+
const targetWindows = quotaWindowsForGateTarget(providerType, quota, target);
|
|
75202
|
+
const sessionWindow = targetWindows ? targetWindows.session : quota.session;
|
|
75203
|
+
const weeklyWindow = targetWindows ? targetWindows.weekly : quota.weekly;
|
|
75164
75204
|
let sessionTrustworthy = true;
|
|
75165
75205
|
let weeklyTrustworthy = true;
|
|
75166
75206
|
if (quota.status !== "ok") {
|
|
@@ -75173,14 +75213,14 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
75173
75213
|
};
|
|
75174
75214
|
}
|
|
75175
75215
|
if (quota.metadata?.lastGoodWindows !== true) return null;
|
|
75176
|
-
sessionTrustworthy = isWindowTrustworthy(
|
|
75177
|
-
weeklyTrustworthy = isWindowTrustworthy(
|
|
75216
|
+
sessionTrustworthy = isWindowTrustworthy(sessionWindow, facts, quota, policy, now);
|
|
75217
|
+
weeklyTrustworthy = isWindowTrustworthy(weeklyWindow, facts, quota, policy, now);
|
|
75178
75218
|
} else {
|
|
75179
|
-
sessionTrustworthy = isWindowTrustworthy(
|
|
75180
|
-
weeklyTrustworthy = isWindowTrustworthy(
|
|
75219
|
+
sessionTrustworthy = isWindowTrustworthy(sessionWindow, facts, quota, policy, now);
|
|
75220
|
+
weeklyTrustworthy = isWindowTrustworthy(weeklyWindow, facts, quota, policy, now);
|
|
75181
75221
|
}
|
|
75182
|
-
if (sessionTrustworthy && isWindowExpired(
|
|
75183
|
-
if (weeklyTrustworthy && isWindowExpired(
|
|
75222
|
+
if (sessionTrustworthy && isWindowExpired(sessionWindow, facts, quota, now)) sessionTrustworthy = false;
|
|
75223
|
+
if (weeklyTrustworthy && isWindowExpired(weeklyWindow, facts, quota, now)) weeklyTrustworthy = false;
|
|
75184
75224
|
if (quota.status === "ok" && !sessionTrustworthy && !weeklyTrustworthy) {
|
|
75185
75225
|
if (!isQuotaSnapshotFresh(facts, quota, policy, now)) {
|
|
75186
75226
|
logStaleQuotaFailOpen(node, providerType, facts, quota, policy, now, context);
|
|
@@ -75188,8 +75228,8 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
75188
75228
|
return null;
|
|
75189
75229
|
}
|
|
75190
75230
|
const resolved = resolveQuotaRoutingPolicy(policy);
|
|
75191
|
-
const session = remainingPercent(
|
|
75192
|
-
if (sessionTrustworthy && session !== void 0 && session < resolved.sessionMinRemainingPercent && !isSessionResetImminent(
|
|
75231
|
+
const session = remainingPercent(sessionWindow);
|
|
75232
|
+
if (sessionTrustworthy && session !== void 0 && session < resolved.sessionMinRemainingPercent && !isSessionResetImminent(sessionWindow, facts, quota, resolved.sessionResetImminentMs, now)) {
|
|
75193
75233
|
return {
|
|
75194
75234
|
reason: PROVIDER_QUOTA_SESSION_LOW_SKIP_REASON,
|
|
75195
75235
|
window: "session",
|
|
@@ -75197,7 +75237,7 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
75197
75237
|
thresholdPercent: resolved.sessionMinRemainingPercent
|
|
75198
75238
|
};
|
|
75199
75239
|
}
|
|
75200
|
-
const weekly = remainingPercent(
|
|
75240
|
+
const weekly = remainingPercent(weeklyWindow);
|
|
75201
75241
|
if (weeklyTrustworthy && weekly !== void 0 && weekly < resolved.weeklyMinRemainingPercent) {
|
|
75202
75242
|
return {
|
|
75203
75243
|
reason: PROVIDER_QUOTA_WEEKLY_LOW_SKIP_REASON,
|
|
@@ -75231,11 +75271,18 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
75231
75271
|
const timeLeftFraction = Math.min(1, Math.max(0, (resetsAt - reporterNowMs) / windowMs));
|
|
75232
75272
|
return { remainingPercent: remaining, risk: expiryRiskScore(remaining, timeLeftFraction) };
|
|
75233
75273
|
}
|
|
75234
|
-
function rankProvidersByQuotaGate(node, orderedProviderTypes, policy, now = Date.now(), context) {
|
|
75274
|
+
function rankProvidersByQuotaGate(node, orderedProviderTypes, policy, now = Date.now(), context, targetsByProvider) {
|
|
75235
75275
|
const clear = [];
|
|
75236
75276
|
const gated = [];
|
|
75237
75277
|
for (const providerType of orderedProviderTypes) {
|
|
75238
|
-
const block2 = evaluateProviderQuotaGate(
|
|
75278
|
+
const block2 = evaluateProviderQuotaGate(
|
|
75279
|
+
node,
|
|
75280
|
+
providerType,
|
|
75281
|
+
policy,
|
|
75282
|
+
now,
|
|
75283
|
+
context,
|
|
75284
|
+
targetsByProvider?.get(providerType)
|
|
75285
|
+
);
|
|
75239
75286
|
if (block2) gated.push({ providerType, block: block2 });
|
|
75240
75287
|
else clear.push(providerType);
|
|
75241
75288
|
}
|
|
@@ -76388,7 +76435,11 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
76388
76435
|
candidates.map((candidate) => candidate.providerType),
|
|
76389
76436
|
args.quotaRouting,
|
|
76390
76437
|
now,
|
|
76391
|
-
args.quotaFactsContext
|
|
76438
|
+
args.quotaFactsContext,
|
|
76439
|
+
new Map(candidates.map((candidate) => [
|
|
76440
|
+
candidate.providerType,
|
|
76441
|
+
{ model: candidate.slot.model }
|
|
76442
|
+
]))
|
|
76392
76443
|
);
|
|
76393
76444
|
const winner = ranked.clear.length ? candidates.find((candidate) => candidate.providerType === ranked.clear[0]) ?? candidates[0] : void 0;
|
|
76394
76445
|
const diagnostics = buildProviderSelectionDiagnostics({
|
|
@@ -79726,10 +79777,17 @@ The mesh has no work in flight. For each mission, decide its outcome: continue i
|
|
|
79726
79777
|
return void 0;
|
|
79727
79778
|
}
|
|
79728
79779
|
function evaluateQuotaClaimGateForAssignment(args) {
|
|
79729
|
-
const { meshId, nodeId, sessionId, providerType, trigger, node, mesh, providerLoader, quotaClaimTrace } = args;
|
|
79780
|
+
const { meshId, nodeId, sessionId, providerType, model, trigger, node, mesh, providerLoader, quotaClaimTrace } = args;
|
|
79730
79781
|
const pinCandidateTaskId = providerPinOverrideCandidateTaskId(meshId, providerType);
|
|
79731
79782
|
const isPinOverride = !!pinCandidateTaskId;
|
|
79732
|
-
const quotaClaimBlock = evaluateProviderQuotaGate(
|
|
79783
|
+
const quotaClaimBlock = evaluateProviderQuotaGate(
|
|
79784
|
+
node,
|
|
79785
|
+
providerType,
|
|
79786
|
+
mesh?.policy?.quotaRouting ?? null,
|
|
79787
|
+
Date.now(),
|
|
79788
|
+
quotaFactsContextForLiveRouting(mesh, isLocalAutoLaunchNode, providerLoader),
|
|
79789
|
+
{ model }
|
|
79790
|
+
);
|
|
79733
79791
|
if (quotaClaimTrace) quotaClaimTrace.evaluated += 1;
|
|
79734
79792
|
if (quotaClaimBlock) {
|
|
79735
79793
|
const observation = {
|
|
@@ -81584,7 +81642,7 @@ ${block2.text}`,
|
|
|
81584
81642
|
noteClaimDeferredForNode(meshId, nodeId);
|
|
81585
81643
|
return false;
|
|
81586
81644
|
}
|
|
81587
|
-
if (evaluateQuotaClaimGateForAssignment({ meshId, nodeId, sessionId, providerType, trigger, node, mesh, providerLoader: components.providerLoader, quotaClaimTrace })) {
|
|
81645
|
+
if (evaluateQuotaClaimGateForAssignment({ meshId, nodeId, sessionId, providerType, model: routingDecision?.selectedSlot?.model, trigger, node, mesh, providerLoader: components.providerLoader, quotaClaimTrace })) {
|
|
81588
81646
|
return false;
|
|
81589
81647
|
}
|
|
81590
81648
|
const inlineBootstrapNode = (() => {
|
|
@@ -117192,6 +117250,7 @@ ${marker}`,
|
|
|
117192
117250
|
var path38 = __toESM2(require("path"));
|
|
117193
117251
|
init_terminal_screen();
|
|
117194
117252
|
var import_session_host_core7 = require_dist();
|
|
117253
|
+
init_logger();
|
|
117195
117254
|
var MAX_PTY_EVENTS = 300;
|
|
117196
117255
|
var EVENT_CONTENT_CAP = 240;
|
|
117197
117256
|
function escapeControl(text) {
|
|
@@ -117231,6 +117290,7 @@ ${marker}`,
|
|
|
117231
117290
|
/** Debug-only ring buffer of PTY input/output/resize/cursor events. */
|
|
117232
117291
|
events = [];
|
|
117233
117292
|
lastCursorKey = "";
|
|
117293
|
+
spawnedAtMs = 0;
|
|
117234
117294
|
start() {
|
|
117235
117295
|
if (!this.factory) {
|
|
117236
117296
|
const { NodePtyTransportFactory: NodePtyTransportFactory2 } = (init_pty_transport(), __toCommonJS2(pty_transport_exports));
|
|
@@ -117244,6 +117304,7 @@ ${marker}`,
|
|
|
117244
117304
|
cols: this.cols,
|
|
117245
117305
|
rows: this.rows
|
|
117246
117306
|
});
|
|
117307
|
+
this.spawnedAtMs = Date.now();
|
|
117247
117308
|
this.recordEvent("spawn", `${this.opts.binary} (${this.cols}x${this.rows})`);
|
|
117248
117309
|
this.handlers.init?.({ pid: this.pty.pid });
|
|
117249
117310
|
this.pty.onData((chunk) => this.onChunk(chunk));
|
|
@@ -117286,9 +117347,40 @@ ${marker}`,
|
|
|
117286
117347
|
getScreenSize() {
|
|
117287
117348
|
return { cols: this.cols, rows: this.rows };
|
|
117288
117349
|
}
|
|
117289
|
-
send_keys(text) {
|
|
117350
|
+
send_keys(text, primeContext) {
|
|
117290
117351
|
this.recordEvent("input", capPreview(escapeControl(text)), text.length);
|
|
117291
|
-
|
|
117352
|
+
if (!primeContext) {
|
|
117353
|
+
this.pty?.write(text);
|
|
117354
|
+
return;
|
|
117355
|
+
}
|
|
117356
|
+
return this.writeInput(text, primeContext);
|
|
117357
|
+
}
|
|
117358
|
+
async writeInput(text, primeContext) {
|
|
117359
|
+
const bytes = Buffer.byteLength(text, "utf8");
|
|
117360
|
+
const source = primeContext.source;
|
|
117361
|
+
const prefix = `[${primeContext.specTag}] `;
|
|
117362
|
+
const afterSpawnMs = Math.max(0, Date.now() - this.spawnedAtMs);
|
|
117363
|
+
const pty = this.pty;
|
|
117364
|
+
if (!pty) {
|
|
117365
|
+
LOG2.warn("FsmDriver", `${prefix}PTY write skipped source=${source} bytes=${bytes} afterSpawnMs=${afterSpawnMs} outcome=no-active-pty`);
|
|
117366
|
+
return false;
|
|
117367
|
+
}
|
|
117368
|
+
LOG2.debug("FsmDriver", `${prefix}PTY write before source=${source} bytes=${bytes} afterSpawnMs=${afterSpawnMs}`);
|
|
117369
|
+
try {
|
|
117370
|
+
const result = await pty.write(text);
|
|
117371
|
+
const completedAfterSpawnMs = Math.max(0, Date.now() - this.spawnedAtMs);
|
|
117372
|
+
if (result === false) {
|
|
117373
|
+
LOG2.warn("FsmDriver", `${prefix}PTY write after source=${source} bytes=${bytes} afterSpawnMs=${completedAfterSpawnMs} outcome=false`);
|
|
117374
|
+
return false;
|
|
117375
|
+
}
|
|
117376
|
+
LOG2.debug("FsmDriver", `${prefix}PTY write after source=${source} bytes=${bytes} afterSpawnMs=${completedAfterSpawnMs} outcome=success`);
|
|
117377
|
+
return true;
|
|
117378
|
+
} catch (error48) {
|
|
117379
|
+
const completedAfterSpawnMs = Math.max(0, Date.now() - this.spawnedAtMs);
|
|
117380
|
+
const message = error48 instanceof Error ? error48.message : String(error48);
|
|
117381
|
+
LOG2.warn("FsmDriver", `${prefix}PTY write after source=${source} bytes=${bytes} afterSpawnMs=${completedAfterSpawnMs} outcome=error error=${message}`);
|
|
117382
|
+
return false;
|
|
117383
|
+
}
|
|
117292
117384
|
}
|
|
117293
117385
|
/** Forward runtime metadata (meshNodeId, workspaceLabel, lifecycle, …) to
|
|
117294
117386
|
* the underlying transport so it reaches the session registry. The spec
|
|
@@ -117636,6 +117728,8 @@ ${marker}`,
|
|
|
117636
117728
|
return n;
|
|
117637
117729
|
}
|
|
117638
117730
|
var SUBMIT_DELAY_FLOOR_MS = 200;
|
|
117731
|
+
var DEFAULT_SPAWN_PRIME_MAX_WAIT_MS = 2e3;
|
|
117732
|
+
var STALL_REFOCUS_INFO_LIMIT = 3;
|
|
117639
117733
|
var SEND_IN_FLIGHT_MAX_MS = 3e4;
|
|
117640
117734
|
var DUPLICATE_RESEND_WINDOW_MS = 6e4;
|
|
117641
117735
|
function hashSendText(text) {
|
|
@@ -117694,6 +117788,7 @@ ${marker}`,
|
|
|
117694
117788
|
init: () => this.emitInitialState(),
|
|
117695
117789
|
on_pty_data: (chunk) => {
|
|
117696
117790
|
this.lastPtyDataAt = Date.now();
|
|
117791
|
+
this.scheduleSpawnPrimeAfterFirstOutput();
|
|
117697
117792
|
this.emit({ kind: "pty_data", chunk });
|
|
117698
117793
|
},
|
|
117699
117794
|
on_screen_changed: () => this.reevaluate(),
|
|
@@ -117735,10 +117830,21 @@ ${marker}`,
|
|
|
117735
117830
|
* Re-arms itself while the machine is generating so a re-prime can fire
|
|
117736
117831
|
* even when the PTY has gone completely quiet. */
|
|
117737
117832
|
stallTimer = null;
|
|
117833
|
+
/** Spawn-prime delay timer. First PTY output is useful readiness evidence,
|
|
117834
|
+
* not proof that stdin is ready; a separate max-wait timer preserves the
|
|
117835
|
+
* old spawn-relative fallback when no output ever arrives. */
|
|
117836
|
+
spawnPrimeTimer = null;
|
|
117837
|
+
spawnPrimeMaxWaitTimer = null;
|
|
117838
|
+
spawnPrimeAwaitingOutput = false;
|
|
117839
|
+
/** Shared latch for the first-output and max-wait paths. Both timers call
|
|
117840
|
+
* fireSpawnPrimeOnce(), which consumes it before writing. */
|
|
117841
|
+
spawnPrimePending = false;
|
|
117738
117842
|
/** Wall-clock time the last stall re-prime was injected. The cooldown gate:
|
|
117739
117843
|
* after a re-prime we don't re-inject until the screen changes (which
|
|
117740
117844
|
* resets the stall reference) or another full stall window lapses. */
|
|
117741
117845
|
lastRefocusAt = 0;
|
|
117846
|
+
stallRefocusInfoCount = 0;
|
|
117847
|
+
stallRefocusSuppressedCount = 0;
|
|
117742
117848
|
/** Timer driving the win32 verification-based submit resend loop (see
|
|
117743
117849
|
* WIN32_SUBMIT_* and scheduleVerifiedSubmit). Re-arms itself until the FSM
|
|
117744
117850
|
* leaves idle (submitted) or the resend budget is spent. */
|
|
@@ -117825,25 +117931,71 @@ ${marker}`,
|
|
|
117825
117931
|
this.startupDismissConfig = normalizeStartupDismissConfig(this.spec.startup_dismiss);
|
|
117826
117932
|
this.startupDismissState = createStartupDismissState();
|
|
117827
117933
|
this.startupDismissSpawnAt = now;
|
|
117934
|
+
this.armSpawnPrime();
|
|
117828
117935
|
this.adapter.start();
|
|
117829
|
-
this.scheduleSpawnPrime();
|
|
117830
117936
|
this.scheduleWakeForState();
|
|
117831
117937
|
}
|
|
117832
|
-
|
|
117938
|
+
armSpawnPrime() {
|
|
117833
117939
|
const seqs = this.spec.send_on_spawn;
|
|
117834
117940
|
if (!Array.isArray(seqs) || seqs.length === 0) return;
|
|
117941
|
+
this.spawnPrimeAwaitingOutput = true;
|
|
117942
|
+
this.spawnPrimePending = true;
|
|
117943
|
+
const configuredMaxWait = this.spec.send_on_spawn_max_wait_ms;
|
|
117944
|
+
const maxWait = typeof configuredMaxWait === "number" && Number.isFinite(configuredMaxWait) ? Math.max(0, configuredMaxWait) : DEFAULT_SPAWN_PRIME_MAX_WAIT_MS;
|
|
117945
|
+
this.spawnPrimeMaxWaitTimer = setTimeout(() => {
|
|
117946
|
+
this.spawnPrimeMaxWaitTimer = null;
|
|
117947
|
+
this.fireSpawnPrimeOnce("max-wait");
|
|
117948
|
+
}, maxWait);
|
|
117949
|
+
}
|
|
117950
|
+
/** Start the configured prime delay only after the child produces output.
|
|
117951
|
+
* On macOS, writing at a fixed spawn-relative deadline can land while the
|
|
117952
|
+
* slave is still in canonical echo mode: focus-in is echoed as `^[[I` and
|
|
117953
|
+
* lost before the TUI installs its input handler. First output is the
|
|
117954
|
+
* earliest transport-level readiness evidence available to the engine. */
|
|
117955
|
+
scheduleSpawnPrimeAfterFirstOutput() {
|
|
117956
|
+
if (!this.spawnPrimeAwaitingOutput) return;
|
|
117957
|
+
this.spawnPrimeAwaitingOutput = false;
|
|
117958
|
+
if (this.spawnPrimeMaxWaitTimer) {
|
|
117959
|
+
clearTimeout(this.spawnPrimeMaxWaitTimer);
|
|
117960
|
+
this.spawnPrimeMaxWaitTimer = null;
|
|
117961
|
+
}
|
|
117835
117962
|
const delay2 = Math.max(0, this.spec.send_on_spawn_delay_ms ?? 250);
|
|
117836
|
-
setTimeout(() =>
|
|
117963
|
+
this.spawnPrimeTimer = setTimeout(() => {
|
|
117964
|
+
this.spawnPrimeTimer = null;
|
|
117965
|
+
this.fireSpawnPrimeOnce("first-output");
|
|
117966
|
+
}, delay2);
|
|
117967
|
+
}
|
|
117968
|
+
/** Consume the one-shot spawn-prime latch and cancel the competing timer
|
|
117969
|
+
* before writing. JavaScript callbacks are serialized, so whichever path
|
|
117970
|
+
* gets here first makes every later/queued callback a no-op. */
|
|
117971
|
+
fireSpawnPrimeOnce(trigger) {
|
|
117972
|
+
if (!this.spawnPrimePending) return;
|
|
117973
|
+
this.spawnPrimePending = false;
|
|
117974
|
+
this.spawnPrimeAwaitingOutput = false;
|
|
117975
|
+
if (this.spawnPrimeTimer) {
|
|
117976
|
+
clearTimeout(this.spawnPrimeTimer);
|
|
117977
|
+
this.spawnPrimeTimer = null;
|
|
117978
|
+
}
|
|
117979
|
+
if (this.spawnPrimeMaxWaitTimer) {
|
|
117980
|
+
clearTimeout(this.spawnPrimeMaxWaitTimer);
|
|
117981
|
+
this.spawnPrimeMaxWaitTimer = null;
|
|
117982
|
+
}
|
|
117983
|
+
this.sendSpawnPrime("spawn-prime", trigger);
|
|
117837
117984
|
}
|
|
117838
117985
|
/** Write the declared `send_on_spawn` sequences to the PTY once. Used both
|
|
117839
|
-
*
|
|
117986
|
+
* on the first-output spawn path and, for focus-gated TUIs, by the stall
|
|
117840
117987
|
* watchdog to re-inject the focus-in wake mid-turn. No-op when the spec
|
|
117841
117988
|
* declares no prime, so non-focus-gated CLIs are never poked. */
|
|
117842
|
-
sendSpawnPrime() {
|
|
117989
|
+
sendSpawnPrime(source, trigger) {
|
|
117843
117990
|
const seqs = this.spec.send_on_spawn;
|
|
117844
117991
|
if (!Array.isArray(seqs) || seqs.length === 0) return;
|
|
117845
|
-
|
|
117846
|
-
|
|
117992
|
+
const validSeqs = seqs.filter((seq2) => typeof seq2 === "string" && seq2.length > 0);
|
|
117993
|
+
if (validSeqs.length === 0) return;
|
|
117994
|
+
if (source === "spawn-prime") {
|
|
117995
|
+
LOG2.info("FsmDriver", `[${this.specTag()}] spawn prime firing trigger=${trigger ?? "unknown"} sequences=${validSeqs.length}`);
|
|
117996
|
+
}
|
|
117997
|
+
for (const seq2 of validSeqs) {
|
|
117998
|
+
void this.adapter.send_keys(seq2, { source, specTag: this.specTag() });
|
|
117847
117999
|
}
|
|
117848
118000
|
}
|
|
117849
118001
|
/** Boot-prompt dismissal (CliSpecV4.startup_dismiss). Runs on every
|
|
@@ -117950,6 +118102,17 @@ ${marker}`,
|
|
|
117950
118102
|
clearTimeout(this.stallTimer);
|
|
117951
118103
|
this.stallTimer = null;
|
|
117952
118104
|
}
|
|
118105
|
+
if (this.spawnPrimeTimer) {
|
|
118106
|
+
clearTimeout(this.spawnPrimeTimer);
|
|
118107
|
+
this.spawnPrimeTimer = null;
|
|
118108
|
+
}
|
|
118109
|
+
if (this.spawnPrimeMaxWaitTimer) {
|
|
118110
|
+
clearTimeout(this.spawnPrimeMaxWaitTimer);
|
|
118111
|
+
this.spawnPrimeMaxWaitTimer = null;
|
|
118112
|
+
}
|
|
118113
|
+
this.spawnPrimeAwaitingOutput = false;
|
|
118114
|
+
this.spawnPrimePending = false;
|
|
118115
|
+
this.flushStallRefocusLogSummary();
|
|
117953
118116
|
if (this.win32SubmitTimer) {
|
|
117954
118117
|
clearTimeout(this.win32SubmitTimer);
|
|
117955
118118
|
this.win32SubmitTimer = null;
|
|
@@ -118428,12 +118591,26 @@ ${marker}`,
|
|
|
118428
118591
|
const stalledFor = now - this.lastScreenChangeAt();
|
|
118429
118592
|
const sinceLastRefocus = now - this.lastRefocusAt;
|
|
118430
118593
|
if (stalledFor >= windowMs && sinceLastRefocus >= windowMs) {
|
|
118431
|
-
|
|
118432
|
-
|
|
118594
|
+
if (this.stallRefocusInfoCount < STALL_REFOCUS_INFO_LIMIT) {
|
|
118595
|
+
this.stallRefocusInfoCount += 1;
|
|
118596
|
+
LOG2.info("FsmDriver", `[${this.specTag()}] stall detected (${stalledFor}ms quiet, generating) \u2014 re-injecting focus-in (${this.stallRefocusInfoCount}/${STALL_REFOCUS_INFO_LIMIT} detailed)`);
|
|
118597
|
+
} else {
|
|
118598
|
+
this.stallRefocusSuppressedCount += 1;
|
|
118599
|
+
}
|
|
118600
|
+
this.sendSpawnPrime("stall-refocus");
|
|
118433
118601
|
this.lastRefocusAt = now;
|
|
118434
118602
|
}
|
|
118435
118603
|
this.scheduleStallWatchdog();
|
|
118436
118604
|
}
|
|
118605
|
+
/** Emit one exact session summary, then zero the counter so recursive or
|
|
118606
|
+
* repeated shutdown calls cannot duplicate it. Write failures remain warn
|
|
118607
|
+
* per attempt in TerminalAdapter and are never suppressed here. */
|
|
118608
|
+
flushStallRefocusLogSummary() {
|
|
118609
|
+
const suppressed = this.stallRefocusSuppressedCount;
|
|
118610
|
+
if (suppressed === 0) return;
|
|
118611
|
+
this.stallRefocusSuppressedCount = 0;
|
|
118612
|
+
LOG2.info("FsmDriver", `[${this.specTag()}] stall refocus log summary: ${suppressed} later reinjection(s) suppressed after first ${STALL_REFOCUS_INFO_LIMIT}`);
|
|
118613
|
+
}
|
|
118437
118614
|
maybeMarkReady() {
|
|
118438
118615
|
if (this.readySeenOnce) return;
|
|
118439
118616
|
const st = stateById(this.spec, this.currentStateId);
|
|
@@ -122929,21 +123106,64 @@ ${marker}`,
|
|
|
122929
123106
|
}
|
|
122930
123107
|
return attrs;
|
|
122931
123108
|
}
|
|
123109
|
+
var TAIL_JSONL_CACHE_MAX_ENTRIES = 8;
|
|
123110
|
+
var TAIL_JSONL_CACHE_MAX_SOURCE_BYTES = 16 * 1024 * 1024;
|
|
123111
|
+
var tailJsonlCache = /* @__PURE__ */ new Map();
|
|
123112
|
+
var tailJsonlCacheSourceBytes = 0;
|
|
123113
|
+
var tailJsonlCacheStats = { hits: 0, misses: 0, fileReads: 0, parsePasses: 0 };
|
|
123114
|
+
function tailJsonlSignature(stat2) {
|
|
123115
|
+
return `${stat2.dev}:${stat2.ino}:${stat2.size}:${stat2.mtimeMs}:${stat2.ctimeMs}`;
|
|
123116
|
+
}
|
|
123117
|
+
function storeTailJsonlCache(p, entry) {
|
|
123118
|
+
const prior = tailJsonlCache.get(p);
|
|
123119
|
+
if (prior) tailJsonlCacheSourceBytes -= prior.sourceBytes;
|
|
123120
|
+
tailJsonlCache.delete(p);
|
|
123121
|
+
if (entry.sourceBytes > TAIL_JSONL_CACHE_MAX_SOURCE_BYTES) return;
|
|
123122
|
+
tailJsonlCache.set(p, entry);
|
|
123123
|
+
tailJsonlCacheSourceBytes += entry.sourceBytes;
|
|
123124
|
+
while (tailJsonlCache.size > TAIL_JSONL_CACHE_MAX_ENTRIES || tailJsonlCacheSourceBytes > TAIL_JSONL_CACHE_MAX_SOURCE_BYTES) {
|
|
123125
|
+
const oldestKey = tailJsonlCache.keys().next().value;
|
|
123126
|
+
if (oldestKey === void 0) break;
|
|
123127
|
+
const oldest = tailJsonlCache.get(oldestKey);
|
|
123128
|
+
if (oldest) tailJsonlCacheSourceBytes -= oldest.sourceBytes;
|
|
123129
|
+
tailJsonlCache.delete(oldestKey);
|
|
123130
|
+
}
|
|
123131
|
+
}
|
|
122932
123132
|
function readTailJsonlLines(filePath, maxBytes) {
|
|
122933
|
-
|
|
123133
|
+
let stat2;
|
|
123134
|
+
try {
|
|
123135
|
+
stat2 = fs41.statSync(filePath);
|
|
123136
|
+
} catch {
|
|
123137
|
+
return [];
|
|
123138
|
+
}
|
|
123139
|
+
const signature = tailJsonlSignature(stat2);
|
|
123140
|
+
const cached5 = tailJsonlCache.get(filePath);
|
|
123141
|
+
if (cached5?.signature === signature && cached5.maxBytes === maxBytes) {
|
|
123142
|
+
tailJsonlCacheStats.hits++;
|
|
123143
|
+
tailJsonlCache.delete(filePath);
|
|
123144
|
+
tailJsonlCache.set(filePath, cached5);
|
|
123145
|
+
return cached5.lines;
|
|
123146
|
+
}
|
|
123147
|
+
tailJsonlCacheStats.misses++;
|
|
122934
123148
|
const size = stat2.size;
|
|
122935
123149
|
const start = size > maxBytes ? size - maxBytes : 0;
|
|
122936
123150
|
const length = size - start;
|
|
122937
|
-
if (length <= 0)
|
|
122938
|
-
|
|
123151
|
+
if (length <= 0) {
|
|
123152
|
+
const out2 = [];
|
|
123153
|
+
storeTailJsonlCache(filePath, { signature, maxBytes, sourceBytes: size, lines: out2 });
|
|
123154
|
+
return out2;
|
|
123155
|
+
}
|
|
122939
123156
|
let text;
|
|
123157
|
+
const fd = fs41.openSync(filePath, "r");
|
|
122940
123158
|
try {
|
|
122941
123159
|
const buf = Buffer.alloc(length);
|
|
122942
123160
|
const bytes = fs41.readSync(fd, buf, 0, length, start);
|
|
122943
123161
|
text = buf.subarray(0, bytes).toString("utf8");
|
|
123162
|
+
tailJsonlCacheStats.fileReads++;
|
|
122944
123163
|
} finally {
|
|
122945
123164
|
fs41.closeSync(fd);
|
|
122946
123165
|
}
|
|
123166
|
+
tailJsonlCacheStats.parsePasses++;
|
|
122947
123167
|
const rawLines = text.split("\n");
|
|
122948
123168
|
if (start > 0 && rawLines.length > 0) rawLines.shift();
|
|
122949
123169
|
const out = [];
|
|
@@ -122955,6 +123175,7 @@ ${marker}`,
|
|
|
122955
123175
|
} catch {
|
|
122956
123176
|
}
|
|
122957
123177
|
}
|
|
123178
|
+
storeTailJsonlCache(filePath, { signature, maxBytes, sourceBytes: size, lines: out });
|
|
122958
123179
|
return out;
|
|
122959
123180
|
}
|
|
122960
123181
|
var USER_RE = /^>\s+(\S.*)$/;
|