switchroom 0.16.47 → 0.17.0
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-scheduler/index.js +3 -1
- package/dist/auth-broker/index.js +24 -8
- package/dist/cli/drive-write-pretool.mjs +0 -5
- package/dist/cli/notion-write-pretool.mjs +3 -1
- package/dist/cli/switchroom.js +1358 -1030
- package/dist/cli/ui/index.html +84 -12
- package/dist/host-control/main.js +53 -17
- package/dist/vault/approvals/kernel-server.js +4 -1
- package/dist/vault/broker/server.js +201 -56
- package/package.json +3 -3
- package/profiles/_base/cron-session.sh.hbs +1 -1
- package/profiles/_base/start.sh.hbs +54 -3
- package/skills/switchroom-architecture/telegram.md +8 -15
- package/skills/switchroom-cli/SKILL.md +4 -5
- package/skills/telegram-test-harness/SKILL.md +1 -1
- package/telegram-plugin/README.md +18 -29
- package/telegram-plugin/bridge/bridge.ts +1 -41
- package/telegram-plugin/bridge/tool-filter.ts +3 -4
- package/telegram-plugin/dist/bridge/bridge.js +8 -43
- package/telegram-plugin/dist/gateway/gateway.js +682 -773
- package/telegram-plugin/dist/server.js +8 -43
- package/telegram-plugin/gateway/busy-key-reaper.ts +113 -0
- package/telegram-plugin/gateway/disconnect-flush.ts +11 -0
- package/telegram-plugin/gateway/escalation-bridge-gate.ts +46 -0
- package/telegram-plugin/gateway/gate-parity-probe.ts +102 -0
- package/telegram-plugin/gateway/gateway.ts +518 -624
- package/telegram-plugin/gateway/inbound-delivery-confirm.ts +89 -7
- package/telegram-plugin/gateway/inbound-spool.ts +108 -10
- package/telegram-plugin/gateway/model-command.ts +51 -3
- package/telegram-plugin/gateway/pending-inbound-buffer.ts +26 -0
- package/telegram-plugin/gateway/represent-guard.ts +28 -11
- package/telegram-plugin/gateway/status-pin-store.ts +124 -45
- package/telegram-plugin/gateway/worker-feed-dispatch.ts +19 -0
- package/telegram-plugin/history.ts +5 -0
- package/telegram-plugin/hooks/silent-end-interrupt-stop.mjs +1 -2
- package/telegram-plugin/hooks/subagent-tracker-pretool.mjs +9 -1
- package/telegram-plugin/registry/subagents-schema.ts +126 -1
- package/telegram-plugin/registry/turns-schema.ts +65 -1
- package/telegram-plugin/session-tail.ts +26 -4
- package/telegram-plugin/slot-banner-driver.ts +42 -2
- package/telegram-plugin/status-query-telemetry.ts +100 -0
- package/telegram-plugin/stream-reply-handler.ts +15 -16
- package/telegram-plugin/subagent-watcher.ts +182 -30
- package/telegram-plugin/tests/buffer-gate-broadened.test.ts +4 -10
- package/telegram-plugin/tests/busy-key-reaper.test.ts +191 -0
- package/telegram-plugin/tests/emission-authority-facade.test.ts +11 -17
- package/telegram-plugin/tests/emission-determinism-wiring.test.ts +5 -26
- package/telegram-plugin/tests/escalation-bridge-gate.test.ts +38 -0
- package/telegram-plugin/tests/gate-parity-probe.test.ts +171 -0
- package/telegram-plugin/tests/gateway-disconnect-flush.test.ts +13 -0
- package/telegram-plugin/tests/gateway-outbound-redact.test.ts +14 -11
- package/telegram-plugin/tests/inbound-delivery-confirm.test.ts +146 -0
- package/telegram-plugin/tests/inbound-spool.test.ts +143 -0
- package/telegram-plugin/tests/model-command.test.ts +54 -1
- package/telegram-plugin/tests/multitopic-routing-wiring.test.ts +5 -11
- package/telegram-plugin/tests/nested-worker-visibility-harness.test.ts +329 -0
- package/telegram-plugin/tests/pending-inbound-buffer.test.ts +53 -0
- package/telegram-plugin/tests/progress-update-redact.test.ts +99 -0
- package/telegram-plugin/tests/registry-turns.test.ts +67 -0
- package/telegram-plugin/tests/represent-guard.test.ts +42 -6
- package/telegram-plugin/tests/resume-inbound-builder.test.ts +1 -0
- package/telegram-plugin/tests/session-tail.test.ts +10 -1
- package/telegram-plugin/tests/slot-banner-boot-recovery.test.ts +246 -0
- package/telegram-plugin/tests/status-pin-boot-recovery.test.ts +0 -14
- package/telegram-plugin/tests/status-pin-store.test.ts +220 -5
- package/telegram-plugin/tests/status-query-telemetry.test.ts +115 -0
- package/telegram-plugin/tests/subagent-nested-dispatch.test.ts +209 -0
- package/telegram-plugin/tests/subagent-tracker-hooks.test.ts +37 -0
- package/telegram-plugin/tests/subagent-watcher-boot-promotion-replay.test.ts +167 -0
- package/telegram-plugin/tests/subagent-watcher-env-thresholds.test.ts +46 -3
- package/telegram-plugin/tests/subagent-watcher-stall-notification.test.ts +70 -0
- package/telegram-plugin/tests/tool-activity-summary.test.ts +16 -0
- package/telegram-plugin/tests/tool-filter.test.ts +1 -3
- package/telegram-plugin/tests/tool-label-pretool.test.ts +1 -4
- package/telegram-plugin/tests/turn-flush-safety.test.ts +222 -1
- package/telegram-plugin/tests/worker-activity-feed.test.ts +202 -9
- package/telegram-plugin/tests/worker-feed-dispatch.test.ts +25 -0
- package/telegram-plugin/tests/worker-visibility-prose-silent-harness.test.ts +295 -0
- package/telegram-plugin/tool-activity-summary.ts +19 -0
- package/telegram-plugin/turn-flush-safety.ts +16 -1
- package/telegram-plugin/uat/scenarios/jtbd-answer-pings.test.ts +8 -9
- package/telegram-plugin/uat/scenarios/jtbd-foreground-feed-visibility-dm.test.ts +1 -1
- package/telegram-plugin/uat/scenarios/jtbd-narration-intent-dm.test.ts +1 -1
- package/telegram-plugin/worker-activity-feed.ts +75 -15
- package/vendor/hindsight-memory/CHANGELOG.md +24 -0
- package/vendor/hindsight-memory/README.md +5 -0
- package/vendor/hindsight-memory/scripts/lib/client.py +31 -1
- package/vendor/hindsight-memory/scripts/lib/config.py +41 -2
- package/vendor/hindsight-memory/scripts/lib/content.py +4 -1
- package/vendor/hindsight-memory/scripts/lib/daemon.py +11 -2
- package/vendor/hindsight-memory/scripts/recall.py +74 -1
- package/vendor/hindsight-memory/scripts/retain.py +8 -1
- package/vendor/hindsight-memory/scripts/tests/test_config_client_casts.py +111 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_integration.py +85 -1
- package/vendor/hindsight-memory/scripts/tests/test_recall_tag_filters.py +107 -0
- package/vendor/hindsight-memory/settings.json +4 -0
- package/vendor/hindsight-memory/tests/test_client.py +130 -0
- package/vendor/hindsight-memory/tests/test_config.py +47 -0
- package/vendor/hindsight-memory/tests/test_content.py +18 -0
- package/vendor/hindsight-memory/tests/test_hooks.py +62 -0
- package/telegram-plugin/gateway/error-envelope-card.ts +0 -64
- package/telegram-plugin/gateway/resolve-calling-subagent.ts +0 -78
- package/telegram-plugin/silent-reply.ts +0 -58
- package/telegram-plugin/tests/error-envelope-unlock-card.test.ts +0 -79
- package/telegram-plugin/tests/resolve-calling-subagent.test.ts +0 -269
- package/telegram-plugin/tests/silent-reply-guard.test.ts +0 -122
|
@@ -16826,13 +16826,11 @@ var exports_client = {};
|
|
|
16826
16826
|
__export(exports_client, {
|
|
16827
16827
|
withAuthBrokerClient: () => withAuthBrokerClient,
|
|
16828
16828
|
resolveAuthBrokerSocketPath: () => resolveAuthBrokerSocketPath,
|
|
16829
|
-
authBrokerSocketExists: () => authBrokerSocketExists,
|
|
16830
16829
|
AuthBrokerUnreachableError: () => AuthBrokerUnreachableError,
|
|
16831
16830
|
AuthBrokerError: () => AuthBrokerError,
|
|
16832
16831
|
AuthBrokerClient: () => AuthBrokerClient
|
|
16833
16832
|
});
|
|
16834
16833
|
import * as net from "node:net";
|
|
16835
|
-
import { existsSync as existsSync7 } from "node:fs";
|
|
16836
16834
|
import { homedir as homedir3 } from "node:os";
|
|
16837
16835
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
16838
16836
|
import { join as join11 } from "node:path";
|
|
@@ -17133,9 +17131,6 @@ async function withAuthBrokerClient(fn, opts) {
|
|
|
17133
17131
|
await client3.close();
|
|
17134
17132
|
}
|
|
17135
17133
|
}
|
|
17136
|
-
function authBrokerSocketExists(opts) {
|
|
17137
|
-
return existsSync7(resolveAuthBrokerSocketPath(opts));
|
|
17138
|
-
}
|
|
17139
17134
|
var DEFAULT_TIMEOUT_MS = 5000, AuthBrokerError, AuthBrokerUnreachableError;
|
|
17140
17135
|
var init_client = __esm(() => {
|
|
17141
17136
|
init_protocol();
|
|
@@ -17166,11 +17161,14 @@ var init_flock = () => {};
|
|
|
17166
17161
|
import { randomBytes as randomBytes3, scryptSync, createCipheriv, createDecipheriv } from "node:crypto";
|
|
17167
17162
|
import {
|
|
17168
17163
|
readFileSync as readFileSync7,
|
|
17169
|
-
|
|
17170
|
-
existsSync as
|
|
17164
|
+
writeSync,
|
|
17165
|
+
existsSync as existsSync7,
|
|
17171
17166
|
renameSync as renameSync4,
|
|
17172
17167
|
mkdirSync as mkdirSync10,
|
|
17173
17168
|
unlinkSync as unlinkSync4,
|
|
17169
|
+
fsyncSync,
|
|
17170
|
+
openSync,
|
|
17171
|
+
closeSync,
|
|
17174
17172
|
lstatSync,
|
|
17175
17173
|
realpathSync
|
|
17176
17174
|
} from "node:fs";
|
|
@@ -17205,7 +17203,7 @@ function normalizeSecrets(raw) {
|
|
|
17205
17203
|
return out;
|
|
17206
17204
|
}
|
|
17207
17205
|
function openVault(passphrase, vaultPath) {
|
|
17208
|
-
if (!
|
|
17206
|
+
if (!existsSync7(vaultPath)) {
|
|
17209
17207
|
throw new VaultError(`Vault file not found: ${vaultPath}`);
|
|
17210
17208
|
}
|
|
17211
17209
|
let vaultFile;
|
|
@@ -24385,7 +24383,9 @@ var init_schema = __esm(() => {
|
|
|
24385
24383
|
enabled: exports_external.boolean().optional().describe("Master switch for the session-handoff briefing (default true)."),
|
|
24386
24384
|
max_turns_in_briefing: exports_external.number().int().positive().optional().describe("Cap on recent user/assistant turn pairs fed to the summarizer."),
|
|
24387
24385
|
resume_mode: exports_external.enum(["auto", "continue", "handoff", "none"]).optional().describe("How to resume the next session. 'handoff' (default as of #362) " + "never passes --continue; a fresh Claude starts each restart and " + "reads a briefing assembled from recent Telegram messages, Hindsight " + "recall, and today's daily memory file. 'auto' uses --continue when " + "the latest JSONL is smaller than resume_max_bytes, else falls back " + "to the handoff briefing. 'continue' always passes --continue. " + "'none' starts completely fresh every time."),
|
|
24388
|
-
resume_max_bytes: exports_external.number().int().positive().optional().describe("Byte threshold above which 'auto' mode falls back to handoff " + "instead of --continue. Default 2_000_000 (~2MB). Large transcripts " + "can blow out the context window even with prefix caching, and " + "--continue replay is known-fragile at scale.")
|
|
24386
|
+
resume_max_bytes: exports_external.number().int().positive().optional().describe("Byte threshold above which 'auto' mode falls back to handoff " + "instead of --continue. Default 2_000_000 (~2MB). Large transcripts " + "can blow out the context window even with prefix caching, and " + "--continue replay is known-fragile at scale."),
|
|
24387
|
+
session_retention_max_count: exports_external.number().int().nonnegative().optional().describe("Session-JSONL retention (issue #2792): keep at most this many " + "newest session transcripts under .claude/projects; older ones " + "past both this count and the age bound are pruned by the Stop " + "hook. The newest sessions (and the handoff source) are always " + "kept. Default 20; set 0 to disable the count bound."),
|
|
24388
|
+
session_retention_max_age_days: exports_external.number().int().nonnegative().optional().describe("Session-JSONL retention (issue #2792): prune session transcripts " + "older than this many days (a file is deleted only when it is BOTH " + "over the count bound and older than this). Default 30; set 0 to " + "disable the age bound.")
|
|
24389
24389
|
}).optional();
|
|
24390
24390
|
webhookDispatchRule = exports_external.object({
|
|
24391
24391
|
description: exports_external.string().optional(),
|
|
@@ -24912,7 +24912,7 @@ var init_schema = __esm(() => {
|
|
|
24912
24912
|
});
|
|
24913
24913
|
|
|
24914
24914
|
// ../src/config/paths.ts
|
|
24915
|
-
import { existsSync as
|
|
24915
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
24916
24916
|
import { resolve as resolve2 } from "node:path";
|
|
24917
24917
|
function home() {
|
|
24918
24918
|
return process.env.HOME ?? "/root";
|
|
@@ -24928,7 +24928,7 @@ function resolveStatePath2(fragment) {
|
|
|
24928
24928
|
const h = home();
|
|
24929
24929
|
const primary = resolve2(h, DEFAULT_STATE_DIR, fragment);
|
|
24930
24930
|
const legacy = resolve2(h, LEGACY_STATE_DIR, fragment);
|
|
24931
|
-
if (!
|
|
24931
|
+
if (!existsSync8(primary) && existsSync8(legacy)) {
|
|
24932
24932
|
warnLegacyStateOnce(legacy);
|
|
24933
24933
|
return legacy;
|
|
24934
24934
|
}
|
|
@@ -24941,9 +24941,9 @@ function resolveDualPath(pathStr) {
|
|
|
24941
24941
|
const absolute = resolve2(h, rest);
|
|
24942
24942
|
if (rest.startsWith(`${DEFAULT_STATE_DIR}/`)) {
|
|
24943
24943
|
const frag = rest.slice(DEFAULT_STATE_DIR.length + 1);
|
|
24944
|
-
if (!
|
|
24944
|
+
if (!existsSync8(absolute)) {
|
|
24945
24945
|
const legacy = resolve2(h, LEGACY_STATE_DIR, frag);
|
|
24946
|
-
if (
|
|
24946
|
+
if (existsSync8(legacy)) {
|
|
24947
24947
|
warnLegacyStateOnce(legacy);
|
|
24948
24948
|
return legacy;
|
|
24949
24949
|
}
|
|
@@ -24968,7 +24968,7 @@ var init_overlay_schema = __esm(() => {
|
|
|
24968
24968
|
});
|
|
24969
24969
|
|
|
24970
24970
|
// ../src/config/overlay-loader.ts
|
|
24971
|
-
import { existsSync as
|
|
24971
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
|
|
24972
24972
|
import { basename as basename4, resolve as resolve3 } from "node:path";
|
|
24973
24973
|
function deriveOverlayTitle(raw, fileName) {
|
|
24974
24974
|
const titleFromComment = raw.match(/^#[^\S\n]*name:[^\S\n]*(\S.*?)[^\S\n]*$/m)?.[1];
|
|
@@ -24984,7 +24984,7 @@ function overlayDirFor(agentName3, subdir) {
|
|
|
24984
24984
|
return resolve3(base);
|
|
24985
24985
|
}
|
|
24986
24986
|
function listYamlFiles(dir) {
|
|
24987
|
-
if (!
|
|
24987
|
+
if (!existsSync9(dir))
|
|
24988
24988
|
return [];
|
|
24989
24989
|
let entries;
|
|
24990
24990
|
try {
|
|
@@ -25509,7 +25509,7 @@ function validateNotionWorkspaceConfig(config) {
|
|
|
25509
25509
|
}
|
|
25510
25510
|
|
|
25511
25511
|
// ../src/config/loader.ts
|
|
25512
|
-
import { readFileSync as readFileSync9, existsSync as
|
|
25512
|
+
import { readFileSync as readFileSync9, existsSync as existsSync10 } from "node:fs";
|
|
25513
25513
|
import { homedir as homedir4 } from "node:os";
|
|
25514
25514
|
import { resolve as resolve4 } from "node:path";
|
|
25515
25515
|
function formatZodErrors(error) {
|
|
@@ -25576,7 +25576,7 @@ function findConfigFile(startDir) {
|
|
|
25576
25576
|
resolve4(userDir, "clerk.yml")
|
|
25577
25577
|
].filter(Boolean);
|
|
25578
25578
|
for (const path of searchPaths) {
|
|
25579
|
-
if (
|
|
25579
|
+
if (existsSync10(path)) {
|
|
25580
25580
|
return path;
|
|
25581
25581
|
}
|
|
25582
25582
|
}
|
|
@@ -25584,7 +25584,7 @@ function findConfigFile(startDir) {
|
|
|
25584
25584
|
}
|
|
25585
25585
|
function loadConfig(configPath) {
|
|
25586
25586
|
const filePath = configPath ?? findConfigFile();
|
|
25587
|
-
if (!
|
|
25587
|
+
if (!existsSync10(filePath)) {
|
|
25588
25588
|
throw new ConfigError(`Config file not found: ${filePath}`);
|
|
25589
25589
|
}
|
|
25590
25590
|
let raw;
|
|
@@ -25725,7 +25725,6 @@ function getPeerCred(fd) {
|
|
|
25725
25725
|
return null;
|
|
25726
25726
|
}
|
|
25727
25727
|
}
|
|
25728
|
-
|
|
25729
25728
|
// ../src/vault/broker/peercred.ts
|
|
25730
25729
|
function isReservedAgentName(name) {
|
|
25731
25730
|
return RESERVED_AGENT_NAMES.has(name);
|
|
@@ -26258,7 +26257,7 @@ import {
|
|
|
26258
26257
|
openSync as openSync2,
|
|
26259
26258
|
rmSync,
|
|
26260
26259
|
statSync as statSync5,
|
|
26261
|
-
writeSync
|
|
26260
|
+
writeSync as writeSync2
|
|
26262
26261
|
} from "node:fs";
|
|
26263
26262
|
import { join as join13 } from "node:path";
|
|
26264
26263
|
import { tmpdir } from "node:os";
|
|
@@ -26325,7 +26324,7 @@ function writeFileExclusive(filePath, content) {
|
|
|
26325
26324
|
const buf = typeof content === "string" ? Buffer.from(content, "utf8") : content;
|
|
26326
26325
|
const fd = openSync2(filePath, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL, 384);
|
|
26327
26326
|
try {
|
|
26328
|
-
|
|
26327
|
+
writeSync2(fd, buf);
|
|
26329
26328
|
} finally {
|
|
26330
26329
|
closeSync2(fd);
|
|
26331
26330
|
}
|
|
@@ -28861,7 +28860,7 @@ __export(exports_history, {
|
|
|
28861
28860
|
checkpointWal: () => checkpointWal,
|
|
28862
28861
|
_resetForTests: () => _resetForTests
|
|
28863
28862
|
});
|
|
28864
|
-
import { chmodSync as chmodSync3, existsSync as
|
|
28863
|
+
import { chmodSync as chmodSync3, existsSync as existsSync17, mkdirSync as mkdirSync15 } from "fs";
|
|
28865
28864
|
import { join as join16 } from "path";
|
|
28866
28865
|
function loadDatabaseClass() {
|
|
28867
28866
|
if (DatabaseClass != null)
|
|
@@ -28890,6 +28889,7 @@ function initHistory(stateDir, retentionDays = 30) {
|
|
|
28890
28889
|
db = new Database(path, { create: true });
|
|
28891
28890
|
db.exec("PRAGMA journal_mode = WAL");
|
|
28892
28891
|
db.exec("PRAGMA synchronous = NORMAL");
|
|
28892
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
28893
28893
|
db.exec(`
|
|
28894
28894
|
CREATE TABLE IF NOT EXISTS messages (
|
|
28895
28895
|
chat_id TEXT NOT NULL,
|
|
@@ -28922,7 +28922,7 @@ function initHistory(stateDir, retentionDays = 30) {
|
|
|
28922
28922
|
}
|
|
28923
28923
|
for (const suffix of ["", "-shm", "-wal"]) {
|
|
28924
28924
|
const f = path + suffix;
|
|
28925
|
-
if (
|
|
28925
|
+
if (existsSync17(f)) {
|
|
28926
28926
|
try {
|
|
28927
28927
|
chmodSync3(f, 420);
|
|
28928
28928
|
} catch {}
|
|
@@ -28947,7 +28947,7 @@ function checkpointWal() {
|
|
|
28947
28947
|
if (dbPath) {
|
|
28948
28948
|
for (const suffix of ["-shm", "-wal"]) {
|
|
28949
28949
|
const f = dbPath + suffix;
|
|
28950
|
-
if (
|
|
28950
|
+
if (existsSync17(f)) {
|
|
28951
28951
|
try {
|
|
28952
28952
|
chmodSync3(f, 420);
|
|
28953
28953
|
} catch {}
|
|
@@ -29114,11 +29114,11 @@ var init_history = __esm(() => {
|
|
|
29114
29114
|
});
|
|
29115
29115
|
|
|
29116
29116
|
// quota-check.ts
|
|
29117
|
-
import { readFileSync as readFileSync14, existsSync as
|
|
29117
|
+
import { readFileSync as readFileSync14, existsSync as existsSync18 } from "fs";
|
|
29118
29118
|
import { join as join17 } from "path";
|
|
29119
29119
|
function readOauthToken(claudeConfigDir) {
|
|
29120
29120
|
const tokenFile = join17(claudeConfigDir, ".oauth-token");
|
|
29121
|
-
if (!
|
|
29121
|
+
if (!existsSync18(tokenFile))
|
|
29122
29122
|
return null;
|
|
29123
29123
|
try {
|
|
29124
29124
|
const raw = readFileSync14(tokenFile, "utf-8").trim();
|
|
@@ -29455,7 +29455,7 @@ function renderAuthLine(state4, agentName3, now = Date.now()) {
|
|
|
29455
29455
|
}
|
|
29456
29456
|
|
|
29457
29457
|
// gateway/quota-cache.ts
|
|
29458
|
-
import { existsSync as
|
|
29458
|
+
import { existsSync as existsSync31, readFileSync as readFileSync29, writeFileSync as writeFileSync20, mkdirSync as mkdirSync22 } from "fs";
|
|
29459
29459
|
import { join as join28, dirname as dirname9 } from "path";
|
|
29460
29460
|
function defaultCachePath() {
|
|
29461
29461
|
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join28(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
|
|
@@ -29463,7 +29463,7 @@ function defaultCachePath() {
|
|
|
29463
29463
|
function readQuotaCache(opts = {}) {
|
|
29464
29464
|
const path = opts.path ?? defaultCachePath();
|
|
29465
29465
|
const now = opts.now ?? Date.now();
|
|
29466
|
-
if (!
|
|
29466
|
+
if (!existsSync31(path))
|
|
29467
29467
|
return null;
|
|
29468
29468
|
let entry;
|
|
29469
29469
|
try {
|
|
@@ -29494,7 +29494,7 @@ function writeQuotaCache(result, opts = {}) {
|
|
|
29494
29494
|
};
|
|
29495
29495
|
try {
|
|
29496
29496
|
mkdirSync22(dirname9(path), { recursive: true });
|
|
29497
|
-
|
|
29497
|
+
writeFileSync20(path, JSON.stringify(entry, null, 2), { mode: 384 });
|
|
29498
29498
|
} catch {}
|
|
29499
29499
|
}
|
|
29500
29500
|
var DEFAULT_TTL_MS4, RATE_LIMIT_TTL_MS;
|
|
@@ -29504,10 +29504,10 @@ var init_quota_cache = __esm(() => {
|
|
|
29504
29504
|
});
|
|
29505
29505
|
|
|
29506
29506
|
// gateway/boot-probes.ts
|
|
29507
|
-
import { readFileSync as readFileSync30, readdirSync as readdirSync6, existsSync as
|
|
29507
|
+
import { readFileSync as readFileSync30, readdirSync as readdirSync6, existsSync as existsSync32 } from "fs";
|
|
29508
29508
|
import { join as join29 } from "path";
|
|
29509
29509
|
import { execFile as execFileCb } from "child_process";
|
|
29510
|
-
import { promisify
|
|
29510
|
+
import { promisify } from "util";
|
|
29511
29511
|
async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
29512
29512
|
let timer3;
|
|
29513
29513
|
const timeout = new Promise((resolve6) => {
|
|
@@ -29572,7 +29572,7 @@ async function probeAccount(agentDir) {
|
|
|
29572
29572
|
join29(claudeDir, ".oauth-token.meta.json"),
|
|
29573
29573
|
join29(claudeDir, "accounts", "default", ".oauth-token.meta.json")
|
|
29574
29574
|
]) {
|
|
29575
|
-
if (
|
|
29575
|
+
if (existsSync32(candidate)) {
|
|
29576
29576
|
try {
|
|
29577
29577
|
const meta = JSON.parse(readFileSync30(candidate, "utf8"));
|
|
29578
29578
|
if (meta.expiresAt) {
|
|
@@ -29749,7 +29749,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
29749
29749
|
if (!cgroup)
|
|
29750
29750
|
return null;
|
|
29751
29751
|
const procsPath = `/sys/fs/cgroup${cgroup}/cgroup.procs`;
|
|
29752
|
-
if (!
|
|
29752
|
+
if (!existsSync32(procsPath))
|
|
29753
29753
|
return null;
|
|
29754
29754
|
const pidsRaw = readFileSync30(procsPath, "utf-8");
|
|
29755
29755
|
const pids = pidsRaw.split(`
|
|
@@ -29826,7 +29826,7 @@ async function probeAgentProcess(agentName3, opts = {}) {
|
|
|
29826
29826
|
const retryIntervalMs = opts.retryIntervalMs ?? AGENT_RETRY_INTERVAL_MS;
|
|
29827
29827
|
const retryMaxMs = opts.retryMaxMs ?? AGENT_RETRY_MAX_MS;
|
|
29828
29828
|
const sleep2 = opts.sleepImpl ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
|
|
29829
|
-
const execFileFn = opts.execFileImpl ??
|
|
29829
|
+
const execFileFn = opts.execFileImpl ?? execFile;
|
|
29830
29830
|
return withTimeout("Agent", (async () => {
|
|
29831
29831
|
const startMs = Date.now();
|
|
29832
29832
|
while (true) {
|
|
@@ -29868,7 +29868,7 @@ async function* watchAgentProcess(agentName3, opts = {}) {
|
|
|
29868
29868
|
const pollIntervalMs = opts.pollIntervalMs ?? AGENT_LIVE_POLL_INTERVAL_MS;
|
|
29869
29869
|
const followupRepollMs = opts.followupRepollMs ?? AGENT_LIVE_FOLLOWUP_REPOLL_MS;
|
|
29870
29870
|
const sleep2 = opts.sleepImpl ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
|
|
29871
|
-
const execFileFn = opts.execFileImpl ??
|
|
29871
|
+
const execFileFn = opts.execFileImpl ?? execFile;
|
|
29872
29872
|
const now = opts.nowImpl ?? (() => Date.now());
|
|
29873
29873
|
const startMs = now();
|
|
29874
29874
|
let lastYieldedDetail = null;
|
|
@@ -29958,7 +29958,7 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
|
|
|
29958
29958
|
claudeConfigDir,
|
|
29959
29959
|
join29(claudeConfigDir, "accounts", "default")
|
|
29960
29960
|
]) {
|
|
29961
|
-
if (
|
|
29961
|
+
if (existsSync32(join29(candidate, ".oauth-token"))) {
|
|
29962
29962
|
claudeDirForProbe = candidate;
|
|
29963
29963
|
break;
|
|
29964
29964
|
}
|
|
@@ -30129,7 +30129,7 @@ async function probeUds(label, socketPath, opts = {}) {
|
|
|
30129
30129
|
}
|
|
30130
30130
|
return withTimeout(label, (async () => {
|
|
30131
30131
|
if (!opts.connectImpl) {
|
|
30132
|
-
if (!
|
|
30132
|
+
if (!existsSync32(socketPath)) {
|
|
30133
30133
|
return {
|
|
30134
30134
|
status: "fail",
|
|
30135
30135
|
label,
|
|
@@ -30285,11 +30285,11 @@ async function probeConnections(agentDir, opts = {}) {
|
|
|
30285
30285
|
};
|
|
30286
30286
|
})());
|
|
30287
30287
|
}
|
|
30288
|
-
var
|
|
30288
|
+
var execFile, PROBE_TIMEOUT_MS = 2000, QUOTA_BROKER_TIMEOUT_MS = 7000, QUOTA_DIRECT_FALLBACK_TIMEOUT_MS = 5000, QUOTA_PROBE_OUTER_TIMEOUT_MS = 9000, TOKEN_EXPIRING_SOON_DAYS = 7, AGENT_RETRY_INTERVAL_MS = 1500, AGENT_RETRY_MAX_MS = 12000, AGENT_LIVE_WINDOW_MS = 45000, AGENT_LIVE_POLL_INTERVAL_MS = 2000, AGENT_LIVE_FOLLOWUP_REPOLL_MS = 30000, realProcFs, SCHEDULER_LOCK_PATH_DEFAULT = "/state/agent/scheduler.lock", SCHEDULER_JSONL_PATH_DEFAULT = "/state/agent/scheduler.jsonl", SCHEDULER_FRESH_BOOT_MS = 30000, realSchedulerFs, realSkillsFs;
|
|
30289
30289
|
var init_boot_probes = __esm(() => {
|
|
30290
30290
|
init_quota_cache();
|
|
30291
30291
|
init_quota_check();
|
|
30292
|
-
|
|
30292
|
+
execFile = promisify(execFileCb);
|
|
30293
30293
|
realProcFs = {
|
|
30294
30294
|
readdir: (p) => readdirSync6(p),
|
|
30295
30295
|
readFile: (p) => readFileSync30(p, "utf-8")
|
|
@@ -30300,16 +30300,16 @@ var init_boot_probes = __esm(() => {
|
|
|
30300
30300
|
const { statSync: statSync10 } = __require("fs");
|
|
30301
30301
|
return statSync10(p).mtimeMs;
|
|
30302
30302
|
},
|
|
30303
|
-
exists: (p) =>
|
|
30303
|
+
exists: (p) => existsSync32(p)
|
|
30304
30304
|
};
|
|
30305
30305
|
realSkillsFs = {
|
|
30306
30306
|
readdir: (p) => readdirSync6(p),
|
|
30307
|
-
exists: (p) =>
|
|
30307
|
+
exists: (p) => existsSync32(p)
|
|
30308
30308
|
};
|
|
30309
30309
|
});
|
|
30310
30310
|
|
|
30311
30311
|
// gateway/boot-issue-cache.ts
|
|
30312
|
-
import { existsSync as
|
|
30312
|
+
import { existsSync as existsSync33, readFileSync as readFileSync31, writeFileSync as writeFileSync21, mkdirSync as mkdirSync23, renameSync as renameSync11 } from "fs";
|
|
30313
30313
|
import { dirname as dirname10 } from "path";
|
|
30314
30314
|
function fingerprintProbe(key, r) {
|
|
30315
30315
|
if (r.status === "ok")
|
|
@@ -30389,7 +30389,7 @@ function diffProbes(probes, cache, opts = {}) {
|
|
|
30389
30389
|
return out;
|
|
30390
30390
|
}
|
|
30391
30391
|
function loadCache(path, now = Date.now) {
|
|
30392
|
-
if (!
|
|
30392
|
+
if (!existsSync33(path))
|
|
30393
30393
|
return { ...EMPTY_CACHE, probes: {} };
|
|
30394
30394
|
let raw;
|
|
30395
30395
|
try {
|
|
@@ -30438,7 +30438,7 @@ function applyAndSave(path, cache, diff) {
|
|
|
30438
30438
|
try {
|
|
30439
30439
|
mkdirSync23(dirname10(path), { recursive: true });
|
|
30440
30440
|
const tmp = `${path}.tmp`;
|
|
30441
|
-
|
|
30441
|
+
writeFileSync21(tmp, JSON.stringify(next), { mode: 384 });
|
|
30442
30442
|
renameSync11(tmp, path);
|
|
30443
30443
|
} catch {}
|
|
30444
30444
|
return next;
|
|
@@ -30452,7 +30452,7 @@ var init_boot_issue_cache = __esm(() => {
|
|
|
30452
30452
|
|
|
30453
30453
|
// gateway/config-snapshot.ts
|
|
30454
30454
|
import { createHash as createHash2 } from "crypto";
|
|
30455
|
-
import { existsSync as
|
|
30455
|
+
import { existsSync as existsSync34, readFileSync as readFileSync32, writeFileSync as writeFileSync22, mkdirSync as mkdirSync24, renameSync as renameSync12 } from "fs";
|
|
30456
30456
|
import { dirname as dirname11 } from "path";
|
|
30457
30457
|
function hashStringArray(items) {
|
|
30458
30458
|
if (!items || items.length === 0)
|
|
@@ -30517,7 +30517,7 @@ function renderConfigChangeDim(dim) {
|
|
|
30517
30517
|
}
|
|
30518
30518
|
}
|
|
30519
30519
|
function loadSnapshot(path, now = Date.now) {
|
|
30520
|
-
if (!
|
|
30520
|
+
if (!existsSync34(path))
|
|
30521
30521
|
return null;
|
|
30522
30522
|
let raw;
|
|
30523
30523
|
try {
|
|
@@ -30553,7 +30553,7 @@ function persistSnapshot(path, snapshot) {
|
|
|
30553
30553
|
try {
|
|
30554
30554
|
mkdirSync24(dirname11(path), { recursive: true });
|
|
30555
30555
|
const tmp = `${path}.tmp`;
|
|
30556
|
-
|
|
30556
|
+
writeFileSync22(tmp, JSON.stringify(snapshot), { mode: 384 });
|
|
30557
30557
|
renameSync12(tmp, path);
|
|
30558
30558
|
} catch {}
|
|
30559
30559
|
}
|
|
@@ -30562,7 +30562,7 @@ var init_config_snapshot = __esm(() => {
|
|
|
30562
30562
|
});
|
|
30563
30563
|
|
|
30564
30564
|
// gateway/boot-card-msgid.ts
|
|
30565
|
-
import { readFileSync as readFileSync33, writeFileSync as
|
|
30565
|
+
import { readFileSync as readFileSync33, writeFileSync as writeFileSync23 } from "node:fs";
|
|
30566
30566
|
function bootCardChatKey(chatId, threadId) {
|
|
30567
30567
|
return `${chatId}:${threadId ?? ""}`;
|
|
30568
30568
|
}
|
|
@@ -30587,7 +30587,7 @@ function saveBootCardMsgId(path, chatKey3, messageId) {
|
|
|
30587
30587
|
if (store2[chatKey3] === messageId)
|
|
30588
30588
|
return;
|
|
30589
30589
|
store2[chatKey3] = messageId;
|
|
30590
|
-
|
|
30590
|
+
writeFileSync23(path, JSON.stringify(store2), "utf8");
|
|
30591
30591
|
} catch {}
|
|
30592
30592
|
}
|
|
30593
30593
|
var init_boot_card_msgid = () => {};
|
|
@@ -31476,7 +31476,7 @@ __export(exports_materialize_bot_token, {
|
|
|
31476
31476
|
materializeBotToken: () => materializeBotToken,
|
|
31477
31477
|
BotTokenMaterializeError: () => BotTokenMaterializeError
|
|
31478
31478
|
});
|
|
31479
|
-
import { existsSync as
|
|
31479
|
+
import { existsSync as existsSync42 } from "node:fs";
|
|
31480
31480
|
function pickConfiguredToken(config, agentName3) {
|
|
31481
31481
|
if (agentName3) {
|
|
31482
31482
|
const agent = config.agents?.[agentName3];
|
|
@@ -31490,7 +31490,7 @@ function tryDirectVaultRead3(ref, config, passphrase) {
|
|
|
31490
31490
|
if (!passphrase)
|
|
31491
31491
|
return null;
|
|
31492
31492
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
31493
|
-
if (!
|
|
31493
|
+
if (!existsSync42(vaultPath))
|
|
31494
31494
|
return null;
|
|
31495
31495
|
try {
|
|
31496
31496
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -31582,7 +31582,7 @@ __export(exports_tmux, {
|
|
|
31582
31582
|
captureAgentPane: () => captureAgentPane
|
|
31583
31583
|
});
|
|
31584
31584
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
31585
|
-
import { mkdirSync as mkdirSync31, readdirSync as readdirSync8, statSync as statSync14, unlinkSync as unlinkSync15, writeFileSync as
|
|
31585
|
+
import { mkdirSync as mkdirSync31, readdirSync as readdirSync8, statSync as statSync14, unlinkSync as unlinkSync15, writeFileSync as writeFileSync29 } from "node:fs";
|
|
31586
31586
|
import { resolve as resolve7 } from "node:path";
|
|
31587
31587
|
function captureAgentPane(opts) {
|
|
31588
31588
|
const { agentName: agentName3, agentDir, reason } = opts;
|
|
@@ -31628,7 +31628,7 @@ function captureAgentPane(opts) {
|
|
|
31628
31628
|
` + `
|
|
31629
31629
|
`;
|
|
31630
31630
|
try {
|
|
31631
|
-
|
|
31631
|
+
writeFileSync29(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
|
|
31632
31632
|
mode: 420
|
|
31633
31633
|
});
|
|
31634
31634
|
} catch (err) {
|
|
@@ -32191,7 +32191,7 @@ import { randomBytes as randomBytes8 } from "crypto";
|
|
|
32191
32191
|
import { execFileSync as execFileSync6, execSync as execSync2, spawn } from "child_process";
|
|
32192
32192
|
import {
|
|
32193
32193
|
readFileSync as readFileSync41,
|
|
32194
|
-
writeFileSync as
|
|
32194
|
+
writeFileSync as writeFileSync30,
|
|
32195
32195
|
mkdirSync as mkdirSync32,
|
|
32196
32196
|
readdirSync as readdirSync9,
|
|
32197
32197
|
rmSync as rmSync4,
|
|
@@ -32201,7 +32201,7 @@ import {
|
|
|
32201
32201
|
chmodSync as chmodSync7,
|
|
32202
32202
|
openSync as openSync9,
|
|
32203
32203
|
closeSync as closeSync9,
|
|
32204
|
-
existsSync as
|
|
32204
|
+
existsSync as existsSync43,
|
|
32205
32205
|
unlinkSync as unlinkSync16,
|
|
32206
32206
|
appendFileSync as appendFileSync6
|
|
32207
32207
|
} from "fs";
|
|
@@ -34776,6 +34776,12 @@ function formatFeedElapsed(ms) {
|
|
|
34776
34776
|
const m = Math.floor(s / 60);
|
|
34777
34777
|
return `${m}m${(s % 60).toString().padStart(2, "0")}s`;
|
|
34778
34778
|
}
|
|
34779
|
+
var STEP_TIMER_MIN_MS = 1e4;
|
|
34780
|
+
function formatStepSuffix(stepElapsedMs) {
|
|
34781
|
+
if (stepElapsedMs < STEP_TIMER_MIN_MS)
|
|
34782
|
+
return "";
|
|
34783
|
+
return ` \u00b7 ${formatFeedElapsed(stepElapsedMs)}`;
|
|
34784
|
+
}
|
|
34779
34785
|
function escapeStepLine(raw) {
|
|
34780
34786
|
const cleaned = stripMarkdown(raw).replace(/\s+/g, " ").trim();
|
|
34781
34787
|
return escapeMarkdown(truncate(cleaned, STATUS_LINE_MAX));
|
|
@@ -34976,9 +34982,10 @@ function createWorkerActivityFeed(opts) {
|
|
|
34976
34982
|
const line = view.latestSummary.trim();
|
|
34977
34983
|
if (line.length === 0)
|
|
34978
34984
|
return;
|
|
34979
|
-
if (h.narrative
|
|
34985
|
+
if (h.narrative.includes(line))
|
|
34980
34986
|
return;
|
|
34981
34987
|
h.narrative.push(line);
|
|
34988
|
+
h.stepStartedAtMs = nowFn();
|
|
34982
34989
|
if (h.narrative.length > STATUS_ROLLING_LINES) {
|
|
34983
34990
|
h.narrative.splice(0, h.narrative.length - STATUS_ROLLING_LINES);
|
|
34984
34991
|
}
|
|
@@ -35045,7 +35052,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
35045
35052
|
function heartbeatTick() {
|
|
35046
35053
|
const now = nowFn();
|
|
35047
35054
|
for (const h of handles.values()) {
|
|
35048
|
-
if (h.
|
|
35055
|
+
if (!handles.has(h.agentId))
|
|
35049
35056
|
continue;
|
|
35050
35057
|
if (h.lastView == null)
|
|
35051
35058
|
continue;
|
|
@@ -35053,13 +35060,23 @@ function createWorkerActivityFeed(opts) {
|
|
|
35053
35060
|
continue;
|
|
35054
35061
|
if (now < h.cooldownUntil)
|
|
35055
35062
|
continue;
|
|
35063
|
+
const liveElapsed = h.dispatchAtMs != null ? now - h.dispatchAtMs : h.lastView.elapsedMs;
|
|
35064
|
+
if (h.messageId == null) {
|
|
35065
|
+
if (liveElapsed < firstPaintMin)
|
|
35066
|
+
continue;
|
|
35067
|
+
const view2 = { ...h.lastView, elapsedMs: Math.max(h.lastView.elapsedMs, liveElapsed) };
|
|
35068
|
+
h.chain = h.chain.then(() => doUpdate(h, view2)).catch((err) => {
|
|
35069
|
+
log(`worker-feed: heartbeat first-paint chain error ${h.agentId}: ${err.message}`);
|
|
35070
|
+
});
|
|
35071
|
+
continue;
|
|
35072
|
+
}
|
|
35056
35073
|
if (now - h.lastEditAt < minEditInterval)
|
|
35057
35074
|
continue;
|
|
35058
35075
|
const stale = now - h.lastEditAt >= heartbeatTickMs;
|
|
35059
35076
|
if (!stale)
|
|
35060
35077
|
continue;
|
|
35061
|
-
const
|
|
35062
|
-
const liveSuffix =
|
|
35078
|
+
const stepElapsed = h.stepStartedAtMs != null ? now - h.stepStartedAtMs : liveElapsed;
|
|
35079
|
+
const liveSuffix = formatStepSuffix(stepElapsed);
|
|
35063
35080
|
const view = { ...h.lastView, elapsedMs: Math.max(h.lastView.elapsedMs, liveElapsed) };
|
|
35064
35081
|
h.chain = h.chain.then(() => doUpdate(h, view, liveSuffix)).catch((err) => {
|
|
35065
35082
|
log(`worker-feed: heartbeat chain error ${h.agentId}: ${err.message}`);
|
|
@@ -35093,7 +35110,8 @@ function createWorkerActivityFeed(opts) {
|
|
|
35093
35110
|
narrative: [],
|
|
35094
35111
|
chain: Promise.resolve(),
|
|
35095
35112
|
lastView: null,
|
|
35096
|
-
dispatchAtMs: null
|
|
35113
|
+
dispatchAtMs: null,
|
|
35114
|
+
stepStartedAtMs: null
|
|
35097
35115
|
};
|
|
35098
35116
|
handles.set(agentId, h);
|
|
35099
35117
|
}
|
|
@@ -35389,6 +35407,12 @@ function formatFeedElapsed2(ms) {
|
|
|
35389
35407
|
const m = Math.floor(s / 60);
|
|
35390
35408
|
return `${m}m${(s % 60).toString().padStart(2, "0")}s`;
|
|
35391
35409
|
}
|
|
35410
|
+
var STEP_TIMER_MIN_MS2 = 1e4;
|
|
35411
|
+
function formatStepSuffix2(stepElapsedMs) {
|
|
35412
|
+
if (stepElapsedMs < STEP_TIMER_MIN_MS2)
|
|
35413
|
+
return "";
|
|
35414
|
+
return ` \u00b7 ${formatFeedElapsed2(stepElapsedMs)}`;
|
|
35415
|
+
}
|
|
35392
35416
|
function escapeStepLine2(raw) {
|
|
35393
35417
|
const cleaned = stripMarkdown(raw).replace(/\s+/g, " ").trim();
|
|
35394
35418
|
return escapeMarkdown(truncate(cleaned, STATUS_LINE_MAX));
|
|
@@ -35767,9 +35791,6 @@ function chatKey(chatId, threadId) {
|
|
|
35767
35791
|
const t = threadId == null || threadId === 0 ? "_" : String(threadId);
|
|
35768
35792
|
return `${chatId}:${t}`;
|
|
35769
35793
|
}
|
|
35770
|
-
function chatKeyWithSuffix(chatId, threadId, suffix) {
|
|
35771
|
-
return `${chatKey(chatId, threadId)}:${suffix}`;
|
|
35772
|
-
}
|
|
35773
35794
|
function chatIdOfChatKey(key) {
|
|
35774
35795
|
const idx = key.indexOf(":");
|
|
35775
35796
|
return idx === -1 ? key : key.slice(0, idx);
|
|
@@ -36218,185 +36239,6 @@ function handlePtyPartialPure(text, state, deps) {
|
|
|
36218
36239
|
stream.update(text).catch(() => {});
|
|
36219
36240
|
return created ? "update-new" : "update-existing";
|
|
36220
36241
|
}
|
|
36221
|
-
// stream-reply-handler.ts
|
|
36222
|
-
function buildAccentHeader(accent) {
|
|
36223
|
-
switch (accent) {
|
|
36224
|
-
case "in-progress":
|
|
36225
|
-
return `\uD83D\uDD35 _In progress\u2026_
|
|
36226
|
-
|
|
36227
|
-
`;
|
|
36228
|
-
case "done":
|
|
36229
|
-
return `\u2705 **Done**
|
|
36230
|
-
|
|
36231
|
-
`;
|
|
36232
|
-
case "issue":
|
|
36233
|
-
return `\u26a0\ufe0f **Issue**
|
|
36234
|
-
|
|
36235
|
-
`;
|
|
36236
|
-
default:
|
|
36237
|
-
return "";
|
|
36238
|
-
}
|
|
36239
|
-
}
|
|
36240
|
-
function streamKey2(chatId, threadId, lane, turnKey) {
|
|
36241
|
-
const base = lane != null && lane.length > 0 ? chatKeyWithSuffix(chatId, threadId ?? null, lane) : chatKey(chatId, threadId ?? null);
|
|
36242
|
-
return turnKey != null && turnKey.length > 0 ? `${base}:${turnKey}` : base;
|
|
36243
|
-
}
|
|
36244
|
-
async function handleStreamReply(args, state, deps) {
|
|
36245
|
-
const chat_id = args.chat_id;
|
|
36246
|
-
let rawText = deps.normalizeParagraphBreaks ? deps.normalizeParagraphBreaks(deps.repairEscapedWhitespace(args.text)) : deps.repairEscapedWhitespace(args.text);
|
|
36247
|
-
if (deps.normalizePunctuation)
|
|
36248
|
-
rawText = deps.normalizePunctuation(rawText);
|
|
36249
|
-
if (deps.stripExcessBold)
|
|
36250
|
-
rawText = deps.stripExcessBold(rawText);
|
|
36251
|
-
const done = Boolean(args.done);
|
|
36252
|
-
const format = args.format ?? deps.defaultFormat;
|
|
36253
|
-
if (done) {
|
|
36254
|
-
process.stderr.write(`telegram channel: stream_reply: invoked done=true chatId=${chat_id} lane=${args.lane ?? "default"} charCount=${rawText.length}
|
|
36255
|
-
`);
|
|
36256
|
-
}
|
|
36257
|
-
deps.assertAllowedChat(chat_id);
|
|
36258
|
-
const threadId = deps.resolveThreadId(chat_id, args.message_thread_id);
|
|
36259
|
-
const literalText = format === "text";
|
|
36260
|
-
let effectiveText = !literalText && deps.addParagraphSpacers ? deps.addParagraphSpacers(rawText) : rawText;
|
|
36261
|
-
if (args.accent != null) {
|
|
36262
|
-
const accentHeader = buildAccentHeader(args.accent);
|
|
36263
|
-
if (accentHeader.length > 0) {
|
|
36264
|
-
effectiveText = accentHeader + effectiveText;
|
|
36265
|
-
}
|
|
36266
|
-
}
|
|
36267
|
-
if (effectiveText.length > RICH_MESSAGE_MAX_CHARS) {
|
|
36268
|
-
throw new Error(`stream_reply rejected: text exceeds Telegram's ${RICH_MESSAGE_MAX_CHARS}-char rich-message limit ` + `(length=${effectiveText.length}, format=${format}). stream_reply does not ` + `auto-chunk \u2014 split the text or use \`reply\`, which chunks.`);
|
|
36269
|
-
}
|
|
36270
|
-
const sKey = streamKey2(chat_id, threadId, args.lane, args.turnKey);
|
|
36271
|
-
state.suppressPtyPreview?.add(streamKey2(chat_id, threadId));
|
|
36272
|
-
let stream = state.activeDraftStreams.get(sKey);
|
|
36273
|
-
const streamExisted = stream != null;
|
|
36274
|
-
deps.logStreamingEvent({
|
|
36275
|
-
kind: "stream_reply_called",
|
|
36276
|
-
chatId: chat_id,
|
|
36277
|
-
charCount: effectiveText.length,
|
|
36278
|
-
done,
|
|
36279
|
-
streamExisted
|
|
36280
|
-
});
|
|
36281
|
-
if (!stream) {
|
|
36282
|
-
let replyToMessageId;
|
|
36283
|
-
if (args.reply_to != null) {
|
|
36284
|
-
replyToMessageId = Number(args.reply_to);
|
|
36285
|
-
} else if (args.quote !== false && deps.getLatestInboundMessageId != null) {
|
|
36286
|
-
try {
|
|
36287
|
-
const latest = deps.getLatestInboundMessageId(chat_id, threadId ?? null);
|
|
36288
|
-
if (latest != null)
|
|
36289
|
-
replyToMessageId = latest;
|
|
36290
|
-
} catch (err) {
|
|
36291
|
-
deps.writeError(`telegram channel: stream_reply quote-lookup failed: ${err}
|
|
36292
|
-
`);
|
|
36293
|
-
}
|
|
36294
|
-
}
|
|
36295
|
-
let initialMessageId;
|
|
36296
|
-
if (deps.lookupExistingMessageId != null) {
|
|
36297
|
-
try {
|
|
36298
|
-
const looked = deps.lookupExistingMessageId({
|
|
36299
|
-
chatId: chat_id,
|
|
36300
|
-
threadId,
|
|
36301
|
-
lane: args.lane,
|
|
36302
|
-
turnKey: args.turnKey
|
|
36303
|
-
});
|
|
36304
|
-
if (typeof looked === "number" && Number.isFinite(looked)) {
|
|
36305
|
-
initialMessageId = looked;
|
|
36306
|
-
}
|
|
36307
|
-
} catch (err) {
|
|
36308
|
-
deps.writeError(`telegram channel: stream_reply lookupExistingMessageId failed: ${err}
|
|
36309
|
-
`);
|
|
36310
|
-
}
|
|
36311
|
-
}
|
|
36312
|
-
stream = createStreamController({
|
|
36313
|
-
bot: deps.bot,
|
|
36314
|
-
chatId: chat_id,
|
|
36315
|
-
threadId,
|
|
36316
|
-
literalText,
|
|
36317
|
-
disableLinkPreview: deps.disableLinkPreview,
|
|
36318
|
-
...deps.throttleMs != null ? { throttleMs: deps.throttleMs } : {},
|
|
36319
|
-
retry: deps.retry,
|
|
36320
|
-
...replyToMessageId != null ? { replyToMessageId } : {},
|
|
36321
|
-
...args.quote_text != null && replyToMessageId != null ? { quoteText: args.quote_text } : {},
|
|
36322
|
-
...args.protect_content === true ? { protectContent: true } : {},
|
|
36323
|
-
...args.disable_notification === true ? { disableNotification: true } : {},
|
|
36324
|
-
...args.reply_markup != null ? { replyMarkup: args.reply_markup } : {},
|
|
36325
|
-
isPrivateChat: deps.isPrivateChat === true,
|
|
36326
|
-
...initialMessageId != null ? { initialMessageId } : {},
|
|
36327
|
-
onSend: (messageId, charCount) => deps.logStreamingEvent({ kind: "draft_send", chatId: chat_id, messageId, charCount }),
|
|
36328
|
-
onEdit: (messageId, charCount) => deps.logStreamingEvent({
|
|
36329
|
-
kind: "draft_edit",
|
|
36330
|
-
chatId: chat_id,
|
|
36331
|
-
messageId,
|
|
36332
|
-
charCount,
|
|
36333
|
-
sameAsLast: false
|
|
36334
|
-
}),
|
|
36335
|
-
log: (msg) => {
|
|
36336
|
-
if (msg.startsWith("stream \u2192 sent") || msg.startsWith("stream \u2192 edited") || msg.startsWith("stream \u2192 not modified") || msg.startsWith("stream finalized") || msg.startsWith("stream \u2192 materialized"))
|
|
36337
|
-
return;
|
|
36338
|
-
deps.writeError(`telegram channel: stream_reply ${msg}
|
|
36339
|
-
`);
|
|
36340
|
-
},
|
|
36341
|
-
warn: (msg) => {
|
|
36342
|
-
deps.writeError(`telegram channel: stream_reply ${msg}
|
|
36343
|
-
`);
|
|
36344
|
-
}
|
|
36345
|
-
});
|
|
36346
|
-
state.activeDraftStreams.set(sKey, stream);
|
|
36347
|
-
}
|
|
36348
|
-
await stream.update(effectiveText);
|
|
36349
|
-
if (done) {
|
|
36350
|
-
await stream.finalize();
|
|
36351
|
-
state.activeDraftStreams.delete(sKey);
|
|
36352
|
-
if (stream.getMessageId() == null) {
|
|
36353
|
-
throw new Error(`stream_reply finalized without sending any message (length=${rawText.length}, ` + `max=${RICH_MESSAGE_MAX_CHARS}). Telegram's rich-message limit is ${RICH_MESSAGE_MAX_CHARS} chars and ` + `stream_reply does not auto-chunk. Split the text or use \`reply\` (which chunks).`);
|
|
36354
|
-
}
|
|
36355
|
-
if (deps.historyEnabled) {
|
|
36356
|
-
const finalId = stream.getMessageId();
|
|
36357
|
-
if (finalId != null) {
|
|
36358
|
-
try {
|
|
36359
|
-
deps.recordOutbound({
|
|
36360
|
-
chat_id,
|
|
36361
|
-
thread_id: threadId ?? null,
|
|
36362
|
-
message_ids: [finalId],
|
|
36363
|
-
texts: [rawText]
|
|
36364
|
-
});
|
|
36365
|
-
} catch (err) {
|
|
36366
|
-
deps.writeError(`telegram channel: history recordOutbound (stream_reply) failed: ${err}
|
|
36367
|
-
`);
|
|
36368
|
-
}
|
|
36369
|
-
}
|
|
36370
|
-
}
|
|
36371
|
-
}
|
|
36372
|
-
const finalMessageId = stream.getMessageId();
|
|
36373
|
-
if (done) {
|
|
36374
|
-
process.stderr.write(`telegram channel: stream_reply: finalized done=true chatId=${chat_id} lane=${args.lane ?? "default"} messageId=${finalMessageId ?? "null"}
|
|
36375
|
-
`);
|
|
36376
|
-
const isDefaultLaneForCompletion = args.lane == null || args.lane.length === 0;
|
|
36377
|
-
if (deps.recordOutboundDelivered != null && finalMessageId != null && isDefaultLaneForCompletion) {
|
|
36378
|
-
try {
|
|
36379
|
-
deps.recordOutboundDelivered(chat_id, threadId);
|
|
36380
|
-
} catch (err) {
|
|
36381
|
-
deps.writeError(`telegram channel: stream_reply: recordOutboundDelivered hook threw: ${err}
|
|
36382
|
-
`);
|
|
36383
|
-
}
|
|
36384
|
-
}
|
|
36385
|
-
if (deps.forceCompleteTurn != null && isDefaultLaneForCompletion) {
|
|
36386
|
-
try {
|
|
36387
|
-
deps.forceCompleteTurn(chat_id, threadId);
|
|
36388
|
-
} catch (err) {
|
|
36389
|
-
deps.writeError(`telegram channel: stream_reply: forceCompleteTurn hook threw: ${err}
|
|
36390
|
-
`);
|
|
36391
|
-
}
|
|
36392
|
-
}
|
|
36393
|
-
}
|
|
36394
|
-
return {
|
|
36395
|
-
messageId: finalMessageId,
|
|
36396
|
-
status: done ? "finalized" : "updated"
|
|
36397
|
-
};
|
|
36398
|
-
}
|
|
36399
|
-
|
|
36400
36242
|
// chat-lock.ts
|
|
36401
36243
|
function createChatLock() {
|
|
36402
36244
|
const chains = new Map;
|
|
@@ -43677,8 +43519,8 @@ function defaultAddAccount(label, credentials, opts) {
|
|
|
43677
43519
|
}
|
|
43678
43520
|
|
|
43679
43521
|
// ../src/auth/broker/client.ts
|
|
43680
|
-
import * as net3 from "node:net";
|
|
43681
43522
|
init_protocol();
|
|
43523
|
+
import * as net3 from "node:net";
|
|
43682
43524
|
import { homedir as homedir6 } from "node:os";
|
|
43683
43525
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
43684
43526
|
import { join as join14 } from "node:path";
|
|
@@ -43997,13 +43839,13 @@ class AuthBrokerClient2 {
|
|
|
43997
43839
|
init_loader();
|
|
43998
43840
|
init_resolver();
|
|
43999
43841
|
init_vault();
|
|
44000
|
-
import { existsSync as
|
|
43842
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
44001
43843
|
var DEFAULT_VOICE_API_KEY_REF = "vault:openai/api-key";
|
|
44002
43844
|
function tryDirectVaultRead(ref, config, passphrase) {
|
|
44003
43845
|
if (!passphrase)
|
|
44004
43846
|
return null;
|
|
44005
43847
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
44006
|
-
if (!
|
|
43848
|
+
if (!existsSync12(vaultPath))
|
|
44007
43849
|
return null;
|
|
44008
43850
|
try {
|
|
44009
43851
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -44059,14 +43901,14 @@ async function materializeVoiceKey(opts = {}, logger2 = (line) => process.stderr
|
|
|
44059
43901
|
init_loader();
|
|
44060
43902
|
init_resolver();
|
|
44061
43903
|
init_vault();
|
|
44062
|
-
import { existsSync as
|
|
43904
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
44063
43905
|
var VOICE_SIDECAR_TOKEN_KEY = "voice/sidecar-token";
|
|
44064
43906
|
var DEFAULT_VOICE_SIDECAR_TOKEN_REF = `vault:${VOICE_SIDECAR_TOKEN_KEY}`;
|
|
44065
43907
|
function tryDirectVaultRead2(ref, config, passphrase) {
|
|
44066
43908
|
if (!passphrase)
|
|
44067
43909
|
return null;
|
|
44068
43910
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
44069
|
-
if (!
|
|
43911
|
+
if (!existsSync13(vaultPath))
|
|
44070
43912
|
return null;
|
|
44071
43913
|
try {
|
|
44072
43914
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -44119,14 +43961,14 @@ async function materializeSidecarToken(opts = {}, logger2 = (line) => process.st
|
|
|
44119
43961
|
}
|
|
44120
43962
|
|
|
44121
43963
|
// ../src/setup/host-capabilities.ts
|
|
44122
|
-
import { existsSync as
|
|
43964
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync12, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "node:fs";
|
|
44123
43965
|
init_paths();
|
|
44124
43966
|
function hostCapabilitiesPath() {
|
|
44125
43967
|
return resolveStatePath2("host-capabilities.json");
|
|
44126
43968
|
}
|
|
44127
43969
|
function loadHostCapabilities() {
|
|
44128
43970
|
const path = hostCapabilitiesPath();
|
|
44129
|
-
if (!
|
|
43971
|
+
if (!existsSync14(path))
|
|
44130
43972
|
return null;
|
|
44131
43973
|
try {
|
|
44132
43974
|
const parsed = JSON.parse(readFileSync11(path, "utf-8"));
|
|
@@ -44227,7 +44069,7 @@ function resolveExhaustUntil(resetAtMs, now = Date.now()) {
|
|
|
44227
44069
|
|
|
44228
44070
|
// gateway/auth-add-flow.ts
|
|
44229
44071
|
import { execFileSync } from "node:child_process";
|
|
44230
|
-
import { existsSync as
|
|
44072
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync14, readFileSync as readFileSync13, readdirSync as readdirSync4, rmSync as rmSync3, statSync as statSync7, writeFileSync as writeFileSync10 } from "node:fs";
|
|
44231
44073
|
import { homedir as homedir7 } from "node:os";
|
|
44232
44074
|
import { join as join15 } from "node:path";
|
|
44233
44075
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
@@ -44236,8 +44078,8 @@ import { randomBytes as randomBytes4 } from "node:crypto";
|
|
|
44236
44078
|
import {
|
|
44237
44079
|
readFileSync as readFileSync12,
|
|
44238
44080
|
readdirSync as readdirSync3,
|
|
44239
|
-
existsSync as
|
|
44240
|
-
writeFileSync as
|
|
44081
|
+
existsSync as existsSync15,
|
|
44082
|
+
writeFileSync as writeFileSync9,
|
|
44241
44083
|
mkdirSync as mkdirSync13,
|
|
44242
44084
|
mkdtempSync as mkdtempSync2,
|
|
44243
44085
|
rmSync as rmSync2,
|
|
@@ -44263,7 +44105,7 @@ function parseSetupTokenUrl(output) {
|
|
|
44263
44105
|
}
|
|
44264
44106
|
function readTokenFromCredentialsFile(credentialsFilePath) {
|
|
44265
44107
|
try {
|
|
44266
|
-
if (!
|
|
44108
|
+
if (!existsSync15(credentialsFilePath))
|
|
44267
44109
|
return null;
|
|
44268
44110
|
const raw = readFileSync12(credentialsFilePath, "utf-8");
|
|
44269
44111
|
const parsed = JSON.parse(raw);
|
|
@@ -44335,7 +44177,7 @@ function cleanScratchDir(scratchDir) {
|
|
|
44335
44177
|
var AUTH_TMUX_SESSION_FILE = ".auth-tmux-session";
|
|
44336
44178
|
function sweepOrphanSessions(home2, tmux, nowMs2 = Date.now()) {
|
|
44337
44179
|
const inProgressDir = join15(home2, ".switchroom", "accounts", ".in-progress");
|
|
44338
|
-
if (!
|
|
44180
|
+
if (!existsSync16(inProgressDir))
|
|
44339
44181
|
return;
|
|
44340
44182
|
let entries;
|
|
44341
44183
|
try {
|
|
@@ -44347,7 +44189,7 @@ function sweepOrphanSessions(home2, tmux, nowMs2 = Date.now()) {
|
|
|
44347
44189
|
for (const entry of entries) {
|
|
44348
44190
|
const dir = join15(inProgressDir, entry);
|
|
44349
44191
|
const sessionFile = join15(dir, AUTH_TMUX_SESSION_FILE);
|
|
44350
|
-
if (!
|
|
44192
|
+
if (!existsSync16(sessionFile))
|
|
44351
44193
|
continue;
|
|
44352
44194
|
let fileContents;
|
|
44353
44195
|
let fileMtime;
|
|
@@ -44386,7 +44228,7 @@ async function startAccountAuthSession(label, opts = {}) {
|
|
|
44386
44228
|
const tmuxSocket = `switchroom-${agentName3}`;
|
|
44387
44229
|
const tmuxSession = `auth-add-${label}-${hexSuffix}`.slice(0, 64);
|
|
44388
44230
|
try {
|
|
44389
|
-
|
|
44231
|
+
writeFileSync10(join15(scratchDir, AUTH_TMUX_SESSION_FILE), `${tmuxSocket}
|
|
44390
44232
|
${tmuxSession}`, "utf8");
|
|
44391
44233
|
} catch {}
|
|
44392
44234
|
const sessionEnv = {
|
|
@@ -44445,7 +44287,7 @@ async function submitAccountAuthCode(flow, code, opts = {}) {
|
|
|
44445
44287
|
const deadline = Date.now() + pollTimeoutMs;
|
|
44446
44288
|
while (Date.now() < deadline) {
|
|
44447
44289
|
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
44448
|
-
if (
|
|
44290
|
+
if (existsSync16(credentialsPath)) {
|
|
44449
44291
|
const token = readTokenFromCredentialsFile(credentialsPath);
|
|
44450
44292
|
if (token) {
|
|
44451
44293
|
try {
|
|
@@ -44459,7 +44301,7 @@ async function submitAccountAuthCode(flow, code, opts = {}) {
|
|
|
44459
44301
|
}
|
|
44460
44302
|
}
|
|
44461
44303
|
if (!tmux.hasSession(flow.tmuxSocket, flow.tmuxSession)) {
|
|
44462
|
-
if (!
|
|
44304
|
+
if (!existsSync16(credentialsPath)) {
|
|
44463
44305
|
cleanScratchDir(flow.scratchDir);
|
|
44464
44306
|
throw new Error("claude setup-token exited without writing credentials \u2014 the code may be invalid or expired");
|
|
44465
44307
|
}
|
|
@@ -46228,6 +46070,7 @@ function decideTurnFlush(input) {
|
|
|
46228
46070
|
if (input.chatId == null)
|
|
46229
46071
|
return { kind: "skip", reason: "no-inbound-chat" };
|
|
46230
46072
|
const joined = input.capturedText.join(`
|
|
46073
|
+
|
|
46231
46074
|
`).trim();
|
|
46232
46075
|
if (joined.length === 0)
|
|
46233
46076
|
return { kind: "skip", reason: "empty-text" };
|
|
@@ -46259,7 +46102,7 @@ function resolveAgentDirFromEnv() {
|
|
|
46259
46102
|
}
|
|
46260
46103
|
|
|
46261
46104
|
// active-reactions.ts
|
|
46262
|
-
import { readFileSync as readFileSync15, writeFileSync as
|
|
46105
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync11, renameSync as renameSync5, existsSync as existsSync19, unlinkSync as unlinkSync5 } from "node:fs";
|
|
46263
46106
|
import { join as join18 } from "node:path";
|
|
46264
46107
|
var ACTIVE_REACTIONS_FILENAME = ".active-reactions.json";
|
|
46265
46108
|
function reactionsPath(agentDir) {
|
|
@@ -46267,7 +46110,7 @@ function reactionsPath(agentDir) {
|
|
|
46267
46110
|
}
|
|
46268
46111
|
function readActiveReactions(agentDir) {
|
|
46269
46112
|
const p = reactionsPath(agentDir);
|
|
46270
|
-
if (!
|
|
46113
|
+
if (!existsSync19(p))
|
|
46271
46114
|
return [];
|
|
46272
46115
|
let raw;
|
|
46273
46116
|
try {
|
|
@@ -46303,7 +46146,7 @@ function writeActiveReactions(agentDir, reactions) {
|
|
|
46303
46146
|
}
|
|
46304
46147
|
const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
|
|
46305
46148
|
try {
|
|
46306
|
-
|
|
46149
|
+
writeFileSync11(tmp, JSON.stringify(reactions) + `
|
|
46307
46150
|
`, "utf-8");
|
|
46308
46151
|
renameSync5(tmp, p);
|
|
46309
46152
|
} catch {}
|
|
@@ -46327,7 +46170,7 @@ function clearActiveReactions(agentDir) {
|
|
|
46327
46170
|
}
|
|
46328
46171
|
|
|
46329
46172
|
// active-reactions.ts
|
|
46330
|
-
import { readFileSync as readFileSync16, writeFileSync as
|
|
46173
|
+
import { readFileSync as readFileSync16, writeFileSync as writeFileSync12, renameSync as renameSync6, existsSync as existsSync20, unlinkSync as unlinkSync6 } from "node:fs";
|
|
46331
46174
|
import { join as join19 } from "node:path";
|
|
46332
46175
|
var ACTIVE_REACTIONS_FILENAME2 = ".active-reactions.json";
|
|
46333
46176
|
function reactionsPath2(agentDir) {
|
|
@@ -46335,7 +46178,7 @@ function reactionsPath2(agentDir) {
|
|
|
46335
46178
|
}
|
|
46336
46179
|
function readActiveReactions2(agentDir) {
|
|
46337
46180
|
const p = reactionsPath2(agentDir);
|
|
46338
|
-
if (!
|
|
46181
|
+
if (!existsSync20(p))
|
|
46339
46182
|
return [];
|
|
46340
46183
|
let raw;
|
|
46341
46184
|
try {
|
|
@@ -46399,6 +46242,7 @@ function flushOnAgentDisconnect(deps) {
|
|
|
46399
46242
|
activeReactionMsgIds,
|
|
46400
46243
|
activeTurnStartedAt,
|
|
46401
46244
|
claudeBusyKeys,
|
|
46245
|
+
claudeBusyKeySince,
|
|
46402
46246
|
activeDraftStreams,
|
|
46403
46247
|
clearActiveReactions: clearActiveReactions3,
|
|
46404
46248
|
disposeProgressDriver,
|
|
@@ -46415,6 +46259,7 @@ function flushOnAgentDisconnect(deps) {
|
|
|
46415
46259
|
activeReactionMsgIds.delete(key);
|
|
46416
46260
|
activeTurnStartedAt.delete(key);
|
|
46417
46261
|
claudeBusyKeys.delete(key);
|
|
46262
|
+
claudeBusyKeySince.delete(key);
|
|
46418
46263
|
}
|
|
46419
46264
|
clearActiveReactions3();
|
|
46420
46265
|
const danglingKeys = [...activeTurnStartedAt.keys()];
|
|
@@ -46423,6 +46268,7 @@ function flushOnAgentDisconnect(deps) {
|
|
|
46423
46268
|
activeTurnStartedAt.delete(k);
|
|
46424
46269
|
activeReactionMsgIds.delete(k);
|
|
46425
46270
|
claudeBusyKeys.delete(k);
|
|
46271
|
+
claudeBusyKeySince.delete(k);
|
|
46426
46272
|
}
|
|
46427
46273
|
log(`telegram gateway: disconnect-flush swept ${danglingKeys.length} dangling turn key(s) ` + `post-bridge-death (controller loop missed \u2014 finalize raced disconnect)`);
|
|
46428
46274
|
onDanglingTurnsSwept?.(danglingKeys);
|
|
@@ -46431,6 +46277,8 @@ function flushOnAgentDisconnect(deps) {
|
|
|
46431
46277
|
const orphanCount = claudeBusyKeys.size;
|
|
46432
46278
|
const orphanKeys = [...claudeBusyKeys];
|
|
46433
46279
|
claudeBusyKeys.clear();
|
|
46280
|
+
for (const k of orphanKeys)
|
|
46281
|
+
claudeBusyKeySince.delete(k);
|
|
46434
46282
|
log(`telegram gateway: disconnect-flush cleared ${orphanCount} orphan claudeBusyKeys ` + `entr${orphanCount === 1 ? "y" : "ies"} (synthetic-inbound deliveries that never turn_ended)` + ` keys=${orphanKeys.join(",")}`);
|
|
46435
46283
|
}
|
|
46436
46284
|
disposeProgressDriver();
|
|
@@ -46442,6 +46290,33 @@ function flushOnAgentDisconnect(deps) {
|
|
|
46442
46290
|
return true;
|
|
46443
46291
|
}
|
|
46444
46292
|
|
|
46293
|
+
// gateway/busy-key-reaper.ts
|
|
46294
|
+
function markBusyKeyLockstep(keys, since, key, now) {
|
|
46295
|
+
const wasBusy = keys.has(key);
|
|
46296
|
+
keys.add(key);
|
|
46297
|
+
if (!wasBusy)
|
|
46298
|
+
since.set(key, now);
|
|
46299
|
+
}
|
|
46300
|
+
function reapOrphanBusyKeys(keys, since, now, opts) {
|
|
46301
|
+
const reaped = [];
|
|
46302
|
+
for (const key of [...since.keys()]) {
|
|
46303
|
+
if (!keys.has(key)) {
|
|
46304
|
+
since.delete(key);
|
|
46305
|
+
continue;
|
|
46306
|
+
}
|
|
46307
|
+
if (opts.hasPendingDelivery(key))
|
|
46308
|
+
continue;
|
|
46309
|
+
const stampedAt = since.get(key) ?? now;
|
|
46310
|
+
if (now - stampedAt >= opts.ttlMs) {
|
|
46311
|
+
keys.delete(key);
|
|
46312
|
+
since.delete(key);
|
|
46313
|
+
reaped.push(key);
|
|
46314
|
+
opts.log?.(`telegram gateway: reaped orphan busy key=${key} (no turn in flight, ` + `no pending delivery, >${opts.ttlMs}ms) \u2014 unwedging idle-drain`);
|
|
46315
|
+
}
|
|
46316
|
+
}
|
|
46317
|
+
return reaped;
|
|
46318
|
+
}
|
|
46319
|
+
|
|
46445
46320
|
// gateway/preamble-suppressor.ts
|
|
46446
46321
|
var DEFAULT_BUFFER_MS = 150;
|
|
46447
46322
|
|
|
@@ -47214,14 +47089,14 @@ async function approvalRecord(args, opts) {
|
|
|
47214
47089
|
}
|
|
47215
47090
|
|
|
47216
47091
|
// quota-check.ts
|
|
47217
|
-
import { readFileSync as readFileSync17, existsSync as
|
|
47092
|
+
import { readFileSync as readFileSync17, existsSync as existsSync21 } from "fs";
|
|
47218
47093
|
import { join as join20 } from "path";
|
|
47219
47094
|
var OAUTH_BETA2 = "oauth-2025-04-20";
|
|
47220
47095
|
var DEFAULT_USER_AGENT2 = "claude-cli/1.0.0 (external, cli)";
|
|
47221
47096
|
var DEFAULT_PROBE_MODEL2 = "claude-haiku-4-5-20251001";
|
|
47222
47097
|
function readOauthToken2(claudeConfigDir) {
|
|
47223
47098
|
const tokenFile = join20(claudeConfigDir, ".oauth-token");
|
|
47224
|
-
if (!
|
|
47099
|
+
if (!existsSync21(tokenFile))
|
|
47225
47100
|
return null;
|
|
47226
47101
|
try {
|
|
47227
47102
|
const raw = readFileSync17(tokenFile, "utf-8").trim();
|
|
@@ -47401,8 +47276,7 @@ init_loader();
|
|
|
47401
47276
|
var TOKEN_VALIDITY_MS2 = 365 * 24 * 60 * 60000;
|
|
47402
47277
|
|
|
47403
47278
|
// ../src/agents/inject.ts
|
|
47404
|
-
import {
|
|
47405
|
-
import { promisify } from "node:util";
|
|
47279
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
47406
47280
|
|
|
47407
47281
|
// ../src/agents/pane-lock.ts
|
|
47408
47282
|
var tails = new Map;
|
|
@@ -47419,7 +47293,6 @@ function withPaneLock(key, fn) {
|
|
|
47419
47293
|
}
|
|
47420
47294
|
|
|
47421
47295
|
// ../src/agents/inject.ts
|
|
47422
|
-
var execFileAsync = promisify(execFile);
|
|
47423
47296
|
var INJECT_COMMANDS = new Map([
|
|
47424
47297
|
["/cost", { description: "Show session cost", expectsOutput: true, dialog: true }],
|
|
47425
47298
|
["/status", { description: "Show session status", expectsOutput: true, dialog: true }],
|
|
@@ -47715,9 +47588,7 @@ async function injectSlashCommandWith(runner, args) {
|
|
|
47715
47588
|
}
|
|
47716
47589
|
|
|
47717
47590
|
// ../src/agents/inject.ts
|
|
47718
|
-
import {
|
|
47719
|
-
import { promisify as promisify2 } from "node:util";
|
|
47720
|
-
var execFileAsync2 = promisify2(execFile2);
|
|
47591
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
47721
47592
|
var INJECT_COMMANDS2 = new Map([
|
|
47722
47593
|
["/cost", { description: "Show session cost", expectsOutput: true, dialog: true }],
|
|
47723
47594
|
["/status", { description: "Show session status", expectsOutput: true, dialog: true }],
|
|
@@ -47785,7 +47656,7 @@ function makeTmuxRunner2(tmuxBin) {
|
|
|
47785
47656
|
};
|
|
47786
47657
|
}
|
|
47787
47658
|
// stream-reply-handler.ts
|
|
47788
|
-
function
|
|
47659
|
+
function buildAccentHeader(accent) {
|
|
47789
47660
|
switch (accent) {
|
|
47790
47661
|
case "in-progress":
|
|
47791
47662
|
return `\uD83D\uDD35 _In progress\u2026_
|
|
@@ -47909,7 +47780,7 @@ Allowed: \`${deps.escapeHtml(allow)}\``, { html: true });
|
|
|
47909
47780
|
}
|
|
47910
47781
|
}
|
|
47911
47782
|
const { body, accent } = shapeReply(result, slashCommand, deps);
|
|
47912
|
-
const finalText =
|
|
47783
|
+
const finalText = buildAccentHeader(accent) + body;
|
|
47913
47784
|
await deps.reply(ctx, finalText, { html: true, accent });
|
|
47914
47785
|
}
|
|
47915
47786
|
|
|
@@ -48023,11 +47894,22 @@ async function handleModelCommand(parsed, deps) {
|
|
|
48023
47894
|
};
|
|
48024
47895
|
}
|
|
48025
47896
|
if (result.outcome === "ok") {
|
|
47897
|
+
const confirmation = modelSwitchConfirmationLine(result.output);
|
|
47898
|
+
if (confirmation) {
|
|
47899
|
+
return {
|
|
47900
|
+
text: [
|
|
47901
|
+
`${verbHtml}`,
|
|
47902
|
+
deps.preBlock(confirmation),
|
|
47903
|
+
...result.truncated ? ["_truncated_"] : [],
|
|
47904
|
+
PERSIST_NOTE
|
|
47905
|
+
].join(`
|
|
47906
|
+
`),
|
|
47907
|
+
html: true
|
|
47908
|
+
};
|
|
47909
|
+
}
|
|
48026
47910
|
return {
|
|
48027
47911
|
text: [
|
|
48028
|
-
`${verbHtml}
|
|
48029
|
-
deps.preBlock(result.output),
|
|
48030
|
-
...result.truncated ? ["_truncated_"] : [],
|
|
47912
|
+
`${verbHtml} \u2014 switched (session).`,
|
|
48031
47913
|
PERSIST_NOTE
|
|
48032
47914
|
].join(`
|
|
48033
47915
|
`),
|
|
@@ -48321,6 +48203,12 @@ async function handleModelMenuCallback(data, deps) {
|
|
|
48321
48203
|
function isSrToClaudeTransition(prevModel, nextModel) {
|
|
48322
48204
|
return !!prevModel?.startsWith("sr-") && !nextModel.startsWith("sr-");
|
|
48323
48205
|
}
|
|
48206
|
+
function modelSwitchConfirmationLine(output) {
|
|
48207
|
+
const line = output.split(`
|
|
48208
|
+
`).map((l) => l.trim()).find((l) => MODEL_SWITCH_CONFIRMATION_PREFIX.test(l));
|
|
48209
|
+
return line && line.length > 0 ? line : null;
|
|
48210
|
+
}
|
|
48211
|
+
var MODEL_SWITCH_CONFIRMATION_PREFIX = /^\s*[\u23fa\u25cf\u2022>-]?\s*(?:Set model to|Switched to|Kept model as)\b/i;
|
|
48324
48212
|
function sessionModelFromConfirmation(confirmation) {
|
|
48325
48213
|
const m = /(?:Set model to|Switched to)\s+(.+?)(?:\s+for (?:this|the) session|\s*\(|\s*$)/i.exec(confirmation.trim());
|
|
48326
48214
|
const name = m?.[1]?.trim();
|
|
@@ -48781,7 +48669,7 @@ init_schema();
|
|
|
48781
48669
|
init_paths();
|
|
48782
48670
|
init_overlay_loader();
|
|
48783
48671
|
init_merge();
|
|
48784
|
-
import { readFileSync as readFileSync18, existsSync as
|
|
48672
|
+
import { readFileSync as readFileSync18, existsSync as existsSync22 } from "node:fs";
|
|
48785
48673
|
import { homedir as homedir8 } from "node:os";
|
|
48786
48674
|
import { resolve as resolve5 } from "node:path";
|
|
48787
48675
|
|
|
@@ -48857,7 +48745,7 @@ function findConfigFile2(startDir) {
|
|
|
48857
48745
|
resolve5(userDir, "clerk.yml")
|
|
48858
48746
|
].filter(Boolean);
|
|
48859
48747
|
for (const path of searchPaths) {
|
|
48860
|
-
if (
|
|
48748
|
+
if (existsSync22(path)) {
|
|
48861
48749
|
return path;
|
|
48862
48750
|
}
|
|
48863
48751
|
}
|
|
@@ -48865,7 +48753,7 @@ function findConfigFile2(startDir) {
|
|
|
48865
48753
|
}
|
|
48866
48754
|
function loadConfig2(configPath) {
|
|
48867
48755
|
const filePath = configPath ?? findConfigFile2();
|
|
48868
|
-
if (!
|
|
48756
|
+
if (!existsSync22(filePath)) {
|
|
48869
48757
|
throw new ConfigError2(`Config file not found: ${filePath}`);
|
|
48870
48758
|
}
|
|
48871
48759
|
let raw;
|
|
@@ -49363,9 +49251,9 @@ function topicForRecipient(args) {
|
|
|
49363
49251
|
}
|
|
49364
49252
|
|
|
49365
49253
|
// ../src/agents/perf.ts
|
|
49366
|
-
import { existsSync as
|
|
49254
|
+
import { existsSync as existsSync23, readFileSync as readFileSync19 } from "node:fs";
|
|
49367
49255
|
function readTurnUsages(jsonlPath, lastN) {
|
|
49368
|
-
if (!
|
|
49256
|
+
if (!existsSync23(jsonlPath))
|
|
49369
49257
|
return [];
|
|
49370
49258
|
if (lastN <= 0)
|
|
49371
49259
|
return [];
|
|
@@ -49462,7 +49350,7 @@ function numField(obj, key) {
|
|
|
49462
49350
|
}
|
|
49463
49351
|
|
|
49464
49352
|
// gateway/context-occupancy.ts
|
|
49465
|
-
import { mkdirSync as mkdirSync16, writeFileSync as
|
|
49353
|
+
import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync13 } from "node:fs";
|
|
49466
49354
|
import { join as join21 } from "node:path";
|
|
49467
49355
|
var CONTEXT_OCCUPANCY_FILENAME = "context-occupancy.json";
|
|
49468
49356
|
var TIGHT_FRACTION = 0.8;
|
|
@@ -49488,7 +49376,7 @@ function writeContextOccupancySnapshot(stateDir, snapshot, deps) {
|
|
|
49488
49376
|
try {
|
|
49489
49377
|
const path = join21(stateDir, CONTEXT_OCCUPANCY_FILENAME);
|
|
49490
49378
|
(deps?.mkdir ?? ((p, o) => mkdirSync16(p, o)))(stateDir, { recursive: true });
|
|
49491
|
-
(deps?.writeFile ?? ((p, d) =>
|
|
49379
|
+
(deps?.writeFile ?? ((p, d) => writeFileSync13(p, d)))(path, JSON.stringify(snapshot, null, 2) + `
|
|
49492
49380
|
`);
|
|
49493
49381
|
} catch {}
|
|
49494
49382
|
}
|
|
@@ -49571,7 +49459,7 @@ function nextCompactNotify(state3, ev) {
|
|
|
49571
49459
|
}
|
|
49572
49460
|
|
|
49573
49461
|
// gateway/hostd-dispatch.ts
|
|
49574
|
-
import { existsSync as
|
|
49462
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
49575
49463
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
49576
49464
|
|
|
49577
49465
|
// ../src/host-control/client.ts
|
|
@@ -49896,13 +49784,13 @@ function hostdSocketPath(agentName3) {
|
|
|
49896
49784
|
function hostdWillBeUsed(agentName3) {
|
|
49897
49785
|
if (!isHostdEnabled())
|
|
49898
49786
|
return false;
|
|
49899
|
-
return
|
|
49787
|
+
return existsSync24(hostdSocketPath(agentName3));
|
|
49900
49788
|
}
|
|
49901
49789
|
async function tryHostdDispatch(agentName3, req, timeoutMs = 5000) {
|
|
49902
49790
|
if (!isHostdEnabled())
|
|
49903
49791
|
return "not-configured";
|
|
49904
49792
|
const sockPath = hostdSocketPath(agentName3);
|
|
49905
|
-
if (!
|
|
49793
|
+
if (!existsSync24(sockPath))
|
|
49906
49794
|
return "not-configured";
|
|
49907
49795
|
try {
|
|
49908
49796
|
return await hostdRequest({ socketPath: sockPath, timeoutMs }, req);
|
|
@@ -49926,7 +49814,7 @@ async function hostdGetStatusOnce(agentName3, targetRequestId) {
|
|
|
49926
49814
|
if (!isHostdEnabled())
|
|
49927
49815
|
return "not-configured";
|
|
49928
49816
|
const sockPath = hostdSocketPath(agentName3);
|
|
49929
|
-
if (!
|
|
49817
|
+
if (!existsSync24(sockPath))
|
|
49930
49818
|
return "not-configured";
|
|
49931
49819
|
try {
|
|
49932
49820
|
const resp = await hostdRequest({ socketPath: sockPath, timeoutMs: 3000 }, {
|
|
@@ -49947,7 +49835,7 @@ async function pollHostdStatus(agentName3, targetRequestId, opts) {
|
|
|
49947
49835
|
if (!isHostdEnabled())
|
|
49948
49836
|
return "not-configured";
|
|
49949
49837
|
const sockPath = hostdSocketPath(agentName3);
|
|
49950
|
-
if (!
|
|
49838
|
+
if (!existsSync24(sockPath))
|
|
49951
49839
|
return "not-configured";
|
|
49952
49840
|
const now = opts.now ?? Date.now;
|
|
49953
49841
|
const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
@@ -50026,7 +49914,7 @@ function shouldSweepChatAtBoot(chatId) {
|
|
|
50026
49914
|
|
|
50027
49915
|
// gateway/webhook-ingest-server.ts
|
|
50028
49916
|
import net4 from "node:net";
|
|
50029
|
-
import { chmodSync as chmodSync4, existsSync as
|
|
49917
|
+
import { chmodSync as chmodSync4, existsSync as existsSync25, unlinkSync as unlinkSync7 } from "node:fs";
|
|
50030
49918
|
var MAX_REQUEST_BYTES = 1024 * 1024;
|
|
50031
49919
|
function fdOf(conn) {
|
|
50032
49920
|
const handle = conn._handle;
|
|
@@ -50038,7 +49926,7 @@ function startWebhookIngestServer(opts) {
|
|
|
50038
49926
|
const log = opts.log ?? ((s) => process.stderr.write(s));
|
|
50039
49927
|
const allowed = new Set(opts.allowedUids);
|
|
50040
49928
|
try {
|
|
50041
|
-
if (
|
|
49929
|
+
if (existsSync25(opts.socketPath))
|
|
50042
49930
|
unlinkSync7(opts.socketPath);
|
|
50043
49931
|
} catch (err) {
|
|
50044
49932
|
log(`webhook-ingest-server: could not unlink stale socket: ${err.message}
|
|
@@ -50131,7 +50019,7 @@ function startWebhookIngestServer(opts) {
|
|
|
50131
50019
|
server.close();
|
|
50132
50020
|
} catch {}
|
|
50133
50021
|
try {
|
|
50134
|
-
if (
|
|
50022
|
+
if (existsSync25(opts.socketPath))
|
|
50135
50023
|
unlinkSync7(opts.socketPath);
|
|
50136
50024
|
} catch {}
|
|
50137
50025
|
}
|
|
@@ -50144,13 +50032,13 @@ import { join as join24 } from "path";
|
|
|
50144
50032
|
import { homedir as homedir10 } from "os";
|
|
50145
50033
|
|
|
50146
50034
|
// ../src/web/webhook-handler.ts
|
|
50147
|
-
import { appendFileSync as appendFileSync4, existsSync as
|
|
50035
|
+
import { appendFileSync as appendFileSync4, existsSync as existsSync26, mkdirSync as mkdirSync17, readFileSync as readFileSync20, writeFileSync as writeFileSync14 } from "fs";
|
|
50148
50036
|
import { join as join22 } from "path";
|
|
50149
50037
|
var DEDUP_MAX = 1000;
|
|
50150
50038
|
var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
|
|
50151
50039
|
function loadDedupFile(path) {
|
|
50152
50040
|
try {
|
|
50153
|
-
if (!
|
|
50041
|
+
if (!existsSync26(path))
|
|
50154
50042
|
return {};
|
|
50155
50043
|
const raw = JSON.parse(readFileSync20(path, "utf-8"));
|
|
50156
50044
|
return typeof raw.deliveries === "object" && raw.deliveries !== null ? raw.deliveries : {};
|
|
@@ -50166,7 +50054,7 @@ function saveDedupFile(path, deliveries, now) {
|
|
|
50166
50054
|
}
|
|
50167
50055
|
const sorted = Object.entries(pruned).sort((a, b) => b[1] - a[1]).slice(0, DEDUP_MAX);
|
|
50168
50056
|
const final = Object.fromEntries(sorted);
|
|
50169
|
-
|
|
50057
|
+
writeFileSync14(path, JSON.stringify({ deliveries: final }), {
|
|
50170
50058
|
mode: 384
|
|
50171
50059
|
});
|
|
50172
50060
|
}
|
|
@@ -50196,7 +50084,7 @@ var tokenBuckets = new Map;
|
|
|
50196
50084
|
var throttleIssueWindow = new Map;
|
|
50197
50085
|
|
|
50198
50086
|
// ../src/web/webhook-dispatch.ts
|
|
50199
|
-
import { existsSync as
|
|
50087
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync18, readFileSync as readFileSync21, writeFileSync as writeFileSync15 } from "fs";
|
|
50200
50088
|
import { join as join23 } from "path";
|
|
50201
50089
|
import { homedir as homedir9 } from "os";
|
|
50202
50090
|
|
|
@@ -50496,7 +50384,7 @@ function cooldownKey(source, eventType, repo, number, ruleIndex) {
|
|
|
50496
50384
|
}
|
|
50497
50385
|
function loadCooldownFile(path) {
|
|
50498
50386
|
try {
|
|
50499
|
-
if (!
|
|
50387
|
+
if (!existsSync27(path))
|
|
50500
50388
|
return {};
|
|
50501
50389
|
const raw = JSON.parse(readFileSync21(path, "utf-8"));
|
|
50502
50390
|
return typeof raw.dispatches === "object" && raw.dispatches !== null ? raw.dispatches : {};
|
|
@@ -50506,7 +50394,7 @@ function loadCooldownFile(path) {
|
|
|
50506
50394
|
}
|
|
50507
50395
|
function saveCooldownFile(path, dispatches) {
|
|
50508
50396
|
try {
|
|
50509
|
-
|
|
50397
|
+
writeFileSync15(path, JSON.stringify({ dispatches }), {
|
|
50510
50398
|
mode: 384
|
|
50511
50399
|
});
|
|
50512
50400
|
} catch {}
|
|
@@ -51971,6 +51859,11 @@ function createPendingInboundBuffer(opts = {}) {
|
|
|
51971
51859
|
evicted = true;
|
|
51972
51860
|
log(`pending-inbound-buffer: agent=${agent} cap=${cap} reached \u2014 ` + `dropped oldest entry source=${dropped?.meta?.source ?? "-"} ts=${dropped?.ts ?? "-"}
|
|
51973
51861
|
`);
|
|
51862
|
+
if (dropped != null && opts.onEvict != null) {
|
|
51863
|
+
try {
|
|
51864
|
+
opts.onEvict(agent, dropped);
|
|
51865
|
+
} catch {}
|
|
51866
|
+
}
|
|
51974
51867
|
}
|
|
51975
51868
|
q.push(msg);
|
|
51976
51869
|
spool?.put(agent, msg);
|
|
@@ -52265,28 +52158,41 @@ async function runStatusPinBootCleanup(args) {
|
|
|
52265
52158
|
persistStatusPins(args.path, args.fs, [], log);
|
|
52266
52159
|
return { cleared, total: persisted.length };
|
|
52267
52160
|
}
|
|
52268
|
-
|
|
52161
|
+
var storeLockTails = new Map;
|
|
52162
|
+
function withStoreLock(path, fn) {
|
|
52163
|
+
const prev = storeLockTails.get(path) ?? Promise.resolve();
|
|
52164
|
+
const run3 = prev.then(fn, fn);
|
|
52165
|
+
storeLockTails.set(path, run3.then(() => {
|
|
52166
|
+
return;
|
|
52167
|
+
}, () => {
|
|
52168
|
+
return;
|
|
52169
|
+
}));
|
|
52170
|
+
return run3;
|
|
52171
|
+
}
|
|
52172
|
+
function applyStatusPinRow(path, fs2, pinKey, row, log) {
|
|
52173
|
+
const current = loadStatusPins(path, fs2);
|
|
52174
|
+
const others = current.filter((p) => p.pinKey !== pinKey);
|
|
52175
|
+
const next = row == null ? others : [...others, row];
|
|
52176
|
+
persistStatusPins(path, fs2, next, log);
|
|
52177
|
+
}
|
|
52178
|
+
function reconcileAndPersistStatusPin(args) {
|
|
52269
52179
|
const { path, fs: fs2, pinKey, chatId, op } = args;
|
|
52270
52180
|
const log = args.log ?? ((l) => process.stderr.write(l));
|
|
52271
|
-
|
|
52272
|
-
|
|
52273
|
-
|
|
52274
|
-
|
|
52275
|
-
|
|
52276
|
-
|
|
52277
|
-
|
|
52278
|
-
|
|
52279
|
-
|
|
52181
|
+
return withStoreLock(path, async () => {
|
|
52182
|
+
if (op.kind === "pin") {
|
|
52183
|
+
applyStatusPinRow(path, fs2, pinKey, { pinKey, chatId, messageId: op.messageId, pending: true }, log);
|
|
52184
|
+
const next2 = await args.applyPin();
|
|
52185
|
+
if (next2 == null) {
|
|
52186
|
+
applyStatusPinRow(path, fs2, pinKey, null, log);
|
|
52187
|
+
return null;
|
|
52188
|
+
}
|
|
52189
|
+
applyStatusPinRow(path, fs2, pinKey, { pinKey, chatId, messageId: next2.messageId }, log);
|
|
52190
|
+
return next2;
|
|
52280
52191
|
}
|
|
52281
|
-
|
|
52282
|
-
|
|
52283
|
-
|
|
52284
|
-
|
|
52285
|
-
return next2;
|
|
52286
|
-
}
|
|
52287
|
-
const next = await args.applyPin();
|
|
52288
|
-
persistStatusPins(path, fs2, args.snapshotOthers(), log);
|
|
52289
|
-
return next;
|
|
52192
|
+
const next = await args.applyPin();
|
|
52193
|
+
applyStatusPinRow(path, fs2, pinKey, null, log);
|
|
52194
|
+
return next;
|
|
52195
|
+
});
|
|
52290
52196
|
}
|
|
52291
52197
|
|
|
52292
52198
|
// gateway/with-deadline.ts
|
|
@@ -52336,11 +52242,19 @@ function driveEscalation(args) {
|
|
|
52336
52242
|
function shouldSuppressRepresent(o, deps) {
|
|
52337
52243
|
if (!deps.historyEnabled)
|
|
52338
52244
|
return false;
|
|
52339
|
-
if (o.lastRepresentedAt == null)
|
|
52340
|
-
|
|
52245
|
+
if (o.lastRepresentedAt == null) {
|
|
52246
|
+
if (o.openedAt == null)
|
|
52247
|
+
return false;
|
|
52248
|
+
return deps.hasOutboundDeliveredSince(o.chatId, o.openedAt, o.threadId);
|
|
52249
|
+
}
|
|
52341
52250
|
return deps.hasOutboundDeliveredSince(o.chatId, o.lastRepresentedAt, o.threadId);
|
|
52342
52251
|
}
|
|
52343
52252
|
|
|
52253
|
+
// gateway/escalation-bridge-gate.ts
|
|
52254
|
+
function shouldDeferEscalationForBridge(deps) {
|
|
52255
|
+
return !deps.bridgeAlive;
|
|
52256
|
+
}
|
|
52257
|
+
|
|
52344
52258
|
// gateway/inbound-spool.ts
|
|
52345
52259
|
function spoolId(msg) {
|
|
52346
52260
|
if (msg.meta?.source === "subagent_handback" && typeof msg.meta?.subagent_jsonl_id === "string" && msg.meta.subagent_jsonl_id.length > 0) {
|
|
@@ -52372,6 +52286,8 @@ function createInboundSpool(opts) {
|
|
|
52372
52286
|
const escalateNoticeCooldownMs = opts.escalateNoticeCooldownMs ?? 30 * 60 * 1000;
|
|
52373
52287
|
const live = new Map;
|
|
52374
52288
|
const escAttemptByChat = new Map;
|
|
52289
|
+
let consecutiveAppendFailures = 0;
|
|
52290
|
+
let degraded = false;
|
|
52375
52291
|
function parseLine(line) {
|
|
52376
52292
|
const s = line.trim();
|
|
52377
52293
|
if (!s)
|
|
@@ -52439,9 +52355,33 @@ function createInboundSpool(opts) {
|
|
|
52439
52355
|
try {
|
|
52440
52356
|
fs2.appendFileSync(path, JSON.stringify(rec) + `
|
|
52441
52357
|
`);
|
|
52358
|
+
if (consecutiveAppendFailures > 0) {
|
|
52359
|
+
log(`inbound-spool: append recovered path=${path} after ` + `${consecutiveAppendFailures} failure(s) \u2014 durability restored
|
|
52360
|
+
`);
|
|
52361
|
+
consecutiveAppendFailures = 0;
|
|
52362
|
+
if (degraded) {
|
|
52363
|
+
degraded = false;
|
|
52364
|
+
try {
|
|
52365
|
+
opts.onDegraded?.({ degraded: false, consecutiveFailures: 0, path });
|
|
52366
|
+
} catch {}
|
|
52367
|
+
}
|
|
52368
|
+
}
|
|
52442
52369
|
} catch (err) {
|
|
52443
|
-
|
|
52370
|
+
consecutiveAppendFailures++;
|
|
52371
|
+
const message = err.message;
|
|
52372
|
+
log(`inbound-spool: append FAILED path=${path} id=${rec.id} t=${rec.t}: ` + `${message} \u2014 durability degraded to in-memory ` + `(consecutive failures=${consecutiveAppendFailures})
|
|
52444
52373
|
`);
|
|
52374
|
+
if (!degraded) {
|
|
52375
|
+
degraded = true;
|
|
52376
|
+
try {
|
|
52377
|
+
opts.onDegraded?.({
|
|
52378
|
+
degraded: true,
|
|
52379
|
+
consecutiveFailures: consecutiveAppendFailures,
|
|
52380
|
+
path,
|
|
52381
|
+
error: message
|
|
52382
|
+
});
|
|
52383
|
+
} catch {}
|
|
52384
|
+
}
|
|
52445
52385
|
}
|
|
52446
52386
|
}
|
|
52447
52387
|
function maybeCompact() {
|
|
@@ -52531,11 +52471,18 @@ function createInboundSpool(opts) {
|
|
|
52531
52471
|
sweepEscalations(onEscalate) {
|
|
52532
52472
|
const tNow = now();
|
|
52533
52473
|
const cutoff = tNow - escalateAfterMs;
|
|
52534
|
-
|
|
52535
|
-
|
|
52536
|
-
for (const [id, e] of
|
|
52474
|
+
const toDrop = [];
|
|
52475
|
+
const perChatCount = new Map;
|
|
52476
|
+
for (const [id, e] of live.entries()) {
|
|
52537
52477
|
if (e.firstAt > cutoff)
|
|
52538
52478
|
continue;
|
|
52479
|
+
toDrop.push({ id, e });
|
|
52480
|
+
const key = escChatKey(e.msg);
|
|
52481
|
+
perChatCount.set(key, (perChatCount.get(key) ?? 0) + 1);
|
|
52482
|
+
}
|
|
52483
|
+
let dropped = 0;
|
|
52484
|
+
let posted = 0;
|
|
52485
|
+
for (const { id, e } of toDrop) {
|
|
52539
52486
|
live.delete(id);
|
|
52540
52487
|
appendRecord({ t: "ack", id });
|
|
52541
52488
|
const key = escChatKey(e.msg);
|
|
@@ -52546,7 +52493,7 @@ function createInboundSpool(opts) {
|
|
|
52546
52493
|
const thread = typeof threadRaw === "string" && threadRaw.length > 0 ? threadRaw : undefined;
|
|
52547
52494
|
appendRecord({ t: "esc", chat: e.msg.chatId, thread, at: tNow });
|
|
52548
52495
|
try {
|
|
52549
|
-
onEscalate({ agent: e.agent, msg: e.msg }, { postNotice });
|
|
52496
|
+
onEscalate({ agent: e.agent, msg: e.msg }, { postNotice, droppedCount: perChatCount.get(key) ?? 1 });
|
|
52550
52497
|
} catch (err) {
|
|
52551
52498
|
log(`inbound-spool: onEscalate threw id=${id}: ${err.message}
|
|
52552
52499
|
`);
|
|
@@ -52565,6 +52512,12 @@ function createInboundSpool(opts) {
|
|
|
52565
52512
|
},
|
|
52566
52513
|
liveCount() {
|
|
52567
52514
|
return live.size;
|
|
52515
|
+
},
|
|
52516
|
+
isDegraded() {
|
|
52517
|
+
return degraded;
|
|
52518
|
+
},
|
|
52519
|
+
appendFailureCount() {
|
|
52520
|
+
return consecutiveAppendFailures;
|
|
52568
52521
|
}
|
|
52569
52522
|
};
|
|
52570
52523
|
}
|
|
@@ -52931,12 +52884,23 @@ function createDeliveryQueue() {
|
|
|
52931
52884
|
function trackDelivery(q, key, inbound, now, messageId = null) {
|
|
52932
52885
|
q.pending.set(key, { key, inbound, messageId, lastAttemptAt: now });
|
|
52933
52886
|
}
|
|
52934
|
-
function
|
|
52887
|
+
function extractEnqueueMessageIds(content) {
|
|
52888
|
+
const ids = [];
|
|
52889
|
+
const re = /(?:^|[\s"'])message_id="([^"]+)"/g;
|
|
52890
|
+
let m;
|
|
52891
|
+
while ((m = re.exec(content)) != null)
|
|
52892
|
+
ids.push(m[1]);
|
|
52893
|
+
return ids;
|
|
52894
|
+
}
|
|
52895
|
+
function ackDelivery(q, key, enqueueMessageId = null, enqueueContent = null) {
|
|
52935
52896
|
const entry = q.pending.get(key);
|
|
52936
52897
|
if (!entry)
|
|
52937
52898
|
return false;
|
|
52938
|
-
if (entry.messageId != null
|
|
52939
|
-
|
|
52899
|
+
if (entry.messageId != null) {
|
|
52900
|
+
const matches = entry.messageId === enqueueMessageId || enqueueContent != null && extractEnqueueMessageIds(enqueueContent).includes(entry.messageId);
|
|
52901
|
+
if (!matches)
|
|
52902
|
+
return false;
|
|
52903
|
+
}
|
|
52940
52904
|
q.pending.delete(key);
|
|
52941
52905
|
return true;
|
|
52942
52906
|
}
|
|
@@ -52950,6 +52914,13 @@ function sweep(q, now, timeoutMs) {
|
|
|
52950
52914
|
}
|
|
52951
52915
|
return redeliver;
|
|
52952
52916
|
}
|
|
52917
|
+
function isRedeliverySuspended(key, suspended) {
|
|
52918
|
+
if (suspended.keys.has(key))
|
|
52919
|
+
return true;
|
|
52920
|
+
const idx = key.indexOf(":");
|
|
52921
|
+
const chatId = idx < 0 ? key : key.slice(0, idx);
|
|
52922
|
+
return suspended.chats.has(chatId);
|
|
52923
|
+
}
|
|
52953
52924
|
function forgetDelivery(q, key) {
|
|
52954
52925
|
q.pending.delete(key);
|
|
52955
52926
|
}
|
|
@@ -53426,6 +53397,27 @@ function dispatchOne(effect, ctx) {
|
|
|
53426
53397
|
}
|
|
53427
53398
|
}
|
|
53428
53399
|
|
|
53400
|
+
// gateway/gate-parity-probe.ts
|
|
53401
|
+
function gateParityDivergence(machineInTurn, busyKeysSize) {
|
|
53402
|
+
const busy = busyKeysSize > 0;
|
|
53403
|
+
if (machineInTurn === busy)
|
|
53404
|
+
return "none";
|
|
53405
|
+
if (!machineInTurn && busy)
|
|
53406
|
+
return "busykeys_dangle";
|
|
53407
|
+
return "machine_over_holds";
|
|
53408
|
+
}
|
|
53409
|
+
function isDangerousGateDivergence(d) {
|
|
53410
|
+
return d === "machine_over_holds";
|
|
53411
|
+
}
|
|
53412
|
+
function probeGateParity(machineInTurn, busyKeysSize, log = (line) => process.stderr.write(line)) {
|
|
53413
|
+
const d = gateParityDivergence(machineInTurn, busyKeysSize);
|
|
53414
|
+
if (isDangerousGateDivergence(d)) {
|
|
53415
|
+
log(`gw-trace gate-drift kind=${d} machineInTurn=${machineInTurn} ` + `busyKeys=${busyKeysSize} note=machine-authoritative-gate-holds-while-imperative-idle
|
|
53416
|
+
`);
|
|
53417
|
+
}
|
|
53418
|
+
return machineInTurn;
|
|
53419
|
+
}
|
|
53420
|
+
|
|
53429
53421
|
// gateway/prefix-warmup.ts
|
|
53430
53422
|
var WARMUP_COOLDOWN_MS = 5 * 60000;
|
|
53431
53423
|
var lastWarmupAtPerAgent = new Map;
|
|
@@ -53686,11 +53678,11 @@ function buildSkillProposalApplyInbound(opts) {
|
|
|
53686
53678
|
// ../src/self-improve/skill-proposals.ts
|
|
53687
53679
|
import {
|
|
53688
53680
|
closeSync as closeSync3,
|
|
53689
|
-
existsSync as
|
|
53681
|
+
existsSync as existsSync28,
|
|
53690
53682
|
mkdirSync as mkdirSync20,
|
|
53691
53683
|
openSync as openSync3,
|
|
53692
53684
|
readFileSync as readFileSync22,
|
|
53693
|
-
writeSync as
|
|
53685
|
+
writeSync as writeSync3
|
|
53694
53686
|
} from "node:fs";
|
|
53695
53687
|
import { join as join25 } from "node:path";
|
|
53696
53688
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
@@ -53705,21 +53697,21 @@ function rejectedPath(stateDir) {
|
|
|
53705
53697
|
return join25(stateDir, REJECTED_FILE);
|
|
53706
53698
|
}
|
|
53707
53699
|
function ensureDir2(stateDir) {
|
|
53708
|
-
if (!
|
|
53700
|
+
if (!existsSync28(stateDir)) {
|
|
53709
53701
|
mkdirSync20(stateDir, { recursive: true, mode: 493 });
|
|
53710
53702
|
}
|
|
53711
53703
|
}
|
|
53712
53704
|
function appendLine(path, obj) {
|
|
53713
53705
|
const fd = openSync3(path, "a");
|
|
53714
53706
|
try {
|
|
53715
|
-
|
|
53707
|
+
writeSync3(fd, JSON.stringify(obj) + `
|
|
53716
53708
|
`);
|
|
53717
53709
|
} finally {
|
|
53718
53710
|
closeSync3(fd);
|
|
53719
53711
|
}
|
|
53720
53712
|
}
|
|
53721
53713
|
function readLines(path, isValid2) {
|
|
53722
|
-
if (!
|
|
53714
|
+
if (!existsSync28(path))
|
|
53723
53715
|
return [];
|
|
53724
53716
|
let raw;
|
|
53725
53717
|
try {
|
|
@@ -54352,10 +54344,10 @@ function escapeBody2(s) {
|
|
|
54352
54344
|
}
|
|
54353
54345
|
|
|
54354
54346
|
// gateway/pid-file.ts
|
|
54355
|
-
import { writeFileSync as
|
|
54347
|
+
import { writeFileSync as writeFileSync16, readFileSync as readFileSync23, unlinkSync as unlinkSync9, renameSync as renameSync8 } from "node:fs";
|
|
54356
54348
|
function writePidFile(path, record) {
|
|
54357
54349
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
54358
|
-
|
|
54350
|
+
writeFileSync16(tmp, JSON.stringify(record), "utf-8");
|
|
54359
54351
|
renameSync8(tmp, path);
|
|
54360
54352
|
}
|
|
54361
54353
|
function clearPidFile(path) {
|
|
@@ -54577,10 +54569,10 @@ function safeCount(fn) {
|
|
|
54577
54569
|
}
|
|
54578
54570
|
|
|
54579
54571
|
// gateway/session-marker.ts
|
|
54580
|
-
import { writeFileSync as
|
|
54572
|
+
import { writeFileSync as writeFileSync17, readFileSync as readFileSync25, renameSync as renameSync9, unlinkSync as unlinkSync10 } from "node:fs";
|
|
54581
54573
|
function writeSessionMarker(path, marker) {
|
|
54582
54574
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
54583
|
-
|
|
54575
|
+
writeFileSync17(tmp, JSON.stringify(marker), "utf-8");
|
|
54584
54576
|
renameSync9(tmp, path);
|
|
54585
54577
|
}
|
|
54586
54578
|
function readSessionMarker(path) {
|
|
@@ -54607,11 +54599,11 @@ function shouldFireRestartBanner(input) {
|
|
|
54607
54599
|
}
|
|
54608
54600
|
|
|
54609
54601
|
// gateway/clean-shutdown-marker.ts
|
|
54610
|
-
import { writeFileSync as
|
|
54602
|
+
import { writeFileSync as writeFileSync18, readFileSync as readFileSync26, renameSync as renameSync10, unlinkSync as unlinkSync11 } from "node:fs";
|
|
54611
54603
|
var DEFAULT_MAX_AGE_MS = 60000;
|
|
54612
54604
|
function writeCleanShutdownMarker(path, marker) {
|
|
54613
54605
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
54614
|
-
|
|
54606
|
+
writeFileSync18(tmp, JSON.stringify(marker), "utf-8");
|
|
54615
54607
|
renameSync10(tmp, path);
|
|
54616
54608
|
}
|
|
54617
54609
|
function readCleanShutdownMarker(path) {
|
|
@@ -55207,7 +55199,7 @@ function classifyAdminGate(text, myAgentName) {
|
|
|
55207
55199
|
|
|
55208
55200
|
// subagent-watcher.ts
|
|
55209
55201
|
import {
|
|
55210
|
-
existsSync as
|
|
55202
|
+
existsSync as existsSync30,
|
|
55211
55203
|
openSync as openSync5,
|
|
55212
55204
|
readSync,
|
|
55213
55205
|
statSync as statSync9,
|
|
@@ -55408,7 +55400,12 @@ function projectSubagentLine(line, agentId, state4) {
|
|
|
55408
55400
|
if (ct === "tool_use") {
|
|
55409
55401
|
const name = c.name ?? "";
|
|
55410
55402
|
if (name === "Agent" || name === "Task") {
|
|
55411
|
-
events.push({
|
|
55403
|
+
events.push({
|
|
55404
|
+
kind: "sub_agent_nested_spawn",
|
|
55405
|
+
agentId,
|
|
55406
|
+
toolUseId: c.id ?? null,
|
|
55407
|
+
input: c.input ?? undefined
|
|
55408
|
+
});
|
|
55412
55409
|
} else {
|
|
55413
55410
|
events.push({
|
|
55414
55411
|
kind: "sub_agent_tool_use",
|
|
@@ -55657,24 +55654,43 @@ function bumpSubagentActivity(db2, args) {
|
|
|
55657
55654
|
WHERE id = ?
|
|
55658
55655
|
`).run(args.ts, args.id);
|
|
55659
55656
|
}
|
|
55657
|
+
function recordNestedSubagentDispatch(db2, args) {
|
|
55658
|
+
db2.prepare(`
|
|
55659
|
+
INSERT OR IGNORE INTO subagents
|
|
55660
|
+
(id, parent_session_id, parent_turn_key, agent_type, description,
|
|
55661
|
+
background, started_at, last_activity_at, status, jsonl_agent_id,
|
|
55662
|
+
parent_agent_id)
|
|
55663
|
+
VALUES (?, NULL, NULL, ?, ?, ?, ?, ?, 'running', NULL, ?)
|
|
55664
|
+
`).run(args.toolUseId, args.agentType ?? null, args.description ?? null, args.background ? 1 : 0, args.now, args.now, args.parentJsonlAgentId);
|
|
55665
|
+
db2.prepare(`
|
|
55666
|
+
UPDATE subagents
|
|
55667
|
+
SET parent_agent_id = COALESCE(parent_agent_id, ?),
|
|
55668
|
+
parent_turn_key = COALESCE(
|
|
55669
|
+
parent_turn_key,
|
|
55670
|
+
(SELECT p.parent_turn_key FROM subagents p
|
|
55671
|
+
WHERE p.jsonl_agent_id = ? LIMIT 1)
|
|
55672
|
+
)
|
|
55673
|
+
WHERE id = ?
|
|
55674
|
+
`).run(args.parentJsonlAgentId, args.parentJsonlAgentId, args.toolUseId);
|
|
55675
|
+
}
|
|
55660
55676
|
|
|
55661
55677
|
// gateway/turn-active-marker.ts
|
|
55662
55678
|
import {
|
|
55663
55679
|
closeSync as closeSync4,
|
|
55664
|
-
existsSync as
|
|
55680
|
+
existsSync as existsSync29,
|
|
55665
55681
|
mkdirSync as mkdirSync21,
|
|
55666
55682
|
openSync as openSync4,
|
|
55667
55683
|
readFileSync as readFileSync27,
|
|
55668
55684
|
statSync as statSync8,
|
|
55669
55685
|
unlinkSync as unlinkSync12,
|
|
55670
55686
|
utimesSync,
|
|
55671
|
-
writeFileSync as
|
|
55687
|
+
writeFileSync as writeFileSync19
|
|
55672
55688
|
} from "node:fs";
|
|
55673
55689
|
import { join as join26 } from "node:path";
|
|
55674
55690
|
var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
|
|
55675
55691
|
function touchTurnActiveMarker(stateDir) {
|
|
55676
55692
|
const path = join26(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
55677
|
-
if (!
|
|
55693
|
+
if (!existsSync29(path))
|
|
55678
55694
|
return;
|
|
55679
55695
|
const now = new Date;
|
|
55680
55696
|
try {
|
|
@@ -55692,6 +55708,10 @@ var DEFAULT_RESCAN_MS = 1000;
|
|
|
55692
55708
|
var DEFAULT_STALL_THRESHOLD_MS = 60000;
|
|
55693
55709
|
var DEFAULT_SILENT_SYNTHESIS_STALL_THRESHOLD_MS = 300000;
|
|
55694
55710
|
var DEFAULT_SILENT_STALL_TERMINAL_MS = 300000;
|
|
55711
|
+
var LONG_RUNNING_TOOLS = new Set(["Bash"]);
|
|
55712
|
+
function isLongRunningTool(name) {
|
|
55713
|
+
return name != null && LONG_RUNNING_TOOLS.has(name);
|
|
55714
|
+
}
|
|
55695
55715
|
var DEFAULT_INFLIGHT_PROMOTE_MAX_AGE_MS = 15 * 60000;
|
|
55696
55716
|
var SUBAGENT_RESULT_TEXT_MAX = 3000;
|
|
55697
55717
|
function parseEnvMs(varName) {
|
|
@@ -55706,6 +55726,7 @@ function parseEnvMs(varName) {
|
|
|
55706
55726
|
var DEFAULT_REAPER_TTL_MS = 60 * 60000;
|
|
55707
55727
|
var DEFAULT_REAPER_INTERVAL_MS = 15 * 60000;
|
|
55708
55728
|
var TERMINAL_CLEANUP_GRACE_MS = 30000;
|
|
55729
|
+
var BACKFILL_RETRY_INTERVAL_MS = 3000;
|
|
55709
55730
|
function backfillJsonlAgentId(db2, jsonlPath, agentId, log) {
|
|
55710
55731
|
const metaPath = jsonlPath.replace(/\.jsonl$/, ".meta.json");
|
|
55711
55732
|
let meta;
|
|
@@ -55754,14 +55775,38 @@ function backfillJsonlAgentId(db2, jsonlPath, agentId, log) {
|
|
|
55754
55775
|
db2.prepare("UPDATE subagents SET jsonl_agent_id = ? WHERE id = ?").run(agentId, candidateId);
|
|
55755
55776
|
log?.(`subagent-watcher: backfill linked ${agentId} \u2192 ${candidateId}`);
|
|
55756
55777
|
try {
|
|
55757
|
-
const linkedRow = db2.prepare("SELECT started_at, parent_turn_key FROM subagents WHERE id = ?").get(candidateId);
|
|
55778
|
+
const linkedRow = db2.prepare("SELECT started_at, parent_turn_key, parent_agent_id FROM subagents WHERE id = ?").get(candidateId);
|
|
55758
55779
|
if (linkedRow != null && linkedRow.parent_turn_key == null) {
|
|
55759
|
-
|
|
55760
|
-
|
|
55761
|
-
|
|
55762
|
-
|
|
55763
|
-
|
|
55764
|
-
|
|
55780
|
+
let resolvedKey = null;
|
|
55781
|
+
if (linkedRow.parent_agent_id != null && linkedRow.parent_agent_id.length > 0) {
|
|
55782
|
+
const seen = new Set([agentId]);
|
|
55783
|
+
let cursor = linkedRow.parent_agent_id;
|
|
55784
|
+
for (let hop = 0;hop < 5 && cursor != null && !seen.has(cursor); hop++) {
|
|
55785
|
+
seen.add(cursor);
|
|
55786
|
+
const parentRow = db2.prepare("SELECT parent_turn_key, parent_agent_id FROM subagents WHERE jsonl_agent_id = ? LIMIT 1").get(cursor);
|
|
55787
|
+
if (parentRow == null)
|
|
55788
|
+
break;
|
|
55789
|
+
if (parentRow.parent_turn_key != null && parentRow.parent_turn_key.length > 0) {
|
|
55790
|
+
resolvedKey = parentRow.parent_turn_key;
|
|
55791
|
+
break;
|
|
55792
|
+
}
|
|
55793
|
+
cursor = parentRow.parent_agent_id ?? null;
|
|
55794
|
+
}
|
|
55795
|
+
if (resolvedKey != null) {
|
|
55796
|
+
log?.(`subagent-watcher: backfill parent_turn_key ${candidateId} \u2192 ${resolvedKey} (inherited via nested-parent chain)`);
|
|
55797
|
+
}
|
|
55798
|
+
}
|
|
55799
|
+
if (resolvedKey == null) {
|
|
55800
|
+
const turn = db2.prepare(`SELECT turn_key FROM turns
|
|
55801
|
+
WHERE started_at <= ? AND (ended_at IS NULL OR ended_at >= ?)
|
|
55802
|
+
ORDER BY started_at DESC LIMIT 1`).get(linkedRow.started_at, linkedRow.started_at);
|
|
55803
|
+
if (turn?.turn_key != null) {
|
|
55804
|
+
resolvedKey = turn.turn_key;
|
|
55805
|
+
log?.(`subagent-watcher: backfill parent_turn_key ${candidateId} \u2192 ${resolvedKey}`);
|
|
55806
|
+
}
|
|
55807
|
+
}
|
|
55808
|
+
if (resolvedKey != null) {
|
|
55809
|
+
db2.prepare("UPDATE subagents SET parent_turn_key = ? WHERE id = ?").run(resolvedKey, candidateId);
|
|
55765
55810
|
}
|
|
55766
55811
|
}
|
|
55767
55812
|
} catch (err) {
|
|
@@ -55791,6 +55836,14 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
55791
55836
|
const existing = db2.prepare("SELECT id, background FROM subagents WHERE jsonl_agent_id = ?").get(entry.agentId);
|
|
55792
55837
|
if (existing == null) {
|
|
55793
55838
|
log?.(`subagent-watcher: liveness skip ${entry.agentId} \u2014 row not in DB yet (Phase 2 Pre hook pending)`);
|
|
55839
|
+
if (entry.lastBackfillAttemptAt == null || now - entry.lastBackfillAttemptAt >= BACKFILL_RETRY_INTERVAL_MS) {
|
|
55840
|
+
entry.lastBackfillAttemptAt = now;
|
|
55841
|
+
try {
|
|
55842
|
+
backfillJsonlAgentId(db2, entry.filePath, entry.agentId, log);
|
|
55843
|
+
} catch (bfErr) {
|
|
55844
|
+
log?.(`subagent-watcher: backfill retry error ${entry.agentId}: ${bfErr.message}`);
|
|
55845
|
+
}
|
|
55846
|
+
}
|
|
55794
55847
|
} else {
|
|
55795
55848
|
bumpSubagentActivity(db2, { id: existing.id, ts: now });
|
|
55796
55849
|
isForeground = existing.background === 0;
|
|
@@ -55913,6 +55966,23 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
55913
55966
|
}
|
|
55914
55967
|
}
|
|
55915
55968
|
}
|
|
55969
|
+
} else if (ev.kind === "sub_agent_nested_spawn") {
|
|
55970
|
+
if (db2 != null && ev.toolUseId != null && ev.toolUseId.length > 0) {
|
|
55971
|
+
try {
|
|
55972
|
+
const nestedInput = ev.input ?? {};
|
|
55973
|
+
recordNestedSubagentDispatch(db2, {
|
|
55974
|
+
toolUseId: ev.toolUseId,
|
|
55975
|
+
parentJsonlAgentId: entry.agentId,
|
|
55976
|
+
agentType: typeof nestedInput.subagent_type === "string" ? nestedInput.subagent_type : null,
|
|
55977
|
+
description: typeof nestedInput.description === "string" ? nestedInput.description : null,
|
|
55978
|
+
background: nestedInput.run_in_background === true,
|
|
55979
|
+
now
|
|
55980
|
+
});
|
|
55981
|
+
log?.(`subagent-watcher: nested dispatch recorded parent=${entry.agentId} toolUseId=${ev.toolUseId}`);
|
|
55982
|
+
} catch (dbErr) {
|
|
55983
|
+
log?.(`subagent-watcher: nested dispatch record error ${entry.agentId}: ${dbErr.message}`);
|
|
55984
|
+
}
|
|
55985
|
+
}
|
|
55916
55986
|
} else if (ev.kind === "sub_agent_text") {
|
|
55917
55987
|
entry.lastSummaryLine = clipNarrative(ev.text);
|
|
55918
55988
|
entry.lastResultText = ev.text.trim().slice(0, SUBAGENT_RESULT_TEXT_MAX);
|
|
@@ -55984,7 +56054,7 @@ function startSubagentWatcher(config) {
|
|
|
55984
56054
|
clearTimeout(ref.ref);
|
|
55985
56055
|
});
|
|
55986
56056
|
const fs2 = config.fs ?? {
|
|
55987
|
-
existsSync:
|
|
56057
|
+
existsSync: existsSync30,
|
|
55988
56058
|
readdirSync: readdirSync5,
|
|
55989
56059
|
statSync: statSync9,
|
|
55990
56060
|
openSync: openSync5,
|
|
@@ -56064,6 +56134,15 @@ function startSubagentWatcher(config) {
|
|
|
56064
56134
|
log?.(`subagent-watcher: backfill error for ${agentId}: ${err.message}`);
|
|
56065
56135
|
}
|
|
56066
56136
|
}
|
|
56137
|
+
entry.toolCount = 0;
|
|
56138
|
+
entry.lastTool = null;
|
|
56139
|
+
entry.pendingNarrative = null;
|
|
56140
|
+
tail.cursor = 0;
|
|
56141
|
+
tail.pendingPartial = "";
|
|
56142
|
+
tail.hasEmittedStart = false;
|
|
56143
|
+
readSubTail(entry, tail, n, (desc) => {
|
|
56144
|
+
log?.(`subagent-watcher: description updated for ${agentId}: ${desc}`);
|
|
56145
|
+
}, fs2, log, db2, parentStateDir, config.onUnstall, undefined, config.onProgress);
|
|
56067
56146
|
}
|
|
56068
56147
|
}
|
|
56069
56148
|
if (isHistorical && entry.state === "done") {
|
|
@@ -56172,7 +56251,7 @@ function startSubagentWatcher(config) {
|
|
|
56172
56251
|
if (entry.stallNotified)
|
|
56173
56252
|
continue;
|
|
56174
56253
|
const idleMs = n - entry.lastActivityAt;
|
|
56175
|
-
const threshold = entry.toolCount === 0 ? silentSynthesisStallThresholdMs : stallThresholdMs;
|
|
56254
|
+
const threshold = entry.toolCount === 0 || isLongRunningTool(entry.lastTool?.name) ? silentSynthesisStallThresholdMs : stallThresholdMs;
|
|
56176
56255
|
if (idleMs >= threshold) {
|
|
56177
56256
|
entry.stallNotified = true;
|
|
56178
56257
|
entry.stalledAt = n;
|
|
@@ -56448,7 +56527,7 @@ function determineRestartReason(opts) {
|
|
|
56448
56527
|
init_boot_card();
|
|
56449
56528
|
|
|
56450
56529
|
// gateway/update-announce.ts
|
|
56451
|
-
import { existsSync as
|
|
56530
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync25, openSync as openSync6, closeSync as closeSync6, readFileSync as readFileSync34 } from "node:fs";
|
|
56452
56531
|
import { join as join32 } from "node:path";
|
|
56453
56532
|
import { homedir as homedir12 } from "node:os";
|
|
56454
56533
|
|
|
@@ -56577,7 +56656,7 @@ function readAndFilter(raw, filters, limit) {
|
|
|
56577
56656
|
var DEFAULT_LOOKBACK_MS = 10 * 60 * 1000;
|
|
56578
56657
|
function readLastTerminalUpdateAudit(opts = {}) {
|
|
56579
56658
|
const path = opts.auditLogPath ?? defaultAuditLogPath();
|
|
56580
|
-
const exists = opts.exists ??
|
|
56659
|
+
const exists = opts.exists ?? existsSync35;
|
|
56581
56660
|
const readFile = opts.readFile ?? ((p) => readFileSync34(p, "utf-8"));
|
|
56582
56661
|
if (!exists(path))
|
|
56583
56662
|
return null;
|
|
@@ -56667,7 +56746,7 @@ function maybeRenderUpdateAnnouncement(opts = {}) {
|
|
|
56667
56746
|
|
|
56668
56747
|
// issues-card.ts
|
|
56669
56748
|
init_card_format();
|
|
56670
|
-
import { readFileSync as readFileSync35, writeFileSync as
|
|
56749
|
+
import { readFileSync as readFileSync35, writeFileSync as writeFileSync24 } from "node:fs";
|
|
56671
56750
|
var SEVERITY_EMOJI = {
|
|
56672
56751
|
info: "\u2139\ufe0f",
|
|
56673
56752
|
warn: "\u26a0\ufe0f",
|
|
@@ -56775,7 +56854,7 @@ function readPersistedMessageId(path, log) {
|
|
|
56775
56854
|
}
|
|
56776
56855
|
function writePersistedMessageId(path, messageId, log) {
|
|
56777
56856
|
try {
|
|
56778
|
-
|
|
56857
|
+
writeFileSync24(path, JSON.stringify({ messageId }) + `
|
|
56779
56858
|
`, { mode: 384 });
|
|
56780
56859
|
} catch (err) {
|
|
56781
56860
|
log(`issues-card: persist write failed (${err.message})`);
|
|
@@ -56868,13 +56947,13 @@ function createIssuesCardHandle(opts) {
|
|
|
56868
56947
|
}
|
|
56869
56948
|
|
|
56870
56949
|
// issues-watcher.ts
|
|
56871
|
-
import { existsSync as
|
|
56950
|
+
import { existsSync as existsSync37, statSync as statSync11 } from "node:fs";
|
|
56872
56951
|
import { join as join34 } from "node:path";
|
|
56873
56952
|
|
|
56874
56953
|
// ../src/issues/store.ts
|
|
56875
56954
|
import {
|
|
56876
56955
|
closeSync as closeSync7,
|
|
56877
|
-
existsSync as
|
|
56956
|
+
existsSync as existsSync36,
|
|
56878
56957
|
mkdirSync as mkdirSync26,
|
|
56879
56958
|
openSync as openSync7,
|
|
56880
56959
|
readdirSync as readdirSync7,
|
|
@@ -56882,8 +56961,8 @@ import {
|
|
|
56882
56961
|
renameSync as renameSync13,
|
|
56883
56962
|
statSync as statSync10,
|
|
56884
56963
|
unlinkSync as unlinkSync13,
|
|
56885
|
-
writeFileSync as
|
|
56886
|
-
writeSync as
|
|
56964
|
+
writeFileSync as writeFileSync25,
|
|
56965
|
+
writeSync as writeSync4
|
|
56887
56966
|
} from "node:fs";
|
|
56888
56967
|
import { join as join33 } from "node:path";
|
|
56889
56968
|
import { randomBytes as randomBytes6 } from "node:crypto";
|
|
@@ -56905,7 +56984,7 @@ var ISSUES_FILE = "issues.jsonl";
|
|
|
56905
56984
|
var ISSUES_LOCK = "issues.lock";
|
|
56906
56985
|
function readAll(stateDir) {
|
|
56907
56986
|
const path = join33(stateDir, ISSUES_FILE);
|
|
56908
|
-
if (!
|
|
56987
|
+
if (!existsSync36(path))
|
|
56909
56988
|
return [];
|
|
56910
56989
|
let raw;
|
|
56911
56990
|
try {
|
|
@@ -56941,7 +57020,7 @@ function list(stateDir, opts = {}) {
|
|
|
56941
57020
|
});
|
|
56942
57021
|
}
|
|
56943
57022
|
function resolve6(stateDir, fingerprint, nowFn = Date.now) {
|
|
56944
|
-
if (!
|
|
57023
|
+
if (!existsSync36(join33(stateDir, ISSUES_FILE)))
|
|
56945
57024
|
return 0;
|
|
56946
57025
|
return withLock(stateDir, () => {
|
|
56947
57026
|
const all = readAll(stateDir);
|
|
@@ -56965,7 +57044,7 @@ function writeAll(stateDir, events) {
|
|
|
56965
57044
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
56966
57045
|
`) + `
|
|
56967
57046
|
`;
|
|
56968
|
-
|
|
57047
|
+
writeFileSync25(tmp, body, "utf-8");
|
|
56969
57048
|
renameSync13(tmp, path);
|
|
56970
57049
|
}
|
|
56971
57050
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
@@ -57000,7 +57079,7 @@ function withLock(stateDir, fn) {
|
|
|
57000
57079
|
try {
|
|
57001
57080
|
fd = openSync7(lockPath, "wx");
|
|
57002
57081
|
try {
|
|
57003
|
-
|
|
57082
|
+
writeSync4(fd, String(process.pid));
|
|
57004
57083
|
} catch {}
|
|
57005
57084
|
} catch (err) {
|
|
57006
57085
|
const e = err;
|
|
@@ -57126,7 +57205,7 @@ function startIssuesWatcher(opts) {
|
|
|
57126
57205
|
};
|
|
57127
57206
|
}
|
|
57128
57207
|
function defaultSignatureProvider(path) {
|
|
57129
|
-
if (!
|
|
57208
|
+
if (!existsSync37(path))
|
|
57130
57209
|
return null;
|
|
57131
57210
|
try {
|
|
57132
57211
|
const stat = statSync11(path);
|
|
@@ -58127,7 +58206,7 @@ function extractFlowItems(line) {
|
|
|
58127
58206
|
|
|
58128
58207
|
// credits-watch.ts
|
|
58129
58208
|
init_card_format();
|
|
58130
|
-
import { readFileSync as readFileSync37, writeFileSync as
|
|
58209
|
+
import { readFileSync as readFileSync37, writeFileSync as writeFileSync26, existsSync as existsSync38, mkdirSync as mkdirSync27 } from "fs";
|
|
58131
58210
|
import { join as join35 } from "path";
|
|
58132
58211
|
var STATE_FILE = "credits-watch.json";
|
|
58133
58212
|
var DEFAULT_CREDIT_FATAL_REASONS = new Set;
|
|
@@ -58151,7 +58230,7 @@ function emptyCreditState() {
|
|
|
58151
58230
|
}
|
|
58152
58231
|
function readClaudeJsonOverage(claudeConfigDir) {
|
|
58153
58232
|
const path = join35(claudeConfigDir, ".claude.json");
|
|
58154
|
-
if (!
|
|
58233
|
+
if (!existsSync38(path))
|
|
58155
58234
|
return null;
|
|
58156
58235
|
let raw;
|
|
58157
58236
|
try {
|
|
@@ -58234,7 +58313,7 @@ function humanizeReason(reason) {
|
|
|
58234
58313
|
}
|
|
58235
58314
|
function loadCreditState(stateDir) {
|
|
58236
58315
|
const path = join35(stateDir, STATE_FILE);
|
|
58237
|
-
if (!
|
|
58316
|
+
if (!existsSync38(path))
|
|
58238
58317
|
return emptyCreditState();
|
|
58239
58318
|
try {
|
|
58240
58319
|
const raw = readFileSync37(path, "utf-8");
|
|
@@ -58251,12 +58330,12 @@ function loadCreditState(stateDir) {
|
|
|
58251
58330
|
function saveCreditState(stateDir, state4) {
|
|
58252
58331
|
mkdirSync27(stateDir, { recursive: true });
|
|
58253
58332
|
const path = join35(stateDir, STATE_FILE);
|
|
58254
|
-
|
|
58333
|
+
writeFileSync26(path, JSON.stringify(state4, null, 2) + `
|
|
58255
58334
|
`, { mode: 384 });
|
|
58256
58335
|
}
|
|
58257
58336
|
|
|
58258
58337
|
// quota-watch.ts
|
|
58259
|
-
import { readFileSync as readFileSync38, writeFileSync as
|
|
58338
|
+
import { readFileSync as readFileSync38, writeFileSync as writeFileSync27, existsSync as existsSync39, mkdirSync as mkdirSync28 } from "fs";
|
|
58260
58339
|
import { join as join36 } from "path";
|
|
58261
58340
|
init_card_format();
|
|
58262
58341
|
var STATE_FILE2 = "quota-watch.json";
|
|
@@ -58453,7 +58532,7 @@ function buildRecoveryMessage(agentName3, snap) {
|
|
|
58453
58532
|
}
|
|
58454
58533
|
function loadQuotaWatchState(stateDir) {
|
|
58455
58534
|
const path = join36(stateDir, STATE_FILE2);
|
|
58456
|
-
if (!
|
|
58535
|
+
if (!existsSync39(path))
|
|
58457
58536
|
return emptyQuotaWatchState();
|
|
58458
58537
|
try {
|
|
58459
58538
|
const raw = readFileSync38(path, "utf-8");
|
|
@@ -58475,7 +58554,7 @@ function loadQuotaWatchState(stateDir) {
|
|
|
58475
58554
|
function saveQuotaWatchState(stateDir, state4) {
|
|
58476
58555
|
mkdirSync28(stateDir, { recursive: true });
|
|
58477
58556
|
const path = join36(stateDir, STATE_FILE2);
|
|
58478
|
-
|
|
58557
|
+
writeFileSync27(path, JSON.stringify(state4, null, 2) + `
|
|
58479
58558
|
`, { mode: 384 });
|
|
58480
58559
|
}
|
|
58481
58560
|
function patchQuotaWatchState(current, accountLabel, accountState) {
|
|
@@ -58505,27 +58584,27 @@ function maskVaultKey(name) {
|
|
|
58505
58584
|
// gateway/turn-active-marker.ts
|
|
58506
58585
|
import {
|
|
58507
58586
|
closeSync as closeSync8,
|
|
58508
|
-
existsSync as
|
|
58587
|
+
existsSync as existsSync40,
|
|
58509
58588
|
mkdirSync as mkdirSync29,
|
|
58510
58589
|
openSync as openSync8,
|
|
58511
58590
|
readFileSync as readFileSync39,
|
|
58512
58591
|
statSync as statSync12,
|
|
58513
58592
|
unlinkSync as unlinkSync14,
|
|
58514
58593
|
utimesSync as utimesSync2,
|
|
58515
|
-
writeFileSync as
|
|
58594
|
+
writeFileSync as writeFileSync28
|
|
58516
58595
|
} from "node:fs";
|
|
58517
58596
|
import { join as join37 } from "node:path";
|
|
58518
58597
|
var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
|
|
58519
58598
|
function writeTurnActiveMarker(stateDir, marker) {
|
|
58520
58599
|
try {
|
|
58521
58600
|
mkdirSync29(stateDir, { recursive: true });
|
|
58522
|
-
|
|
58601
|
+
writeFileSync28(join37(stateDir, TURN_ACTIVE_MARKER_FILE2), JSON.stringify(marker, null, 2) + `
|
|
58523
58602
|
`, { mode: 384 });
|
|
58524
58603
|
} catch {}
|
|
58525
58604
|
}
|
|
58526
58605
|
function touchTurnActiveMarker2(stateDir) {
|
|
58527
58606
|
const path = join37(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
58528
|
-
if (!
|
|
58607
|
+
if (!existsSync40(path))
|
|
58529
58608
|
return;
|
|
58530
58609
|
const now = new Date;
|
|
58531
58610
|
try {
|
|
@@ -58544,7 +58623,7 @@ function removeTurnActiveMarker(stateDir) {
|
|
|
58544
58623
|
}
|
|
58545
58624
|
function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
58546
58625
|
const path = join37(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
58547
|
-
if (!
|
|
58626
|
+
if (!existsSync40(path))
|
|
58548
58627
|
return false;
|
|
58549
58628
|
const now = opts.now ?? Date.now();
|
|
58550
58629
|
try {
|
|
@@ -58584,11 +58663,11 @@ function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
|
58584
58663
|
}
|
|
58585
58664
|
|
|
58586
58665
|
// ../src/build-info.ts
|
|
58587
|
-
var VERSION = "0.
|
|
58588
|
-
var COMMIT_SHA = "
|
|
58589
|
-
var COMMIT_DATE = "2026-07-
|
|
58590
|
-
var LATEST_PR =
|
|
58591
|
-
var COMMITS_AHEAD_OF_TAG =
|
|
58666
|
+
var VERSION = "0.17.0";
|
|
58667
|
+
var COMMIT_SHA = "6bb8a20";
|
|
58668
|
+
var COMMIT_DATE = "2026-07-05T13:24:36+10:00";
|
|
58669
|
+
var LATEST_PR = 2825;
|
|
58670
|
+
var COMMITS_AHEAD_OF_TAG = 0;
|
|
58592
58671
|
|
|
58593
58672
|
// gateway/boot-version.ts
|
|
58594
58673
|
function formatRelativeAgo(iso) {
|
|
@@ -59630,6 +59709,7 @@ var SCHEMA_SQL = `
|
|
|
59630
59709
|
assistant_reply_preview TEXT,
|
|
59631
59710
|
tool_call_count INTEGER,
|
|
59632
59711
|
interrupt_reason TEXT,
|
|
59712
|
+
resumed_at INTEGER,
|
|
59633
59713
|
created_at INTEGER NOT NULL,
|
|
59634
59714
|
updated_at INTEGER NOT NULL
|
|
59635
59715
|
);
|
|
@@ -59643,12 +59723,15 @@ var PHASE1_MIGRATIONS = [
|
|
|
59643
59723
|
var PHASE2_MIGRATIONS = [
|
|
59644
59724
|
`ALTER TABLE turns ADD COLUMN interrupt_reason TEXT`
|
|
59645
59725
|
];
|
|
59726
|
+
var PHASE3_MIGRATIONS = [
|
|
59727
|
+
`ALTER TABLE turns ADD COLUMN resumed_at INTEGER`
|
|
59728
|
+
];
|
|
59646
59729
|
function applySchema(db2) {
|
|
59647
59730
|
db2.exec("PRAGMA journal_mode = WAL");
|
|
59648
59731
|
db2.exec("PRAGMA synchronous = NORMAL");
|
|
59649
59732
|
db2.exec("PRAGMA busy_timeout = 5000");
|
|
59650
59733
|
db2.exec(SCHEMA_SQL);
|
|
59651
|
-
for (const sql of [...PHASE1_MIGRATIONS, ...PHASE2_MIGRATIONS]) {
|
|
59734
|
+
for (const sql of [...PHASE1_MIGRATIONS, ...PHASE2_MIGRATIONS, ...PHASE3_MIGRATIONS]) {
|
|
59652
59735
|
try {
|
|
59653
59736
|
db2.exec(sql);
|
|
59654
59737
|
} catch (err) {
|
|
@@ -59690,6 +59773,7 @@ function mapRow(row) {
|
|
|
59690
59773
|
assistant_reply_preview: row.assistant_reply_preview,
|
|
59691
59774
|
tool_call_count: row.tool_call_count,
|
|
59692
59775
|
interrupt_reason: row.interrupt_reason,
|
|
59776
|
+
resumed_at: row.resumed_at,
|
|
59693
59777
|
created_at: row.created_at,
|
|
59694
59778
|
updated_at: row.updated_at
|
|
59695
59779
|
};
|
|
@@ -59753,6 +59837,14 @@ var INTERRUPTED_VIA = new Set([
|
|
|
59753
59837
|
"timeout",
|
|
59754
59838
|
"unknown"
|
|
59755
59839
|
]);
|
|
59840
|
+
function markTurnResumed(db2, turnKey, now = Date.now()) {
|
|
59841
|
+
db2.prepare(`
|
|
59842
|
+
UPDATE turns
|
|
59843
|
+
SET resumed_at = ?,
|
|
59844
|
+
updated_at = ?
|
|
59845
|
+
WHERE turn_key = ? AND resumed_at IS NULL
|
|
59846
|
+
`).run(now, now, turnKey);
|
|
59847
|
+
}
|
|
59756
59848
|
function findLatestTurnIfInterrupted(db2) {
|
|
59757
59849
|
const row = db2.prepare(`
|
|
59758
59850
|
SELECT * FROM turns
|
|
@@ -59762,6 +59854,8 @@ function findLatestTurnIfInterrupted(db2) {
|
|
|
59762
59854
|
if (!row)
|
|
59763
59855
|
return null;
|
|
59764
59856
|
const turn = mapRow(row);
|
|
59857
|
+
if (turn.resumed_at != null)
|
|
59858
|
+
return null;
|
|
59765
59859
|
if (turn.ended_at == null)
|
|
59766
59860
|
return turn;
|
|
59767
59861
|
if (turn.ended_via != null && INTERRUPTED_VIA.has(turn.ended_via))
|
|
@@ -59885,7 +59979,8 @@ var SUBAGENTS_SCHEMA_SQL = `
|
|
|
59885
59979
|
ended_at INTEGER,
|
|
59886
59980
|
status TEXT NOT NULL,
|
|
59887
59981
|
result_summary TEXT,
|
|
59888
|
-
jsonl_agent_id TEXT
|
|
59982
|
+
jsonl_agent_id TEXT,
|
|
59983
|
+
parent_agent_id TEXT
|
|
59889
59984
|
);
|
|
59890
59985
|
CREATE INDEX IF NOT EXISTS subagents_turn ON subagents(parent_turn_key);
|
|
59891
59986
|
CREATE INDEX IF NOT EXISTS subagents_status ON subagents(status);
|
|
@@ -59897,6 +59992,10 @@ function applySubagentsSchema(db2) {
|
|
|
59897
59992
|
if (!hasJsonlId) {
|
|
59898
59993
|
db2.exec("ALTER TABLE subagents ADD COLUMN jsonl_agent_id TEXT");
|
|
59899
59994
|
}
|
|
59995
|
+
const hasParentAgentId = cols.some((c) => c.name === "parent_agent_id");
|
|
59996
|
+
if (!hasParentAgentId) {
|
|
59997
|
+
db2.exec("ALTER TABLE subagents ADD COLUMN parent_agent_id TEXT");
|
|
59998
|
+
}
|
|
59900
59999
|
db2.exec("CREATE INDEX IF NOT EXISTS subagents_jsonl_id ON subagents(jsonl_agent_id)");
|
|
59901
60000
|
}
|
|
59902
60001
|
function mapSubagentRow(row) {
|
|
@@ -59912,19 +60011,38 @@ function mapSubagentRow(row) {
|
|
|
59912
60011
|
ended_at: row.ended_at,
|
|
59913
60012
|
status: row.status,
|
|
59914
60013
|
result_summary: row.result_summary,
|
|
59915
|
-
jsonl_agent_id: row.jsonl_agent_id
|
|
60014
|
+
jsonl_agent_id: row.jsonl_agent_id,
|
|
60015
|
+
parent_agent_id: row.parent_agent_id ?? null
|
|
59916
60016
|
};
|
|
59917
60017
|
}
|
|
59918
60018
|
function getSubagentByJsonlId(db2, jsonlAgentId) {
|
|
59919
60019
|
const row = db2.prepare("SELECT * FROM subagents WHERE jsonl_agent_id = ?").get(jsonlAgentId);
|
|
59920
60020
|
return row ? mapSubagentRow(row) : null;
|
|
59921
60021
|
}
|
|
60022
|
+
function resolveSubagentOriginTurnKey(db2, jsonlAgentId, maxHops = 5) {
|
|
60023
|
+
const seen = new Set;
|
|
60024
|
+
let currentJsonlId = jsonlAgentId;
|
|
60025
|
+
for (let hop = 0;hop <= maxHops && currentJsonlId != null; hop++) {
|
|
60026
|
+
if (seen.has(currentJsonlId))
|
|
60027
|
+
return null;
|
|
60028
|
+
seen.add(currentJsonlId);
|
|
60029
|
+
const row = db2.prepare("SELECT parent_turn_key, parent_agent_id FROM subagents WHERE jsonl_agent_id = ? LIMIT 1").get(currentJsonlId);
|
|
60030
|
+
if (row == null)
|
|
60031
|
+
return null;
|
|
60032
|
+
if (row.parent_turn_key != null && row.parent_turn_key.length > 0)
|
|
60033
|
+
return row.parent_turn_key;
|
|
60034
|
+
currentJsonlId = row.parent_agent_id ?? null;
|
|
60035
|
+
}
|
|
60036
|
+
return null;
|
|
60037
|
+
}
|
|
59922
60038
|
|
|
59923
60039
|
// gateway/worker-feed-dispatch.ts
|
|
59924
60040
|
function resolveWorkerFeedDispatch(sub, watcherDescription) {
|
|
59925
60041
|
return {
|
|
59926
60042
|
isBackground: sub?.background ?? false,
|
|
59927
|
-
feedDescription: (sub?.description ?? "") || watcherDescription
|
|
60043
|
+
feedDescription: (sub?.description ?? "") || watcherDescription,
|
|
60044
|
+
hasRow: sub != null,
|
|
60045
|
+
isNested: sub?.parent_agent_id != null
|
|
59928
60046
|
};
|
|
59929
60047
|
}
|
|
59930
60048
|
|
|
@@ -59943,35 +60061,19 @@ function isOrphanSubagentStatusEnabled(envVal) {
|
|
|
59943
60061
|
return envVal !== "0";
|
|
59944
60062
|
}
|
|
59945
60063
|
|
|
59946
|
-
//
|
|
59947
|
-
function
|
|
59948
|
-
|
|
59949
|
-
|
|
59950
|
-
|
|
59951
|
-
|
|
59952
|
-
|
|
59953
|
-
|
|
59954
|
-
|
|
59955
|
-
|
|
59956
|
-
|
|
59957
|
-
|
|
59958
|
-
|
|
59959
|
-
return { agentId: row.jsonl_agent_id };
|
|
59960
|
-
}
|
|
59961
|
-
const turnRow = opts.db.prepare("SELECT turn_key FROM turns WHERE chat_id = ? AND ended_at IS NULL ORDER BY started_at DESC LIMIT 1").get(opts.chatId);
|
|
59962
|
-
if (turnRow?.turn_key == null)
|
|
59963
|
-
return null;
|
|
59964
|
-
const candidates = opts.db.prepare("SELECT jsonl_agent_id FROM subagents WHERE parent_turn_key = ? AND status = 'running' AND jsonl_agent_id IS NOT NULL ORDER BY started_at DESC").all(turnRow.turn_key);
|
|
59965
|
-
if (candidates.length === 0)
|
|
59966
|
-
return null;
|
|
59967
|
-
if (candidates.length > 1) {
|
|
59968
|
-
console.warn(`progress_update: heuristic resolution selected most-recent of ${candidates.length} running sub-agents (chat=${opts.chatId}); pass agent_id explicitly to avoid mis-attribution`);
|
|
59969
|
-
}
|
|
59970
|
-
return { agentId: candidates[0].jsonl_agent_id };
|
|
59971
|
-
} catch (err) {
|
|
59972
|
-
console.warn("progress_update: resolveCallingSubagent SQL error", err);
|
|
59973
|
-
return null;
|
|
59974
|
-
}
|
|
60064
|
+
// status-query-telemetry.ts
|
|
60065
|
+
function deriveStatusQueryTelemetry(s) {
|
|
60066
|
+
const idle = !s.turnActive && s.runningWorkers <= 0 && s.workerFeedSize <= 0 && s.pendingAsync <= 0;
|
|
60067
|
+
const stage = idle ? "idle" : s.turnActive ? "turn-active" : "background-work";
|
|
60068
|
+
return { stage, idle };
|
|
60069
|
+
}
|
|
60070
|
+
function formatStatusQueryLine(agentName3, chatId, thread, s, t) {
|
|
60071
|
+
return `telegram gateway: status-query agent=${agentName3} chat_id=${chatId} thread=${thread} ` + `stage=${t.stage} turn_age_s=${s.turnAgeS} running_workers=${s.runningWorkers} ` + `worker_feed=${s.workerFeedSize} pending_async=${s.pendingAsync}
|
|
60072
|
+
`;
|
|
60073
|
+
}
|
|
60074
|
+
function formatStatusQueryUxFailureLine(agentName3, chatId, thread) {
|
|
60075
|
+
return `telegram gateway: ux-failure: status-query agent=${agentName3} chat_id=${chatId} thread=${thread} ` + `stage=idle turn_age_s=-1 running_workers=0 worker_feed=0 pending_async=0
|
|
60076
|
+
`;
|
|
59975
60077
|
}
|
|
59976
60078
|
|
|
59977
60079
|
// gateway/gateway.ts
|
|
@@ -60277,7 +60379,7 @@ function saveAccess(a) {
|
|
|
60277
60379
|
return;
|
|
60278
60380
|
mkdirSync32(STATE_DIR, { recursive: true, mode: 448 });
|
|
60279
60381
|
const tmp = ACCESS_FILE + ".tmp";
|
|
60280
|
-
|
|
60382
|
+
writeFileSync30(tmp, JSON.stringify(a, null, 2) + `
|
|
60281
60383
|
`, { mode: 384 });
|
|
60282
60384
|
renameSync14(tmp, ACCESS_FILE);
|
|
60283
60385
|
}
|
|
@@ -60314,7 +60416,7 @@ try {
|
|
|
60314
60416
|
let markerAgeMs = null;
|
|
60315
60417
|
try {
|
|
60316
60418
|
const markerPath = join40(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
60317
|
-
if (
|
|
60419
|
+
if (existsSync43(markerPath)) {
|
|
60318
60420
|
const st = statSync15(markerPath);
|
|
60319
60421
|
markerAgeMs = Date.now() - st.mtimeMs;
|
|
60320
60422
|
try {
|
|
@@ -60400,13 +60502,13 @@ try {
|
|
|
60400
60502
|
pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
|
|
60401
60503
|
];
|
|
60402
60504
|
const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
|
|
60403
|
-
|
|
60505
|
+
writeFileSync30(pendingEnvTmp, lines.join(`
|
|
60404
60506
|
`) + `
|
|
60405
60507
|
`, { mode: 384 });
|
|
60406
60508
|
renameSync14(pendingEnvTmp, pendingEnvPath);
|
|
60407
60509
|
process.stderr.write(`telegram gateway: pending-turn env written to ${pendingEnvPath} turnKey=${pending2.turn_key} endedVia=${pending2.ended_via ?? "open"}
|
|
60408
60510
|
`);
|
|
60409
|
-
} else if (
|
|
60511
|
+
} else if (existsSync43(pendingEnvPath)) {
|
|
60410
60512
|
rmSync4(pendingEnvPath, { force: true });
|
|
60411
60513
|
process.stderr.write(`telegram gateway: pending-turn env cleared (clean previous shutdown)
|
|
60412
60514
|
`);
|
|
@@ -60424,10 +60526,10 @@ function resolveSubagentOriginChat(agentId) {
|
|
|
60424
60526
|
if (turnsDb == null)
|
|
60425
60527
|
return null;
|
|
60426
60528
|
try {
|
|
60427
|
-
const
|
|
60428
|
-
if (
|
|
60529
|
+
const originKey = resolveSubagentOriginTurnKey(turnsDb, agentId);
|
|
60530
|
+
if (originKey == null)
|
|
60429
60531
|
return null;
|
|
60430
|
-
const turn = getTurnByKey(turnsDb,
|
|
60532
|
+
const turn = getTurnByKey(turnsDb, originKey);
|
|
60431
60533
|
if (turn == null || turn.chat_id.length === 0)
|
|
60432
60534
|
return null;
|
|
60433
60535
|
const threadNum = turn.thread_id != null && turn.thread_id.length > 0 ? Number(turn.thread_id) : NaN;
|
|
@@ -60506,6 +60608,7 @@ var chatAvailableReactions = new Map;
|
|
|
60506
60608
|
var chatProbesInFlight = new Set;
|
|
60507
60609
|
var activeTurnStartedAt = new Map;
|
|
60508
60610
|
var claudeBusyKeys = new Set;
|
|
60611
|
+
var claudeBusyKeySince = new Map;
|
|
60509
60612
|
var reactionTransitionCounts = new Map;
|
|
60510
60613
|
var firstTextReplyLogged = new Set;
|
|
60511
60614
|
function markClaudeBusyForInbound(m) {
|
|
@@ -60516,9 +60619,20 @@ function markClaudeBusyForInbound(m) {
|
|
|
60516
60619
|
tid = n;
|
|
60517
60620
|
}
|
|
60518
60621
|
const key = chatKey2(m.chatId, tid);
|
|
60519
|
-
claudeBusyKeys.
|
|
60622
|
+
markBusyKeyLockstep(claudeBusyKeys, claudeBusyKeySince, key, Date.now());
|
|
60520
60623
|
return key;
|
|
60521
60624
|
}
|
|
60625
|
+
var _busyOrphanTtlRaw = process.env.SWITCHROOM_BUSY_ORPHAN_TTL_MS;
|
|
60626
|
+
var _busyOrphanTtlParsed = _busyOrphanTtlRaw != null && _busyOrphanTtlRaw !== "" ? Number(_busyOrphanTtlRaw) : 360000;
|
|
60627
|
+
var CLAUDE_BUSY_ORPHAN_TTL_MS = Number.isFinite(_busyOrphanTtlParsed) && _busyOrphanTtlParsed > 0 ? _busyOrphanTtlParsed : 360000;
|
|
60628
|
+
function reapOrphanBusyKeysNow(now) {
|
|
60629
|
+
reapOrphanBusyKeys(claudeBusyKeys, claudeBusyKeySince, now, {
|
|
60630
|
+
ttlMs: CLAUDE_BUSY_ORPHAN_TTL_MS,
|
|
60631
|
+
hasPendingDelivery: (key) => deliveryQueue.pending.has(key),
|
|
60632
|
+
log: (msg) => process.stderr.write(`${msg}
|
|
60633
|
+
`)
|
|
60634
|
+
});
|
|
60635
|
+
}
|
|
60522
60636
|
var DELIVERY_CONFIRM_ENABLED = process.env.SWITCHROOM_INBOUND_DELIVERY_CONFIRM !== "0";
|
|
60523
60637
|
var _deliveryTimeoutRaw = process.env.SWITCHROOM_INBOUND_DELIVERY_TIMEOUT_MS;
|
|
60524
60638
|
var _deliveryTimeoutParsed = _deliveryTimeoutRaw != null && _deliveryTimeoutRaw !== "" ? Number(_deliveryTimeoutRaw) : 15000;
|
|
@@ -60572,9 +60686,9 @@ function noteAgentOutputAt(key, ts) {
|
|
|
60572
60686
|
var OBLIGATION_STORE_PATH = join40(STATE_DIR, "obligations.json");
|
|
60573
60687
|
var obligationStoreFs = {
|
|
60574
60688
|
readFileSync: (p) => readFileSync41(p, "utf8"),
|
|
60575
|
-
writeFileSync: (p, d) =>
|
|
60689
|
+
writeFileSync: (p, d) => writeFileSync30(p, d),
|
|
60576
60690
|
renameSync: (a, b) => renameSync14(a, b),
|
|
60577
|
-
existsSync: (p) =>
|
|
60691
|
+
existsSync: (p) => existsSync43(p)
|
|
60578
60692
|
};
|
|
60579
60693
|
var obligationLedger = new ObligationLedger(OBLIGATION_REPRESENT_MAX, {
|
|
60580
60694
|
onChange: STATIC || !OBLIGATION_LEDGER_ENABLED ? undefined : (snapshot) => persistObligations(OBLIGATION_STORE_PATH, obligationStoreFs, snapshot)
|
|
@@ -60608,15 +60722,10 @@ var FEED_LIVENESS_OPEN_MS = (() => {
|
|
|
60608
60722
|
return Number.isFinite(n) && n > 0 ? n : 1200;
|
|
60609
60723
|
})();
|
|
60610
60724
|
var POST_ANSWER_LIVENESS_STALE_MS = parsePostAnswerLivenessMs(process.env.SWITCHROOM_POST_ANSWER_LIVENESS_STALE_MS) || 30000;
|
|
60611
|
-
function formatFeedElapsed3(ms) {
|
|
60612
|
-
const s = Math.floor(ms / 1000);
|
|
60613
|
-
if (s < 60)
|
|
60614
|
-
return `${s}s`;
|
|
60615
|
-
const m = Math.floor(s / 60);
|
|
60616
|
-
return `${m}m${(s % 60).toString().padStart(2, "0")}s`;
|
|
60617
|
-
}
|
|
60618
60725
|
function turnInFlightForGate() {
|
|
60619
|
-
|
|
60726
|
+
if (!isDeliveryCutoverEnabled())
|
|
60727
|
+
return claudeBusyKeys.size > 0;
|
|
60728
|
+
return probeGateParity(isMachineInTurn(), claudeBusyKeys.size);
|
|
60620
60729
|
}
|
|
60621
60730
|
function deliverResumeSyntheticOrBuffer(agent, inbound) {
|
|
60622
60731
|
const decision = decideInboundDelivery({
|
|
@@ -60936,7 +61045,7 @@ var pendingPtyPartial = null;
|
|
|
60936
61045
|
function statusKey(chatId, threadId) {
|
|
60937
61046
|
return chatKey2(chatId, threadId);
|
|
60938
61047
|
}
|
|
60939
|
-
function
|
|
61048
|
+
function streamKey2(chatId, threadId) {
|
|
60940
61049
|
return chatKey2(chatId, threadId);
|
|
60941
61050
|
}
|
|
60942
61051
|
var pendingCrossTurnGate = new Map;
|
|
@@ -61002,6 +61111,7 @@ function purgeReactionTracking(key, endingTurn) {
|
|
|
61002
61111
|
reapQueuedStatus(pqChatId, Number.isFinite(pqThread) ? pqThread : undefined);
|
|
61003
61112
|
}
|
|
61004
61113
|
claudeBusyKeys.delete(key);
|
|
61114
|
+
claudeBusyKeySince.delete(key);
|
|
61005
61115
|
reactionTransitionCounts.delete(key);
|
|
61006
61116
|
firstTextReplyLogged.delete(key);
|
|
61007
61117
|
if (endingTurn != null) {
|
|
@@ -61064,6 +61174,7 @@ function releaseTurnBufferGate(key, endingTurn) {
|
|
|
61064
61174
|
return;
|
|
61065
61175
|
activeTurnStartedAt.delete(key);
|
|
61066
61176
|
claudeBusyKeys.delete(key);
|
|
61177
|
+
claudeBusyKeySince.delete(key);
|
|
61067
61178
|
shadowEmit({ kind: "turnEnd", key, at: Date.now(), outboundEmitted: true });
|
|
61068
61179
|
drainBufferedIfAllowed(endingTurn, "reply-released-gate");
|
|
61069
61180
|
}
|
|
@@ -62024,24 +62135,12 @@ var statusPinChatIds = new Map;
|
|
|
62024
62135
|
var STATUS_PIN_STORE_PATH = join40(STATE_DIR, "status-pins.json");
|
|
62025
62136
|
var statusPinStoreFs = {
|
|
62026
62137
|
readFileSync: (p) => readFileSync41(p, "utf8"),
|
|
62027
|
-
writeFileSync: (p, d) =>
|
|
62138
|
+
writeFileSync: (p, d) => writeFileSync30(p, d),
|
|
62028
62139
|
renameSync: (a, b) => renameSync14(a, b),
|
|
62029
|
-
existsSync: (p) =>
|
|
62140
|
+
existsSync: (p) => existsSync43(p)
|
|
62030
62141
|
};
|
|
62031
62142
|
var statusPinPersistEnabled = !STATIC && PIN_STATUS_WHILE_WORKING;
|
|
62032
|
-
|
|
62033
|
-
const snapshot = [];
|
|
62034
|
-
for (const [pinKey, state4] of statusPinState) {
|
|
62035
|
-
const chatId = statusPinChatIds.get(pinKey);
|
|
62036
|
-
if (chatId == null)
|
|
62037
|
-
continue;
|
|
62038
|
-
snapshot.push({ pinKey, chatId, messageId: state4.messageId });
|
|
62039
|
-
}
|
|
62040
|
-
return snapshot;
|
|
62041
|
-
}
|
|
62042
|
-
function snapshotStatusPinsExcept(exceptKey) {
|
|
62043
|
-
return snapshotStatusPins().filter((p) => p.pinKey !== exceptKey);
|
|
62044
|
-
}
|
|
62143
|
+
var bannerPinPersistEnabled = !STATIC;
|
|
62045
62144
|
function statusPinApi() {
|
|
62046
62145
|
return {
|
|
62047
62146
|
pinChatMessage: (chat_id, message_id, opts) => robustApiCall(() => lockedBot.api.pinChatMessage(chat_id, message_id, opts), { chat_id: String(chat_id), verb: "status-pin.pin" }),
|
|
@@ -62049,7 +62148,7 @@ function statusPinApi() {
|
|
|
62049
62148
|
};
|
|
62050
62149
|
}
|
|
62051
62150
|
async function statusPinBootCleanup() {
|
|
62052
|
-
if (!statusPinPersistEnabled)
|
|
62151
|
+
if (!statusPinPersistEnabled && !bannerPinPersistEnabled)
|
|
62053
62152
|
return;
|
|
62054
62153
|
const api = statusPinApi();
|
|
62055
62154
|
const { cleared, total } = await runStatusPinBootCleanup({
|
|
@@ -62107,7 +62206,6 @@ async function reconcileStatusPinInner(pinKey, chatId, desired) {
|
|
|
62107
62206
|
pinKey,
|
|
62108
62207
|
chatId,
|
|
62109
62208
|
op,
|
|
62110
|
-
snapshotOthers: () => snapshotStatusPinsExcept(pinKey),
|
|
62111
62209
|
applyPin: runReconcile
|
|
62112
62210
|
});
|
|
62113
62211
|
if (next == null) {
|
|
@@ -62459,14 +62557,35 @@ async function redeliverStrandedInbound(p) {
|
|
|
62459
62557
|
forgetDelivery(deliveryQueue, p.key);
|
|
62460
62558
|
}
|
|
62461
62559
|
}
|
|
62560
|
+
function sweepSuspendedTargets() {
|
|
62561
|
+
const keys = new Set;
|
|
62562
|
+
const chats = new Set;
|
|
62563
|
+
for (const p of pendingPermissions.values()) {
|
|
62564
|
+
for (const c of p.cards) {
|
|
62565
|
+
if (c.threadId != null)
|
|
62566
|
+
keys.add(chatKey2(c.chatId, c.threadId));
|
|
62567
|
+
else
|
|
62568
|
+
chats.add(c.chatId);
|
|
62569
|
+
}
|
|
62570
|
+
}
|
|
62571
|
+
for (const a of pendingAskUser.values()) {
|
|
62572
|
+
if (a.threadId != null)
|
|
62573
|
+
keys.add(chatKey2(a.chatId, a.threadId));
|
|
62574
|
+
else
|
|
62575
|
+
chats.add(a.chatId);
|
|
62576
|
+
}
|
|
62577
|
+
return { keys, chats };
|
|
62578
|
+
}
|
|
62462
62579
|
var _deliveryConfirmSweep = setInterval(() => {
|
|
62463
62580
|
if (!DELIVERY_CONFIRM_ENABLED)
|
|
62464
62581
|
return;
|
|
62465
62582
|
if (currentTurn != null)
|
|
62466
62583
|
return;
|
|
62467
|
-
|
|
62468
|
-
|
|
62584
|
+
reapOrphanBusyKeysNow(Date.now());
|
|
62585
|
+
const suspended = sweepSuspendedTargets();
|
|
62469
62586
|
for (const p of sweep(deliveryQueue, Date.now(), DELIVERY_CONFIRM_TIMEOUT_MS)) {
|
|
62587
|
+
if (isRedeliverySuspended(p.key, suspended))
|
|
62588
|
+
continue;
|
|
62470
62589
|
redeliverStrandedInbound(p);
|
|
62471
62590
|
}
|
|
62472
62591
|
}, DELIVERY_CONFIRM_SWEEP_MS);
|
|
@@ -62490,13 +62609,33 @@ var inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
|
62490
62609
|
fs: {
|
|
62491
62610
|
appendFileSync: (p, d) => appendFileSync6(p, d),
|
|
62492
62611
|
readFileSync: (p) => readFileSync41(p, "utf8"),
|
|
62493
|
-
writeFileSync: (p, d) =>
|
|
62612
|
+
writeFileSync: (p, d) => writeFileSync30(p, d),
|
|
62494
62613
|
renameSync: (a, b) => renameSync14(a, b),
|
|
62495
|
-
existsSync: (p) =>
|
|
62614
|
+
existsSync: (p) => existsSync43(p),
|
|
62496
62615
|
statSizeSync: (p) => statSync15(p).size
|
|
62616
|
+
},
|
|
62617
|
+
onDegraded: (info) => {
|
|
62618
|
+
process.stderr.write(`telegram gateway: inbound-spool durability ${info.degraded ? "DEGRADED to in-memory-only" : "RECOVERED"} path=${info.path} consecutiveFailures=${info.consecutiveFailures}${info.error != null ? ` error=${info.error}` : ""}
|
|
62619
|
+
`);
|
|
62620
|
+
}
|
|
62621
|
+
});
|
|
62622
|
+
var EVICT_NOTICE_COOLDOWN_MS = 300000;
|
|
62623
|
+
var evictNoticeByChat = new Map;
|
|
62624
|
+
var pendingInboundBuffer = createPendingInboundBuffer({
|
|
62625
|
+
spool: inboundSpool,
|
|
62626
|
+
onEvict: (_agent, evicted) => {
|
|
62627
|
+
const chat = evicted.chatId;
|
|
62628
|
+
const evThread = typeof evicted.meta?.threadId === "string" && evicted.meta.threadId ? Number(evicted.meta.threadId) : undefined;
|
|
62629
|
+
const key = `${chat}:${evThread ?? "-"}`;
|
|
62630
|
+
const last = evictNoticeByChat.get(key);
|
|
62631
|
+
const nowMs2 = Date.now();
|
|
62632
|
+
if (last != null && nowMs2 - last < EVICT_NOTICE_COOLDOWN_MS)
|
|
62633
|
+
return;
|
|
62634
|
+
evictNoticeByChat.set(key, nowMs2);
|
|
62635
|
+
const threadOpts = evThread != null ? { message_thread_id: evThread } : {};
|
|
62636
|
+
swallowingApiCall(() => bot.api.sendMessage(chat, "\u23F3 Messages are arriving faster than I can process them. Your messages are saved and will be handled once I finish the current turn \u2014 if any can't be picked up, I'll ask you to resend it.", { ...threadOpts }), { chat_id: chat, verb: "inbound-buffer-eviction" });
|
|
62497
62637
|
}
|
|
62498
62638
|
});
|
|
62499
|
-
var pendingInboundBuffer = createPendingInboundBuffer({ spool: inboundSpool });
|
|
62500
62639
|
function agentHasInFlightBackgroundWork(now) {
|
|
62501
62640
|
if (countRunningWorkers() > 0)
|
|
62502
62641
|
return true;
|
|
@@ -62546,6 +62685,11 @@ function obligationSweep() {
|
|
|
62546
62685
|
pendingCrossTurnGate.set(o.originTurnId, { sinceMs: o.openedAt });
|
|
62547
62686
|
const attempt = obligationLedger.markRepresented(o.originTurnId);
|
|
62548
62687
|
process.stderr.write(`telegram gateway: obligation re-presented origin=${o.originTurnId} attempt=${attempt}/${OBLIGATION_REPRESENT_MAX}
|
|
62688
|
+
`);
|
|
62689
|
+
return;
|
|
62690
|
+
}
|
|
62691
|
+
if (shouldDeferEscalationForBridge({ bridgeAlive: ipcServer.getClient(agent)?.isAlive() === true })) {
|
|
62692
|
+
process.stderr.write(`telegram gateway: obligation escalation deferred \u2014 bridge down (nudge waits for reconnect) origin=${o.originTurnId}
|
|
62549
62693
|
`);
|
|
62550
62694
|
return;
|
|
62551
62695
|
}
|
|
@@ -62575,6 +62719,15 @@ if (bootResumeInbound != null) {
|
|
|
62575
62719
|
} else {
|
|
62576
62720
|
pendingInboundBuffer.push(bootResumeInbound.agent, bootResumeInbound.msg);
|
|
62577
62721
|
}
|
|
62722
|
+
const resumeTurnKey = bootResumeInbound.msg.meta?.resume_turn_key;
|
|
62723
|
+
if (turnsDb != null && typeof resumeTurnKey === "string" && resumeTurnKey.length > 0) {
|
|
62724
|
+
try {
|
|
62725
|
+
markTurnResumed(turnsDb, resumeTurnKey);
|
|
62726
|
+
} catch (err) {
|
|
62727
|
+
process.stderr.write(`telegram gateway: markTurnResumed failed turnKey=${resumeTurnKey}: ${err.message}
|
|
62728
|
+
`);
|
|
62729
|
+
}
|
|
62730
|
+
}
|
|
62578
62731
|
}
|
|
62579
62732
|
if (inboundSpool != null) {
|
|
62580
62733
|
const replay = inboundSpool.liveEntries();
|
|
@@ -62759,6 +62912,7 @@ var ipcServer = createIpcServer({
|
|
|
62759
62912
|
activeReactionMsgIds,
|
|
62760
62913
|
activeTurnStartedAt,
|
|
62761
62914
|
claudeBusyKeys,
|
|
62915
|
+
claudeBusyKeySince,
|
|
62762
62916
|
activeDraftStreams,
|
|
62763
62917
|
clearActiveReactions: () => {
|
|
62764
62918
|
const ad = resolveAgentDirFromEnv();
|
|
@@ -62800,9 +62954,6 @@ var ipcServer = createIpcServer({
|
|
|
62800
62954
|
if (msg.activeFile)
|
|
62801
62955
|
lastSessionActiveFile = msg.activeFile;
|
|
62802
62956
|
const ev = msg.event;
|
|
62803
|
-
const chatHint = msg.chatId || null;
|
|
62804
|
-
const threadHint = msg.threadId != null ? String(msg.threadId) : undefined;
|
|
62805
|
-
progressDriver?.ingest(ev, chatHint, threadHint);
|
|
62806
62957
|
handleSessionEvent(ev);
|
|
62807
62958
|
toolFlightTracker.onEvent(ev);
|
|
62808
62959
|
if (pendingDeferredInterrupt != null && !toolFlightTracker.isMidToolCall()) {
|
|
@@ -62872,7 +63023,8 @@ var ipcServer = createIpcServer({
|
|
|
62872
63023
|
}), { threadId, chat_id: chatId, verb: "permission_request" }).then((sent) => {
|
|
62873
63024
|
const pend = pendingPermissions.get(requestId);
|
|
62874
63025
|
if (pend && sent && typeof sent.message_id === "number") {
|
|
62875
|
-
|
|
63026
|
+
const landedThreadId = sent.message_thread_id ?? undefined;
|
|
63027
|
+
pend.cards.push({ chatId, messageId: sent.message_id, threadId: landedThreadId });
|
|
62876
63028
|
permCardStore.add({
|
|
62877
63029
|
requestId,
|
|
62878
63030
|
chatId,
|
|
@@ -63383,20 +63535,21 @@ if (!STATIC) {
|
|
|
63383
63535
|
process.stderr.write(`telegram gateway: idle-drain flushed ${r.redelivered}/${r.drained} buffered inbound for ${selfAgent}${r.rebuffered > 0 ? ` (${r.rebuffered} re-buffered)` : ""}
|
|
63384
63536
|
`);
|
|
63385
63537
|
}
|
|
63386
|
-
inboundSpool?.sweepEscalations((e, { postNotice }) => {
|
|
63538
|
+
inboundSpool?.sweepEscalations((e, { postNotice, droppedCount }) => {
|
|
63387
63539
|
const chat = e.msg.chatId;
|
|
63388
63540
|
const escThread = typeof e.msg.meta?.threadId === "string" && e.msg.meta.threadId ? Number(e.msg.meta.threadId) : undefined;
|
|
63389
63541
|
const threadOpts = escThread != null ? { message_thread_id: escThread } : {};
|
|
63390
63542
|
reapQueuedStatus(chat, escThread);
|
|
63391
63543
|
if (!postNotice)
|
|
63392
63544
|
return;
|
|
63393
|
-
|
|
63545
|
+
const n = droppedCount > 0 ? droppedCount : 1;
|
|
63546
|
+
const noticeText = n === 1 ? "\u26A0\uFE0F I couldn't deliver an earlier message to the agent after repeated retries (it survived restarts but the agent never picked it up). Please resend it." : `\u26A0\uFE0F I couldn't deliver ${n} earlier messages to the agent after repeated retries (they survived restarts but the agent never picked them up). Please resend them.`;
|
|
63547
|
+
swallowingApiCall(() => bot.api.sendMessage(chat, noticeText, { ...threadOpts }), { chat_id: chat, verb: "inbound-spool-escalation" });
|
|
63394
63548
|
});
|
|
63395
63549
|
}, IDLE_DRAIN_INTERVAL_MS).unref();
|
|
63396
63550
|
}
|
|
63397
63551
|
var ALLOWED_TOOLS = new Set([
|
|
63398
63552
|
"reply",
|
|
63399
|
-
"stream_reply",
|
|
63400
63553
|
"progress_update",
|
|
63401
63554
|
"react",
|
|
63402
63555
|
"download_attachment",
|
|
@@ -63425,8 +63578,6 @@ async function executeToolCall(tool, args) {
|
|
|
63425
63578
|
switch (tool) {
|
|
63426
63579
|
case "reply":
|
|
63427
63580
|
return executeReply(args);
|
|
63428
|
-
case "stream_reply":
|
|
63429
|
-
return executeStreamReply(args);
|
|
63430
63581
|
case "progress_update":
|
|
63431
63582
|
return executeProgressUpdate(args);
|
|
63432
63583
|
case "react":
|
|
@@ -63895,7 +64046,7 @@ ${url}`;
|
|
|
63895
64046
|
}
|
|
63896
64047
|
}
|
|
63897
64048
|
}
|
|
63898
|
-
const replySKey =
|
|
64049
|
+
const replySKey = streamKey2(chat_id, threadId);
|
|
63899
64050
|
suppressPtyPreview.add(replySKey);
|
|
63900
64051
|
let previewMessageId = null;
|
|
63901
64052
|
const openStream = activeDraftStreams.get(replySKey);
|
|
@@ -64293,221 +64444,6 @@ ${url}`;
|
|
|
64293
64444
|
}
|
|
64294
64445
|
return { content: [{ type: "text", text: result }] };
|
|
64295
64446
|
}
|
|
64296
|
-
async function executeStreamReply(args) {
|
|
64297
|
-
const turn = currentTurn;
|
|
64298
|
-
if (!args.chat_id)
|
|
64299
|
-
throw new Error("stream_reply: chat_id is required");
|
|
64300
|
-
if (args.text == null || args.text === "")
|
|
64301
|
-
throw new Error("stream_reply: text is required and cannot be empty");
|
|
64302
|
-
{
|
|
64303
|
-
const _rawChatId = String(args.chat_id ?? "");
|
|
64304
|
-
const resolved = resolveChatIdFallback(_rawChatId, loadAccess(), turn?.sessionChatId, lastActiveTurnChatId, turn != null);
|
|
64305
|
-
if (resolved.tier !== "raw") {
|
|
64306
|
-
process.stderr.write(`telegram gateway: stream_reply: model passed chat_id "${_rawChatId}" (not allowlisted) \u2014 ` + `routing to ${resolved.tier} turn chat "${resolved.chatId}"
|
|
64307
|
-
`);
|
|
64308
|
-
args.chat_id = resolved.chatId;
|
|
64309
|
-
}
|
|
64310
|
-
}
|
|
64311
|
-
let streamRoutedOriginTurn = null;
|
|
64312
|
-
let streamOriginVia = null;
|
|
64313
|
-
if (TURN_ORIGIN_ROUTING_ENABLED) {
|
|
64314
|
-
const echoedTurn = findTurnByOriginId(args.origin_turn_id);
|
|
64315
|
-
const quotedTurn = echoedTurn == null ? findTurnByQuotedMessageId(String(args.chat_id), args.reply_to) : null;
|
|
64316
|
-
const originTurn = echoedTurn ?? quotedTurn;
|
|
64317
|
-
streamRoutedOriginTurn = originTurn ?? null;
|
|
64318
|
-
streamOriginVia = originTurn == null ? null : echoedTurn != null ? "echo" : "quoted";
|
|
64319
|
-
}
|
|
64320
|
-
if (args.message_thread_id == null) {
|
|
64321
|
-
let injected;
|
|
64322
|
-
if (TURN_ORIGIN_ROUTING_ENABLED) {
|
|
64323
|
-
injected = resolveAnswerThreadWithLog(String(args.chat_id), undefined, streamRoutedOriginTurn, streamOriginVia, turn, "stream_reply");
|
|
64324
|
-
} else {
|
|
64325
|
-
injected = turn?.sessionThreadId;
|
|
64326
|
-
}
|
|
64327
|
-
if (injected != null)
|
|
64328
|
-
args.message_thread_id = String(injected);
|
|
64329
|
-
}
|
|
64330
|
-
args.text = redactOutboundText(args.text, "stream_reply");
|
|
64331
|
-
{
|
|
64332
|
-
args.text = normalizePunctuation(args.text);
|
|
64333
|
-
const scrub = scrubVoice(args.text);
|
|
64334
|
-
if (scrub.replaced > 0) {
|
|
64335
|
-
args.text = scrub.scrubbed;
|
|
64336
|
-
emitRuntimeMetric({
|
|
64337
|
-
kind: "voice_scrub_applied",
|
|
64338
|
-
chatKey: statusKey(String(args.chat_id ?? ""), args.message_thread_id != null ? Number(args.message_thread_id) : undefined),
|
|
64339
|
-
replaced: scrub.replaced,
|
|
64340
|
-
site: "stream_reply"
|
|
64341
|
-
});
|
|
64342
|
-
}
|
|
64343
|
-
}
|
|
64344
|
-
if (args.done === true) {
|
|
64345
|
-
const sChatId = String(args.chat_id ?? "");
|
|
64346
|
-
const sThreadId = args.message_thread_id != null ? Number(args.message_thread_id) : undefined;
|
|
64347
|
-
const sText = args.text;
|
|
64348
|
-
const dup = outboundDedup.check(sChatId, sThreadId, sText, Date.now(), currentTurn?.registryKey ?? null);
|
|
64349
|
-
if (dup != null) {
|
|
64350
|
-
process.stderr.write(`telegram gateway: stream_reply: deduped (#546) chatId=${sChatId} ageMs=${dup.ageMs} preview=${JSON.stringify(dup.preview)}
|
|
64351
|
-
`);
|
|
64352
|
-
return { content: [{ type: "text", text: "sent (deduped \u2014 same content sent via earlier path)" }] };
|
|
64353
|
-
}
|
|
64354
|
-
}
|
|
64355
|
-
const access = loadAccess();
|
|
64356
|
-
const streamChatId = String(args.chat_id ?? "");
|
|
64357
|
-
const streamIsPrivate = isDmChatId(streamChatId);
|
|
64358
|
-
const streamIsForumTopic = args.message_thread_id != null && args.message_thread_id !== "";
|
|
64359
|
-
let streamReplyMarkup;
|
|
64360
|
-
let streamButtonMeta;
|
|
64361
|
-
const rawStreamKeyboard = args.inline_keyboard;
|
|
64362
|
-
if (rawStreamKeyboard != null && Boolean(args.done)) {
|
|
64363
|
-
const validationErrors = validateInlineKeyboard(rawStreamKeyboard);
|
|
64364
|
-
if (validationErrors.length > 0) {
|
|
64365
|
-
const summary = validationErrors.map((e) => `${e.path}.${e.field}: ${e.reason}`).join("; ");
|
|
64366
|
-
throw new Error(`inline_keyboard validation failed: ${summary}`);
|
|
64367
|
-
}
|
|
64368
|
-
streamButtonMeta = extractAgentButtonMeta(rawStreamKeyboard);
|
|
64369
|
-
streamReplyMarkup = { inline_keyboard: wrapAgentCallbacks(rawStreamKeyboard) };
|
|
64370
|
-
}
|
|
64371
|
-
{
|
|
64372
|
-
const streamThreadId = args.message_thread_id != null ? Number(args.message_thread_id) : undefined;
|
|
64373
|
-
const sKeyBefore = streamKey3(streamChatId, streamThreadId);
|
|
64374
|
-
if (!activeDraftStreams.has(sKeyBefore)) {
|
|
64375
|
-
const sKey = statusKey(streamChatId, streamThreadId);
|
|
64376
|
-
noteOutbound(sKey, Date.now());
|
|
64377
|
-
noteOutbound2(sKey, Date.now());
|
|
64378
|
-
shadowEmit({ kind: "modelOutbound", key: sKey, at: Date.now() });
|
|
64379
|
-
if (turn != null && !firstTextReplyLogged.has(sKey)) {
|
|
64380
|
-
firstTextReplyLogged.add(sKey);
|
|
64381
|
-
logStreamingEvent({
|
|
64382
|
-
kind: "turn_reply_timing",
|
|
64383
|
-
chatId: streamChatId,
|
|
64384
|
-
threadId: streamThreadId,
|
|
64385
|
-
turnId: turn.turnId,
|
|
64386
|
-
timeToFirstTextReplyMs: Date.now() - turn.gatewayReceiveAt
|
|
64387
|
-
});
|
|
64388
|
-
}
|
|
64389
|
-
if (isFinalAnswerReply({
|
|
64390
|
-
text: args.text ?? "",
|
|
64391
|
-
disableNotification: args.disable_notification === true,
|
|
64392
|
-
done: args.done === true
|
|
64393
|
-
})) {
|
|
64394
|
-
clearSilentEndState(sKey);
|
|
64395
|
-
}
|
|
64396
|
-
}
|
|
64397
|
-
}
|
|
64398
|
-
if (turn != null && isSubstantiveFinalReply({
|
|
64399
|
-
text: args.text ?? "",
|
|
64400
|
-
disableNotification: args.disable_notification === true,
|
|
64401
|
-
done: args.done === true
|
|
64402
|
-
})) {
|
|
64403
|
-
const ea = emissionAuthorityFor(turn);
|
|
64404
|
-
ea.markSubstantiveFinalDelivered(() => {
|
|
64405
|
-
turn.finalAnswerEverDelivered = true;
|
|
64406
|
-
if (turn.finalAnswerDeliveredAt == null)
|
|
64407
|
-
turn.finalAnswerDeliveredAt = Date.now();
|
|
64408
|
-
});
|
|
64409
|
-
ea.finalizeCard(() => {
|
|
64410
|
-
clearActivitySummary(turn);
|
|
64411
|
-
});
|
|
64412
|
-
}
|
|
64413
|
-
const result = await handleStreamReply({
|
|
64414
|
-
chat_id: streamChatId,
|
|
64415
|
-
text: args.text,
|
|
64416
|
-
done: Boolean(args.done),
|
|
64417
|
-
message_thread_id: args.message_thread_id,
|
|
64418
|
-
format: args.format,
|
|
64419
|
-
reply_to: args.reply_to,
|
|
64420
|
-
quote: args.quote,
|
|
64421
|
-
...args.protect_content === true ? { protect_content: true } : {},
|
|
64422
|
-
...args.quote_text != null ? { quote_text: args.quote_text } : {},
|
|
64423
|
-
...streamReplyMarkup != null ? { reply_markup: streamReplyMarkup } : {},
|
|
64424
|
-
...args.disable_notification === true ? { disable_notification: true } : {}
|
|
64425
|
-
}, { activeDraftStreams, suppressPtyPreview }, {
|
|
64426
|
-
bot: lockedBot,
|
|
64427
|
-
retry: robustApiCall,
|
|
64428
|
-
repairEscapedWhitespace,
|
|
64429
|
-
normalizeParagraphBreaks,
|
|
64430
|
-
normalizePunctuation,
|
|
64431
|
-
stripExcessBold,
|
|
64432
|
-
addParagraphSpacers,
|
|
64433
|
-
assertAllowedChat,
|
|
64434
|
-
resolveThreadId,
|
|
64435
|
-
disableLinkPreview: access.disableLinkPreview !== false,
|
|
64436
|
-
defaultFormat: access.parseMode ?? "html",
|
|
64437
|
-
logStreamingEvent,
|
|
64438
|
-
isPrivateChat: streamIsPrivate,
|
|
64439
|
-
isForumTopic: streamIsForumTopic,
|
|
64440
|
-
recordOutboundDelivered: (chatId, threadId) => {
|
|
64441
|
-
progressDriver?.recordOutboundDelivered(chatId, threadId != null ? String(threadId) : undefined);
|
|
64442
|
-
},
|
|
64443
|
-
forceCompleteTurn: (chatId, threadId) => {
|
|
64444
|
-
progressDriver?.forceCompleteTurn({
|
|
64445
|
-
chatId,
|
|
64446
|
-
threadId: threadId != null ? String(threadId) : undefined
|
|
64447
|
-
});
|
|
64448
|
-
},
|
|
64449
|
-
historyEnabled: HISTORY_ENABLED,
|
|
64450
|
-
recordOutbound,
|
|
64451
|
-
...HISTORY_ENABLED ? { getLatestInboundMessageId } : {},
|
|
64452
|
-
writeError: (line) => process.stderr.write(line),
|
|
64453
|
-
...STREAM_THROTTLE_MS_OVERRIDE != null ? { throttleMs: STREAM_THROTTLE_MS_OVERRIDE } : {},
|
|
64454
|
-
progressCardActive: streamMode === "checklist"
|
|
64455
|
-
});
|
|
64456
|
-
if (result.messageId != null) {
|
|
64457
|
-
try {
|
|
64458
|
-
progressDriver?.recordOutboundDelivered(String(args.chat_id ?? ""), args.message_thread_id);
|
|
64459
|
-
} catch {}
|
|
64460
|
-
try {
|
|
64461
|
-
const threadIdNum2 = args.message_thread_id != null ? Number(args.message_thread_id) : undefined;
|
|
64462
|
-
noteSignal(statusKey(String(args.chat_id ?? ""), threadIdNum2), Date.now());
|
|
64463
|
-
} catch {}
|
|
64464
|
-
}
|
|
64465
|
-
if (args.done === true && result.messageId != null && streamButtonMeta != null && streamButtonMeta.size > 0) {
|
|
64466
|
-
rememberAgentButtonMeta(String(args.chat_id ?? ""), result.messageId, streamButtonMeta);
|
|
64467
|
-
}
|
|
64468
|
-
if (args.done === true && result.messageId != null) {
|
|
64469
|
-
const sChatId = String(args.chat_id ?? "");
|
|
64470
|
-
const sThreadId = args.message_thread_id != null ? Number(args.message_thread_id) : undefined;
|
|
64471
|
-
outboundDedup.record(sChatId, sThreadId, args.text, Date.now(), currentTurn?.registryKey ?? null);
|
|
64472
|
-
const streamFormat = args.format ?? (access.parseMode ?? "html");
|
|
64473
|
-
const streamLiteralText = streamFormat === "text";
|
|
64474
|
-
clearPending(statusKey(sChatId, sThreadId), "reply_finalize");
|
|
64475
|
-
noteOutbound3(statusKey(sChatId, sThreadId), {
|
|
64476
|
-
messageId: result.messageId,
|
|
64477
|
-
text: args.text,
|
|
64478
|
-
literalText: streamLiteralText
|
|
64479
|
-
});
|
|
64480
|
-
}
|
|
64481
|
-
if (turn != null && isFinalAnswerReply({
|
|
64482
|
-
text: args.text ?? "",
|
|
64483
|
-
disableNotification: args.disable_notification === true,
|
|
64484
|
-
done: args.done === true
|
|
64485
|
-
})) {
|
|
64486
|
-
turn.finalAnswerDelivered = true;
|
|
64487
|
-
turn.finalAnswerSubstantive = isSubstantiveFinalReply({
|
|
64488
|
-
text: args.text ?? "",
|
|
64489
|
-
disableNotification: args.disable_notification === true,
|
|
64490
|
-
done: args.done === true
|
|
64491
|
-
});
|
|
64492
|
-
if (turn.finalAnswerSubstantive)
|
|
64493
|
-
turn.finalAnswerEverDelivered = true;
|
|
64494
|
-
if (turn.finalAnswerSubstantive && turn.finalAnswerDeliveredAt == null)
|
|
64495
|
-
turn.finalAnswerDeliveredAt = Date.now();
|
|
64496
|
-
if (turn.finalAnswerSubstantive)
|
|
64497
|
-
closeObligationOnSubstantiveReply(args, turn, streamRoutedOriginTurn);
|
|
64498
|
-
const streamThreadIdForClear = args.message_thread_id != null ? Number(args.message_thread_id) : undefined;
|
|
64499
|
-
clearSilentEndState(statusKey(streamChatId, streamThreadIdForClear));
|
|
64500
|
-
}
|
|
64501
|
-
{
|
|
64502
|
-
const sChat = String(args.chat_id ?? "");
|
|
64503
|
-
const sThread = resolveThreadId(sChat, args.message_thread_id);
|
|
64504
|
-
releaseTurnBufferGate(statusKey(sChat, sThread), turn ?? undefined);
|
|
64505
|
-
if (turn?.finalAnswerDelivered === true) {
|
|
64506
|
-
reapQueuedStatus(turn.sessionChatId, turn.sessionThreadId);
|
|
64507
|
-
}
|
|
64508
|
-
}
|
|
64509
|
-
return { content: [{ type: "text", text: `${result.status} (id: ${result.messageId ?? "pending"})` }] };
|
|
64510
|
-
}
|
|
64511
64447
|
async function executeProgressUpdate(args) {
|
|
64512
64448
|
if (!args.chat_id)
|
|
64513
64449
|
throw new Error("progress_update: chat_id is required");
|
|
@@ -64518,6 +64454,7 @@ async function executeProgressUpdate(args) {
|
|
|
64518
64454
|
const threadId = resolveThreadId(chat_id, args.message_thread_id);
|
|
64519
64455
|
const key = statusKey(chat_id, threadId);
|
|
64520
64456
|
assertAllowedChat(chat_id);
|
|
64457
|
+
text2 = redactOutboundText(text2, "progress_update");
|
|
64521
64458
|
if (text2.length > 300) {
|
|
64522
64459
|
text2 = text2.slice(0, 299) + "\u2026";
|
|
64523
64460
|
}
|
|
@@ -64551,38 +64488,6 @@ async function executeProgressUpdate(args) {
|
|
|
64551
64488
|
}
|
|
64552
64489
|
progressUpdateTurnCount.set(key, currentCount + 1);
|
|
64553
64490
|
}
|
|
64554
|
-
const agentIdHint = typeof args.agent_id === "string" && args.agent_id || null;
|
|
64555
|
-
const toolUseIdHint = typeof args.tool_use_id === "string" && args.tool_use_id || null;
|
|
64556
|
-
const subAgent = resolveCallingSubagent({
|
|
64557
|
-
db: turnsDb,
|
|
64558
|
-
chatId: chat_id,
|
|
64559
|
-
threadId,
|
|
64560
|
-
agentIdHint,
|
|
64561
|
-
toolUseIdHint
|
|
64562
|
-
});
|
|
64563
|
-
if (subAgent != null && progressDriver != null) {
|
|
64564
|
-
const cardText = text2.length > 200 ? text2.slice(0, 199) + "\u2026" : text2;
|
|
64565
|
-
const result = progressDriver.recordSubAgentNarrative({
|
|
64566
|
-
chatId: chat_id,
|
|
64567
|
-
threadId: threadId != null ? String(threadId) : undefined,
|
|
64568
|
-
agentId: subAgent.agentId,
|
|
64569
|
-
text: cardText
|
|
64570
|
-
});
|
|
64571
|
-
if (result.ok) {
|
|
64572
|
-
progressUpdateLastSent.set(key, now);
|
|
64573
|
-
try {
|
|
64574
|
-
noteSignal(key, Date.now());
|
|
64575
|
-
} catch {}
|
|
64576
|
-
return {
|
|
64577
|
-
content: [
|
|
64578
|
-
{
|
|
64579
|
-
type: "text",
|
|
64580
|
-
text: JSON.stringify({ ok: true, mode: "card", agent_id: subAgent.agentId })
|
|
64581
|
-
}
|
|
64582
|
-
]
|
|
64583
|
-
};
|
|
64584
|
-
}
|
|
64585
|
-
}
|
|
64586
64491
|
const access = loadAccess();
|
|
64587
64492
|
const literalText = (access.parseMode ?? "html") === "text";
|
|
64588
64493
|
const sendOpts = {
|
|
@@ -64764,7 +64669,7 @@ async function publishToTelegraph(text2, shortName, authorName) {
|
|
|
64764
64669
|
const accountPath = join40(STATE_DIR, "telegraph-account.json");
|
|
64765
64670
|
let account = null;
|
|
64766
64671
|
try {
|
|
64767
|
-
if (
|
|
64672
|
+
if (existsSync43(accountPath)) {
|
|
64768
64673
|
const raw = readFileSync41(accountPath, "utf-8");
|
|
64769
64674
|
const parsed = JSON.parse(raw);
|
|
64770
64675
|
if (parsed.shortName && parsed.accessToken) {
|
|
@@ -64785,7 +64690,7 @@ async function publishToTelegraph(text2, shortName, authorName) {
|
|
|
64785
64690
|
account = created.value;
|
|
64786
64691
|
try {
|
|
64787
64692
|
mkdirSync32(STATE_DIR, { recursive: true, mode: 448 });
|
|
64788
|
-
|
|
64693
|
+
writeFileSync30(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
|
|
64789
64694
|
} catch (err) {
|
|
64790
64695
|
process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
|
|
64791
64696
|
`);
|
|
@@ -65227,7 +65132,7 @@ async function executeDownloadAttachment(args) {
|
|
|
65227
65132
|
});
|
|
65228
65133
|
mkdirSync32(INBOX_DIR, { recursive: true, mode: 448 });
|
|
65229
65134
|
assertInsideInbox(INBOX_DIR, dlPath);
|
|
65230
|
-
|
|
65135
|
+
writeFileSync30(dlPath, buf, { mode: 384 });
|
|
65231
65136
|
return { content: [{ type: "text", text: dlPath }] };
|
|
65232
65137
|
}
|
|
65233
65138
|
async function executeEditMessage(args) {
|
|
@@ -65402,7 +65307,7 @@ function resetOrphanedReplyTimeout() {
|
|
|
65402
65307
|
currentSessionChatId: turn.sessionChatId,
|
|
65403
65308
|
capturedTextCount: turn.capturedText.length,
|
|
65404
65309
|
replyCalled: turn.replyCalled,
|
|
65405
|
-
progressCardActive:
|
|
65310
|
+
progressCardActive: false
|
|
65406
65311
|
})) {
|
|
65407
65312
|
turn.orphanedReplyTimeoutId = setTimeout(() => {
|
|
65408
65313
|
const t = currentTurn;
|
|
@@ -65413,7 +65318,7 @@ function resetOrphanedReplyTimeout() {
|
|
|
65413
65318
|
currentSessionChatId: t.sessionChatId,
|
|
65414
65319
|
capturedTextCount: t.capturedText.length,
|
|
65415
65320
|
replyCalled: t.replyCalled,
|
|
65416
|
-
progressCardActive:
|
|
65321
|
+
progressCardActive: false
|
|
65417
65322
|
})) {
|
|
65418
65323
|
const turnKey = statusKey(t.sessionChatId, t.sessionThreadId);
|
|
65419
65324
|
const working = isLegitimatelyWorking(turnKey);
|
|
@@ -65583,7 +65488,7 @@ function openLivenessFeedIfDue(turn) {
|
|
|
65583
65488
|
toolCount: turn.labeledToolCount,
|
|
65584
65489
|
state: "running"
|
|
65585
65490
|
};
|
|
65586
|
-
const rendered = renderActivityFeedWithNested(lines, [], false,
|
|
65491
|
+
const rendered = renderActivityFeedWithNested(lines, [], false, formatStepSuffix2(age), undefined, livenessHeader);
|
|
65587
65492
|
if (rendered == null)
|
|
65588
65493
|
return;
|
|
65589
65494
|
turn.activityPendingRender = rendered;
|
|
@@ -65646,7 +65551,7 @@ function feedHeartbeatTick() {
|
|
|
65646
65551
|
};
|
|
65647
65552
|
const lines = turn.mirrorLines.length > 0 ? turn.mirrorLines : ["Working in background\u2026"];
|
|
65648
65553
|
const elapsed2 = Date.now() - subagentAt;
|
|
65649
|
-
const rendered2 = renderActivityFeedWithNested(lines, [], false,
|
|
65554
|
+
const rendered2 = renderActivityFeedWithNested(lines, [], false, formatStepSuffix2(elapsed2), undefined, livenessHeader);
|
|
65650
65555
|
if (rendered2 == null)
|
|
65651
65556
|
return;
|
|
65652
65557
|
turn.activityPendingRender = rendered2;
|
|
@@ -65671,7 +65576,7 @@ function feedHeartbeatTick() {
|
|
|
65671
65576
|
const elapsed = Date.now() - turn.lastToolLabelAt;
|
|
65672
65577
|
if (elapsed < FEED_HEARTBEAT_MIN_STALE_MS)
|
|
65673
65578
|
return;
|
|
65674
|
-
const rendered = composeTurnActivity(turn, false,
|
|
65579
|
+
const rendered = composeTurnActivity(turn, false, formatStepSuffix2(elapsed));
|
|
65675
65580
|
if (rendered == null)
|
|
65676
65581
|
return;
|
|
65677
65582
|
turn.activityPendingRender = rendered;
|
|
@@ -65799,7 +65704,7 @@ function handleSessionEvent(ev) {
|
|
|
65799
65704
|
rememberRecentTurn(next);
|
|
65800
65705
|
promoteQueuedStatus(ev.chatId, enqThreadIdNum);
|
|
65801
65706
|
if (DELIVERY_CONFIRM_ENABLED) {
|
|
65802
|
-
ackDelivery(deliveryQueue, chatKey2(ev.chatId, ev.threadId != null ? Number(ev.threadId) : null), ev.messageId);
|
|
65707
|
+
ackDelivery(deliveryQueue, chatKey2(ev.chatId, ev.threadId != null ? Number(ev.threadId) : null), ev.messageId, ev.rawContent);
|
|
65803
65708
|
}
|
|
65804
65709
|
shadowEmit({
|
|
65805
65710
|
kind: "turnStart",
|
|
@@ -66149,7 +66054,7 @@ function handleSessionEvent(ev) {
|
|
|
66149
66054
|
unpinProgressCardForChat?.(chatId, threadId);
|
|
66150
66055
|
finalizeStatusReaction(chatId, threadId, "done");
|
|
66151
66056
|
{
|
|
66152
|
-
const sKey =
|
|
66057
|
+
const sKey = streamKey2(chatId, threadId);
|
|
66153
66058
|
const turnDurationMs = turn.startedAt > 0 ? Date.now() - turn.startedAt : 0;
|
|
66154
66059
|
logStreamingEvent({
|
|
66155
66060
|
kind: "turn_end",
|
|
@@ -66194,10 +66099,12 @@ function handleSessionEvent(ev) {
|
|
|
66194
66099
|
}
|
|
66195
66100
|
if (flushDecision.kind === "flush") {
|
|
66196
66101
|
let capturedText = flushDecision.text;
|
|
66102
|
+
capturedText = normalizeParagraphBreaks(repairEscapedWhitespace(capturedText));
|
|
66197
66103
|
const backstopChatId = chatId;
|
|
66198
66104
|
const backstopThreadId = threadId;
|
|
66199
66105
|
const backstopCtrl = ctrl;
|
|
66200
66106
|
capturedText = redactOutboundText(capturedText, "turn_flush");
|
|
66107
|
+
capturedText = stripExcessBold(normalizePunctuation(capturedText));
|
|
66201
66108
|
{
|
|
66202
66109
|
const scrub = scrubVoice(capturedText);
|
|
66203
66110
|
if (scrub.replaced > 0) {
|
|
@@ -66247,7 +66154,8 @@ function handleSessionEvent(ev) {
|
|
|
66247
66154
|
link_preview_options: { is_disabled: true }
|
|
66248
66155
|
};
|
|
66249
66156
|
const limit = RICH_MESSAGE_MAX_CHARS2;
|
|
66250
|
-
const
|
|
66157
|
+
const renderedText = addParagraphSpacers(capturedText);
|
|
66158
|
+
const htmlChunks = splitMarkdownChunks(renderedText, limit);
|
|
66251
66159
|
const sentIds = [];
|
|
66252
66160
|
try {
|
|
66253
66161
|
let firstSendUsedEdit = false;
|
|
@@ -66336,7 +66244,7 @@ function handleSessionEvent(ev) {
|
|
|
66336
66244
|
}
|
|
66337
66245
|
finalizeStatusReaction(chatId, threadId, terminalReason);
|
|
66338
66246
|
{
|
|
66339
|
-
const sKey =
|
|
66247
|
+
const sKey = streamKey2(chatId, threadId);
|
|
66340
66248
|
const turnDurationMs = turn.startedAt > 0 ? Date.now() - turn.startedAt : 0;
|
|
66341
66249
|
logStreamingEvent({
|
|
66342
66250
|
kind: "turn_end",
|
|
@@ -66736,15 +66644,23 @@ async function handleInbound(ctx, text2, downloadImage, attachment, extraAttachm
|
|
|
66736
66644
|
}
|
|
66737
66645
|
if (STATUS_QUERY_RE.test(text2)) {
|
|
66738
66646
|
try {
|
|
66739
|
-
const
|
|
66740
|
-
const
|
|
66741
|
-
const
|
|
66742
|
-
const
|
|
66743
|
-
const
|
|
66744
|
-
|
|
66647
|
+
const thread = messageThreadId != null ? String(messageThreadId) : "none";
|
|
66648
|
+
const key = statusKey(chat_id, messageThreadId);
|
|
66649
|
+
const turnStartedAt = activeTurnStartedAt.get(key);
|
|
66650
|
+
const turnActive = turnStartedAt != null;
|
|
66651
|
+
const surfaces = {
|
|
66652
|
+
turnActive,
|
|
66653
|
+
turnAgeS: turnActive ? Math.max(0, Math.floor((Date.now() - turnStartedAt) / 1000)) : -1,
|
|
66654
|
+
runningWorkers: countRunningWorkers(),
|
|
66655
|
+
workerFeedSize: workerActivityFeed?.size ?? 0,
|
|
66656
|
+
pendingAsync: hasPendingAsyncDispatch(key) ? 1 : 0
|
|
66657
|
+
};
|
|
66658
|
+
const telemetry = deriveStatusQueryTelemetry(surfaces);
|
|
66745
66659
|
const agentName3 = process.env.SWITCHROOM_AGENT_NAME ?? "-";
|
|
66746
|
-
process.stderr.write(
|
|
66747
|
-
|
|
66660
|
+
process.stderr.write(formatStatusQueryLine(agentName3, String(chat_id), thread, surfaces, telemetry));
|
|
66661
|
+
if (telemetry.idle) {
|
|
66662
|
+
process.stderr.write(formatStatusQueryUxFailureLine(agentName3, String(chat_id), thread));
|
|
66663
|
+
}
|
|
66748
66664
|
} catch (err) {
|
|
66749
66665
|
process.stderr.write(`telegram gateway: status-query telemetry failed: ${err.message}
|
|
66750
66666
|
`);
|
|
@@ -67113,7 +67029,7 @@ ${preBlock(write.output)}`;
|
|
|
67113
67029
|
bot.api.setMessageReaction(chat_id, msgId, [{ type: "emoji", emoji: "\uD83D\uDC40" }]).catch(() => {});
|
|
67114
67030
|
logStreamingEvent({ kind: "inbound_ack", chatId: chat_id, messageId: msgId, ackDelayMs: Date.now() - inboundReceivedAt });
|
|
67115
67031
|
} else {
|
|
67116
|
-
const sKey =
|
|
67032
|
+
const sKey = streamKey2(chat_id, messageThreadId);
|
|
67117
67033
|
const priorStream = activeDraftStreams.get(sKey);
|
|
67118
67034
|
if (priorStream && !priorStream.isFinal()) {
|
|
67119
67035
|
activeDraftStreams.delete(sKey);
|
|
@@ -67486,7 +67402,7 @@ function writeRestartMarker(marker) {
|
|
|
67486
67402
|
if (!p)
|
|
67487
67403
|
return;
|
|
67488
67404
|
try {
|
|
67489
|
-
|
|
67405
|
+
writeFileSync30(p, JSON.stringify(marker));
|
|
67490
67406
|
lastPlannedRestartAt = Date.now();
|
|
67491
67407
|
process.stderr.write(`telegram gateway: restart-marker: write chat_id=${marker.chat_id} thread_id=${marker.thread_id ?? "-"} ack=${marker.ack_message_id ?? "-"} path=${p}
|
|
67492
67408
|
`);
|
|
@@ -67654,7 +67570,7 @@ var _dockerReachable;
|
|
|
67654
67570
|
function isDockerReachable() {
|
|
67655
67571
|
if (_dockerReachable !== undefined)
|
|
67656
67572
|
return _dockerReachable;
|
|
67657
|
-
if (!
|
|
67573
|
+
if (!existsSync43("/var/run/docker.sock")) {
|
|
67658
67574
|
_dockerReachable = false;
|
|
67659
67575
|
return _dockerReachable;
|
|
67660
67576
|
}
|
|
@@ -67676,7 +67592,7 @@ function spawnSwitchroomDetached(args, onFailure) {
|
|
|
67676
67592
|
try {
|
|
67677
67593
|
mkdirSync32(STATE_DIR, { recursive: true });
|
|
67678
67594
|
outFd = openSync9(logPath, "a");
|
|
67679
|
-
|
|
67595
|
+
writeFileSync30(logPath, `
|
|
67680
67596
|
[${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
|
|
67681
67597
|
`, { flag: "a" });
|
|
67682
67598
|
} catch {}
|
|
@@ -68070,7 +67986,7 @@ bot.use(async (ctx, next) => {
|
|
|
68070
67986
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
68071
67987
|
try {
|
|
68072
67988
|
const auditPath = join40(homedir14(), ".switchroom", "vault-audit.log");
|
|
68073
|
-
if (!
|
|
67989
|
+
if (!existsSync43(auditPath))
|
|
68074
67990
|
return [];
|
|
68075
67991
|
const raw = readFileSync41(auditPath, "utf8");
|
|
68076
67992
|
return recentDenialsFromAuditLog(raw, { agentName: agentName3, windowMs, limit });
|
|
@@ -68498,7 +68414,7 @@ function flushAgentHandoff(agentDir) {
|
|
|
68498
68414
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
68499
68415
|
const p = join40(agentDir, fname);
|
|
68500
68416
|
try {
|
|
68501
|
-
if (
|
|
68417
|
+
if (existsSync43(p)) {
|
|
68502
68418
|
unlinkSync16(p);
|
|
68503
68419
|
removed++;
|
|
68504
68420
|
}
|
|
@@ -68554,7 +68470,7 @@ async function handleNewCommand(ctx) {
|
|
|
68554
68470
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
68555
68471
|
if (agentDir != null) {
|
|
68556
68472
|
try {
|
|
68557
|
-
|
|
68473
|
+
writeFileSync30(join40(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
68558
68474
|
`, "utf8");
|
|
68559
68475
|
} catch (err) {
|
|
68560
68476
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -68918,15 +68834,15 @@ bot.command("interrupt", async (ctx) => {
|
|
|
68918
68834
|
});
|
|
68919
68835
|
var lockoutOps = {
|
|
68920
68836
|
readFileSync: (p, enc) => readFileSync41(p, enc),
|
|
68921
|
-
writeFileSync: (p, data, opts) =>
|
|
68922
|
-
existsSync: (p) =>
|
|
68837
|
+
writeFileSync: (p, data, opts) => writeFileSync30(p, data, opts),
|
|
68838
|
+
existsSync: (p) => existsSync43(p),
|
|
68923
68839
|
mkdirSync: (p, opts) => mkdirSync32(p, opts),
|
|
68924
68840
|
joinPath: (...parts) => join40(...parts)
|
|
68925
68841
|
};
|
|
68926
68842
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
68927
68843
|
function isAuthBrokerSocketReachable() {
|
|
68928
68844
|
try {
|
|
68929
|
-
return
|
|
68845
|
+
return existsSync43(resolveAuthBrokerSocketPath2());
|
|
68930
68846
|
} catch {
|
|
68931
68847
|
return false;
|
|
68932
68848
|
}
|
|
@@ -69582,7 +69498,7 @@ async function handleVaultRecentDenialCallback(ctx, data) {
|
|
|
69582
69498
|
const tokenPath = join40(homedir14(), ".switchroom", "agents", agentName3, ".vault-token");
|
|
69583
69499
|
try {
|
|
69584
69500
|
mkdirSync32(join40(homedir14(), ".switchroom", "agents", agentName3), { recursive: true });
|
|
69585
|
-
|
|
69501
|
+
writeFileSync30(tokenPath, token, { mode: 384 });
|
|
69586
69502
|
} catch (err) {
|
|
69587
69503
|
await switchroomReply(ctx, `**Grant created (${escapeHtmlForTg2(id)}) but token write failed:** ${escapeHtmlForTg2(String(err))}
|
|
69588
69504
|
_Recover with: \`switchroom vault grant ${escapeHtmlForTg2(agentName3)} --keys ${escapeHtmlForTg2(keyName)} --duration 30d\` on the host._`, { html: true });
|
|
@@ -69660,7 +69576,7 @@ async function performVaultAccessApproval(ctx, pending2, stageId, senderId, atte
|
|
|
69660
69576
|
const tokenPath = join40(homedir14(), ".switchroom", "agents", pending2.agent, ".vault-token");
|
|
69661
69577
|
try {
|
|
69662
69578
|
mkdirSync32(join40(homedir14(), ".switchroom", "agents", pending2.agent), { recursive: true });
|
|
69663
|
-
|
|
69579
|
+
writeFileSync30(tokenPath, token, { mode: 384 });
|
|
69664
69580
|
} catch (err) {
|
|
69665
69581
|
await switchroomReply(ctx, `**Grant created (${escapeHtmlForTg2(id)}) but token write failed:** ${escapeHtmlForTg2(String(err))}
|
|
69666
69582
|
_Recover with: \`switchroom vault grant ${escapeHtmlForTg2(pending2.agent)} --keys ${escapeHtmlForTg2(pending2.key)} --duration ${Math.round(pending2.ttl_seconds / 86400)}d\` on the host._`, { html: true });
|
|
@@ -70232,7 +70148,7 @@ async function executeGrantWizard(ctx, chatId, state4) {
|
|
|
70232
70148
|
const tokenPath = join40(homedir14(), ".switchroom", "agents", state4.agent, ".vault-token");
|
|
70233
70149
|
try {
|
|
70234
70150
|
mkdirSync32(join40(homedir14(), ".switchroom", "agents", state4.agent), { recursive: true });
|
|
70235
|
-
|
|
70151
|
+
writeFileSync30(tokenPath, token, { mode: 384 });
|
|
70236
70152
|
} catch (err) {
|
|
70237
70153
|
await switchroomReply(ctx, `**Grant created but token write failed:** ${escapeHtmlForTg2(String(err))}`, { html: true });
|
|
70238
70154
|
return;
|
|
@@ -71962,7 +71878,7 @@ bot.on("message:photo", async (ctx) => {
|
|
|
71962
71878
|
});
|
|
71963
71879
|
mkdirSync32(INBOX_DIR, { recursive: true, mode: 448 });
|
|
71964
71880
|
assertInsideInbox(INBOX_DIR, dlPath);
|
|
71965
|
-
|
|
71881
|
+
writeFileSync30(dlPath, buf, { mode: 384 });
|
|
71966
71882
|
return dlPath;
|
|
71967
71883
|
} catch (err) {
|
|
71968
71884
|
const msg = err instanceof Error ? err.message : "unknown error";
|
|
@@ -73152,28 +73068,6 @@ var didOneTimeSetup = false;
|
|
|
73152
73068
|
parentStateDir: STATE_DIR,
|
|
73153
73069
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
73154
73070
|
`),
|
|
73155
|
-
onStall: (agentId, idleMs, description) => {
|
|
73156
|
-
progressDriver?.onSubAgentStall(agentId, idleMs, description);
|
|
73157
|
-
},
|
|
73158
|
-
onUnstall: (agentId, description) => {
|
|
73159
|
-
progressDriver?.onSubAgentUnstall?.(agentId, description);
|
|
73160
|
-
},
|
|
73161
|
-
onStallTerminal: (agentId) => {
|
|
73162
|
-
try {
|
|
73163
|
-
const fleets = progressDriver?.peekAllFleets() ?? [];
|
|
73164
|
-
for (const f of fleets) {
|
|
73165
|
-
if (f.fleet.has(agentId)) {
|
|
73166
|
-
progressDriver?.ingest({ kind: "sub_agent_turn_end", agentId }, f.chatId ?? null, undefined);
|
|
73167
|
-
return;
|
|
73168
|
-
}
|
|
73169
|
-
}
|
|
73170
|
-
process.stderr.write(`telegram gateway: subagent-watcher: onStallTerminal ${agentId} \u2014 no fleet entry to retarget; synthetic sub_agent_turn_end skipped
|
|
73171
|
-
`);
|
|
73172
|
-
} catch (err) {
|
|
73173
|
-
process.stderr.write(`telegram gateway: subagent-watcher: onStallTerminal ${agentId} ingest error: ${err.message}
|
|
73174
|
-
`);
|
|
73175
|
-
}
|
|
73176
|
-
},
|
|
73177
73071
|
onFinish: ({ agentId, outcome, description, resultText, toolCount, durationMs }) => {
|
|
73178
73072
|
deferredDoneReactions.promote();
|
|
73179
73073
|
let fleetChatId = "";
|
|
@@ -73193,6 +73087,20 @@ var didOneTimeSetup = false;
|
|
|
73193
73087
|
} catch {}
|
|
73194
73088
|
}
|
|
73195
73089
|
const isBackground = dispatch.isBackground;
|
|
73090
|
+
if (dispatch.isNested) {
|
|
73091
|
+
if (workerFeedEnabled) {
|
|
73092
|
+
workerActivityFeed?.finish(agentId, {
|
|
73093
|
+
description: dispatch.feedDescription,
|
|
73094
|
+
lastTool: null,
|
|
73095
|
+
toolCount,
|
|
73096
|
+
latestSummary: resultText,
|
|
73097
|
+
elapsedMs: durationMs,
|
|
73098
|
+
state: outcome === "failed" ? "failed" : "done"
|
|
73099
|
+
});
|
|
73100
|
+
reconcileWorkerPin(agentId, null, false);
|
|
73101
|
+
}
|
|
73102
|
+
return;
|
|
73103
|
+
}
|
|
73196
73104
|
if (!isBackground) {
|
|
73197
73105
|
const turn = currentTurn;
|
|
73198
73106
|
if (turn != null && turn.foregroundSubAgents.has(agentId)) {
|
|
@@ -73302,11 +73210,12 @@ var didOneTimeSetup = false;
|
|
|
73302
73210
|
dispatch = resolveWorkerFeedDispatch(getSubagentByJsonlId(turnsDb, agentId), description);
|
|
73303
73211
|
} catch {}
|
|
73304
73212
|
}
|
|
73305
|
-
const isBackground = dispatch.isBackground;
|
|
73213
|
+
const isBackground = dispatch.isBackground || dispatch.isNested;
|
|
73214
|
+
const stepLine = progressLine != null && progressLine.length > 0 ? progressLine : latestSummary;
|
|
73306
73215
|
if (!isBackground) {
|
|
73307
73216
|
const surface = resolveSubagentStatusSurface({
|
|
73308
73217
|
isBackground: false,
|
|
73309
|
-
liveTurnPresent: currentTurn != null,
|
|
73218
|
+
liveTurnPresent: currentTurn != null && dispatch.hasRow,
|
|
73310
73219
|
workerFeedEnabled,
|
|
73311
73220
|
orphanStatusEnabled
|
|
73312
73221
|
});
|
|
@@ -73317,7 +73226,7 @@ var didOneTimeSetup = false;
|
|
|
73317
73226
|
description: dispatch.feedDescription,
|
|
73318
73227
|
lastTool,
|
|
73319
73228
|
toolCount,
|
|
73320
|
-
latestSummary,
|
|
73229
|
+
latestSummary: stepLine,
|
|
73321
73230
|
elapsedMs,
|
|
73322
73231
|
state: "running"
|
|
73323
73232
|
}, origin?.threadId)?.then(() => reconcileWorkerPin(agentId, wkChat, true));
|
|
@@ -73341,7 +73250,7 @@ var didOneTimeSetup = false;
|
|
|
73341
73250
|
narrative = [];
|
|
73342
73251
|
turn.foregroundSubAgents.set(agentId, narrative);
|
|
73343
73252
|
}
|
|
73344
|
-
if (narrative
|
|
73253
|
+
if (!narrative.includes(child)) {
|
|
73345
73254
|
narrative.push(child);
|
|
73346
73255
|
if (narrative.length > FOREGROUND_SUBAGENT_ACCUM_MAX) {
|
|
73347
73256
|
narrative.splice(0, narrative.length - FOREGROUND_SUBAGENT_ACCUM_MAX);
|
|
@@ -73375,7 +73284,7 @@ var didOneTimeSetup = false;
|
|
|
73375
73284
|
description: dispatch.feedDescription,
|
|
73376
73285
|
lastTool,
|
|
73377
73286
|
toolCount,
|
|
73378
|
-
latestSummary,
|
|
73287
|
+
latestSummary: stepLine,
|
|
73379
73288
|
elapsedMs,
|
|
73380
73289
|
state: "running"
|
|
73381
73290
|
}, origin?.threadId)?.then(() => reconcileWorkerPin(agentId, wkChat, true));
|