switchroom 0.20.3 → 0.20.5
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/cli/switchroom.js +10256 -1233
- package/dist/host-control/main.js +1 -1
- package/package.json +3 -3
- package/skills/switchroom-release/SKILL.md +3 -2
- package/telegram-plugin/dist/gateway/gateway.js +452 -99
- package/telegram-plugin/edit-flood-fuse.ts +332 -14
- package/telegram-plugin/gateway/feed-open-gate.ts +28 -8
- package/telegram-plugin/gateway/feed-reopen-gate.ts +88 -6
- package/telegram-plugin/gateway/gateway.ts +130 -104
- package/telegram-plugin/gateway/narrative-lane.ts +19 -0
- package/telegram-plugin/gateway/progress-fallback-cap.ts +195 -0
- package/telegram-plugin/gateway/stream-render.ts +67 -12
- package/telegram-plugin/gateway/subagent-handback-inbound-builder.ts +13 -0
- package/telegram-plugin/gateway/subagent-origin-surface.ts +181 -0
- package/telegram-plugin/gateway/subagent-progress-inbound-builder.ts +7 -0
- package/telegram-plugin/registry/subagents-schema.ts +61 -0
- package/telegram-plugin/registry/turns-schema.ts +26 -0
- package/telegram-plugin/silence-poke.ts +80 -0
- package/telegram-plugin/tests/edit-flood-fuse-cosmetic-fairness.test.ts +229 -0
- package/telegram-plugin/tests/feed-open-gate.test.ts +42 -0
- package/telegram-plugin/tests/feed-reopen-gate.test.ts +114 -0
- package/telegram-plugin/tests/progress-cap.test.ts +182 -0
- package/telegram-plugin/tests/progress-fallback-cap.test.ts +91 -0
- package/telegram-plugin/tests/progress-update.test.ts +108 -12
- package/telegram-plugin/tests/silence-poke-card-render.test.ts +300 -0
- package/telegram-plugin/tests/subagent-handback-inbound-builder.test.ts +46 -0
- package/telegram-plugin/tests/subagent-progress-inbound-builder.test.ts +26 -0
- package/telegram-plugin/tests/worker-origin-gap-dispatch.test.ts +304 -0
|
@@ -65060,6 +65060,9 @@ var EDIT_FLOOD_FUSE_DEFAULTS = {
|
|
|
65060
65060
|
perChatSendMaxPerWindow: 25,
|
|
65061
65061
|
cosmeticPerMessageMaxPerWindow: 4,
|
|
65062
65062
|
cosmeticPerChatMaxPerWindow: 6,
|
|
65063
|
+
cosmeticFloorPerWindow: 2,
|
|
65064
|
+
cosmeticFloorSlots: 2,
|
|
65065
|
+
throttleNoticeMs: 45000,
|
|
65063
65066
|
perChatTotalMaxPerWindow: 20,
|
|
65064
65067
|
perChatReplyReserve: 8,
|
|
65065
65068
|
perChatCriticalMinPerWindow: 3,
|
|
@@ -65094,6 +65097,11 @@ function editFloodFuseConfigFromEnv(env) {
|
|
|
65094
65097
|
};
|
|
65095
65098
|
assign("cosmeticPerMessageMaxPerWindow", envInt(env.SWITCHROOM_FEED_EDIT_MAX_PER_MSG_PER_MIN));
|
|
65096
65099
|
assign("cosmeticPerChatMaxPerWindow", envInt(env.SWITCHROOM_FEED_EDIT_MAX_PER_CHAT_PER_MIN));
|
|
65100
|
+
if (env.SWITCHROOM_FEED_FAIR_SHARE === "0")
|
|
65101
|
+
cfg.cosmeticFairShareEnabled = false;
|
|
65102
|
+
assign("cosmeticFloorPerWindow", envInt(env.SWITCHROOM_FEED_EDIT_FLOOR_PER_MSG_PER_MIN));
|
|
65103
|
+
assign("cosmeticFloorSlots", envInt(env.SWITCHROOM_FEED_EDIT_FLOOR_SLOTS));
|
|
65104
|
+
assign("throttleNoticeMs", envInt(env.SWITCHROOM_FEED_THROTTLE_NOTICE_MS));
|
|
65097
65105
|
assign("perChatTotalMaxPerWindow", envInt(env.SWITCHROOM_CHAT_TOTAL_MAX_PER_MIN));
|
|
65098
65106
|
assign("perChatReplyReserve", envInt(env.SWITCHROOM_CHAT_REPLY_RESERVE));
|
|
65099
65107
|
assign("perChatCriticalMinPerWindow", envInt(env.SWITCHROOM_CHAT_CRITICAL_MIN_PER_MIN));
|
|
@@ -65118,6 +65126,11 @@ function createEditFloodFuse(config = {}) {
|
|
|
65118
65126
|
const perChatSendMax = config.perChatSendMaxPerWindow ?? D.perChatSendMaxPerWindow;
|
|
65119
65127
|
const cosmeticPerMessageMax = Math.min(perMessageMax, config.cosmeticPerMessageMaxPerWindow ?? D.cosmeticPerMessageMaxPerWindow);
|
|
65120
65128
|
const cosmeticPerChatMax = Math.min(perChatEditMax, config.cosmeticPerChatMaxPerWindow ?? D.cosmeticPerChatMaxPerWindow);
|
|
65129
|
+
const cosmeticFairShareEnabled = config.cosmeticFairShareEnabled ?? true;
|
|
65130
|
+
const cosmeticFloorPerWindow = Math.min(cosmeticPerMessageMax, Math.max(0, config.cosmeticFloorPerWindow ?? D.cosmeticFloorPerWindow));
|
|
65131
|
+
const cosmeticFloorAggMax = Math.min(cosmeticPerChatMax, cosmeticFloorPerWindow * Math.max(0, config.cosmeticFloorSlots ?? D.cosmeticFloorSlots));
|
|
65132
|
+
const cosmeticRemainderMax = Math.max(0, cosmeticPerChatMax - cosmeticFloorAggMax);
|
|
65133
|
+
const throttleNoticeMs = Math.max(0, config.throttleNoticeMs ?? D.throttleNoticeMs);
|
|
65121
65134
|
const perChatTotalMax = config.perChatTotalMaxPerWindow ?? D.perChatTotalMaxPerWindow;
|
|
65122
65135
|
const perChatWindowMs = config.perChatWindowMs ?? D.perChatWindowMs;
|
|
65123
65136
|
const perChatReplyReserve = Math.min(Math.max(0, config.perChatReplyReserve ?? D.perChatReplyReserve), Math.max(0, perChatTotalMax - 1));
|
|
@@ -65142,7 +65155,8 @@ function createEditFloodFuse(config = {}) {
|
|
|
65142
65155
|
superseded: 0,
|
|
65143
65156
|
floodObserved: 0,
|
|
65144
65157
|
meteredByDefault: 0,
|
|
65145
|
-
chatless: 0
|
|
65158
|
+
chatless: 0,
|
|
65159
|
+
throttled: 0
|
|
65146
65160
|
};
|
|
65147
65161
|
let tightenLevel = 0;
|
|
65148
65162
|
let tightenedUntil = 0;
|
|
@@ -65189,6 +65203,12 @@ function createEditFloodFuse(config = {}) {
|
|
|
65189
65203
|
return eff;
|
|
65190
65204
|
return Math.max(eff, Math.min(base, perChatCriticalMin));
|
|
65191
65205
|
}
|
|
65206
|
+
function cosmeticMessageCeiling(now) {
|
|
65207
|
+
const eff = ceiling(cosmeticPerMessageMax, now);
|
|
65208
|
+
if (!cosmeticFairShareEnabled)
|
|
65209
|
+
return eff;
|
|
65210
|
+
return Math.max(eff, Math.min(cosmeticPerMessageMax, cosmeticFloorPerWindow));
|
|
65211
|
+
}
|
|
65192
65212
|
function cosmeticTotalMax(now) {
|
|
65193
65213
|
const eff = ceiling(perChatTotalMax, now);
|
|
65194
65214
|
if (replyReserveFraction <= 0)
|
|
@@ -65281,9 +65301,87 @@ function createEditFloodFuse(config = {}) {
|
|
|
65281
65301
|
if (i >= 0)
|
|
65282
65302
|
w.ts.splice(i, 1);
|
|
65283
65303
|
}
|
|
65304
|
+
function makeThrottleNotice(method, key, cls) {
|
|
65305
|
+
if (!cosmeticFairShareEnabled || cls !== "cosmetic" || throttleNoticeMs <= 0) {
|
|
65306
|
+
return { tick: () => {}, sleepCap: () => Number.POSITIVE_INFINITY };
|
|
65307
|
+
}
|
|
65308
|
+
let firstAt = Number.NaN;
|
|
65309
|
+
let fired = false;
|
|
65310
|
+
return {
|
|
65311
|
+
tick: (now) => {
|
|
65312
|
+
if (Number.isNaN(firstAt))
|
|
65313
|
+
firstAt = now;
|
|
65314
|
+
if (fired || now - firstAt < throttleNoticeMs)
|
|
65315
|
+
return;
|
|
65316
|
+
fired = true;
|
|
65317
|
+
counters.throttled++;
|
|
65318
|
+
onTrip?.({ method, key, action: "throttled", cls });
|
|
65319
|
+
},
|
|
65320
|
+
sleepCap: (now) => {
|
|
65321
|
+
if (fired || Number.isNaN(firstAt))
|
|
65322
|
+
return Number.POSITIVE_INFINITY;
|
|
65323
|
+
return Math.max(1, firstAt + throttleNoticeMs - now);
|
|
65324
|
+
}
|
|
65325
|
+
};
|
|
65326
|
+
}
|
|
65327
|
+
async function awaitCosmeticChatFair(chat, msg, method, cls, dropGuard, deadline, lateReleaseKey) {
|
|
65328
|
+
const fMsgKey = `cmf:${chat}:${msg}`;
|
|
65329
|
+
const fAggKey = `cfa:${chat}`;
|
|
65330
|
+
const remKey = `cer:${chat}`;
|
|
65331
|
+
const fw = win(fMsgKey);
|
|
65332
|
+
const aw = win(fAggKey);
|
|
65333
|
+
const rw = win(remKey);
|
|
65334
|
+
let counted = false;
|
|
65335
|
+
const throttle = makeThrottleNotice(method, remKey, cls);
|
|
65336
|
+
for (;; ) {
|
|
65337
|
+
const now = clock.now();
|
|
65338
|
+
const wFloor = Math.max(waitFor(fw, now, perChatWindowMs, cosmeticFloorPerWindow), waitFor(aw, now, perChatWindowMs, cosmeticFloorAggMax));
|
|
65339
|
+
const remCap = cosmeticRemainderMax <= 0 ? 0 : ceiling(cosmeticRemainderMax, now);
|
|
65340
|
+
const wRem = waitFor(rw, now, perChatWindowMs, remCap);
|
|
65341
|
+
if (wFloor === 0) {
|
|
65342
|
+
fw.ts.push(now);
|
|
65343
|
+
aw.ts.push(now);
|
|
65344
|
+
return [[fMsgKey, now], [fAggKey, now]];
|
|
65345
|
+
}
|
|
65346
|
+
if (remCap > 0 && wRem === 0) {
|
|
65347
|
+
rw.ts.push(now);
|
|
65348
|
+
return [[remKey, now]];
|
|
65349
|
+
}
|
|
65350
|
+
if (now >= deadline) {
|
|
65351
|
+
if (dropGuard()) {
|
|
65352
|
+
counters.dropped++;
|
|
65353
|
+
onTrip?.({ method, key: remKey, action: "dropped", cls });
|
|
65354
|
+
return null;
|
|
65355
|
+
}
|
|
65356
|
+
const lw = win(lateReleaseKey);
|
|
65357
|
+
prune(lw, now, perChatWindowMs);
|
|
65358
|
+
if (lw.ts.length >= ceiling(lateReleaseMax, now)) {
|
|
65359
|
+
counters.dropped++;
|
|
65360
|
+
onTrip?.({ method, key: remKey, action: "dropped", cls });
|
|
65361
|
+
return null;
|
|
65362
|
+
}
|
|
65363
|
+
lw.ts.push(now);
|
|
65364
|
+
if (cosmeticRemainderMax > 0) {
|
|
65365
|
+
rw.ts.push(now);
|
|
65366
|
+
return [[remKey, now]];
|
|
65367
|
+
}
|
|
65368
|
+
fw.ts.push(now);
|
|
65369
|
+
aw.ts.push(now);
|
|
65370
|
+
return [[fMsgKey, now], [fAggKey, now]];
|
|
65371
|
+
}
|
|
65372
|
+
if (!counted) {
|
|
65373
|
+
counters.deferred++;
|
|
65374
|
+
counted = true;
|
|
65375
|
+
onTrip?.({ method, key: remKey, action: "deferred", cls });
|
|
65376
|
+
}
|
|
65377
|
+
throttle.tick(now);
|
|
65378
|
+
await clock.sleep(Math.min(Math.min(wFloor, wRem), deadline - now, throttle.sleepCap(now)));
|
|
65379
|
+
}
|
|
65380
|
+
}
|
|
65284
65381
|
async function awaitRoom(key, windowMs, maxFor, method, mode, cls, dropGuard, deadline, lateReleaseKey) {
|
|
65285
65382
|
const w = win(key);
|
|
65286
65383
|
let counted = false;
|
|
65384
|
+
const throttle = makeThrottleNotice(method, key, cls);
|
|
65287
65385
|
for (;; ) {
|
|
65288
65386
|
const now = clock.now();
|
|
65289
65387
|
const wait = waitFor(w, now, windowMs, maxFor(now));
|
|
@@ -65315,6 +65413,8 @@ function createEditFloodFuse(config = {}) {
|
|
|
65315
65413
|
counted = true;
|
|
65316
65414
|
onTrip?.({ method, key, action: "deferred", cls });
|
|
65317
65415
|
}
|
|
65416
|
+
throttle.tick(now);
|
|
65417
|
+
const nap = Math.min(wait, deadline - now, throttle.sleepCap(now));
|
|
65318
65418
|
let killed = false;
|
|
65319
65419
|
if (mode === "supersede") {
|
|
65320
65420
|
w.waiter?.kill();
|
|
@@ -65324,7 +65424,7 @@ function createEditFloodFuse(config = {}) {
|
|
|
65324
65424
|
resolve6();
|
|
65325
65425
|
} };
|
|
65326
65426
|
});
|
|
65327
|
-
await Promise.race([clock.sleep(
|
|
65427
|
+
await Promise.race([clock.sleep(nap), superseded]);
|
|
65328
65428
|
if (killed) {
|
|
65329
65429
|
counters.superseded++;
|
|
65330
65430
|
onTrip?.({ method, key, action: "superseded", cls });
|
|
@@ -65332,7 +65432,7 @@ function createEditFloodFuse(config = {}) {
|
|
|
65332
65432
|
}
|
|
65333
65433
|
w.waiter = null;
|
|
65334
65434
|
} else {
|
|
65335
|
-
await clock.sleep(
|
|
65435
|
+
await clock.sleep(nap);
|
|
65336
65436
|
}
|
|
65337
65437
|
}
|
|
65338
65438
|
}
|
|
@@ -65368,7 +65468,7 @@ function createEditFloodFuse(config = {}) {
|
|
|
65368
65468
|
const dropGuard = () => mw.inflight > 1;
|
|
65369
65469
|
const lateKey = cls === "cosmetic" ? `lr:${chat}` : undefined;
|
|
65370
65470
|
try {
|
|
65371
|
-
const msgSlot = await awaitRoom(msgKey, perMessageWindowMs, cls === "cosmetic" ? (t) =>
|
|
65471
|
+
const msgSlot = await awaitRoom(msgKey, perMessageWindowMs, cls === "cosmetic" ? (t) => cosmeticMessageCeiling(t) : (t) => classCeiling(perMessageMax, cls, t), method, "supersede", cls, dropGuard, deadline, lateKey);
|
|
65372
65472
|
if (msgSlot === null)
|
|
65373
65473
|
return DROPPED_RESULT;
|
|
65374
65474
|
const reserved = [[msgKey, msgSlot]];
|
|
@@ -65376,13 +65476,22 @@ function createEditFloodFuse(config = {}) {
|
|
|
65376
65476
|
for (const [k, at] of reserved)
|
|
65377
65477
|
unreserve(k, at);
|
|
65378
65478
|
};
|
|
65379
|
-
|
|
65380
|
-
|
|
65381
|
-
|
|
65382
|
-
|
|
65383
|
-
|
|
65479
|
+
if (cls === "cosmetic" && cosmeticFairShareEnabled) {
|
|
65480
|
+
const fairSlots = await awaitCosmeticChatFair(chat, msg, method, cls, dropGuard, deadline, lateKey);
|
|
65481
|
+
if (fairSlots === null) {
|
|
65482
|
+
giveBack();
|
|
65483
|
+
return DROPPED_RESULT;
|
|
65484
|
+
}
|
|
65485
|
+
reserved.push(...fairSlots);
|
|
65486
|
+
} else {
|
|
65487
|
+
const chatKey2 = `ce:${chat}`;
|
|
65488
|
+
const chatSlot = await awaitRoom(chatKey2, perChatWindowMs, cls === "cosmetic" ? (t) => ceiling(cosmeticPerChatMax, t) : (t) => classCeiling(perChatEditMax, cls, t), method, "drop", cls, dropGuard, deadline, lateKey);
|
|
65489
|
+
if (chatSlot === null) {
|
|
65490
|
+
giveBack();
|
|
65491
|
+
return DROPPED_RESULT;
|
|
65492
|
+
}
|
|
65493
|
+
reserved.push([chatKey2, chatSlot]);
|
|
65384
65494
|
}
|
|
65385
|
-
reserved.push([chatKey2, chatSlot]);
|
|
65386
65495
|
const totalSlot = await awaitRoom(totalKey, perChatWindowMs, chatTotalMax, method, cls === "cosmetic" ? "drop" : "release", cls, dropGuard, deadline, lateKey);
|
|
65387
65496
|
if (totalSlot === null) {
|
|
65388
65497
|
giveBack();
|
|
@@ -65431,6 +65540,8 @@ function createEditFloodFuse(config = {}) {
|
|
|
65431
65540
|
deferred: counters.deferred,
|
|
65432
65541
|
dropped: counters.dropped,
|
|
65433
65542
|
superseded: counters.superseded,
|
|
65543
|
+
throttled: counters.throttled,
|
|
65544
|
+
cosmeticFairShareEnabled,
|
|
65434
65545
|
floodObserved: counters.floodObserved,
|
|
65435
65546
|
tightened: isTightened(now),
|
|
65436
65547
|
tightenLevel: levelAt(now),
|
|
@@ -71444,6 +71555,7 @@ __export(exports_silence_poke, {
|
|
|
71444
71555
|
noteThinking: () => noteThinking,
|
|
71445
71556
|
noteProduction: () => noteProduction,
|
|
71446
71557
|
noteOutbound: () => noteOutbound2,
|
|
71558
|
+
noteCardRender: () => noteCardRender,
|
|
71447
71559
|
noteBackgroundShellDead: () => noteBackgroundShellDead,
|
|
71448
71560
|
noteBackgroundShellAlive: () => noteBackgroundShellAlive,
|
|
71449
71561
|
longestInFlightTool: () => longestInFlightTool,
|
|
@@ -71456,7 +71568,8 @@ __export(exports_silence_poke, {
|
|
|
71456
71568
|
__getStateForTests: () => __getStateForTests,
|
|
71457
71569
|
__bgMarkerParserConfirmedForTests: () => __bgMarkerParserConfirmedForTests,
|
|
71458
71570
|
DEFAULT_THRESHOLDS: () => DEFAULT_THRESHOLDS,
|
|
71459
|
-
DEFAULT_POLL_INTERVAL_MS: () => DEFAULT_POLL_INTERVAL_MS
|
|
71571
|
+
DEFAULT_POLL_INTERVAL_MS: () => DEFAULT_POLL_INTERVAL_MS,
|
|
71572
|
+
CARD_RENDER_FRESH_MS: () => CARD_RENDER_FRESH_MS
|
|
71460
71573
|
});
|
|
71461
71574
|
|
|
71462
71575
|
// turn-liveness-floor.ts
|
|
@@ -71522,6 +71635,7 @@ var DEFAULT_THRESHOLDS = {
|
|
|
71522
71635
|
fallback: 300000
|
|
71523
71636
|
};
|
|
71524
71637
|
var DEFAULT_POLL_INTERVAL_MS = 5000;
|
|
71638
|
+
var CARD_RENDER_FRESH_MS = 30000;
|
|
71525
71639
|
var state2 = new Map;
|
|
71526
71640
|
var timer = null;
|
|
71527
71641
|
var activeDeps = null;
|
|
@@ -71540,6 +71654,7 @@ function startTurn(key, now) {
|
|
|
71540
71654
|
fallbackFired: false,
|
|
71541
71655
|
floorFired: false,
|
|
71542
71656
|
inFlightTools: new Map,
|
|
71657
|
+
lastCardRenderAt: null,
|
|
71543
71658
|
sawBashThisTurn: false,
|
|
71544
71659
|
aliveShells: new Set
|
|
71545
71660
|
});
|
|
@@ -71571,6 +71686,12 @@ function noteProduction(key, now) {
|
|
|
71571
71686
|
s.lastOutboundAt = now;
|
|
71572
71687
|
s.fallbackFired = false;
|
|
71573
71688
|
}
|
|
71689
|
+
function noteCardRender(key, now) {
|
|
71690
|
+
const s = state2.get(key);
|
|
71691
|
+
if (s == null)
|
|
71692
|
+
return;
|
|
71693
|
+
s.lastCardRenderAt = now;
|
|
71694
|
+
}
|
|
71574
71695
|
function noteThinking(key, now) {
|
|
71575
71696
|
const s = state2.get(key);
|
|
71576
71697
|
if (s == null)
|
|
@@ -71717,6 +71838,8 @@ function tick(now) {
|
|
|
71717
71838
|
if (underCeiling) {
|
|
71718
71839
|
if (activeDeps.isCompactionInFlight?.(key) === true)
|
|
71719
71840
|
continue;
|
|
71841
|
+
if (s.lastCardRenderAt != null && now - s.lastCardRenderAt < CARD_RENDER_FRESH_MS)
|
|
71842
|
+
continue;
|
|
71720
71843
|
const forceDisable = process.env.SWITCHROOM_SILENCE_DEFER_INFLIGHT_TOOLS === "0";
|
|
71721
71844
|
if (!forceDisable && activeDeps.isLegitimatelyWorking != null) {
|
|
71722
71845
|
if (activeDeps.isLegitimatelyWorking(key))
|
|
@@ -78753,6 +78876,28 @@ init_rich_send();
|
|
|
78753
78876
|
init_format();
|
|
78754
78877
|
|
|
78755
78878
|
// registry/subagents-schema.ts
|
|
78879
|
+
function mapSubagentRow(row) {
|
|
78880
|
+
return {
|
|
78881
|
+
id: row.id,
|
|
78882
|
+
parent_session_id: row.parent_session_id,
|
|
78883
|
+
parent_turn_key: row.parent_turn_key,
|
|
78884
|
+
agent_type: row.agent_type,
|
|
78885
|
+
description: row.description,
|
|
78886
|
+
background: row.background !== 0,
|
|
78887
|
+
started_at: row.started_at,
|
|
78888
|
+
last_activity_at: row.last_activity_at,
|
|
78889
|
+
ended_at: row.ended_at,
|
|
78890
|
+
status: row.status,
|
|
78891
|
+
result_summary: row.result_summary,
|
|
78892
|
+
jsonl_agent_id: row.jsonl_agent_id,
|
|
78893
|
+
parent_agent_id: row.parent_agent_id ?? null,
|
|
78894
|
+
model: row.model ?? null
|
|
78895
|
+
};
|
|
78896
|
+
}
|
|
78897
|
+
function getSubagentByJsonlId(db2, jsonlAgentId) {
|
|
78898
|
+
const row = db2.prepare("SELECT * FROM subagents WHERE jsonl_agent_id = ?").get(jsonlAgentId);
|
|
78899
|
+
return row ? mapSubagentRow(row) : null;
|
|
78900
|
+
}
|
|
78756
78901
|
function countRunningBackgroundSubagents(db2) {
|
|
78757
78902
|
const row = db2.prepare("SELECT count(*) AS n FROM subagents WHERE background = 1 AND status = 'running'").get();
|
|
78758
78903
|
return row?.n ?? 0;
|
|
@@ -78847,6 +78992,19 @@ function recordNestedSubagentDispatch(db2, args) {
|
|
|
78847
78992
|
WHERE id = ?
|
|
78848
78993
|
`).run(args.parentJsonlAgentId, args.parentJsonlAgentId, args.toolUseId);
|
|
78849
78994
|
}
|
|
78995
|
+
function stampSubagentDispatchTurn(db2, args) {
|
|
78996
|
+
db2.prepare(`
|
|
78997
|
+
INSERT OR IGNORE INTO subagents
|
|
78998
|
+
(id, parent_session_id, parent_turn_key, agent_type, description,
|
|
78999
|
+
background, started_at, last_activity_at, status)
|
|
79000
|
+
VALUES (?, NULL, ?, ?, ?, ?, ?, ?, 'running')
|
|
79001
|
+
`).run(args.toolUseId, args.parentTurnKey, args.agentType ?? null, args.description ?? null, args.background ? 1 : 0, args.now, args.now);
|
|
79002
|
+
db2.prepare(`
|
|
79003
|
+
UPDATE subagents
|
|
79004
|
+
SET parent_turn_key = COALESCE(parent_turn_key, ?)
|
|
79005
|
+
WHERE id = ?
|
|
79006
|
+
`).run(args.parentTurnKey, args.toolUseId);
|
|
79007
|
+
}
|
|
78850
79008
|
function resolveSubagentOriginTurnKey(db2, jsonlAgentId, maxHops = 5) {
|
|
78851
79009
|
const seen = new Set;
|
|
78852
79010
|
let currentJsonlId = jsonlAgentId;
|
|
@@ -81177,6 +81335,28 @@ function isDraftOfReply(textBlock, replyText) {
|
|
|
81177
81335
|
}
|
|
81178
81336
|
|
|
81179
81337
|
// registry/turns-schema.ts
|
|
81338
|
+
function mapRow(row) {
|
|
81339
|
+
return {
|
|
81340
|
+
turn_key: row.turn_key,
|
|
81341
|
+
chat_id: row.chat_id,
|
|
81342
|
+
thread_id: row.thread_id,
|
|
81343
|
+
started_at: row.started_at,
|
|
81344
|
+
ended_at: row.ended_at,
|
|
81345
|
+
ended_via: row.ended_via ?? null,
|
|
81346
|
+
last_assistant_msg_id: row.last_assistant_msg_id,
|
|
81347
|
+
last_assistant_done: row.last_assistant_done === null ? null : row.last_assistant_done !== 0,
|
|
81348
|
+
last_user_msg_id: row.last_user_msg_id,
|
|
81349
|
+
user_prompt_preview: row.user_prompt_preview,
|
|
81350
|
+
assistant_reply_preview: row.assistant_reply_preview,
|
|
81351
|
+
tool_call_count: row.tool_call_count,
|
|
81352
|
+
interrupt_reason: row.interrupt_reason,
|
|
81353
|
+
resumed_at: row.resumed_at,
|
|
81354
|
+
session_id: row.session_id ?? null,
|
|
81355
|
+
answer_redelivered_at: row.answer_redelivered_at ?? null,
|
|
81356
|
+
created_at: row.created_at,
|
|
81357
|
+
updated_at: row.updated_at
|
|
81358
|
+
};
|
|
81359
|
+
}
|
|
81180
81360
|
function recordTurnStart(db3, args) {
|
|
81181
81361
|
const now = Date.now();
|
|
81182
81362
|
db3.prepare(`
|
|
@@ -81201,6 +81381,19 @@ function recordTurnEnd(db3, args) {
|
|
|
81201
81381
|
WHERE turn_key = ?
|
|
81202
81382
|
`).run(now, args.endedVia, args.lastAssistantMsgId ?? null, args.lastAssistantDone !== undefined ? args.lastAssistantDone ? 1 : 0 : null, args.assistantReplyPreview ?? null, args.toolCallCount !== undefined ? args.toolCallCount : null, now, args.turnKey);
|
|
81203
81383
|
}
|
|
81384
|
+
function getTurnByKey(db3, turnKey3) {
|
|
81385
|
+
const row = db3.prepare(`SELECT * FROM turns WHERE turn_key = ?`).get(turnKey3);
|
|
81386
|
+
return row ? mapRow(row) : null;
|
|
81387
|
+
}
|
|
81388
|
+
function findMostRecentTurn(db3, beforeOrAtMs) {
|
|
81389
|
+
const row = db3.prepare(`
|
|
81390
|
+
SELECT * FROM turns
|
|
81391
|
+
WHERE started_at <= ?
|
|
81392
|
+
ORDER BY started_at DESC
|
|
81393
|
+
LIMIT 1
|
|
81394
|
+
`).get(beforeOrAtMs);
|
|
81395
|
+
return row ? mapRow(row) : null;
|
|
81396
|
+
}
|
|
81204
81397
|
var INTERRUPTED_VIA = new Set([
|
|
81205
81398
|
"restart",
|
|
81206
81399
|
"reaped_stale",
|
|
@@ -81316,7 +81509,7 @@ function mayOpenActivityCard(input) {
|
|
|
81316
81509
|
if (input.crossTurnAnswerDelivered)
|
|
81317
81510
|
return false;
|
|
81318
81511
|
if (input.finalAnswerEverDelivered) {
|
|
81319
|
-
if (input.postAnswerSubagentActivity && input.producer === "tool")
|
|
81512
|
+
if ((input.postAnswerSubagentActivity || input.postAnswerMainActivity) && input.producer === "tool")
|
|
81320
81513
|
return true;
|
|
81321
81514
|
return false;
|
|
81322
81515
|
}
|
|
@@ -81429,17 +81622,24 @@ class EmissionAuthority {
|
|
|
81429
81622
|
}
|
|
81430
81623
|
|
|
81431
81624
|
// gateway/feed-reopen-gate.ts
|
|
81625
|
+
var SUBSTANTIVE_REOPEN_MIN_LABELS = 2;
|
|
81432
81626
|
function shouldReopenFeedAfterAck(input) {
|
|
81433
81627
|
if (!input.finalAnswerDelivered)
|
|
81434
81628
|
return false;
|
|
81435
|
-
if (input.finalAnswerSubstantive)
|
|
81436
|
-
|
|
81629
|
+
if (input.finalAnswerSubstantive) {
|
|
81630
|
+
if (input.reopenAfterSubstantiveEnabled !== true)
|
|
81631
|
+
return false;
|
|
81632
|
+
return (input.postSubstantiveToolLabelCount ?? 0) >= SUBSTANTIVE_REOPEN_MIN_LABELS;
|
|
81633
|
+
}
|
|
81437
81634
|
return input.enabled === true;
|
|
81438
81635
|
}
|
|
81439
81636
|
function decideFeedReopen(input) {
|
|
81440
81637
|
if (!shouldReopenFeedAfterAck(input)) {
|
|
81441
81638
|
return { dropLabel: true };
|
|
81442
81639
|
}
|
|
81640
|
+
if (input.finalAnswerSubstantive) {
|
|
81641
|
+
return { dropLabel: false, liftLeverOne: true };
|
|
81642
|
+
}
|
|
81443
81643
|
return {
|
|
81444
81644
|
dropLabel: false,
|
|
81445
81645
|
reset: {
|
|
@@ -81950,6 +82150,12 @@ function noteProduction2(key, now) {
|
|
|
81950
82150
|
s.lastOutboundAt = now;
|
|
81951
82151
|
s.fallbackFired = false;
|
|
81952
82152
|
}
|
|
82153
|
+
function noteCardRender2(key, now) {
|
|
82154
|
+
const s = state5.get(key);
|
|
82155
|
+
if (s == null)
|
|
82156
|
+
return;
|
|
82157
|
+
s.lastCardRenderAt = now;
|
|
82158
|
+
}
|
|
81953
82159
|
function endTurn2(key) {
|
|
81954
82160
|
state5.delete(key);
|
|
81955
82161
|
}
|
|
@@ -82165,6 +82371,8 @@ function beginTurn(deps, ev) {
|
|
|
82165
82371
|
replyCalled: false,
|
|
82166
82372
|
finalAnswerDelivered: false,
|
|
82167
82373
|
finalAnswerSubstantive: false,
|
|
82374
|
+
postSubstantiveToolLabelCount: 0,
|
|
82375
|
+
postAnswerMainActivity: false,
|
|
82168
82376
|
finalAnswerEverDelivered: false,
|
|
82169
82377
|
answerDelivered: false,
|
|
82170
82378
|
flushedAnswerText: null,
|
|
@@ -82278,6 +82486,7 @@ function handleSessionEvent(deps, ev) {
|
|
|
82278
82486
|
CONTEXT_EXHAUSTION_COOLDOWN_MS,
|
|
82279
82487
|
DELIVERY_CONFIRM_ENABLED,
|
|
82280
82488
|
FEED_REOPEN_AFTER_ACK_ENABLED,
|
|
82489
|
+
FEED_REOPEN_AFTER_SUBSTANTIVE_ENABLED,
|
|
82281
82490
|
HANDBACK_PRETURN_ENABLED,
|
|
82282
82491
|
HISTORY_ENABLED,
|
|
82283
82492
|
LIVENESS_TERMINAL_HONESTY,
|
|
@@ -82472,6 +82681,21 @@ function handleSessionEvent(deps, ev) {
|
|
|
82472
82681
|
resolvePendingNarrativeOnTool(turn, ev.toolName, ev.input);
|
|
82473
82682
|
turn.toolCallCount++;
|
|
82474
82683
|
touchTurnActiveMarker(STATE_DIR);
|
|
82684
|
+
if ((ev.toolName === "Agent" || ev.toolName === "Task") && ev.toolUseId != null && ev.toolUseId.length > 0 && turn.registryKey != null && turnsDb != null) {
|
|
82685
|
+
try {
|
|
82686
|
+
stampSubagentDispatchTurn(turnsDb, {
|
|
82687
|
+
toolUseId: ev.toolUseId,
|
|
82688
|
+
parentTurnKey: turn.registryKey,
|
|
82689
|
+
agentType: typeof ev.input?.subagent_type === "string" ? ev.input.subagent_type : null,
|
|
82690
|
+
description: typeof ev.input?.description === "string" ? ev.input.description : null,
|
|
82691
|
+
background: ev.input?.run_in_background === true,
|
|
82692
|
+
now: Date.now()
|
|
82693
|
+
});
|
|
82694
|
+
} catch (err) {
|
|
82695
|
+
process.stderr.write(`telegram gateway: dispatch-time parent_turn_key stamp failed toolUseId=${ev.toolUseId}: ${err.message}
|
|
82696
|
+
`);
|
|
82697
|
+
}
|
|
82698
|
+
}
|
|
82475
82699
|
preambleSuppressor.onTool({ isReplyTool: isTelegramSurfaceTool(ev.toolName) });
|
|
82476
82700
|
surfaceMemoryLegibility(turn, ev.toolName, ev.toolUseId, ev.input);
|
|
82477
82701
|
const ctrl = activeStatusReactions.get(statusKey(turn.sessionChatId, turn.sessionThreadId));
|
|
@@ -82510,16 +82734,24 @@ function handleSessionEvent(deps, ev) {
|
|
|
82510
82734
|
if (isTelegramSurfaceTool(ev.toolName))
|
|
82511
82735
|
return;
|
|
82512
82736
|
if (turn.finalAnswerDelivered) {
|
|
82737
|
+
if (turn.finalAnswerSubstantive)
|
|
82738
|
+
turn.postSubstantiveToolLabelCount++;
|
|
82513
82739
|
const reopen = decideFeedReopen({
|
|
82514
82740
|
finalAnswerDelivered: turn.finalAnswerDelivered,
|
|
82515
82741
|
finalAnswerSubstantive: turn.finalAnswerSubstantive,
|
|
82516
|
-
enabled: FEED_REOPEN_AFTER_ACK_ENABLED
|
|
82742
|
+
enabled: FEED_REOPEN_AFTER_ACK_ENABLED,
|
|
82743
|
+
reopenAfterSubstantiveEnabled: FEED_REOPEN_AFTER_SUBSTANTIVE_ENABLED,
|
|
82744
|
+
postSubstantiveToolLabelCount: turn.postSubstantiveToolLabelCount
|
|
82517
82745
|
});
|
|
82518
82746
|
if (reopen.dropLabel)
|
|
82519
82747
|
return;
|
|
82520
|
-
|
|
82521
|
-
|
|
82522
|
-
|
|
82748
|
+
if (reopen.reset != null) {
|
|
82749
|
+
turn.finalAnswerDelivered = reopen.reset.finalAnswerDelivered;
|
|
82750
|
+
turn.activityMessageId = reopen.reset.activityMessageId;
|
|
82751
|
+
turn.activityLastSentRender = reopen.reset.activityLastSentRender;
|
|
82752
|
+
}
|
|
82753
|
+
if (reopen.liftLeverOne)
|
|
82754
|
+
turn.postAnswerMainActivity = true;
|
|
82523
82755
|
}
|
|
82524
82756
|
const rendered = appendActivityLabel(turn.mirrorLines, ev.label);
|
|
82525
82757
|
if (rendered != null) {
|
|
@@ -83450,7 +83682,8 @@ function createNarrativeLane(deps) {
|
|
|
83450
83682
|
finalAnswerEverDelivered: turn.finalAnswerEverDelivered,
|
|
83451
83683
|
labeledToolCount: turn.labeledToolCount,
|
|
83452
83684
|
crossTurnAnswerDelivered,
|
|
83453
|
-
postAnswerSubagentActivity: openFlags?.postAnswerSubagentActivity
|
|
83685
|
+
postAnswerSubagentActivity: openFlags?.postAnswerSubagentActivity,
|
|
83686
|
+
postAnswerMainActivity: turn.postAnswerMainActivity
|
|
83454
83687
|
})) {
|
|
83455
83688
|
break;
|
|
83456
83689
|
}
|
|
@@ -83491,6 +83724,7 @@ function createNarrativeLane(deps) {
|
|
|
83491
83724
|
if (isSendGateShed(editRes))
|
|
83492
83725
|
break;
|
|
83493
83726
|
}
|
|
83727
|
+
noteCardRender2(statusKey(chat, thread), Date.now());
|
|
83494
83728
|
turn.activityLastSentRender = target;
|
|
83495
83729
|
} catch (err) {
|
|
83496
83730
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -94966,6 +95200,77 @@ function createTurnStartSurfaces(deps) {
|
|
|
94966
95200
|
return { armTurnStartSurfaces };
|
|
94967
95201
|
}
|
|
94968
95202
|
|
|
95203
|
+
// gateway/progress-fallback-cap.ts
|
|
95204
|
+
var recentSends = new Map;
|
|
95205
|
+
var PROGRESS_FALLBACK_WINDOW_MS = 15 * 60000;
|
|
95206
|
+
var PROGRESS_FALLBACK_MAX = 5;
|
|
95207
|
+
function prune(key, now) {
|
|
95208
|
+
const cutoff = now - PROGRESS_FALLBACK_WINDOW_MS;
|
|
95209
|
+
const recent = (recentSends.get(key) ?? []).filter((ts) => ts > cutoff);
|
|
95210
|
+
if (recent.length === 0)
|
|
95211
|
+
recentSends.delete(key);
|
|
95212
|
+
else
|
|
95213
|
+
recentSends.set(key, recent);
|
|
95214
|
+
return recent;
|
|
95215
|
+
}
|
|
95216
|
+
function reserveProgressFallbackSlot(key, now) {
|
|
95217
|
+
const recent = prune(key, now);
|
|
95218
|
+
if (recent.length >= PROGRESS_FALLBACK_MAX)
|
|
95219
|
+
return null;
|
|
95220
|
+
recent.push(now);
|
|
95221
|
+
recentSends.set(key, recent);
|
|
95222
|
+
let released = false;
|
|
95223
|
+
return {
|
|
95224
|
+
release: () => {
|
|
95225
|
+
if (released)
|
|
95226
|
+
return;
|
|
95227
|
+
released = true;
|
|
95228
|
+
const arr = recentSends.get(key);
|
|
95229
|
+
if (!arr)
|
|
95230
|
+
return;
|
|
95231
|
+
const idx = arr.indexOf(now);
|
|
95232
|
+
if (idx >= 0)
|
|
95233
|
+
arr.splice(idx, 1);
|
|
95234
|
+
if (arr.length === 0)
|
|
95235
|
+
recentSends.delete(key);
|
|
95236
|
+
else
|
|
95237
|
+
recentSends.set(key, arr);
|
|
95238
|
+
}
|
|
95239
|
+
};
|
|
95240
|
+
}
|
|
95241
|
+
var PROGRESS_TURN_MAX = 5;
|
|
95242
|
+
function reserveProgressSlot(deps) {
|
|
95243
|
+
const { key, now, turnStart, turnCount } = deps;
|
|
95244
|
+
if (turnStart != null) {
|
|
95245
|
+
const current = turnCount.get(key) ?? 0;
|
|
95246
|
+
if (current >= PROGRESS_TURN_MAX)
|
|
95247
|
+
return null;
|
|
95248
|
+
turnCount.set(key, current + 1);
|
|
95249
|
+
let released = false;
|
|
95250
|
+
return {
|
|
95251
|
+
release: () => {
|
|
95252
|
+
if (released)
|
|
95253
|
+
return;
|
|
95254
|
+
released = true;
|
|
95255
|
+
turnCount.set(key, Math.max(0, (turnCount.get(key) ?? 0) - 1));
|
|
95256
|
+
}
|
|
95257
|
+
};
|
|
95258
|
+
}
|
|
95259
|
+
return reserveProgressFallbackSlot(key, now);
|
|
95260
|
+
}
|
|
95261
|
+
async function sendWithProgressCap(deps, send) {
|
|
95262
|
+
const reservation = reserveProgressSlot(deps);
|
|
95263
|
+
if (reservation === null)
|
|
95264
|
+
return { capped: true };
|
|
95265
|
+
try {
|
|
95266
|
+
const result = await send();
|
|
95267
|
+
return { capped: false, result };
|
|
95268
|
+
} catch (err) {
|
|
95269
|
+
reservation.release();
|
|
95270
|
+
throw err;
|
|
95271
|
+
}
|
|
95272
|
+
}
|
|
95273
|
+
|
|
94969
95274
|
// gateway/delivery-confirm-wiring.ts
|
|
94970
95275
|
function createDeliveryConfirmWiring(deps) {
|
|
94971
95276
|
const {
|
|
@@ -96432,6 +96737,7 @@ ${result}
|
|
|
96432
96737
|
meta: {
|
|
96433
96738
|
source: "subagent_handback",
|
|
96434
96739
|
outcome: opts.ctx.outcome,
|
|
96740
|
+
chat_id: opts.ctx.chatId,
|
|
96435
96741
|
message_id: String(ts),
|
|
96436
96742
|
...opts.ctx.threadId != null ? { message_thread_id: String(opts.ctx.threadId) } : {},
|
|
96437
96743
|
...opts.ctx.jsonlAgentId ? { subagent_jsonl_id: opts.ctx.jsonlAgentId } : {}
|
|
@@ -96511,6 +96817,7 @@ ${summary}
|
|
|
96511
96817
|
text: text4,
|
|
96512
96818
|
meta: {
|
|
96513
96819
|
source: "subagent_progress",
|
|
96820
|
+
chat_id: opts.ctx.chatId,
|
|
96514
96821
|
...opts.ctx.threadId != null ? { message_thread_id: String(opts.ctx.threadId) } : {},
|
|
96515
96822
|
subagent_jsonl_id: opts.ctx.subagentJsonlId,
|
|
96516
96823
|
bucket_idx: String(opts.ctx.bucketIdx),
|
|
@@ -102121,10 +102428,10 @@ function startOutboxSweep(deps) {
|
|
|
102121
102428
|
}
|
|
102122
102429
|
|
|
102123
102430
|
// ../src/build-info.ts
|
|
102124
|
-
var VERSION2 = "0.20.
|
|
102125
|
-
var COMMIT_SHA = "
|
|
102126
|
-
var COMMIT_DATE = "2026-08-
|
|
102127
|
-
var LATEST_PR =
|
|
102431
|
+
var VERSION2 = "0.20.5";
|
|
102432
|
+
var COMMIT_SHA = "fc40cc8e";
|
|
102433
|
+
var COMMIT_DATE = "2026-08-04T04:03:52Z";
|
|
102434
|
+
var LATEST_PR = 4338;
|
|
102128
102435
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
102129
102436
|
|
|
102130
102437
|
// gateway/boot-version.ts
|
|
@@ -103176,7 +103483,7 @@ function openTurnsDb(agentDir) {
|
|
|
103176
103483
|
} catch {}
|
|
103177
103484
|
return db3;
|
|
103178
103485
|
}
|
|
103179
|
-
function
|
|
103486
|
+
function mapRow2(row) {
|
|
103180
103487
|
return {
|
|
103181
103488
|
turn_key: row.turn_key,
|
|
103182
103489
|
chat_id: row.chat_id,
|
|
@@ -103213,10 +103520,6 @@ function recordTurnEnd2(db3, args) {
|
|
|
103213
103520
|
WHERE turn_key = ?
|
|
103214
103521
|
`).run(now, args.endedVia, args.lastAssistantMsgId ?? null, args.lastAssistantDone !== undefined ? args.lastAssistantDone ? 1 : 0 : null, args.assistantReplyPreview ?? null, args.toolCallCount !== undefined ? args.toolCallCount : null, now, args.turnKey);
|
|
103215
103522
|
}
|
|
103216
|
-
function getTurnByKey(db3, turnKey3) {
|
|
103217
|
-
const row = db3.prepare(`SELECT * FROM turns WHERE turn_key = ?`).get(turnKey3);
|
|
103218
|
-
return row ? mapRow(row) : null;
|
|
103219
|
-
}
|
|
103220
103523
|
function markOrphanedWithTimeoutClassification(db3, opts) {
|
|
103221
103524
|
const now = opts.now ?? Date.now();
|
|
103222
103525
|
const isHang = opts.markerAgeMs != null && opts.markerAgeMs >= opts.hangThresholdMs && opts.markerTurnKey != null && opts.markerTurnKey.length > 0;
|
|
@@ -103307,7 +103610,7 @@ function findLatestTurnIfInterrupted(db3) {
|
|
|
103307
103610
|
`).get();
|
|
103308
103611
|
if (!row)
|
|
103309
103612
|
return null;
|
|
103310
|
-
const turn =
|
|
103613
|
+
const turn = mapRow2(row);
|
|
103311
103614
|
if (turn.resumed_at != null)
|
|
103312
103615
|
return null;
|
|
103313
103616
|
if (turn.ended_at == null)
|
|
@@ -104296,7 +104599,7 @@ function applySubagentsSchema(db3) {
|
|
|
104296
104599
|
db3.exec("CREATE INDEX IF NOT EXISTS subagents_jsonl_id ON subagents(jsonl_agent_id)");
|
|
104297
104600
|
db3.exec("CREATE INDEX IF NOT EXISTS subagents_parent_agent ON subagents(parent_agent_id)");
|
|
104298
104601
|
}
|
|
104299
|
-
function
|
|
104602
|
+
function mapSubagentRow2(row) {
|
|
104300
104603
|
return {
|
|
104301
104604
|
id: row.id,
|
|
104302
104605
|
parent_session_id: row.parent_session_id,
|
|
@@ -104314,9 +104617,9 @@ function mapSubagentRow(row) {
|
|
|
104314
104617
|
model: row.model ?? null
|
|
104315
104618
|
};
|
|
104316
104619
|
}
|
|
104317
|
-
function
|
|
104620
|
+
function getSubagentByJsonlId2(db3, jsonlAgentId) {
|
|
104318
104621
|
const row = db3.prepare("SELECT * FROM subagents WHERE jsonl_agent_id = ?").get(jsonlAgentId);
|
|
104319
|
-
return row ?
|
|
104622
|
+
return row ? mapSubagentRow2(row) : null;
|
|
104320
104623
|
}
|
|
104321
104624
|
function listNonTerminalSubagentsForTurn(db3, parentTurnKey) {
|
|
104322
104625
|
const rows = db3.prepare(`
|
|
@@ -104325,23 +104628,74 @@ function listNonTerminalSubagentsForTurn(db3, parentTurnKey) {
|
|
|
104325
104628
|
AND status NOT IN ('completed', 'failed')
|
|
104326
104629
|
ORDER BY started_at ASC
|
|
104327
104630
|
`).all(parentTurnKey);
|
|
104328
|
-
return rows.map(
|
|
104631
|
+
return rows.map(mapSubagentRow2);
|
|
104329
104632
|
}
|
|
104330
|
-
|
|
104331
|
-
|
|
104332
|
-
|
|
104333
|
-
|
|
104334
|
-
|
|
104633
|
+
|
|
104634
|
+
// gateway/subagent-origin-surface.ts
|
|
104635
|
+
function turnToSurfaceChat(turn) {
|
|
104636
|
+
if (turn == null || turn.chat_id.length === 0)
|
|
104637
|
+
return null;
|
|
104638
|
+
const threadNum = turn.thread_id != null && turn.thread_id.length > 0 ? Number(turn.thread_id) : NaN;
|
|
104639
|
+
return {
|
|
104640
|
+
chatId: turn.chat_id,
|
|
104641
|
+
...Number.isFinite(threadNum) ? { threadId: threadNum } : {}
|
|
104642
|
+
};
|
|
104643
|
+
}
|
|
104644
|
+
function resolveSubagentOriginChatDb(db3, jsonlAgentId) {
|
|
104645
|
+
try {
|
|
104646
|
+
const originKey = resolveSubagentOriginTurnKey(db3, jsonlAgentId);
|
|
104647
|
+
if (originKey == null)
|
|
104335
104648
|
return null;
|
|
104336
|
-
|
|
104337
|
-
|
|
104338
|
-
|
|
104649
|
+
return turnToSurfaceChat(getTurnByKey(db3, originKey));
|
|
104650
|
+
} catch {
|
|
104651
|
+
return null;
|
|
104652
|
+
}
|
|
104653
|
+
}
|
|
104654
|
+
function resolveRecentTurnFallbackChat(db3, jsonlAgentId) {
|
|
104655
|
+
try {
|
|
104656
|
+
const worker = getSubagentByJsonlId(db3, jsonlAgentId);
|
|
104657
|
+
if (worker == null)
|
|
104339
104658
|
return null;
|
|
104340
|
-
|
|
104341
|
-
|
|
104342
|
-
|
|
104659
|
+
return turnToSurfaceChat(findMostRecentTurn(db3, worker.started_at));
|
|
104660
|
+
} catch {
|
|
104661
|
+
return null;
|
|
104343
104662
|
}
|
|
104344
|
-
|
|
104663
|
+
}
|
|
104664
|
+
var RECENT_TURN_FLOOR_LOG_CAP = 256;
|
|
104665
|
+
var recentTurnFloorLogged = new Set;
|
|
104666
|
+
function noteWorkerRecentTurnFloor(agentId, dest, log = (line) => process.stderr.write(line)) {
|
|
104667
|
+
if (recentTurnFloorLogged.has(agentId))
|
|
104668
|
+
return;
|
|
104669
|
+
recentTurnFloorLogged.add(agentId);
|
|
104670
|
+
if (recentTurnFloorLogged.size > RECENT_TURN_FLOOR_LOG_CAP) {
|
|
104671
|
+
const oldest = recentTurnFloorLogged.values().next().value;
|
|
104672
|
+
if (oldest != null)
|
|
104673
|
+
recentTurnFloorLogged.delete(oldest);
|
|
104674
|
+
}
|
|
104675
|
+
log(`telegram gateway: worker origin unresolved agent=${agentId} \u2014 flooring to pre-dispatch recent turn chat=${dest.chatId}${dest.threadId != null ? ` thread=${dest.threadId}` : ""}
|
|
104676
|
+
`);
|
|
104677
|
+
}
|
|
104678
|
+
function resolveWorkerSurfaceForDecider(db3, jsonlAgentId, opts) {
|
|
104679
|
+
const dest = resolveWorkerSurfaceChat(db3, jsonlAgentId, opts);
|
|
104680
|
+
if (dest.via === "recent-turn")
|
|
104681
|
+
noteWorkerRecentTurnFloor(jsonlAgentId, dest);
|
|
104682
|
+
return {
|
|
104683
|
+
fleetChatId: dest.via === "owner-dm" || dest.via === "none" ? "" : dest.chatId,
|
|
104684
|
+
...dest.threadId != null ? { originThreadId: dest.threadId } : {}
|
|
104685
|
+
};
|
|
104686
|
+
}
|
|
104687
|
+
function resolveWorkerSurfaceChat(db3, jsonlAgentId, opts) {
|
|
104688
|
+
const origin = db3 != null ? resolveSubagentOriginChatDb(db3, jsonlAgentId) : null;
|
|
104689
|
+
if (origin != null && origin.chatId.length > 0)
|
|
104690
|
+
return { ...origin, via: "origin" };
|
|
104691
|
+
if (opts.fleetChatId.length > 0)
|
|
104692
|
+
return { chatId: opts.fleetChatId, via: "fleet" };
|
|
104693
|
+
const recent = db3 != null ? resolveRecentTurnFallbackChat(db3, jsonlAgentId) : null;
|
|
104694
|
+
if (recent != null)
|
|
104695
|
+
return { ...recent, via: "recent-turn" };
|
|
104696
|
+
if (opts.ownerDm.length > 0)
|
|
104697
|
+
return { chatId: opts.ownerDm, via: "owner-dm" };
|
|
104698
|
+
return { chatId: "", via: "none" };
|
|
104345
104699
|
}
|
|
104346
104700
|
|
|
104347
104701
|
// gateway/worker-feed-dispatch.ts
|
|
@@ -104887,21 +105241,12 @@ if (isGatewayMain)
|
|
|
104887
105241
|
function resolveSubagentOriginChat(agentId) {
|
|
104888
105242
|
if (turnsDb == null)
|
|
104889
105243
|
return null;
|
|
104890
|
-
|
|
104891
|
-
|
|
104892
|
-
|
|
104893
|
-
|
|
104894
|
-
const turn = getTurnByKey(turnsDb, originKey);
|
|
104895
|
-
if (turn == null || turn.chat_id.length === 0)
|
|
104896
|
-
return null;
|
|
104897
|
-
const threadNum = turn.thread_id != null && turn.thread_id.length > 0 ? Number(turn.thread_id) : NaN;
|
|
104898
|
-
return {
|
|
104899
|
-
chatId: turn.chat_id,
|
|
104900
|
-
threadId: Number.isFinite(threadNum) ? threadNum : undefined
|
|
104901
|
-
};
|
|
104902
|
-
} catch {
|
|
105244
|
+
return resolveSubagentOriginChatDb(turnsDb, agentId);
|
|
105245
|
+
}
|
|
105246
|
+
function recentTurnFallbackChat(agentId) {
|
|
105247
|
+
if (turnsDb == null)
|
|
104903
105248
|
return null;
|
|
104904
|
-
|
|
105249
|
+
return resolveRecentTurnFallbackChat(turnsDb, agentId);
|
|
104905
105250
|
}
|
|
104906
105251
|
var WORKER_FEED_FALLBACK_LOG_CAP = 256;
|
|
104907
105252
|
var WORKER_FEED_STALE_TTL_MARGIN_MS = 5 * 60000;
|
|
@@ -104925,17 +105270,16 @@ function noteWorkerFeedOwnerDmFallback(agentId) {
|
|
|
104925
105270
|
}
|
|
104926
105271
|
var workerFeedOriginDeferrals = new Map;
|
|
104927
105272
|
var WORKER_FEED_ORIGIN_DEFER_MAX = 10;
|
|
104928
|
-
function resolveWorkerFeedChat(agentId, fleetChatId
|
|
104929
|
-
const
|
|
104930
|
-
|
|
104931
|
-
|
|
104932
|
-
|
|
104933
|
-
|
|
104934
|
-
|
|
104935
|
-
if (
|
|
105273
|
+
function resolveWorkerFeedChat(agentId, fleetChatId) {
|
|
105274
|
+
const dest = resolveWorkerSurfaceChat(turnsDb, agentId, {
|
|
105275
|
+
fleetChatId,
|
|
105276
|
+
ownerDm: loadAccess().allowFrom[0] ?? ""
|
|
105277
|
+
});
|
|
105278
|
+
if (dest.via === "recent-turn")
|
|
105279
|
+
noteWorkerRecentTurnFloor(agentId, dest);
|
|
105280
|
+
if (dest.via === "owner-dm")
|
|
104936
105281
|
noteWorkerFeedOwnerDmFallback(agentId);
|
|
104937
|
-
}
|
|
104938
|
-
return { chatId: ownerDm, threadId: origin?.threadId ?? fallbackThreadId };
|
|
105282
|
+
return { chatId: dest.chatId, ...dest.threadId != null ? { threadId: dest.threadId } : {} };
|
|
104939
105283
|
}
|
|
104940
105284
|
var REGISTRY_REAPER_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
|
104941
105285
|
function runHistoryReaperNow(reason) {
|
|
@@ -105197,6 +105541,7 @@ var TOPIC_FRAMING_ENABLED = process.env.SWITCHROOM_TOPIC_FRAMING !== "0";
|
|
|
105197
105541
|
var QUEUED_STATUS_UX_ENABLED = process.env.SWITCHROOM_QUEUED_STATUS_UX !== "0";
|
|
105198
105542
|
var MIDFLIGHT_BUSY_ACK_ENABLED = process.env.SWITCHROOM_MIDFLIGHT_BUSY_ACK !== "0";
|
|
105199
105543
|
var FEED_REOPEN_AFTER_ACK_ENABLED = process.env.SWITCHROOM_FEED_REOPEN_AFTER_ACK !== "0";
|
|
105544
|
+
var FEED_REOPEN_AFTER_SUBSTANTIVE_ENABLED = process.env.SWITCHROOM_FEED_REOPEN_AFTER_SUBSTANTIVE !== "0";
|
|
105200
105545
|
var FEED_HEARTBEAT_ENABLED = process.env.SWITCHROOM_FEED_HEARTBEAT !== "0";
|
|
105201
105546
|
var FEED_HEARTBEAT_TICK_MS = 6000;
|
|
105202
105547
|
var FEED_HEARTBEAT_MIN_STALE_MS = 6000;
|
|
@@ -107697,7 +108042,7 @@ async function runMidSessionCardReaper() {
|
|
|
107697
108042
|
if (turnsDb == null)
|
|
107698
108043
|
return "unknown";
|
|
107699
108044
|
try {
|
|
107700
|
-
const row =
|
|
108045
|
+
const row = getSubagentByJsonlId2(turnsDb, agentId);
|
|
107701
108046
|
if (row == null)
|
|
107702
108047
|
return "unknown";
|
|
107703
108048
|
if (row.status === "completed" || row.status === "failed")
|
|
@@ -109571,26 +109916,23 @@ async function executeProgressUpdate(args) {
|
|
|
109571
109916
|
}
|
|
109572
109917
|
}
|
|
109573
109918
|
const turnStart = activeTurnStartedAt.get(key);
|
|
109574
|
-
if (turnStart != null) {
|
|
109575
|
-
const currentCount = progressUpdateTurnCount.get(key) ?? 0;
|
|
109576
|
-
if (currentCount >= 5) {
|
|
109577
|
-
return {
|
|
109578
|
-
content: [
|
|
109579
|
-
{
|
|
109580
|
-
type: "text",
|
|
109581
|
-
text: JSON.stringify({ ok: false, reason: "turn_limit" })
|
|
109582
|
-
}
|
|
109583
|
-
]
|
|
109584
|
-
};
|
|
109585
|
-
}
|
|
109586
|
-
progressUpdateTurnCount.set(key, currentCount + 1);
|
|
109587
|
-
}
|
|
109588
109919
|
const access = loadAccess();
|
|
109589
109920
|
const literalText = (access.parseMode ?? "html") === "text";
|
|
109590
109921
|
const sendOpts = {
|
|
109591
109922
|
...threadId != null ? { message_thread_id: threadId } : {}
|
|
109592
109923
|
};
|
|
109593
|
-
const
|
|
109924
|
+
const capped = await sendWithProgressCap({ key, now, turnStart, turnCount: progressUpdateTurnCount }, () => robustApiCall(() => literalText ? lockedBot.api.sendMessage(chat_id, text5, sendOpts) : lockedBot.api.sendRichMessage(chat_id, richMessage2(text5), sendOpts), { verb: "sendMessage", chat_id, threadId }));
|
|
109925
|
+
if (capped.capped) {
|
|
109926
|
+
return {
|
|
109927
|
+
content: [
|
|
109928
|
+
{
|
|
109929
|
+
type: "text",
|
|
109930
|
+
text: JSON.stringify({ ok: false, reason: "turn_limit" })
|
|
109931
|
+
}
|
|
109932
|
+
]
|
|
109933
|
+
};
|
|
109934
|
+
}
|
|
109935
|
+
const sent = capped.result;
|
|
109594
109936
|
if (HISTORY_ENABLED) {
|
|
109595
109937
|
recordOutbound({
|
|
109596
109938
|
chat_id,
|
|
@@ -109603,6 +109945,9 @@ async function executeProgressUpdate(args) {
|
|
|
109603
109945
|
try {
|
|
109604
109946
|
noteSignal(key, Date.now());
|
|
109605
109947
|
} catch {}
|
|
109948
|
+
try {
|
|
109949
|
+
noteOutbound2(key, Date.now());
|
|
109950
|
+
} catch {}
|
|
109606
109951
|
return {
|
|
109607
109952
|
content: [
|
|
109608
109953
|
{
|
|
@@ -110421,6 +110766,7 @@ function gatewayStreamRenderDeps() {
|
|
|
110421
110766
|
CONTEXT_EXHAUSTION_COOLDOWN_MS,
|
|
110422
110767
|
DELIVERY_CONFIRM_ENABLED,
|
|
110423
110768
|
FEED_REOPEN_AFTER_ACK_ENABLED,
|
|
110769
|
+
FEED_REOPEN_AFTER_SUBSTANTIVE_ENABLED,
|
|
110424
110770
|
HANDBACK_PRETURN_ENABLED,
|
|
110425
110771
|
HISTORY_ENABLED,
|
|
110426
110772
|
LIVENESS_TERMINAL_HONESTY,
|
|
@@ -116006,7 +116352,7 @@ async function startGateway() {
|
|
|
116006
116352
|
let dispatch = resolveWorkerFeedDispatch(null, description2, entryBackground);
|
|
116007
116353
|
if (turnsDb != null) {
|
|
116008
116354
|
try {
|
|
116009
|
-
dispatch = resolveWorkerFeedDispatch(
|
|
116355
|
+
dispatch = resolveWorkerFeedDispatch(getSubagentByJsonlId2(turnsDb, agentId), description2, entryBackground);
|
|
116010
116356
|
} catch {}
|
|
116011
116357
|
}
|
|
116012
116358
|
let isBackground = dispatch.isBackground;
|
|
@@ -116088,14 +116434,13 @@ async function startGateway() {
|
|
|
116088
116434
|
model: dispatch.feedModel ?? undefined
|
|
116089
116435
|
});
|
|
116090
116436
|
}
|
|
116091
|
-
const
|
|
116437
|
+
const hbOwnerDm = loadAccess().allowFrom[0] ?? "";
|
|
116092
116438
|
const decision = decideSubagentHandback({
|
|
116093
116439
|
handbackEnvValue: process.env.SWITCHROOM_SUBAGENT_HANDBACK,
|
|
116094
116440
|
outcome,
|
|
116095
116441
|
isBackground,
|
|
116096
|
-
|
|
116097
|
-
|
|
116098
|
-
ownerChatId: loadAccess().allowFrom[0] ?? "",
|
|
116442
|
+
...resolveWorkerSurfaceForDecider(turnsDb, agentId, { fleetChatId, ownerDm: hbOwnerDm }),
|
|
116443
|
+
ownerChatId: hbOwnerDm,
|
|
116099
116444
|
taskDescription: description2,
|
|
116100
116445
|
resultText,
|
|
116101
116446
|
jsonlAgentId: agentId
|
|
@@ -116136,7 +116481,7 @@ async function startGateway() {
|
|
|
116136
116481
|
let dispatch = resolveWorkerFeedDispatch(null, description2);
|
|
116137
116482
|
if (turnsDb != null) {
|
|
116138
116483
|
try {
|
|
116139
|
-
dispatch = resolveWorkerFeedDispatch(
|
|
116484
|
+
dispatch = resolveWorkerFeedDispatch(getSubagentByJsonlId2(turnsDb, agentId), description2);
|
|
116140
116485
|
} catch {}
|
|
116141
116486
|
}
|
|
116142
116487
|
const isBackground = dispatch.isBackground || dispatch.isNested;
|
|
@@ -116212,14 +116557,16 @@ async function startGateway() {
|
|
|
116212
116557
|
stampTurn.subagentActivityAt = Date.now();
|
|
116213
116558
|
}
|
|
116214
116559
|
if (workerFeedEnabled) {
|
|
116560
|
+
const wfOrigin = resolveSubagentOriginChat(agentId);
|
|
116561
|
+
const wfStamp = stampTurn != null ? { chatId: stampTurn.sessionChatId, threadId: stampTurn.sessionThreadId } : wfOrigin == null ? recentTurnFallbackChat(agentId) : null;
|
|
116215
116562
|
const dest = decideWorkerFeedDestination({
|
|
116216
|
-
origin:
|
|
116563
|
+
origin: wfOrigin,
|
|
116217
116564
|
cardExists: workerActivityFeed?.has(agentId) === true,
|
|
116218
116565
|
priorDeferrals: workerFeedOriginDeferrals.get(agentId) ?? 0,
|
|
116219
116566
|
maxDeferrals: WORKER_FEED_ORIGIN_DEFER_MAX,
|
|
116220
116567
|
fleetChatId,
|
|
116221
|
-
stampChatId:
|
|
116222
|
-
stampThreadId:
|
|
116568
|
+
stampChatId: wfStamp?.chatId,
|
|
116569
|
+
stampThreadId: wfStamp?.threadId,
|
|
116223
116570
|
ownerDm: loadAccess().allowFrom[0] ?? ""
|
|
116224
116571
|
});
|
|
116225
116572
|
if (dest.action === "defer") {
|
|
@@ -116233,6 +116580,9 @@ async function startGateway() {
|
|
|
116233
116580
|
}
|
|
116234
116581
|
if (dest.ownerDmFallback)
|
|
116235
116582
|
noteWorkerFeedOwnerDmFallback(agentId);
|
|
116583
|
+
if (stampTurn == null && wfOrigin == null && wfStamp != null && dest.chatId === wfStamp.chatId) {
|
|
116584
|
+
noteWorkerRecentTurnFloor(agentId, wfStamp);
|
|
116585
|
+
}
|
|
116236
116586
|
workerActivityFeed?.update(agentId, dest.chatId, {
|
|
116237
116587
|
description: dispatch.feedDescription,
|
|
116238
116588
|
lastTool,
|
|
@@ -116245,14 +116595,17 @@ async function startGateway() {
|
|
|
116245
116595
|
}, dest.threadId);
|
|
116246
116596
|
return;
|
|
116247
116597
|
}
|
|
116248
|
-
const
|
|
116598
|
+
const pgOwnerDm = loadAccess().allowFrom[0] ?? "";
|
|
116599
|
+
const progressSurface = resolveWorkerSurfaceForDecider(turnsDb, agentId, {
|
|
116600
|
+
fleetChatId,
|
|
116601
|
+
ownerDm: pgOwnerDm
|
|
116602
|
+
});
|
|
116249
116603
|
const decision = decideSubagentProgress({
|
|
116250
116604
|
skeleton: skeleton === true,
|
|
116251
116605
|
disableEnvValue: process.env.SWITCHROOM_DISABLE_SUBAGENT_PROGRESS,
|
|
116252
116606
|
isBackground,
|
|
116253
|
-
|
|
116254
|
-
|
|
116255
|
-
ownerChatId: loadAccess().allowFrom[0] ?? "",
|
|
116607
|
+
...progressSurface,
|
|
116608
|
+
ownerChatId: pgOwnerDm,
|
|
116256
116609
|
subagentJsonlId: agentId,
|
|
116257
116610
|
taskDescription: description2,
|
|
116258
116611
|
latestSummary,
|
|
@@ -116264,7 +116617,7 @@ async function startGateway() {
|
|
|
116264
116617
|
return;
|
|
116265
116618
|
setBucketIdx(decision.bucketIdx);
|
|
116266
116619
|
pendingInboundBuffer.push(process.env.SWITCHROOM_AGENT_NAME ?? "", decision.inbound);
|
|
116267
|
-
clearPending(statusKey(decision.chatId,
|
|
116620
|
+
clearPending(statusKey(decision.chatId, progressSurface.originThreadId), "progress");
|
|
116268
116621
|
process.stderr.write(`telegram gateway: subagent-progress queued agent=${agentId} bucket=${decision.bucketIdx} elapsed_ms=${elapsedMs} chat=${decision.chatId}
|
|
116269
116622
|
`);
|
|
116270
116623
|
}
|