switchroom 0.16.47 → 0.17.1
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 +1359 -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 +842 -832
- package/telegram-plugin/dist/server.js +8 -43
- package/telegram-plugin/format.ts +119 -17
- package/telegram-plugin/gateway/approvals-commands.ts +6 -2
- 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 +535 -627
- 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/ms365-write-approval.test.ts +13 -0
- package/telegram-plugin/gateway/ms365-write-approval.ts +5 -1
- 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/vault-request-access-card.ts +5 -1
- 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/format-consistency.test.ts +79 -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/vault-request-access-card.test.ts +17 -0
- package/telegram-plugin/tests/welcome-text.test.ts +64 -0
- 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/welcome-text.ts +13 -9
- package/telegram-plugin/worker-activity-feed.ts +75 -15
- package/vendor/hindsight-memory/CHANGELOG.md +66 -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 +43 -4
- 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 +79 -11
- package/vendor/hindsight-memory/scripts/tests/test_config_client_casts.py +111 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_context_slice.py +126 -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/scripts/tests/test_retain_window.py +261 -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 +123 -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
|
@@ -6713,6 +6713,77 @@ function escapeMarkdown(text) {
|
|
|
6713
6713
|
function codeSpanSafe(s) {
|
|
6714
6714
|
return s.replace(/`/g, "`\u200b");
|
|
6715
6715
|
}
|
|
6716
|
+
function maskCodeRegions(text, nonce) {
|
|
6717
|
+
const FENCE_MASK_PH = `\x00RMF${nonce}_`;
|
|
6718
|
+
const INLINE_MASK_PH = `\x00RMI${nonce}_`;
|
|
6719
|
+
const codeMasks = [];
|
|
6720
|
+
const masked = text.replace(/```[\s\S]*?```/g, (m) => {
|
|
6721
|
+
const idx = codeMasks.length;
|
|
6722
|
+
codeMasks.push(m);
|
|
6723
|
+
return `${FENCE_MASK_PH}${idx}\x00`;
|
|
6724
|
+
}).replace(/`[^`\n]+`/g, (m) => {
|
|
6725
|
+
const idx = codeMasks.length;
|
|
6726
|
+
codeMasks.push(m);
|
|
6727
|
+
return `${INLINE_MASK_PH}${idx}\x00`;
|
|
6728
|
+
});
|
|
6729
|
+
const escNonce = nonce.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6730
|
+
const anyMaskRe = new RegExp(`\x00RM[FI]${escNonce}_(\\d+)\x00`, "g");
|
|
6731
|
+
const restore = (s) => s.replace(anyMaskRe, (_m, idx) => codeMasks[Number(idx)] ?? _m);
|
|
6732
|
+
const stripPlaceholders = (s) => s.replace(anyMaskRe, "");
|
|
6733
|
+
return { masked, restore, placeholder: FENCE_MASK_PH, stripPlaceholders };
|
|
6734
|
+
}
|
|
6735
|
+
function hardenCardBreaks(text) {
|
|
6736
|
+
if (!text.includes(`
|
|
6737
|
+
`))
|
|
6738
|
+
return text;
|
|
6739
|
+
const nonce = Math.random().toString(36).slice(2);
|
|
6740
|
+
const { masked, restore, placeholder } = maskCodeRegions(text, nonce);
|
|
6741
|
+
const out = masked.replace(/\n{3,}/g, `
|
|
6742
|
+
|
|
6743
|
+
`);
|
|
6744
|
+
const isBlockConstructLine = (line) => isListItemLine(line) || isTableRowLine(line) || isTableDelimiterLine(line) || isBlockquoteLine(line) || isHeadingLine(line) || isFenceOpenLine(line, placeholder);
|
|
6745
|
+
const lines = out.split(`
|
|
6746
|
+
`);
|
|
6747
|
+
const pieces = [];
|
|
6748
|
+
for (let i = 0;i < lines.length; i++) {
|
|
6749
|
+
let line = lines[i];
|
|
6750
|
+
const isLast = i === lines.length - 1;
|
|
6751
|
+
const next = isLast ? "" : lines[i + 1];
|
|
6752
|
+
const promote = !isLast && line.trim() !== "" && next.trim() !== "" && !isBlockConstructLine(line) && !isBlockConstructLine(next);
|
|
6753
|
+
if (promote) {
|
|
6754
|
+
line = line.replace(/[ \t\r]+$/, "");
|
|
6755
|
+
}
|
|
6756
|
+
pieces.push(line);
|
|
6757
|
+
if (isLast)
|
|
6758
|
+
break;
|
|
6759
|
+
pieces.push(promote ? `
|
|
6760
|
+
` : `
|
|
6761
|
+
`);
|
|
6762
|
+
}
|
|
6763
|
+
return restore(pieces.join(""));
|
|
6764
|
+
}
|
|
6765
|
+
function isListItemLine(line) {
|
|
6766
|
+
const t = line.trimStart();
|
|
6767
|
+
return /^[-*+]\s/.test(t) || /^\d+[.)]\s/.test(t);
|
|
6768
|
+
}
|
|
6769
|
+
function isTableRowLine(line) {
|
|
6770
|
+
return /^\s*\|/.test(line);
|
|
6771
|
+
}
|
|
6772
|
+
function isTableDelimiterLine(line) {
|
|
6773
|
+
return /^\s*\|?\s*:?-{1,}:?\s*(\|\s*:?-{1,}:?\s*)*\|?\s*$/.test(line);
|
|
6774
|
+
}
|
|
6775
|
+
function isFenceOpenLine(line, placeholder) {
|
|
6776
|
+
const t = line.trimStart();
|
|
6777
|
+
if (placeholder != null && placeholder.length > 0 && t.startsWith(placeholder))
|
|
6778
|
+
return true;
|
|
6779
|
+
return t.startsWith("```");
|
|
6780
|
+
}
|
|
6781
|
+
function isBlockquoteLine(line) {
|
|
6782
|
+
return line.trimStart().startsWith(">");
|
|
6783
|
+
}
|
|
6784
|
+
function isHeadingLine(line) {
|
|
6785
|
+
return /^#{1,6}\s/.test(line.trimStart());
|
|
6786
|
+
}
|
|
6716
6787
|
var RICH_MESSAGE_MAX_CHARS = 32768;
|
|
6717
6788
|
|
|
6718
6789
|
// text-voice-scrub.ts
|
|
@@ -16826,13 +16897,11 @@ var exports_client = {};
|
|
|
16826
16897
|
__export(exports_client, {
|
|
16827
16898
|
withAuthBrokerClient: () => withAuthBrokerClient,
|
|
16828
16899
|
resolveAuthBrokerSocketPath: () => resolveAuthBrokerSocketPath,
|
|
16829
|
-
authBrokerSocketExists: () => authBrokerSocketExists,
|
|
16830
16900
|
AuthBrokerUnreachableError: () => AuthBrokerUnreachableError,
|
|
16831
16901
|
AuthBrokerError: () => AuthBrokerError,
|
|
16832
16902
|
AuthBrokerClient: () => AuthBrokerClient
|
|
16833
16903
|
});
|
|
16834
16904
|
import * as net from "node:net";
|
|
16835
|
-
import { existsSync as existsSync7 } from "node:fs";
|
|
16836
16905
|
import { homedir as homedir3 } from "node:os";
|
|
16837
16906
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
16838
16907
|
import { join as join11 } from "node:path";
|
|
@@ -17133,9 +17202,6 @@ async function withAuthBrokerClient(fn, opts) {
|
|
|
17133
17202
|
await client3.close();
|
|
17134
17203
|
}
|
|
17135
17204
|
}
|
|
17136
|
-
function authBrokerSocketExists(opts) {
|
|
17137
|
-
return existsSync7(resolveAuthBrokerSocketPath(opts));
|
|
17138
|
-
}
|
|
17139
17205
|
var DEFAULT_TIMEOUT_MS = 5000, AuthBrokerError, AuthBrokerUnreachableError;
|
|
17140
17206
|
var init_client = __esm(() => {
|
|
17141
17207
|
init_protocol();
|
|
@@ -17166,11 +17232,14 @@ var init_flock = () => {};
|
|
|
17166
17232
|
import { randomBytes as randomBytes3, scryptSync, createCipheriv, createDecipheriv } from "node:crypto";
|
|
17167
17233
|
import {
|
|
17168
17234
|
readFileSync as readFileSync7,
|
|
17169
|
-
|
|
17170
|
-
existsSync as
|
|
17235
|
+
writeSync,
|
|
17236
|
+
existsSync as existsSync7,
|
|
17171
17237
|
renameSync as renameSync4,
|
|
17172
17238
|
mkdirSync as mkdirSync10,
|
|
17173
17239
|
unlinkSync as unlinkSync4,
|
|
17240
|
+
fsyncSync,
|
|
17241
|
+
openSync,
|
|
17242
|
+
closeSync,
|
|
17174
17243
|
lstatSync,
|
|
17175
17244
|
realpathSync
|
|
17176
17245
|
} from "node:fs";
|
|
@@ -17205,7 +17274,7 @@ function normalizeSecrets(raw) {
|
|
|
17205
17274
|
return out;
|
|
17206
17275
|
}
|
|
17207
17276
|
function openVault(passphrase, vaultPath) {
|
|
17208
|
-
if (!
|
|
17277
|
+
if (!existsSync7(vaultPath)) {
|
|
17209
17278
|
throw new VaultError(`Vault file not found: ${vaultPath}`);
|
|
17210
17279
|
}
|
|
17211
17280
|
let vaultFile;
|
|
@@ -24385,7 +24454,9 @@ var init_schema = __esm(() => {
|
|
|
24385
24454
|
enabled: exports_external.boolean().optional().describe("Master switch for the session-handoff briefing (default true)."),
|
|
24386
24455
|
max_turns_in_briefing: exports_external.number().int().positive().optional().describe("Cap on recent user/assistant turn pairs fed to the summarizer."),
|
|
24387
24456
|
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.")
|
|
24457
|
+
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."),
|
|
24458
|
+
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."),
|
|
24459
|
+
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
24460
|
}).optional();
|
|
24390
24461
|
webhookDispatchRule = exports_external.object({
|
|
24391
24462
|
description: exports_external.string().optional(),
|
|
@@ -24912,7 +24983,7 @@ var init_schema = __esm(() => {
|
|
|
24912
24983
|
});
|
|
24913
24984
|
|
|
24914
24985
|
// ../src/config/paths.ts
|
|
24915
|
-
import { existsSync as
|
|
24986
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
24916
24987
|
import { resolve as resolve2 } from "node:path";
|
|
24917
24988
|
function home() {
|
|
24918
24989
|
return process.env.HOME ?? "/root";
|
|
@@ -24928,7 +24999,7 @@ function resolveStatePath2(fragment) {
|
|
|
24928
24999
|
const h = home();
|
|
24929
25000
|
const primary = resolve2(h, DEFAULT_STATE_DIR, fragment);
|
|
24930
25001
|
const legacy = resolve2(h, LEGACY_STATE_DIR, fragment);
|
|
24931
|
-
if (!
|
|
25002
|
+
if (!existsSync8(primary) && existsSync8(legacy)) {
|
|
24932
25003
|
warnLegacyStateOnce(legacy);
|
|
24933
25004
|
return legacy;
|
|
24934
25005
|
}
|
|
@@ -24941,9 +25012,9 @@ function resolveDualPath(pathStr) {
|
|
|
24941
25012
|
const absolute = resolve2(h, rest);
|
|
24942
25013
|
if (rest.startsWith(`${DEFAULT_STATE_DIR}/`)) {
|
|
24943
25014
|
const frag = rest.slice(DEFAULT_STATE_DIR.length + 1);
|
|
24944
|
-
if (!
|
|
25015
|
+
if (!existsSync8(absolute)) {
|
|
24945
25016
|
const legacy = resolve2(h, LEGACY_STATE_DIR, frag);
|
|
24946
|
-
if (
|
|
25017
|
+
if (existsSync8(legacy)) {
|
|
24947
25018
|
warnLegacyStateOnce(legacy);
|
|
24948
25019
|
return legacy;
|
|
24949
25020
|
}
|
|
@@ -24968,7 +25039,7 @@ var init_overlay_schema = __esm(() => {
|
|
|
24968
25039
|
});
|
|
24969
25040
|
|
|
24970
25041
|
// ../src/config/overlay-loader.ts
|
|
24971
|
-
import { existsSync as
|
|
25042
|
+
import { existsSync as existsSync9, readFileSync as readFileSync8, readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
|
|
24972
25043
|
import { basename as basename4, resolve as resolve3 } from "node:path";
|
|
24973
25044
|
function deriveOverlayTitle(raw, fileName) {
|
|
24974
25045
|
const titleFromComment = raw.match(/^#[^\S\n]*name:[^\S\n]*(\S.*?)[^\S\n]*$/m)?.[1];
|
|
@@ -24984,7 +25055,7 @@ function overlayDirFor(agentName3, subdir) {
|
|
|
24984
25055
|
return resolve3(base);
|
|
24985
25056
|
}
|
|
24986
25057
|
function listYamlFiles(dir) {
|
|
24987
|
-
if (!
|
|
25058
|
+
if (!existsSync9(dir))
|
|
24988
25059
|
return [];
|
|
24989
25060
|
let entries;
|
|
24990
25061
|
try {
|
|
@@ -25509,7 +25580,7 @@ function validateNotionWorkspaceConfig(config) {
|
|
|
25509
25580
|
}
|
|
25510
25581
|
|
|
25511
25582
|
// ../src/config/loader.ts
|
|
25512
|
-
import { readFileSync as readFileSync9, existsSync as
|
|
25583
|
+
import { readFileSync as readFileSync9, existsSync as existsSync10 } from "node:fs";
|
|
25513
25584
|
import { homedir as homedir4 } from "node:os";
|
|
25514
25585
|
import { resolve as resolve4 } from "node:path";
|
|
25515
25586
|
function formatZodErrors(error) {
|
|
@@ -25576,7 +25647,7 @@ function findConfigFile(startDir) {
|
|
|
25576
25647
|
resolve4(userDir, "clerk.yml")
|
|
25577
25648
|
].filter(Boolean);
|
|
25578
25649
|
for (const path of searchPaths) {
|
|
25579
|
-
if (
|
|
25650
|
+
if (existsSync10(path)) {
|
|
25580
25651
|
return path;
|
|
25581
25652
|
}
|
|
25582
25653
|
}
|
|
@@ -25584,7 +25655,7 @@ function findConfigFile(startDir) {
|
|
|
25584
25655
|
}
|
|
25585
25656
|
function loadConfig(configPath) {
|
|
25586
25657
|
const filePath = configPath ?? findConfigFile();
|
|
25587
|
-
if (!
|
|
25658
|
+
if (!existsSync10(filePath)) {
|
|
25588
25659
|
throw new ConfigError(`Config file not found: ${filePath}`);
|
|
25589
25660
|
}
|
|
25590
25661
|
let raw;
|
|
@@ -25725,7 +25796,6 @@ function getPeerCred(fd) {
|
|
|
25725
25796
|
return null;
|
|
25726
25797
|
}
|
|
25727
25798
|
}
|
|
25728
|
-
|
|
25729
25799
|
// ../src/vault/broker/peercred.ts
|
|
25730
25800
|
function isReservedAgentName(name) {
|
|
25731
25801
|
return RESERVED_AGENT_NAMES.has(name);
|
|
@@ -26258,7 +26328,7 @@ import {
|
|
|
26258
26328
|
openSync as openSync2,
|
|
26259
26329
|
rmSync,
|
|
26260
26330
|
statSync as statSync5,
|
|
26261
|
-
writeSync
|
|
26331
|
+
writeSync as writeSync2
|
|
26262
26332
|
} from "node:fs";
|
|
26263
26333
|
import { join as join13 } from "node:path";
|
|
26264
26334
|
import { tmpdir } from "node:os";
|
|
@@ -26325,7 +26395,7 @@ function writeFileExclusive(filePath, content) {
|
|
|
26325
26395
|
const buf = typeof content === "string" ? Buffer.from(content, "utf8") : content;
|
|
26326
26396
|
const fd = openSync2(filePath, fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_EXCL, 384);
|
|
26327
26397
|
try {
|
|
26328
|
-
|
|
26398
|
+
writeSync2(fd, buf);
|
|
26329
26399
|
} finally {
|
|
26330
26400
|
closeSync2(fd);
|
|
26331
26401
|
}
|
|
@@ -28861,7 +28931,7 @@ __export(exports_history, {
|
|
|
28861
28931
|
checkpointWal: () => checkpointWal,
|
|
28862
28932
|
_resetForTests: () => _resetForTests
|
|
28863
28933
|
});
|
|
28864
|
-
import { chmodSync as chmodSync3, existsSync as
|
|
28934
|
+
import { chmodSync as chmodSync3, existsSync as existsSync17, mkdirSync as mkdirSync15 } from "fs";
|
|
28865
28935
|
import { join as join16 } from "path";
|
|
28866
28936
|
function loadDatabaseClass() {
|
|
28867
28937
|
if (DatabaseClass != null)
|
|
@@ -28890,6 +28960,7 @@ function initHistory(stateDir, retentionDays = 30) {
|
|
|
28890
28960
|
db = new Database(path, { create: true });
|
|
28891
28961
|
db.exec("PRAGMA journal_mode = WAL");
|
|
28892
28962
|
db.exec("PRAGMA synchronous = NORMAL");
|
|
28963
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
28893
28964
|
db.exec(`
|
|
28894
28965
|
CREATE TABLE IF NOT EXISTS messages (
|
|
28895
28966
|
chat_id TEXT NOT NULL,
|
|
@@ -28922,7 +28993,7 @@ function initHistory(stateDir, retentionDays = 30) {
|
|
|
28922
28993
|
}
|
|
28923
28994
|
for (const suffix of ["", "-shm", "-wal"]) {
|
|
28924
28995
|
const f = path + suffix;
|
|
28925
|
-
if (
|
|
28996
|
+
if (existsSync17(f)) {
|
|
28926
28997
|
try {
|
|
28927
28998
|
chmodSync3(f, 420);
|
|
28928
28999
|
} catch {}
|
|
@@ -28947,7 +29018,7 @@ function checkpointWal() {
|
|
|
28947
29018
|
if (dbPath) {
|
|
28948
29019
|
for (const suffix of ["-shm", "-wal"]) {
|
|
28949
29020
|
const f = dbPath + suffix;
|
|
28950
|
-
if (
|
|
29021
|
+
if (existsSync17(f)) {
|
|
28951
29022
|
try {
|
|
28952
29023
|
chmodSync3(f, 420);
|
|
28953
29024
|
} catch {}
|
|
@@ -29114,11 +29185,11 @@ var init_history = __esm(() => {
|
|
|
29114
29185
|
});
|
|
29115
29186
|
|
|
29116
29187
|
// quota-check.ts
|
|
29117
|
-
import { readFileSync as readFileSync14, existsSync as
|
|
29188
|
+
import { readFileSync as readFileSync14, existsSync as existsSync18 } from "fs";
|
|
29118
29189
|
import { join as join17 } from "path";
|
|
29119
29190
|
function readOauthToken(claudeConfigDir) {
|
|
29120
29191
|
const tokenFile = join17(claudeConfigDir, ".oauth-token");
|
|
29121
|
-
if (!
|
|
29192
|
+
if (!existsSync18(tokenFile))
|
|
29122
29193
|
return null;
|
|
29123
29194
|
try {
|
|
29124
29195
|
const raw = readFileSync14(tokenFile, "utf-8").trim();
|
|
@@ -29455,7 +29526,7 @@ function renderAuthLine(state4, agentName3, now = Date.now()) {
|
|
|
29455
29526
|
}
|
|
29456
29527
|
|
|
29457
29528
|
// gateway/quota-cache.ts
|
|
29458
|
-
import { existsSync as
|
|
29529
|
+
import { existsSync as existsSync31, readFileSync as readFileSync29, writeFileSync as writeFileSync20, mkdirSync as mkdirSync22 } from "fs";
|
|
29459
29530
|
import { join as join28, dirname as dirname9 } from "path";
|
|
29460
29531
|
function defaultCachePath() {
|
|
29461
29532
|
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join28(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
|
|
@@ -29463,7 +29534,7 @@ function defaultCachePath() {
|
|
|
29463
29534
|
function readQuotaCache(opts = {}) {
|
|
29464
29535
|
const path = opts.path ?? defaultCachePath();
|
|
29465
29536
|
const now = opts.now ?? Date.now();
|
|
29466
|
-
if (!
|
|
29537
|
+
if (!existsSync31(path))
|
|
29467
29538
|
return null;
|
|
29468
29539
|
let entry;
|
|
29469
29540
|
try {
|
|
@@ -29494,7 +29565,7 @@ function writeQuotaCache(result, opts = {}) {
|
|
|
29494
29565
|
};
|
|
29495
29566
|
try {
|
|
29496
29567
|
mkdirSync22(dirname9(path), { recursive: true });
|
|
29497
|
-
|
|
29568
|
+
writeFileSync20(path, JSON.stringify(entry, null, 2), { mode: 384 });
|
|
29498
29569
|
} catch {}
|
|
29499
29570
|
}
|
|
29500
29571
|
var DEFAULT_TTL_MS4, RATE_LIMIT_TTL_MS;
|
|
@@ -29504,10 +29575,10 @@ var init_quota_cache = __esm(() => {
|
|
|
29504
29575
|
});
|
|
29505
29576
|
|
|
29506
29577
|
// gateway/boot-probes.ts
|
|
29507
|
-
import { readFileSync as readFileSync30, readdirSync as readdirSync6, existsSync as
|
|
29578
|
+
import { readFileSync as readFileSync30, readdirSync as readdirSync6, existsSync as existsSync32 } from "fs";
|
|
29508
29579
|
import { join as join29 } from "path";
|
|
29509
29580
|
import { execFile as execFileCb } from "child_process";
|
|
29510
|
-
import { promisify
|
|
29581
|
+
import { promisify } from "util";
|
|
29511
29582
|
async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
29512
29583
|
let timer3;
|
|
29513
29584
|
const timeout = new Promise((resolve6) => {
|
|
@@ -29572,7 +29643,7 @@ async function probeAccount(agentDir) {
|
|
|
29572
29643
|
join29(claudeDir, ".oauth-token.meta.json"),
|
|
29573
29644
|
join29(claudeDir, "accounts", "default", ".oauth-token.meta.json")
|
|
29574
29645
|
]) {
|
|
29575
|
-
if (
|
|
29646
|
+
if (existsSync32(candidate)) {
|
|
29576
29647
|
try {
|
|
29577
29648
|
const meta = JSON.parse(readFileSync30(candidate, "utf8"));
|
|
29578
29649
|
if (meta.expiresAt) {
|
|
@@ -29749,7 +29820,7 @@ async function resolveTmuxSupervisorPid(agentName3, execFileImpl) {
|
|
|
29749
29820
|
if (!cgroup)
|
|
29750
29821
|
return null;
|
|
29751
29822
|
const procsPath = `/sys/fs/cgroup${cgroup}/cgroup.procs`;
|
|
29752
|
-
if (!
|
|
29823
|
+
if (!existsSync32(procsPath))
|
|
29753
29824
|
return null;
|
|
29754
29825
|
const pidsRaw = readFileSync30(procsPath, "utf-8");
|
|
29755
29826
|
const pids = pidsRaw.split(`
|
|
@@ -29826,7 +29897,7 @@ async function probeAgentProcess(agentName3, opts = {}) {
|
|
|
29826
29897
|
const retryIntervalMs = opts.retryIntervalMs ?? AGENT_RETRY_INTERVAL_MS;
|
|
29827
29898
|
const retryMaxMs = opts.retryMaxMs ?? AGENT_RETRY_MAX_MS;
|
|
29828
29899
|
const sleep2 = opts.sleepImpl ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
|
|
29829
|
-
const execFileFn = opts.execFileImpl ??
|
|
29900
|
+
const execFileFn = opts.execFileImpl ?? execFile;
|
|
29830
29901
|
return withTimeout("Agent", (async () => {
|
|
29831
29902
|
const startMs = Date.now();
|
|
29832
29903
|
while (true) {
|
|
@@ -29868,7 +29939,7 @@ async function* watchAgentProcess(agentName3, opts = {}) {
|
|
|
29868
29939
|
const pollIntervalMs = opts.pollIntervalMs ?? AGENT_LIVE_POLL_INTERVAL_MS;
|
|
29869
29940
|
const followupRepollMs = opts.followupRepollMs ?? AGENT_LIVE_FOLLOWUP_REPOLL_MS;
|
|
29870
29941
|
const sleep2 = opts.sleepImpl ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
|
|
29871
|
-
const execFileFn = opts.execFileImpl ??
|
|
29942
|
+
const execFileFn = opts.execFileImpl ?? execFile;
|
|
29872
29943
|
const now = opts.nowImpl ?? (() => Date.now());
|
|
29873
29944
|
const startMs = now();
|
|
29874
29945
|
let lastYieldedDetail = null;
|
|
@@ -29958,7 +30029,7 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
|
|
|
29958
30029
|
claudeConfigDir,
|
|
29959
30030
|
join29(claudeConfigDir, "accounts", "default")
|
|
29960
30031
|
]) {
|
|
29961
|
-
if (
|
|
30032
|
+
if (existsSync32(join29(candidate, ".oauth-token"))) {
|
|
29962
30033
|
claudeDirForProbe = candidate;
|
|
29963
30034
|
break;
|
|
29964
30035
|
}
|
|
@@ -30129,7 +30200,7 @@ async function probeUds(label, socketPath, opts = {}) {
|
|
|
30129
30200
|
}
|
|
30130
30201
|
return withTimeout(label, (async () => {
|
|
30131
30202
|
if (!opts.connectImpl) {
|
|
30132
|
-
if (!
|
|
30203
|
+
if (!existsSync32(socketPath)) {
|
|
30133
30204
|
return {
|
|
30134
30205
|
status: "fail",
|
|
30135
30206
|
label,
|
|
@@ -30285,11 +30356,11 @@ async function probeConnections(agentDir, opts = {}) {
|
|
|
30285
30356
|
};
|
|
30286
30357
|
})());
|
|
30287
30358
|
}
|
|
30288
|
-
var
|
|
30359
|
+
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
30360
|
var init_boot_probes = __esm(() => {
|
|
30290
30361
|
init_quota_cache();
|
|
30291
30362
|
init_quota_check();
|
|
30292
|
-
|
|
30363
|
+
execFile = promisify(execFileCb);
|
|
30293
30364
|
realProcFs = {
|
|
30294
30365
|
readdir: (p) => readdirSync6(p),
|
|
30295
30366
|
readFile: (p) => readFileSync30(p, "utf-8")
|
|
@@ -30300,16 +30371,16 @@ var init_boot_probes = __esm(() => {
|
|
|
30300
30371
|
const { statSync: statSync10 } = __require("fs");
|
|
30301
30372
|
return statSync10(p).mtimeMs;
|
|
30302
30373
|
},
|
|
30303
|
-
exists: (p) =>
|
|
30374
|
+
exists: (p) => existsSync32(p)
|
|
30304
30375
|
};
|
|
30305
30376
|
realSkillsFs = {
|
|
30306
30377
|
readdir: (p) => readdirSync6(p),
|
|
30307
|
-
exists: (p) =>
|
|
30378
|
+
exists: (p) => existsSync32(p)
|
|
30308
30379
|
};
|
|
30309
30380
|
});
|
|
30310
30381
|
|
|
30311
30382
|
// gateway/boot-issue-cache.ts
|
|
30312
|
-
import { existsSync as
|
|
30383
|
+
import { existsSync as existsSync33, readFileSync as readFileSync31, writeFileSync as writeFileSync21, mkdirSync as mkdirSync23, renameSync as renameSync11 } from "fs";
|
|
30313
30384
|
import { dirname as dirname10 } from "path";
|
|
30314
30385
|
function fingerprintProbe(key, r) {
|
|
30315
30386
|
if (r.status === "ok")
|
|
@@ -30389,7 +30460,7 @@ function diffProbes(probes, cache, opts = {}) {
|
|
|
30389
30460
|
return out;
|
|
30390
30461
|
}
|
|
30391
30462
|
function loadCache(path, now = Date.now) {
|
|
30392
|
-
if (!
|
|
30463
|
+
if (!existsSync33(path))
|
|
30393
30464
|
return { ...EMPTY_CACHE, probes: {} };
|
|
30394
30465
|
let raw;
|
|
30395
30466
|
try {
|
|
@@ -30438,7 +30509,7 @@ function applyAndSave(path, cache, diff) {
|
|
|
30438
30509
|
try {
|
|
30439
30510
|
mkdirSync23(dirname10(path), { recursive: true });
|
|
30440
30511
|
const tmp = `${path}.tmp`;
|
|
30441
|
-
|
|
30512
|
+
writeFileSync21(tmp, JSON.stringify(next), { mode: 384 });
|
|
30442
30513
|
renameSync11(tmp, path);
|
|
30443
30514
|
} catch {}
|
|
30444
30515
|
return next;
|
|
@@ -30452,7 +30523,7 @@ var init_boot_issue_cache = __esm(() => {
|
|
|
30452
30523
|
|
|
30453
30524
|
// gateway/config-snapshot.ts
|
|
30454
30525
|
import { createHash as createHash2 } from "crypto";
|
|
30455
|
-
import { existsSync as
|
|
30526
|
+
import { existsSync as existsSync34, readFileSync as readFileSync32, writeFileSync as writeFileSync22, mkdirSync as mkdirSync24, renameSync as renameSync12 } from "fs";
|
|
30456
30527
|
import { dirname as dirname11 } from "path";
|
|
30457
30528
|
function hashStringArray(items) {
|
|
30458
30529
|
if (!items || items.length === 0)
|
|
@@ -30517,7 +30588,7 @@ function renderConfigChangeDim(dim) {
|
|
|
30517
30588
|
}
|
|
30518
30589
|
}
|
|
30519
30590
|
function loadSnapshot(path, now = Date.now) {
|
|
30520
|
-
if (!
|
|
30591
|
+
if (!existsSync34(path))
|
|
30521
30592
|
return null;
|
|
30522
30593
|
let raw;
|
|
30523
30594
|
try {
|
|
@@ -30553,7 +30624,7 @@ function persistSnapshot(path, snapshot) {
|
|
|
30553
30624
|
try {
|
|
30554
30625
|
mkdirSync24(dirname11(path), { recursive: true });
|
|
30555
30626
|
const tmp = `${path}.tmp`;
|
|
30556
|
-
|
|
30627
|
+
writeFileSync22(tmp, JSON.stringify(snapshot), { mode: 384 });
|
|
30557
30628
|
renameSync12(tmp, path);
|
|
30558
30629
|
} catch {}
|
|
30559
30630
|
}
|
|
@@ -30562,7 +30633,7 @@ var init_config_snapshot = __esm(() => {
|
|
|
30562
30633
|
});
|
|
30563
30634
|
|
|
30564
30635
|
// gateway/boot-card-msgid.ts
|
|
30565
|
-
import { readFileSync as readFileSync33, writeFileSync as
|
|
30636
|
+
import { readFileSync as readFileSync33, writeFileSync as writeFileSync23 } from "node:fs";
|
|
30566
30637
|
function bootCardChatKey(chatId, threadId) {
|
|
30567
30638
|
return `${chatId}:${threadId ?? ""}`;
|
|
30568
30639
|
}
|
|
@@ -30587,7 +30658,7 @@ function saveBootCardMsgId(path, chatKey3, messageId) {
|
|
|
30587
30658
|
if (store2[chatKey3] === messageId)
|
|
30588
30659
|
return;
|
|
30589
30660
|
store2[chatKey3] = messageId;
|
|
30590
|
-
|
|
30661
|
+
writeFileSync23(path, JSON.stringify(store2), "utf8");
|
|
30591
30662
|
} catch {}
|
|
30592
30663
|
}
|
|
30593
30664
|
var init_boot_card_msgid = () => {};
|
|
@@ -31476,7 +31547,7 @@ __export(exports_materialize_bot_token, {
|
|
|
31476
31547
|
materializeBotToken: () => materializeBotToken,
|
|
31477
31548
|
BotTokenMaterializeError: () => BotTokenMaterializeError
|
|
31478
31549
|
});
|
|
31479
|
-
import { existsSync as
|
|
31550
|
+
import { existsSync as existsSync42 } from "node:fs";
|
|
31480
31551
|
function pickConfiguredToken(config, agentName3) {
|
|
31481
31552
|
if (agentName3) {
|
|
31482
31553
|
const agent = config.agents?.[agentName3];
|
|
@@ -31490,7 +31561,7 @@ function tryDirectVaultRead3(ref, config, passphrase) {
|
|
|
31490
31561
|
if (!passphrase)
|
|
31491
31562
|
return null;
|
|
31492
31563
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
31493
|
-
if (!
|
|
31564
|
+
if (!existsSync42(vaultPath))
|
|
31494
31565
|
return null;
|
|
31495
31566
|
try {
|
|
31496
31567
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -31582,7 +31653,7 @@ __export(exports_tmux, {
|
|
|
31582
31653
|
captureAgentPane: () => captureAgentPane
|
|
31583
31654
|
});
|
|
31584
31655
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
31585
|
-
import { mkdirSync as mkdirSync31, readdirSync as readdirSync8, statSync as statSync14, unlinkSync as unlinkSync15, writeFileSync as
|
|
31656
|
+
import { mkdirSync as mkdirSync31, readdirSync as readdirSync8, statSync as statSync14, unlinkSync as unlinkSync15, writeFileSync as writeFileSync29 } from "node:fs";
|
|
31586
31657
|
import { resolve as resolve7 } from "node:path";
|
|
31587
31658
|
function captureAgentPane(opts) {
|
|
31588
31659
|
const { agentName: agentName3, agentDir, reason } = opts;
|
|
@@ -31628,7 +31699,7 @@ function captureAgentPane(opts) {
|
|
|
31628
31699
|
` + `
|
|
31629
31700
|
`;
|
|
31630
31701
|
try {
|
|
31631
|
-
|
|
31702
|
+
writeFileSync29(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
|
|
31632
31703
|
mode: 420
|
|
31633
31704
|
});
|
|
31634
31705
|
} catch (err) {
|
|
@@ -32074,11 +32145,11 @@ function registerApprovalsCommands(bot, opts) {
|
|
|
32074
32145
|
return `\`${d.id.slice(0, 8)}\` ` + `${escapeMarkdown(d.agent_unit)} \u2192 ` + `\`${codeSpanSafe(d.scope)}\` ` + `(${escapeMarkdown(d.action)}, ${ttl}) ` + `\u00b7 /approvals revoke ${escapeMarkdown(d.id)}`;
|
|
32075
32146
|
}).join(`
|
|
32076
32147
|
`);
|
|
32077
|
-
await ctx.replyWithRichMessage(richMessage(`**Active approvals**
|
|
32148
|
+
await ctx.replyWithRichMessage(richMessage(hardenCardBreaks(`**Active approvals**
|
|
32078
32149
|
|
|
32079
32150
|
${summary}
|
|
32080
32151
|
|
|
32081
|
-
${detail}`));
|
|
32152
|
+
${detail}`)));
|
|
32082
32153
|
return;
|
|
32083
32154
|
}
|
|
32084
32155
|
if (sub === "revoke") {
|
|
@@ -32191,7 +32262,7 @@ import { randomBytes as randomBytes8 } from "crypto";
|
|
|
32191
32262
|
import { execFileSync as execFileSync6, execSync as execSync2, spawn } from "child_process";
|
|
32192
32263
|
import {
|
|
32193
32264
|
readFileSync as readFileSync41,
|
|
32194
|
-
writeFileSync as
|
|
32265
|
+
writeFileSync as writeFileSync30,
|
|
32195
32266
|
mkdirSync as mkdirSync32,
|
|
32196
32267
|
readdirSync as readdirSync9,
|
|
32197
32268
|
rmSync as rmSync4,
|
|
@@ -32201,7 +32272,7 @@ import {
|
|
|
32201
32272
|
chmodSync as chmodSync7,
|
|
32202
32273
|
openSync as openSync9,
|
|
32203
32274
|
closeSync as closeSync9,
|
|
32204
|
-
existsSync as
|
|
32275
|
+
existsSync as existsSync43,
|
|
32205
32276
|
unlinkSync as unlinkSync16,
|
|
32206
32277
|
appendFileSync as appendFileSync6
|
|
32207
32278
|
} from "fs";
|
|
@@ -34776,6 +34847,12 @@ function formatFeedElapsed(ms) {
|
|
|
34776
34847
|
const m = Math.floor(s / 60);
|
|
34777
34848
|
return `${m}m${(s % 60).toString().padStart(2, "0")}s`;
|
|
34778
34849
|
}
|
|
34850
|
+
var STEP_TIMER_MIN_MS = 1e4;
|
|
34851
|
+
function formatStepSuffix(stepElapsedMs) {
|
|
34852
|
+
if (stepElapsedMs < STEP_TIMER_MIN_MS)
|
|
34853
|
+
return "";
|
|
34854
|
+
return ` \u00b7 ${formatFeedElapsed(stepElapsedMs)}`;
|
|
34855
|
+
}
|
|
34779
34856
|
function escapeStepLine(raw) {
|
|
34780
34857
|
const cleaned = stripMarkdown(raw).replace(/\s+/g, " ").trim();
|
|
34781
34858
|
return escapeMarkdown(truncate(cleaned, STATUS_LINE_MAX));
|
|
@@ -34976,9 +35053,10 @@ function createWorkerActivityFeed(opts) {
|
|
|
34976
35053
|
const line = view.latestSummary.trim();
|
|
34977
35054
|
if (line.length === 0)
|
|
34978
35055
|
return;
|
|
34979
|
-
if (h.narrative
|
|
35056
|
+
if (h.narrative.includes(line))
|
|
34980
35057
|
return;
|
|
34981
35058
|
h.narrative.push(line);
|
|
35059
|
+
h.stepStartedAtMs = nowFn();
|
|
34982
35060
|
if (h.narrative.length > STATUS_ROLLING_LINES) {
|
|
34983
35061
|
h.narrative.splice(0, h.narrative.length - STATUS_ROLLING_LINES);
|
|
34984
35062
|
}
|
|
@@ -35045,7 +35123,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
35045
35123
|
function heartbeatTick() {
|
|
35046
35124
|
const now = nowFn();
|
|
35047
35125
|
for (const h of handles.values()) {
|
|
35048
|
-
if (h.
|
|
35126
|
+
if (!handles.has(h.agentId))
|
|
35049
35127
|
continue;
|
|
35050
35128
|
if (h.lastView == null)
|
|
35051
35129
|
continue;
|
|
@@ -35053,13 +35131,23 @@ function createWorkerActivityFeed(opts) {
|
|
|
35053
35131
|
continue;
|
|
35054
35132
|
if (now < h.cooldownUntil)
|
|
35055
35133
|
continue;
|
|
35134
|
+
const liveElapsed = h.dispatchAtMs != null ? now - h.dispatchAtMs : h.lastView.elapsedMs;
|
|
35135
|
+
if (h.messageId == null) {
|
|
35136
|
+
if (liveElapsed < firstPaintMin)
|
|
35137
|
+
continue;
|
|
35138
|
+
const view2 = { ...h.lastView, elapsedMs: Math.max(h.lastView.elapsedMs, liveElapsed) };
|
|
35139
|
+
h.chain = h.chain.then(() => doUpdate(h, view2)).catch((err) => {
|
|
35140
|
+
log(`worker-feed: heartbeat first-paint chain error ${h.agentId}: ${err.message}`);
|
|
35141
|
+
});
|
|
35142
|
+
continue;
|
|
35143
|
+
}
|
|
35056
35144
|
if (now - h.lastEditAt < minEditInterval)
|
|
35057
35145
|
continue;
|
|
35058
35146
|
const stale = now - h.lastEditAt >= heartbeatTickMs;
|
|
35059
35147
|
if (!stale)
|
|
35060
35148
|
continue;
|
|
35061
|
-
const
|
|
35062
|
-
const liveSuffix =
|
|
35149
|
+
const stepElapsed = h.stepStartedAtMs != null ? now - h.stepStartedAtMs : liveElapsed;
|
|
35150
|
+
const liveSuffix = formatStepSuffix(stepElapsed);
|
|
35063
35151
|
const view = { ...h.lastView, elapsedMs: Math.max(h.lastView.elapsedMs, liveElapsed) };
|
|
35064
35152
|
h.chain = h.chain.then(() => doUpdate(h, view, liveSuffix)).catch((err) => {
|
|
35065
35153
|
log(`worker-feed: heartbeat chain error ${h.agentId}: ${err.message}`);
|
|
@@ -35093,7 +35181,8 @@ function createWorkerActivityFeed(opts) {
|
|
|
35093
35181
|
narrative: [],
|
|
35094
35182
|
chain: Promise.resolve(),
|
|
35095
35183
|
lastView: null,
|
|
35096
|
-
dispatchAtMs: null
|
|
35184
|
+
dispatchAtMs: null,
|
|
35185
|
+
stepStartedAtMs: null
|
|
35097
35186
|
};
|
|
35098
35187
|
handles.set(agentId, h);
|
|
35099
35188
|
}
|
|
@@ -35285,8 +35374,8 @@ function renderVaultRequestAccessCard(req) {
|
|
|
35285
35374
|
}
|
|
35286
35375
|
lines.push("");
|
|
35287
35376
|
lines.push(`_Tap Approve to mint a scoped grant token (same flow as \`switchroom vault grant\`). Tap Deny to refuse \u2014 the agent will receive a denial result._`);
|
|
35288
|
-
return lines.join(`
|
|
35289
|
-
`);
|
|
35377
|
+
return hardenCardBreaks(lines.join(`
|
|
35378
|
+
`));
|
|
35290
35379
|
}
|
|
35291
35380
|
|
|
35292
35381
|
// gateway/permission-card-store.ts
|
|
@@ -35389,6 +35478,12 @@ function formatFeedElapsed2(ms) {
|
|
|
35389
35478
|
const m = Math.floor(s / 60);
|
|
35390
35479
|
return `${m}m${(s % 60).toString().padStart(2, "0")}s`;
|
|
35391
35480
|
}
|
|
35481
|
+
var STEP_TIMER_MIN_MS2 = 1e4;
|
|
35482
|
+
function formatStepSuffix2(stepElapsedMs) {
|
|
35483
|
+
if (stepElapsedMs < STEP_TIMER_MIN_MS2)
|
|
35484
|
+
return "";
|
|
35485
|
+
return ` \u00b7 ${formatFeedElapsed2(stepElapsedMs)}`;
|
|
35486
|
+
}
|
|
35392
35487
|
function escapeStepLine2(raw) {
|
|
35393
35488
|
const cleaned = stripMarkdown(raw).replace(/\s+/g, " ").trim();
|
|
35394
35489
|
return escapeMarkdown(truncate(cleaned, STATUS_LINE_MAX));
|
|
@@ -35767,9 +35862,6 @@ function chatKey(chatId, threadId) {
|
|
|
35767
35862
|
const t = threadId == null || threadId === 0 ? "_" : String(threadId);
|
|
35768
35863
|
return `${chatId}:${t}`;
|
|
35769
35864
|
}
|
|
35770
|
-
function chatKeyWithSuffix(chatId, threadId, suffix) {
|
|
35771
|
-
return `${chatKey(chatId, threadId)}:${suffix}`;
|
|
35772
|
-
}
|
|
35773
35865
|
function chatIdOfChatKey(key) {
|
|
35774
35866
|
const idx = key.indexOf(":");
|
|
35775
35867
|
return idx === -1 ? key : key.slice(0, idx);
|
|
@@ -36218,185 +36310,6 @@ function handlePtyPartialPure(text, state, deps) {
|
|
|
36218
36310
|
stream.update(text).catch(() => {});
|
|
36219
36311
|
return created ? "update-new" : "update-existing";
|
|
36220
36312
|
}
|
|
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
36313
|
// chat-lock.ts
|
|
36401
36314
|
function createChatLock() {
|
|
36402
36315
|
const chains = new Map;
|
|
@@ -43677,8 +43590,8 @@ function defaultAddAccount(label, credentials, opts) {
|
|
|
43677
43590
|
}
|
|
43678
43591
|
|
|
43679
43592
|
// ../src/auth/broker/client.ts
|
|
43680
|
-
import * as net3 from "node:net";
|
|
43681
43593
|
init_protocol();
|
|
43594
|
+
import * as net3 from "node:net";
|
|
43682
43595
|
import { homedir as homedir6 } from "node:os";
|
|
43683
43596
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
43684
43597
|
import { join as join14 } from "node:path";
|
|
@@ -43997,13 +43910,13 @@ class AuthBrokerClient2 {
|
|
|
43997
43910
|
init_loader();
|
|
43998
43911
|
init_resolver();
|
|
43999
43912
|
init_vault();
|
|
44000
|
-
import { existsSync as
|
|
43913
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
44001
43914
|
var DEFAULT_VOICE_API_KEY_REF = "vault:openai/api-key";
|
|
44002
43915
|
function tryDirectVaultRead(ref, config, passphrase) {
|
|
44003
43916
|
if (!passphrase)
|
|
44004
43917
|
return null;
|
|
44005
43918
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
44006
|
-
if (!
|
|
43919
|
+
if (!existsSync12(vaultPath))
|
|
44007
43920
|
return null;
|
|
44008
43921
|
try {
|
|
44009
43922
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -44059,14 +43972,14 @@ async function materializeVoiceKey(opts = {}, logger2 = (line) => process.stderr
|
|
|
44059
43972
|
init_loader();
|
|
44060
43973
|
init_resolver();
|
|
44061
43974
|
init_vault();
|
|
44062
|
-
import { existsSync as
|
|
43975
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
44063
43976
|
var VOICE_SIDECAR_TOKEN_KEY = "voice/sidecar-token";
|
|
44064
43977
|
var DEFAULT_VOICE_SIDECAR_TOKEN_REF = `vault:${VOICE_SIDECAR_TOKEN_KEY}`;
|
|
44065
43978
|
function tryDirectVaultRead2(ref, config, passphrase) {
|
|
44066
43979
|
if (!passphrase)
|
|
44067
43980
|
return null;
|
|
44068
43981
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
44069
|
-
if (!
|
|
43982
|
+
if (!existsSync13(vaultPath))
|
|
44070
43983
|
return null;
|
|
44071
43984
|
try {
|
|
44072
43985
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -44119,14 +44032,14 @@ async function materializeSidecarToken(opts = {}, logger2 = (line) => process.st
|
|
|
44119
44032
|
}
|
|
44120
44033
|
|
|
44121
44034
|
// ../src/setup/host-capabilities.ts
|
|
44122
|
-
import { existsSync as
|
|
44035
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync12, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "node:fs";
|
|
44123
44036
|
init_paths();
|
|
44124
44037
|
function hostCapabilitiesPath() {
|
|
44125
44038
|
return resolveStatePath2("host-capabilities.json");
|
|
44126
44039
|
}
|
|
44127
44040
|
function loadHostCapabilities() {
|
|
44128
44041
|
const path = hostCapabilitiesPath();
|
|
44129
|
-
if (!
|
|
44042
|
+
if (!existsSync14(path))
|
|
44130
44043
|
return null;
|
|
44131
44044
|
try {
|
|
44132
44045
|
const parsed = JSON.parse(readFileSync11(path, "utf-8"));
|
|
@@ -44227,7 +44140,7 @@ function resolveExhaustUntil(resetAtMs, now = Date.now()) {
|
|
|
44227
44140
|
|
|
44228
44141
|
// gateway/auth-add-flow.ts
|
|
44229
44142
|
import { execFileSync } from "node:child_process";
|
|
44230
|
-
import { existsSync as
|
|
44143
|
+
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
44144
|
import { homedir as homedir7 } from "node:os";
|
|
44232
44145
|
import { join as join15 } from "node:path";
|
|
44233
44146
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
@@ -44236,8 +44149,8 @@ import { randomBytes as randomBytes4 } from "node:crypto";
|
|
|
44236
44149
|
import {
|
|
44237
44150
|
readFileSync as readFileSync12,
|
|
44238
44151
|
readdirSync as readdirSync3,
|
|
44239
|
-
existsSync as
|
|
44240
|
-
writeFileSync as
|
|
44152
|
+
existsSync as existsSync15,
|
|
44153
|
+
writeFileSync as writeFileSync9,
|
|
44241
44154
|
mkdirSync as mkdirSync13,
|
|
44242
44155
|
mkdtempSync as mkdtempSync2,
|
|
44243
44156
|
rmSync as rmSync2,
|
|
@@ -44263,7 +44176,7 @@ function parseSetupTokenUrl(output) {
|
|
|
44263
44176
|
}
|
|
44264
44177
|
function readTokenFromCredentialsFile(credentialsFilePath) {
|
|
44265
44178
|
try {
|
|
44266
|
-
if (!
|
|
44179
|
+
if (!existsSync15(credentialsFilePath))
|
|
44267
44180
|
return null;
|
|
44268
44181
|
const raw = readFileSync12(credentialsFilePath, "utf-8");
|
|
44269
44182
|
const parsed = JSON.parse(raw);
|
|
@@ -44335,7 +44248,7 @@ function cleanScratchDir(scratchDir) {
|
|
|
44335
44248
|
var AUTH_TMUX_SESSION_FILE = ".auth-tmux-session";
|
|
44336
44249
|
function sweepOrphanSessions(home2, tmux, nowMs2 = Date.now()) {
|
|
44337
44250
|
const inProgressDir = join15(home2, ".switchroom", "accounts", ".in-progress");
|
|
44338
|
-
if (!
|
|
44251
|
+
if (!existsSync16(inProgressDir))
|
|
44339
44252
|
return;
|
|
44340
44253
|
let entries;
|
|
44341
44254
|
try {
|
|
@@ -44347,7 +44260,7 @@ function sweepOrphanSessions(home2, tmux, nowMs2 = Date.now()) {
|
|
|
44347
44260
|
for (const entry of entries) {
|
|
44348
44261
|
const dir = join15(inProgressDir, entry);
|
|
44349
44262
|
const sessionFile = join15(dir, AUTH_TMUX_SESSION_FILE);
|
|
44350
|
-
if (!
|
|
44263
|
+
if (!existsSync16(sessionFile))
|
|
44351
44264
|
continue;
|
|
44352
44265
|
let fileContents;
|
|
44353
44266
|
let fileMtime;
|
|
@@ -44386,7 +44299,7 @@ async function startAccountAuthSession(label, opts = {}) {
|
|
|
44386
44299
|
const tmuxSocket = `switchroom-${agentName3}`;
|
|
44387
44300
|
const tmuxSession = `auth-add-${label}-${hexSuffix}`.slice(0, 64);
|
|
44388
44301
|
try {
|
|
44389
|
-
|
|
44302
|
+
writeFileSync10(join15(scratchDir, AUTH_TMUX_SESSION_FILE), `${tmuxSocket}
|
|
44390
44303
|
${tmuxSession}`, "utf8");
|
|
44391
44304
|
} catch {}
|
|
44392
44305
|
const sessionEnv = {
|
|
@@ -44445,7 +44358,7 @@ async function submitAccountAuthCode(flow, code, opts = {}) {
|
|
|
44445
44358
|
const deadline = Date.now() + pollTimeoutMs;
|
|
44446
44359
|
while (Date.now() < deadline) {
|
|
44447
44360
|
await new Promise((r) => setTimeout(r, pollIntervalMs));
|
|
44448
|
-
if (
|
|
44361
|
+
if (existsSync16(credentialsPath)) {
|
|
44449
44362
|
const token = readTokenFromCredentialsFile(credentialsPath);
|
|
44450
44363
|
if (token) {
|
|
44451
44364
|
try {
|
|
@@ -44459,7 +44372,7 @@ async function submitAccountAuthCode(flow, code, opts = {}) {
|
|
|
44459
44372
|
}
|
|
44460
44373
|
}
|
|
44461
44374
|
if (!tmux.hasSession(flow.tmuxSocket, flow.tmuxSession)) {
|
|
44462
|
-
if (!
|
|
44375
|
+
if (!existsSync16(credentialsPath)) {
|
|
44463
44376
|
cleanScratchDir(flow.scratchDir);
|
|
44464
44377
|
throw new Error("claude setup-token exited without writing credentials \u2014 the code may be invalid or expired");
|
|
44465
44378
|
}
|
|
@@ -45220,26 +45133,29 @@ function repairEscapedWhitespace(text) {
|
|
|
45220
45133
|
return text;
|
|
45221
45134
|
const nonce = Math.random().toString(36).slice(2);
|
|
45222
45135
|
const BACKSLASH_PH = `\x00BK${nonce}_`;
|
|
45223
|
-
const { masked, restore: restore2 } =
|
|
45136
|
+
const { masked, restore: restore2 } = maskCodeRegions2(text, nonce);
|
|
45224
45137
|
const unescaped = masked.replace(/\\\\/g, BACKSLASH_PH).replace(/\\n/g, `
|
|
45225
45138
|
`).replace(/\\r/g, "\r").replace(/\\t/g, "\t").replace(/\\"/g, '"').replace(new RegExp(BACKSLASH_PH.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"), "\\");
|
|
45226
45139
|
return restore2(unescaped);
|
|
45227
45140
|
}
|
|
45228
|
-
function
|
|
45229
|
-
const
|
|
45141
|
+
function maskCodeRegions2(text, nonce) {
|
|
45142
|
+
const FENCE_MASK_PH = `\x00RMF${nonce}_`;
|
|
45143
|
+
const INLINE_MASK_PH = `\x00RMI${nonce}_`;
|
|
45230
45144
|
const codeMasks = [];
|
|
45231
45145
|
const masked = text.replace(/```[\s\S]*?```/g, (m) => {
|
|
45232
45146
|
const idx = codeMasks.length;
|
|
45233
45147
|
codeMasks.push(m);
|
|
45234
|
-
return `${
|
|
45148
|
+
return `${FENCE_MASK_PH}${idx}\x00`;
|
|
45235
45149
|
}).replace(/`[^`\n]+`/g, (m) => {
|
|
45236
45150
|
const idx = codeMasks.length;
|
|
45237
45151
|
codeMasks.push(m);
|
|
45238
|
-
return `${
|
|
45152
|
+
return `${INLINE_MASK_PH}${idx}\x00`;
|
|
45239
45153
|
});
|
|
45240
|
-
const
|
|
45241
|
-
const
|
|
45242
|
-
|
|
45154
|
+
const escNonce = nonce.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
45155
|
+
const anyMaskRe = new RegExp(`\x00RM[FI]${escNonce}_(\\d+)\x00`, "g");
|
|
45156
|
+
const restore2 = (s) => s.replace(anyMaskRe, (_m, idx) => codeMasks[Number(idx)] ?? _m);
|
|
45157
|
+
const stripPlaceholders = (s) => s.replace(anyMaskRe, "");
|
|
45158
|
+
return { masked, restore: restore2, placeholder: FENCE_MASK_PH, stripPlaceholders };
|
|
45243
45159
|
}
|
|
45244
45160
|
function splitCollapsedInlineBullets(text) {
|
|
45245
45161
|
if (!/[\u2022\u00b7]/.test(text))
|
|
@@ -45261,7 +45177,7 @@ function normalizeParagraphBreaks(text) {
|
|
|
45261
45177
|
`) && !/[\u2022\u00b7]/.test(text))
|
|
45262
45178
|
return text;
|
|
45263
45179
|
const nonce = Math.random().toString(36).slice(2);
|
|
45264
|
-
const { masked: maskedRaw, restore: restore2, placeholder } =
|
|
45180
|
+
const { masked: maskedRaw, restore: restore2, placeholder } = maskCodeRegions2(text, nonce);
|
|
45265
45181
|
const masked = splitCollapsedInlineBullets(maskedRaw);
|
|
45266
45182
|
let out = masked.replace(/\n[ \t\r]+\n(?:[ \t\r]*\n)*/g, `
|
|
45267
45183
|
|
|
@@ -45290,6 +45206,36 @@ function normalizeParagraphBreaks(text) {
|
|
|
45290
45206
|
out = ensureBlockBoundaries(out, placeholder);
|
|
45291
45207
|
return restore2(out);
|
|
45292
45208
|
}
|
|
45209
|
+
function hardenCardBreaks2(text) {
|
|
45210
|
+
if (!text.includes(`
|
|
45211
|
+
`))
|
|
45212
|
+
return text;
|
|
45213
|
+
const nonce = Math.random().toString(36).slice(2);
|
|
45214
|
+
const { masked, restore: restore2, placeholder } = maskCodeRegions2(text, nonce);
|
|
45215
|
+
const out = masked.replace(/\n{3,}/g, `
|
|
45216
|
+
|
|
45217
|
+
`);
|
|
45218
|
+
const isBlockConstructLine = (line) => isListItemLine2(line) || isTableRowLine2(line) || isTableDelimiterLine2(line) || isBlockquoteLine2(line) || isHeadingLine2(line) || isFenceOpenLine2(line, placeholder);
|
|
45219
|
+
const lines = out.split(`
|
|
45220
|
+
`);
|
|
45221
|
+
const pieces = [];
|
|
45222
|
+
for (let i = 0;i < lines.length; i++) {
|
|
45223
|
+
let line = lines[i];
|
|
45224
|
+
const isLast = i === lines.length - 1;
|
|
45225
|
+
const next = isLast ? "" : lines[i + 1];
|
|
45226
|
+
const promote = !isLast && line.trim() !== "" && next.trim() !== "" && !isBlockConstructLine(line) && !isBlockConstructLine(next);
|
|
45227
|
+
if (promote) {
|
|
45228
|
+
line = line.replace(/[ \t\r]+$/, "");
|
|
45229
|
+
}
|
|
45230
|
+
pieces.push(line);
|
|
45231
|
+
if (isLast)
|
|
45232
|
+
break;
|
|
45233
|
+
pieces.push(promote ? `
|
|
45234
|
+
` : `
|
|
45235
|
+
`);
|
|
45236
|
+
}
|
|
45237
|
+
return restore2(pieces.join(""));
|
|
45238
|
+
}
|
|
45293
45239
|
var PARAGRAPH_SPACER = "\u00a0";
|
|
45294
45240
|
function addParagraphSpacers(text) {
|
|
45295
45241
|
if (!text.includes(`
|
|
@@ -45297,7 +45243,7 @@ function addParagraphSpacers(text) {
|
|
|
45297
45243
|
`))
|
|
45298
45244
|
return text;
|
|
45299
45245
|
const nonce = Math.random().toString(36).slice(2);
|
|
45300
|
-
const { masked, restore: restore2, placeholder } =
|
|
45246
|
+
const { masked, restore: restore2, placeholder } = maskCodeRegions2(text, nonce);
|
|
45301
45247
|
if (!masked.includes(`
|
|
45302
45248
|
|
|
45303
45249
|
`))
|
|
@@ -45308,15 +45254,15 @@ function addParagraphSpacers(text) {
|
|
|
45308
45254
|
const blockKind = (line) => {
|
|
45309
45255
|
if (asciiTrim(line) === spacerLine)
|
|
45310
45256
|
return "spacer";
|
|
45311
|
-
if (
|
|
45257
|
+
if (isFenceOpenLine2(line, placeholder))
|
|
45312
45258
|
return "fence";
|
|
45313
|
-
if (
|
|
45259
|
+
if (isListItemLine2(line))
|
|
45314
45260
|
return "list";
|
|
45315
|
-
if (
|
|
45261
|
+
if (isTableRowLine2(line) || isTableDelimiterLine2(line))
|
|
45316
45262
|
return "table";
|
|
45317
|
-
if (
|
|
45263
|
+
if (isBlockquoteLine2(line))
|
|
45318
45264
|
return "quote";
|
|
45319
|
-
if (
|
|
45265
|
+
if (isHeadingLine2(line))
|
|
45320
45266
|
return "heading";
|
|
45321
45267
|
if (/^(-{3,}|\*{3,}|_{3,})\s*$/.test(line.trimStart()))
|
|
45322
45268
|
return "divider";
|
|
@@ -45382,7 +45328,7 @@ function normalizePunctuation(text) {
|
|
|
45382
45328
|
if (!/[\u2014\u2013\u2022\u00b7]/.test(text))
|
|
45383
45329
|
return text;
|
|
45384
45330
|
const nonce = Math.random().toString(36).slice(2);
|
|
45385
|
-
const { masked, restore: restore2 } =
|
|
45331
|
+
const { masked, restore: restore2 } = maskCodeRegions2(text, nonce);
|
|
45386
45332
|
let out = masked.replace(/(\S)[ \t][\u2014\u2013][ \t](?=(\S))/g, (_m, a, b) => /\d/.test(a) && /\d/.test(b) ? `${a}-` : `${a}, `).replace(/(\w)\u2014(?=(\w))/g, (_m, a, b) => /\d/.test(a) && /\d/.test(b) ? `${a}-` : `${a}, `).replace(/(\w)\u2013(?=\w)/g, "$1-");
|
|
45387
45333
|
out = out.split(`
|
|
45388
45334
|
`).map((line) => line.replace(/^([ \t]*)[\u2022\u00b7][ \t]+/, "$1- ")).join(`
|
|
@@ -45402,9 +45348,8 @@ function stripExcessBold(text) {
|
|
|
45402
45348
|
if (!text.includes("**"))
|
|
45403
45349
|
return text;
|
|
45404
45350
|
const nonce = Math.random().toString(36).slice(2);
|
|
45405
|
-
const { masked, restore: restore2, placeholder } =
|
|
45406
|
-
const
|
|
45407
|
-
const visible = masked.replace(placeholderRe, "");
|
|
45351
|
+
const { masked, restore: restore2, placeholder, stripPlaceholders } = maskCodeRegions2(text, nonce);
|
|
45352
|
+
const visible = stripPlaceholders(masked);
|
|
45408
45353
|
if (visible.length < 100)
|
|
45409
45354
|
return restore2(masked);
|
|
45410
45355
|
let boldChars = 0;
|
|
@@ -45419,7 +45364,7 @@ function stripExcessBold(text) {
|
|
|
45419
45364
|
`).filter((l) => l.trim() !== "");
|
|
45420
45365
|
if (lines.length === 0 || !block.includes("**"))
|
|
45421
45366
|
return block;
|
|
45422
|
-
const listLines = lines.filter((l) =>
|
|
45367
|
+
const listLines = lines.filter((l) => isListItemLine2(l));
|
|
45423
45368
|
if (listLines.length >= 2 && listLines.length === lines.length) {
|
|
45424
45369
|
if (lines.every((l) => isFullyBolded(listItemContent(l))))
|
|
45425
45370
|
return unbold(block);
|
|
@@ -45442,26 +45387,26 @@ function stripExcessBold(text) {
|
|
|
45442
45387
|
`) + rebuilt[i];
|
|
45443
45388
|
return restore2(out);
|
|
45444
45389
|
}
|
|
45445
|
-
function
|
|
45390
|
+
function isListItemLine2(line) {
|
|
45446
45391
|
const t = line.trimStart();
|
|
45447
45392
|
return /^[-*+]\s/.test(t) || /^\d+[.)]\s/.test(t);
|
|
45448
45393
|
}
|
|
45449
|
-
function
|
|
45394
|
+
function isTableRowLine2(line) {
|
|
45450
45395
|
return /^\s*\|/.test(line);
|
|
45451
45396
|
}
|
|
45452
|
-
function
|
|
45397
|
+
function isTableDelimiterLine2(line) {
|
|
45453
45398
|
return /^\s*\|?\s*:?-{1,}:?\s*(\|\s*:?-{1,}:?\s*)*\|?\s*$/.test(line);
|
|
45454
45399
|
}
|
|
45455
|
-
function
|
|
45400
|
+
function isFenceOpenLine2(line, placeholder) {
|
|
45456
45401
|
const t = line.trimStart();
|
|
45457
45402
|
if (placeholder != null && placeholder.length > 0 && t.startsWith(placeholder))
|
|
45458
45403
|
return true;
|
|
45459
45404
|
return t.startsWith("```");
|
|
45460
45405
|
}
|
|
45461
|
-
function
|
|
45406
|
+
function isBlockquoteLine2(line) {
|
|
45462
45407
|
return line.trimStart().startsWith(">");
|
|
45463
45408
|
}
|
|
45464
|
-
function
|
|
45409
|
+
function isHeadingLine2(line) {
|
|
45465
45410
|
return /^#{1,6}\s/.test(line.trimStart());
|
|
45466
45411
|
}
|
|
45467
45412
|
function ensureBlockBoundaries(text, placeholder) {
|
|
@@ -45478,24 +45423,24 @@ function ensureBlockBoundaries(text, placeholder) {
|
|
|
45478
45423
|
const curBlank = line.trim() === "";
|
|
45479
45424
|
if (prevNonBlank && !curBlank) {
|
|
45480
45425
|
const next = i + 1 < lines.length ? lines[i + 1] : "";
|
|
45481
|
-
const prevIsTable =
|
|
45482
|
-
const startsTableHere = !prevIsTable && (line.includes("|") &&
|
|
45483
|
-
const startsFence =
|
|
45484
|
-
const startsQuote =
|
|
45485
|
-
const startsHeading =
|
|
45486
|
-
const startsList =
|
|
45426
|
+
const prevIsTable = isTableRowLine2(prev);
|
|
45427
|
+
const startsTableHere = !prevIsTable && (line.includes("|") && isTableDelimiterLine2(next) || isTableRowLine2(line) && isTableDelimiterLine2(next));
|
|
45428
|
+
const startsFence = isFenceOpenLine2(line, placeholder) && !isFenceOpenLine2(prev, placeholder);
|
|
45429
|
+
const startsQuote = isBlockquoteLine2(line) && !isBlockquoteLine2(prev);
|
|
45430
|
+
const startsHeading = isHeadingLine2(line) && !isHeadingLine2(prev);
|
|
45431
|
+
const startsList = isListItemLine2(line) && !isListItemLine2(prev);
|
|
45487
45432
|
if (startsTableHere || startsFence || startsQuote || startsHeading || startsList) {
|
|
45488
45433
|
result.push("");
|
|
45489
45434
|
}
|
|
45490
45435
|
}
|
|
45491
|
-
if (prevNonBlank && !curBlank &&
|
|
45436
|
+
if (prevNonBlank && !curBlank && isFenceOpenLine2(prev, placeholder)) {
|
|
45492
45437
|
const alreadySeparated = result.length > 0 && result[result.length - 1].trim() === "";
|
|
45493
|
-
const curIsBlockStart =
|
|
45438
|
+
const curIsBlockStart = isFenceOpenLine2(line, placeholder) || isBlockquoteLine2(line) || isHeadingLine2(line) || isTableRowLine2(line) || isTableDelimiterLine2(line);
|
|
45494
45439
|
if (!alreadySeparated && !curIsBlockStart) {
|
|
45495
45440
|
result.push("");
|
|
45496
45441
|
}
|
|
45497
45442
|
}
|
|
45498
|
-
if (prevNonBlank && !curBlank &&
|
|
45443
|
+
if (prevNonBlank && !curBlank && isListItemLine2(prev) && !isListItemLine2(line)) {
|
|
45499
45444
|
const isIndentedContinuation = /^(\t| {4,})\S/.test(line);
|
|
45500
45445
|
const alreadySeparated = result.length > 0 && result[result.length - 1].trim() === "";
|
|
45501
45446
|
if (!isIndentedContinuation && !alreadySeparated) {
|
|
@@ -45513,7 +45458,7 @@ function isMarkerLine(line, placeholder) {
|
|
|
45513
45458
|
const t = line.trimStart();
|
|
45514
45459
|
if (placeholder != null && placeholder.length > 0 && t.startsWith(placeholder))
|
|
45515
45460
|
return true;
|
|
45516
|
-
return /^[-*+]\s/.test(t) || /^\d+[.)]\s/.test(t) || t.startsWith(">") || /^#{1,6}\s/.test(t) ||
|
|
45461
|
+
return /^[-*+]\s/.test(t) || /^\d+[.)]\s/.test(t) || t.startsWith(">") || /^#{1,6}\s/.test(t) || isTableRowLine2(line) || isTableDelimiterLine2(line) || t.startsWith("```") || /^(-{3,}|\*{3,}|_{3,})\s*$/.test(t);
|
|
45517
45462
|
}
|
|
45518
45463
|
function shouldPromoteBreak(prev, next, placeholder) {
|
|
45519
45464
|
if (isMarkerLine(prev, placeholder) || isMarkerLine(next, placeholder))
|
|
@@ -45944,7 +45889,7 @@ function formatAgentLine(meta) {
|
|
|
45944
45889
|
function startText(agentName3, dmDisabled) {
|
|
45945
45890
|
if (dmDisabled)
|
|
45946
45891
|
return "This bot isn't accepting new connections.";
|
|
45947
|
-
return [
|
|
45892
|
+
return stackCardLines([
|
|
45948
45893
|
`**Switchroom** \u2014 Telegram on your Claude Pro or Max subscription.`,
|
|
45949
45894
|
``,
|
|
45950
45895
|
`This bot is the **${escapeHtml(agentName3)}** agent. Pair first, then send messages here and they reach the agent; replies and reactions come back.`,
|
|
@@ -45954,11 +45899,10 @@ function startText(agentName3, dmDisabled) {
|
|
|
45954
45899
|
`2. In Claude Code: \`/telegram:access pair <code>\``,
|
|
45955
45900
|
``,
|
|
45956
45901
|
`After pairing, try \`/status\` or \`/commands\`.`
|
|
45957
|
-
]
|
|
45958
|
-
`);
|
|
45902
|
+
]);
|
|
45959
45903
|
}
|
|
45960
45904
|
function helpText(agentName3) {
|
|
45961
|
-
return [
|
|
45905
|
+
return stackCardLines([
|
|
45962
45906
|
`**Switchroom** \u2014 your Pro/Max subscription, wired to Telegram.`,
|
|
45963
45907
|
``,
|
|
45964
45908
|
`This bot is the **${escapeHtml(agentName3)}** agent. Text and photos route through to it; replies, reactions and progress cards come back.`,
|
|
@@ -45969,8 +45913,7 @@ function helpText(agentName3) {
|
|
|
45969
45913
|
`\`/status\` \u2014 agent, model, auth`,
|
|
45970
45914
|
`\`/vault audit <agent>\` \u2014 admin: review agent's vault access + one-tap [\uD83D\uDD13 Allow] on recent denials`,
|
|
45971
45915
|
`\`/commands\` \u2014 full command list`
|
|
45972
|
-
]
|
|
45973
|
-
`);
|
|
45916
|
+
]);
|
|
45974
45917
|
}
|
|
45975
45918
|
var STATUS_DOT = {
|
|
45976
45919
|
ok: "\uD83D\uDFE2",
|
|
@@ -46016,13 +45959,14 @@ function statusPairedText(params) {
|
|
|
46016
45959
|
if (audit.memoryBank)
|
|
46017
45960
|
lines.push(`**Memory** ${escapeHtml(audit.memoryBank)}`);
|
|
46018
45961
|
}
|
|
46019
|
-
return lines
|
|
46020
|
-
`);
|
|
45962
|
+
return stackCardLines(lines);
|
|
46021
45963
|
}
|
|
46022
45964
|
function statusPendingText(code) {
|
|
46023
|
-
return
|
|
46024
|
-
|
|
46025
|
-
|
|
45965
|
+
return stackCardLines([
|
|
45966
|
+
`Pending pairing \u2014 run in Claude Code:`,
|
|
45967
|
+
``,
|
|
45968
|
+
`\`/telegram:access pair ${code}\``
|
|
45969
|
+
]);
|
|
46026
45970
|
}
|
|
46027
45971
|
function statusUnpairedText() {
|
|
46028
45972
|
return "Not paired. Send me a message to get a pairing code.";
|
|
@@ -46053,7 +45997,7 @@ var TELEGRAM_MENU_COMMANDS = [
|
|
|
46053
45997
|
var TELEGRAM_BASE_COMMANDS = TELEGRAM_MENU_COMMANDS.slice(0, 3);
|
|
46054
45998
|
var TELEGRAM_SWITCHROOM_COMMANDS = TELEGRAM_MENU_COMMANDS.slice(3);
|
|
46055
45999
|
function switchroomHelpText(agentName3) {
|
|
46056
|
-
return [
|
|
46000
|
+
return stackCardLines([
|
|
46057
46001
|
`**Switchroom bot** \u2014 commands for the **${escapeHtml(agentName3)}** agent.`,
|
|
46058
46002
|
``,
|
|
46059
46003
|
`**Session & approvals**`,
|
|
@@ -46103,8 +46047,7 @@ function switchroomHelpText(agentName3) {
|
|
|
46103
46047
|
`\`/commands\` \u2014 this help`,
|
|
46104
46048
|
``,
|
|
46105
46049
|
`_Tip: \`/update\` shows the plan; \`/update apply\` executes it; \`/restart\` bounces a stuck agent; \`/version\` checks what's running._`
|
|
46106
|
-
]
|
|
46107
|
-
`);
|
|
46050
|
+
]);
|
|
46108
46051
|
}
|
|
46109
46052
|
function restartAckText(agentName3) {
|
|
46110
46053
|
return `\uD83D\uDD04 Restarting **${escapeHtml(agentName3)}**\u2026`;
|
|
@@ -46228,6 +46171,7 @@ function decideTurnFlush(input) {
|
|
|
46228
46171
|
if (input.chatId == null)
|
|
46229
46172
|
return { kind: "skip", reason: "no-inbound-chat" };
|
|
46230
46173
|
const joined = input.capturedText.join(`
|
|
46174
|
+
|
|
46231
46175
|
`).trim();
|
|
46232
46176
|
if (joined.length === 0)
|
|
46233
46177
|
return { kind: "skip", reason: "empty-text" };
|
|
@@ -46259,7 +46203,7 @@ function resolveAgentDirFromEnv() {
|
|
|
46259
46203
|
}
|
|
46260
46204
|
|
|
46261
46205
|
// active-reactions.ts
|
|
46262
|
-
import { readFileSync as readFileSync15, writeFileSync as
|
|
46206
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync11, renameSync as renameSync5, existsSync as existsSync19, unlinkSync as unlinkSync5 } from "node:fs";
|
|
46263
46207
|
import { join as join18 } from "node:path";
|
|
46264
46208
|
var ACTIVE_REACTIONS_FILENAME = ".active-reactions.json";
|
|
46265
46209
|
function reactionsPath(agentDir) {
|
|
@@ -46267,7 +46211,7 @@ function reactionsPath(agentDir) {
|
|
|
46267
46211
|
}
|
|
46268
46212
|
function readActiveReactions(agentDir) {
|
|
46269
46213
|
const p = reactionsPath(agentDir);
|
|
46270
|
-
if (!
|
|
46214
|
+
if (!existsSync19(p))
|
|
46271
46215
|
return [];
|
|
46272
46216
|
let raw;
|
|
46273
46217
|
try {
|
|
@@ -46303,7 +46247,7 @@ function writeActiveReactions(agentDir, reactions) {
|
|
|
46303
46247
|
}
|
|
46304
46248
|
const tmp = `${p}.tmp-${process.pid}-${Date.now()}`;
|
|
46305
46249
|
try {
|
|
46306
|
-
|
|
46250
|
+
writeFileSync11(tmp, JSON.stringify(reactions) + `
|
|
46307
46251
|
`, "utf-8");
|
|
46308
46252
|
renameSync5(tmp, p);
|
|
46309
46253
|
} catch {}
|
|
@@ -46327,7 +46271,7 @@ function clearActiveReactions(agentDir) {
|
|
|
46327
46271
|
}
|
|
46328
46272
|
|
|
46329
46273
|
// active-reactions.ts
|
|
46330
|
-
import { readFileSync as readFileSync16, writeFileSync as
|
|
46274
|
+
import { readFileSync as readFileSync16, writeFileSync as writeFileSync12, renameSync as renameSync6, existsSync as existsSync20, unlinkSync as unlinkSync6 } from "node:fs";
|
|
46331
46275
|
import { join as join19 } from "node:path";
|
|
46332
46276
|
var ACTIVE_REACTIONS_FILENAME2 = ".active-reactions.json";
|
|
46333
46277
|
function reactionsPath2(agentDir) {
|
|
@@ -46335,7 +46279,7 @@ function reactionsPath2(agentDir) {
|
|
|
46335
46279
|
}
|
|
46336
46280
|
function readActiveReactions2(agentDir) {
|
|
46337
46281
|
const p = reactionsPath2(agentDir);
|
|
46338
|
-
if (!
|
|
46282
|
+
if (!existsSync20(p))
|
|
46339
46283
|
return [];
|
|
46340
46284
|
let raw;
|
|
46341
46285
|
try {
|
|
@@ -46399,6 +46343,7 @@ function flushOnAgentDisconnect(deps) {
|
|
|
46399
46343
|
activeReactionMsgIds,
|
|
46400
46344
|
activeTurnStartedAt,
|
|
46401
46345
|
claudeBusyKeys,
|
|
46346
|
+
claudeBusyKeySince,
|
|
46402
46347
|
activeDraftStreams,
|
|
46403
46348
|
clearActiveReactions: clearActiveReactions3,
|
|
46404
46349
|
disposeProgressDriver,
|
|
@@ -46415,6 +46360,7 @@ function flushOnAgentDisconnect(deps) {
|
|
|
46415
46360
|
activeReactionMsgIds.delete(key);
|
|
46416
46361
|
activeTurnStartedAt.delete(key);
|
|
46417
46362
|
claudeBusyKeys.delete(key);
|
|
46363
|
+
claudeBusyKeySince.delete(key);
|
|
46418
46364
|
}
|
|
46419
46365
|
clearActiveReactions3();
|
|
46420
46366
|
const danglingKeys = [...activeTurnStartedAt.keys()];
|
|
@@ -46423,6 +46369,7 @@ function flushOnAgentDisconnect(deps) {
|
|
|
46423
46369
|
activeTurnStartedAt.delete(k);
|
|
46424
46370
|
activeReactionMsgIds.delete(k);
|
|
46425
46371
|
claudeBusyKeys.delete(k);
|
|
46372
|
+
claudeBusyKeySince.delete(k);
|
|
46426
46373
|
}
|
|
46427
46374
|
log(`telegram gateway: disconnect-flush swept ${danglingKeys.length} dangling turn key(s) ` + `post-bridge-death (controller loop missed \u2014 finalize raced disconnect)`);
|
|
46428
46375
|
onDanglingTurnsSwept?.(danglingKeys);
|
|
@@ -46431,6 +46378,8 @@ function flushOnAgentDisconnect(deps) {
|
|
|
46431
46378
|
const orphanCount = claudeBusyKeys.size;
|
|
46432
46379
|
const orphanKeys = [...claudeBusyKeys];
|
|
46433
46380
|
claudeBusyKeys.clear();
|
|
46381
|
+
for (const k of orphanKeys)
|
|
46382
|
+
claudeBusyKeySince.delete(k);
|
|
46434
46383
|
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
46384
|
}
|
|
46436
46385
|
disposeProgressDriver();
|
|
@@ -46442,6 +46391,33 @@ function flushOnAgentDisconnect(deps) {
|
|
|
46442
46391
|
return true;
|
|
46443
46392
|
}
|
|
46444
46393
|
|
|
46394
|
+
// gateway/busy-key-reaper.ts
|
|
46395
|
+
function markBusyKeyLockstep(keys, since, key, now) {
|
|
46396
|
+
const wasBusy = keys.has(key);
|
|
46397
|
+
keys.add(key);
|
|
46398
|
+
if (!wasBusy)
|
|
46399
|
+
since.set(key, now);
|
|
46400
|
+
}
|
|
46401
|
+
function reapOrphanBusyKeys(keys, since, now, opts) {
|
|
46402
|
+
const reaped = [];
|
|
46403
|
+
for (const key of [...since.keys()]) {
|
|
46404
|
+
if (!keys.has(key)) {
|
|
46405
|
+
since.delete(key);
|
|
46406
|
+
continue;
|
|
46407
|
+
}
|
|
46408
|
+
if (opts.hasPendingDelivery(key))
|
|
46409
|
+
continue;
|
|
46410
|
+
const stampedAt = since.get(key) ?? now;
|
|
46411
|
+
if (now - stampedAt >= opts.ttlMs) {
|
|
46412
|
+
keys.delete(key);
|
|
46413
|
+
since.delete(key);
|
|
46414
|
+
reaped.push(key);
|
|
46415
|
+
opts.log?.(`telegram gateway: reaped orphan busy key=${key} (no turn in flight, ` + `no pending delivery, >${opts.ttlMs}ms) \u2014 unwedging idle-drain`);
|
|
46416
|
+
}
|
|
46417
|
+
}
|
|
46418
|
+
return reaped;
|
|
46419
|
+
}
|
|
46420
|
+
|
|
46445
46421
|
// gateway/preamble-suppressor.ts
|
|
46446
46422
|
var DEFAULT_BUFFER_MS = 150;
|
|
46447
46423
|
|
|
@@ -47214,14 +47190,14 @@ async function approvalRecord(args, opts) {
|
|
|
47214
47190
|
}
|
|
47215
47191
|
|
|
47216
47192
|
// quota-check.ts
|
|
47217
|
-
import { readFileSync as readFileSync17, existsSync as
|
|
47193
|
+
import { readFileSync as readFileSync17, existsSync as existsSync21 } from "fs";
|
|
47218
47194
|
import { join as join20 } from "path";
|
|
47219
47195
|
var OAUTH_BETA2 = "oauth-2025-04-20";
|
|
47220
47196
|
var DEFAULT_USER_AGENT2 = "claude-cli/1.0.0 (external, cli)";
|
|
47221
47197
|
var DEFAULT_PROBE_MODEL2 = "claude-haiku-4-5-20251001";
|
|
47222
47198
|
function readOauthToken2(claudeConfigDir) {
|
|
47223
47199
|
const tokenFile = join20(claudeConfigDir, ".oauth-token");
|
|
47224
|
-
if (!
|
|
47200
|
+
if (!existsSync21(tokenFile))
|
|
47225
47201
|
return null;
|
|
47226
47202
|
try {
|
|
47227
47203
|
const raw = readFileSync17(tokenFile, "utf-8").trim();
|
|
@@ -47401,8 +47377,7 @@ init_loader();
|
|
|
47401
47377
|
var TOKEN_VALIDITY_MS2 = 365 * 24 * 60 * 60000;
|
|
47402
47378
|
|
|
47403
47379
|
// ../src/agents/inject.ts
|
|
47404
|
-
import {
|
|
47405
|
-
import { promisify } from "node:util";
|
|
47380
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
47406
47381
|
|
|
47407
47382
|
// ../src/agents/pane-lock.ts
|
|
47408
47383
|
var tails = new Map;
|
|
@@ -47419,7 +47394,6 @@ function withPaneLock(key, fn) {
|
|
|
47419
47394
|
}
|
|
47420
47395
|
|
|
47421
47396
|
// ../src/agents/inject.ts
|
|
47422
|
-
var execFileAsync = promisify(execFile);
|
|
47423
47397
|
var INJECT_COMMANDS = new Map([
|
|
47424
47398
|
["/cost", { description: "Show session cost", expectsOutput: true, dialog: true }],
|
|
47425
47399
|
["/status", { description: "Show session status", expectsOutput: true, dialog: true }],
|
|
@@ -47715,9 +47689,7 @@ async function injectSlashCommandWith(runner, args) {
|
|
|
47715
47689
|
}
|
|
47716
47690
|
|
|
47717
47691
|
// ../src/agents/inject.ts
|
|
47718
|
-
import {
|
|
47719
|
-
import { promisify as promisify2 } from "node:util";
|
|
47720
|
-
var execFileAsync2 = promisify2(execFile2);
|
|
47692
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
47721
47693
|
var INJECT_COMMANDS2 = new Map([
|
|
47722
47694
|
["/cost", { description: "Show session cost", expectsOutput: true, dialog: true }],
|
|
47723
47695
|
["/status", { description: "Show session status", expectsOutput: true, dialog: true }],
|
|
@@ -47785,7 +47757,7 @@ function makeTmuxRunner2(tmuxBin) {
|
|
|
47785
47757
|
};
|
|
47786
47758
|
}
|
|
47787
47759
|
// stream-reply-handler.ts
|
|
47788
|
-
function
|
|
47760
|
+
function buildAccentHeader(accent) {
|
|
47789
47761
|
switch (accent) {
|
|
47790
47762
|
case "in-progress":
|
|
47791
47763
|
return `\uD83D\uDD35 _In progress\u2026_
|
|
@@ -47909,7 +47881,7 @@ Allowed: \`${deps.escapeHtml(allow)}\``, { html: true });
|
|
|
47909
47881
|
}
|
|
47910
47882
|
}
|
|
47911
47883
|
const { body, accent } = shapeReply(result, slashCommand, deps);
|
|
47912
|
-
const finalText =
|
|
47884
|
+
const finalText = buildAccentHeader(accent) + body;
|
|
47913
47885
|
await deps.reply(ctx, finalText, { html: true, accent });
|
|
47914
47886
|
}
|
|
47915
47887
|
|
|
@@ -48023,11 +47995,22 @@ async function handleModelCommand(parsed, deps) {
|
|
|
48023
47995
|
};
|
|
48024
47996
|
}
|
|
48025
47997
|
if (result.outcome === "ok") {
|
|
47998
|
+
const confirmation = modelSwitchConfirmationLine(result.output);
|
|
47999
|
+
if (confirmation) {
|
|
48000
|
+
return {
|
|
48001
|
+
text: [
|
|
48002
|
+
`${verbHtml}`,
|
|
48003
|
+
deps.preBlock(confirmation),
|
|
48004
|
+
...result.truncated ? ["_truncated_"] : [],
|
|
48005
|
+
PERSIST_NOTE
|
|
48006
|
+
].join(`
|
|
48007
|
+
`),
|
|
48008
|
+
html: true
|
|
48009
|
+
};
|
|
48010
|
+
}
|
|
48026
48011
|
return {
|
|
48027
48012
|
text: [
|
|
48028
|
-
`${verbHtml}
|
|
48029
|
-
deps.preBlock(result.output),
|
|
48030
|
-
...result.truncated ? ["_truncated_"] : [],
|
|
48013
|
+
`${verbHtml} \u2014 switched (session).`,
|
|
48031
48014
|
PERSIST_NOTE
|
|
48032
48015
|
].join(`
|
|
48033
48016
|
`),
|
|
@@ -48321,6 +48304,12 @@ async function handleModelMenuCallback(data, deps) {
|
|
|
48321
48304
|
function isSrToClaudeTransition(prevModel, nextModel) {
|
|
48322
48305
|
return !!prevModel?.startsWith("sr-") && !nextModel.startsWith("sr-");
|
|
48323
48306
|
}
|
|
48307
|
+
function modelSwitchConfirmationLine(output) {
|
|
48308
|
+
const line = output.split(`
|
|
48309
|
+
`).map((l) => l.trim()).find((l) => MODEL_SWITCH_CONFIRMATION_PREFIX.test(l));
|
|
48310
|
+
return line && line.length > 0 ? line : null;
|
|
48311
|
+
}
|
|
48312
|
+
var MODEL_SWITCH_CONFIRMATION_PREFIX = /^\s*[\u23fa\u25cf\u2022>-]?\s*(?:Set model to|Switched to|Kept model as)\b/i;
|
|
48324
48313
|
function sessionModelFromConfirmation(confirmation) {
|
|
48325
48314
|
const m = /(?:Set model to|Switched to)\s+(.+?)(?:\s+for (?:this|the) session|\s*\(|\s*$)/i.exec(confirmation.trim());
|
|
48326
48315
|
const name = m?.[1]?.trim();
|
|
@@ -48781,7 +48770,7 @@ init_schema();
|
|
|
48781
48770
|
init_paths();
|
|
48782
48771
|
init_overlay_loader();
|
|
48783
48772
|
init_merge();
|
|
48784
|
-
import { readFileSync as readFileSync18, existsSync as
|
|
48773
|
+
import { readFileSync as readFileSync18, existsSync as existsSync22 } from "node:fs";
|
|
48785
48774
|
import { homedir as homedir8 } from "node:os";
|
|
48786
48775
|
import { resolve as resolve5 } from "node:path";
|
|
48787
48776
|
|
|
@@ -48857,7 +48846,7 @@ function findConfigFile2(startDir) {
|
|
|
48857
48846
|
resolve5(userDir, "clerk.yml")
|
|
48858
48847
|
].filter(Boolean);
|
|
48859
48848
|
for (const path of searchPaths) {
|
|
48860
|
-
if (
|
|
48849
|
+
if (existsSync22(path)) {
|
|
48861
48850
|
return path;
|
|
48862
48851
|
}
|
|
48863
48852
|
}
|
|
@@ -48865,7 +48854,7 @@ function findConfigFile2(startDir) {
|
|
|
48865
48854
|
}
|
|
48866
48855
|
function loadConfig2(configPath) {
|
|
48867
48856
|
const filePath = configPath ?? findConfigFile2();
|
|
48868
|
-
if (!
|
|
48857
|
+
if (!existsSync22(filePath)) {
|
|
48869
48858
|
throw new ConfigError2(`Config file not found: ${filePath}`);
|
|
48870
48859
|
}
|
|
48871
48860
|
let raw;
|
|
@@ -49363,9 +49352,9 @@ function topicForRecipient(args) {
|
|
|
49363
49352
|
}
|
|
49364
49353
|
|
|
49365
49354
|
// ../src/agents/perf.ts
|
|
49366
|
-
import { existsSync as
|
|
49355
|
+
import { existsSync as existsSync23, readFileSync as readFileSync19 } from "node:fs";
|
|
49367
49356
|
function readTurnUsages(jsonlPath, lastN) {
|
|
49368
|
-
if (!
|
|
49357
|
+
if (!existsSync23(jsonlPath))
|
|
49369
49358
|
return [];
|
|
49370
49359
|
if (lastN <= 0)
|
|
49371
49360
|
return [];
|
|
@@ -49462,7 +49451,7 @@ function numField(obj, key) {
|
|
|
49462
49451
|
}
|
|
49463
49452
|
|
|
49464
49453
|
// gateway/context-occupancy.ts
|
|
49465
|
-
import { mkdirSync as mkdirSync16, writeFileSync as
|
|
49454
|
+
import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync13 } from "node:fs";
|
|
49466
49455
|
import { join as join21 } from "node:path";
|
|
49467
49456
|
var CONTEXT_OCCUPANCY_FILENAME = "context-occupancy.json";
|
|
49468
49457
|
var TIGHT_FRACTION = 0.8;
|
|
@@ -49488,7 +49477,7 @@ function writeContextOccupancySnapshot(stateDir, snapshot, deps) {
|
|
|
49488
49477
|
try {
|
|
49489
49478
|
const path = join21(stateDir, CONTEXT_OCCUPANCY_FILENAME);
|
|
49490
49479
|
(deps?.mkdir ?? ((p, o) => mkdirSync16(p, o)))(stateDir, { recursive: true });
|
|
49491
|
-
(deps?.writeFile ?? ((p, d) =>
|
|
49480
|
+
(deps?.writeFile ?? ((p, d) => writeFileSync13(p, d)))(path, JSON.stringify(snapshot, null, 2) + `
|
|
49492
49481
|
`);
|
|
49493
49482
|
} catch {}
|
|
49494
49483
|
}
|
|
@@ -49571,7 +49560,7 @@ function nextCompactNotify(state3, ev) {
|
|
|
49571
49560
|
}
|
|
49572
49561
|
|
|
49573
49562
|
// gateway/hostd-dispatch.ts
|
|
49574
|
-
import { existsSync as
|
|
49563
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
49575
49564
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
49576
49565
|
|
|
49577
49566
|
// ../src/host-control/client.ts
|
|
@@ -49896,13 +49885,13 @@ function hostdSocketPath(agentName3) {
|
|
|
49896
49885
|
function hostdWillBeUsed(agentName3) {
|
|
49897
49886
|
if (!isHostdEnabled())
|
|
49898
49887
|
return false;
|
|
49899
|
-
return
|
|
49888
|
+
return existsSync24(hostdSocketPath(agentName3));
|
|
49900
49889
|
}
|
|
49901
49890
|
async function tryHostdDispatch(agentName3, req, timeoutMs = 5000) {
|
|
49902
49891
|
if (!isHostdEnabled())
|
|
49903
49892
|
return "not-configured";
|
|
49904
49893
|
const sockPath = hostdSocketPath(agentName3);
|
|
49905
|
-
if (!
|
|
49894
|
+
if (!existsSync24(sockPath))
|
|
49906
49895
|
return "not-configured";
|
|
49907
49896
|
try {
|
|
49908
49897
|
return await hostdRequest({ socketPath: sockPath, timeoutMs }, req);
|
|
@@ -49926,7 +49915,7 @@ async function hostdGetStatusOnce(agentName3, targetRequestId) {
|
|
|
49926
49915
|
if (!isHostdEnabled())
|
|
49927
49916
|
return "not-configured";
|
|
49928
49917
|
const sockPath = hostdSocketPath(agentName3);
|
|
49929
|
-
if (!
|
|
49918
|
+
if (!existsSync24(sockPath))
|
|
49930
49919
|
return "not-configured";
|
|
49931
49920
|
try {
|
|
49932
49921
|
const resp = await hostdRequest({ socketPath: sockPath, timeoutMs: 3000 }, {
|
|
@@ -49947,7 +49936,7 @@ async function pollHostdStatus(agentName3, targetRequestId, opts) {
|
|
|
49947
49936
|
if (!isHostdEnabled())
|
|
49948
49937
|
return "not-configured";
|
|
49949
49938
|
const sockPath = hostdSocketPath(agentName3);
|
|
49950
|
-
if (!
|
|
49939
|
+
if (!existsSync24(sockPath))
|
|
49951
49940
|
return "not-configured";
|
|
49952
49941
|
const now = opts.now ?? Date.now;
|
|
49953
49942
|
const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
@@ -50026,7 +50015,7 @@ function shouldSweepChatAtBoot(chatId) {
|
|
|
50026
50015
|
|
|
50027
50016
|
// gateway/webhook-ingest-server.ts
|
|
50028
50017
|
import net4 from "node:net";
|
|
50029
|
-
import { chmodSync as chmodSync4, existsSync as
|
|
50018
|
+
import { chmodSync as chmodSync4, existsSync as existsSync25, unlinkSync as unlinkSync7 } from "node:fs";
|
|
50030
50019
|
var MAX_REQUEST_BYTES = 1024 * 1024;
|
|
50031
50020
|
function fdOf(conn) {
|
|
50032
50021
|
const handle = conn._handle;
|
|
@@ -50038,7 +50027,7 @@ function startWebhookIngestServer(opts) {
|
|
|
50038
50027
|
const log = opts.log ?? ((s) => process.stderr.write(s));
|
|
50039
50028
|
const allowed = new Set(opts.allowedUids);
|
|
50040
50029
|
try {
|
|
50041
|
-
if (
|
|
50030
|
+
if (existsSync25(opts.socketPath))
|
|
50042
50031
|
unlinkSync7(opts.socketPath);
|
|
50043
50032
|
} catch (err) {
|
|
50044
50033
|
log(`webhook-ingest-server: could not unlink stale socket: ${err.message}
|
|
@@ -50131,7 +50120,7 @@ function startWebhookIngestServer(opts) {
|
|
|
50131
50120
|
server.close();
|
|
50132
50121
|
} catch {}
|
|
50133
50122
|
try {
|
|
50134
|
-
if (
|
|
50123
|
+
if (existsSync25(opts.socketPath))
|
|
50135
50124
|
unlinkSync7(opts.socketPath);
|
|
50136
50125
|
} catch {}
|
|
50137
50126
|
}
|
|
@@ -50144,13 +50133,13 @@ import { join as join24 } from "path";
|
|
|
50144
50133
|
import { homedir as homedir10 } from "os";
|
|
50145
50134
|
|
|
50146
50135
|
// ../src/web/webhook-handler.ts
|
|
50147
|
-
import { appendFileSync as appendFileSync4, existsSync as
|
|
50136
|
+
import { appendFileSync as appendFileSync4, existsSync as existsSync26, mkdirSync as mkdirSync17, readFileSync as readFileSync20, writeFileSync as writeFileSync14 } from "fs";
|
|
50148
50137
|
import { join as join22 } from "path";
|
|
50149
50138
|
var DEDUP_MAX = 1000;
|
|
50150
50139
|
var DEDUP_TTL_MS = 24 * 60 * 60 * 1000;
|
|
50151
50140
|
function loadDedupFile(path) {
|
|
50152
50141
|
try {
|
|
50153
|
-
if (!
|
|
50142
|
+
if (!existsSync26(path))
|
|
50154
50143
|
return {};
|
|
50155
50144
|
const raw = JSON.parse(readFileSync20(path, "utf-8"));
|
|
50156
50145
|
return typeof raw.deliveries === "object" && raw.deliveries !== null ? raw.deliveries : {};
|
|
@@ -50166,7 +50155,7 @@ function saveDedupFile(path, deliveries, now) {
|
|
|
50166
50155
|
}
|
|
50167
50156
|
const sorted = Object.entries(pruned).sort((a, b) => b[1] - a[1]).slice(0, DEDUP_MAX);
|
|
50168
50157
|
const final = Object.fromEntries(sorted);
|
|
50169
|
-
|
|
50158
|
+
writeFileSync14(path, JSON.stringify({ deliveries: final }), {
|
|
50170
50159
|
mode: 384
|
|
50171
50160
|
});
|
|
50172
50161
|
}
|
|
@@ -50196,7 +50185,7 @@ var tokenBuckets = new Map;
|
|
|
50196
50185
|
var throttleIssueWindow = new Map;
|
|
50197
50186
|
|
|
50198
50187
|
// ../src/web/webhook-dispatch.ts
|
|
50199
|
-
import { existsSync as
|
|
50188
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync18, readFileSync as readFileSync21, writeFileSync as writeFileSync15 } from "fs";
|
|
50200
50189
|
import { join as join23 } from "path";
|
|
50201
50190
|
import { homedir as homedir9 } from "os";
|
|
50202
50191
|
|
|
@@ -50496,7 +50485,7 @@ function cooldownKey(source, eventType, repo, number, ruleIndex) {
|
|
|
50496
50485
|
}
|
|
50497
50486
|
function loadCooldownFile(path) {
|
|
50498
50487
|
try {
|
|
50499
|
-
if (!
|
|
50488
|
+
if (!existsSync27(path))
|
|
50500
50489
|
return {};
|
|
50501
50490
|
const raw = JSON.parse(readFileSync21(path, "utf-8"));
|
|
50502
50491
|
return typeof raw.dispatches === "object" && raw.dispatches !== null ? raw.dispatches : {};
|
|
@@ -50506,7 +50495,7 @@ function loadCooldownFile(path) {
|
|
|
50506
50495
|
}
|
|
50507
50496
|
function saveCooldownFile(path, dispatches) {
|
|
50508
50497
|
try {
|
|
50509
|
-
|
|
50498
|
+
writeFileSync15(path, JSON.stringify({ dispatches }), {
|
|
50510
50499
|
mode: 384
|
|
50511
50500
|
});
|
|
50512
50501
|
} catch {}
|
|
@@ -51689,8 +51678,8 @@ function buildMs365CardText(p) {
|
|
|
51689
51678
|
}
|
|
51690
51679
|
lines.push("");
|
|
51691
51680
|
lines.push("\u26a0\ufe0f Weak attestation (RFC \u00a78 v1): operator should click through to verify the actual change before approving. Structural diff coming v1.5.");
|
|
51692
|
-
return lines.join(`
|
|
51693
|
-
`);
|
|
51681
|
+
return hardenCardBreaks(lines.join(`
|
|
51682
|
+
`));
|
|
51694
51683
|
}
|
|
51695
51684
|
function truncate3(s, n) {
|
|
51696
51685
|
if (s.length <= n)
|
|
@@ -51971,6 +51960,11 @@ function createPendingInboundBuffer(opts = {}) {
|
|
|
51971
51960
|
evicted = true;
|
|
51972
51961
|
log(`pending-inbound-buffer: agent=${agent} cap=${cap} reached \u2014 ` + `dropped oldest entry source=${dropped?.meta?.source ?? "-"} ts=${dropped?.ts ?? "-"}
|
|
51973
51962
|
`);
|
|
51963
|
+
if (dropped != null && opts.onEvict != null) {
|
|
51964
|
+
try {
|
|
51965
|
+
opts.onEvict(agent, dropped);
|
|
51966
|
+
} catch {}
|
|
51967
|
+
}
|
|
51974
51968
|
}
|
|
51975
51969
|
q.push(msg);
|
|
51976
51970
|
spool?.put(agent, msg);
|
|
@@ -52265,28 +52259,41 @@ async function runStatusPinBootCleanup(args) {
|
|
|
52265
52259
|
persistStatusPins(args.path, args.fs, [], log);
|
|
52266
52260
|
return { cleared, total: persisted.length };
|
|
52267
52261
|
}
|
|
52268
|
-
|
|
52262
|
+
var storeLockTails = new Map;
|
|
52263
|
+
function withStoreLock(path, fn) {
|
|
52264
|
+
const prev = storeLockTails.get(path) ?? Promise.resolve();
|
|
52265
|
+
const run3 = prev.then(fn, fn);
|
|
52266
|
+
storeLockTails.set(path, run3.then(() => {
|
|
52267
|
+
return;
|
|
52268
|
+
}, () => {
|
|
52269
|
+
return;
|
|
52270
|
+
}));
|
|
52271
|
+
return run3;
|
|
52272
|
+
}
|
|
52273
|
+
function applyStatusPinRow(path, fs2, pinKey, row, log) {
|
|
52274
|
+
const current = loadStatusPins(path, fs2);
|
|
52275
|
+
const others = current.filter((p) => p.pinKey !== pinKey);
|
|
52276
|
+
const next = row == null ? others : [...others, row];
|
|
52277
|
+
persistStatusPins(path, fs2, next, log);
|
|
52278
|
+
}
|
|
52279
|
+
function reconcileAndPersistStatusPin(args) {
|
|
52269
52280
|
const { path, fs: fs2, pinKey, chatId, op } = args;
|
|
52270
52281
|
const log = args.log ?? ((l) => process.stderr.write(l));
|
|
52271
|
-
|
|
52272
|
-
|
|
52273
|
-
|
|
52274
|
-
|
|
52275
|
-
|
|
52276
|
-
|
|
52277
|
-
|
|
52278
|
-
|
|
52279
|
-
|
|
52282
|
+
return withStoreLock(path, async () => {
|
|
52283
|
+
if (op.kind === "pin") {
|
|
52284
|
+
applyStatusPinRow(path, fs2, pinKey, { pinKey, chatId, messageId: op.messageId, pending: true }, log);
|
|
52285
|
+
const next2 = await args.applyPin();
|
|
52286
|
+
if (next2 == null) {
|
|
52287
|
+
applyStatusPinRow(path, fs2, pinKey, null, log);
|
|
52288
|
+
return null;
|
|
52289
|
+
}
|
|
52290
|
+
applyStatusPinRow(path, fs2, pinKey, { pinKey, chatId, messageId: next2.messageId }, log);
|
|
52291
|
+
return next2;
|
|
52280
52292
|
}
|
|
52281
|
-
|
|
52282
|
-
|
|
52283
|
-
|
|
52284
|
-
|
|
52285
|
-
return next2;
|
|
52286
|
-
}
|
|
52287
|
-
const next = await args.applyPin();
|
|
52288
|
-
persistStatusPins(path, fs2, args.snapshotOthers(), log);
|
|
52289
|
-
return next;
|
|
52293
|
+
const next = await args.applyPin();
|
|
52294
|
+
applyStatusPinRow(path, fs2, pinKey, null, log);
|
|
52295
|
+
return next;
|
|
52296
|
+
});
|
|
52290
52297
|
}
|
|
52291
52298
|
|
|
52292
52299
|
// gateway/with-deadline.ts
|
|
@@ -52336,11 +52343,19 @@ function driveEscalation(args) {
|
|
|
52336
52343
|
function shouldSuppressRepresent(o, deps) {
|
|
52337
52344
|
if (!deps.historyEnabled)
|
|
52338
52345
|
return false;
|
|
52339
|
-
if (o.lastRepresentedAt == null)
|
|
52340
|
-
|
|
52346
|
+
if (o.lastRepresentedAt == null) {
|
|
52347
|
+
if (o.openedAt == null)
|
|
52348
|
+
return false;
|
|
52349
|
+
return deps.hasOutboundDeliveredSince(o.chatId, o.openedAt, o.threadId);
|
|
52350
|
+
}
|
|
52341
52351
|
return deps.hasOutboundDeliveredSince(o.chatId, o.lastRepresentedAt, o.threadId);
|
|
52342
52352
|
}
|
|
52343
52353
|
|
|
52354
|
+
// gateway/escalation-bridge-gate.ts
|
|
52355
|
+
function shouldDeferEscalationForBridge(deps) {
|
|
52356
|
+
return !deps.bridgeAlive;
|
|
52357
|
+
}
|
|
52358
|
+
|
|
52344
52359
|
// gateway/inbound-spool.ts
|
|
52345
52360
|
function spoolId(msg) {
|
|
52346
52361
|
if (msg.meta?.source === "subagent_handback" && typeof msg.meta?.subagent_jsonl_id === "string" && msg.meta.subagent_jsonl_id.length > 0) {
|
|
@@ -52372,6 +52387,8 @@ function createInboundSpool(opts) {
|
|
|
52372
52387
|
const escalateNoticeCooldownMs = opts.escalateNoticeCooldownMs ?? 30 * 60 * 1000;
|
|
52373
52388
|
const live = new Map;
|
|
52374
52389
|
const escAttemptByChat = new Map;
|
|
52390
|
+
let consecutiveAppendFailures = 0;
|
|
52391
|
+
let degraded = false;
|
|
52375
52392
|
function parseLine(line) {
|
|
52376
52393
|
const s = line.trim();
|
|
52377
52394
|
if (!s)
|
|
@@ -52439,9 +52456,33 @@ function createInboundSpool(opts) {
|
|
|
52439
52456
|
try {
|
|
52440
52457
|
fs2.appendFileSync(path, JSON.stringify(rec) + `
|
|
52441
52458
|
`);
|
|
52459
|
+
if (consecutiveAppendFailures > 0) {
|
|
52460
|
+
log(`inbound-spool: append recovered path=${path} after ` + `${consecutiveAppendFailures} failure(s) \u2014 durability restored
|
|
52461
|
+
`);
|
|
52462
|
+
consecutiveAppendFailures = 0;
|
|
52463
|
+
if (degraded) {
|
|
52464
|
+
degraded = false;
|
|
52465
|
+
try {
|
|
52466
|
+
opts.onDegraded?.({ degraded: false, consecutiveFailures: 0, path });
|
|
52467
|
+
} catch {}
|
|
52468
|
+
}
|
|
52469
|
+
}
|
|
52442
52470
|
} catch (err) {
|
|
52443
|
-
|
|
52471
|
+
consecutiveAppendFailures++;
|
|
52472
|
+
const message = err.message;
|
|
52473
|
+
log(`inbound-spool: append FAILED path=${path} id=${rec.id} t=${rec.t}: ` + `${message} \u2014 durability degraded to in-memory ` + `(consecutive failures=${consecutiveAppendFailures})
|
|
52444
52474
|
`);
|
|
52475
|
+
if (!degraded) {
|
|
52476
|
+
degraded = true;
|
|
52477
|
+
try {
|
|
52478
|
+
opts.onDegraded?.({
|
|
52479
|
+
degraded: true,
|
|
52480
|
+
consecutiveFailures: consecutiveAppendFailures,
|
|
52481
|
+
path,
|
|
52482
|
+
error: message
|
|
52483
|
+
});
|
|
52484
|
+
} catch {}
|
|
52485
|
+
}
|
|
52445
52486
|
}
|
|
52446
52487
|
}
|
|
52447
52488
|
function maybeCompact() {
|
|
@@ -52531,11 +52572,18 @@ function createInboundSpool(opts) {
|
|
|
52531
52572
|
sweepEscalations(onEscalate) {
|
|
52532
52573
|
const tNow = now();
|
|
52533
52574
|
const cutoff = tNow - escalateAfterMs;
|
|
52534
|
-
|
|
52535
|
-
|
|
52536
|
-
for (const [id, e] of
|
|
52575
|
+
const toDrop = [];
|
|
52576
|
+
const perChatCount = new Map;
|
|
52577
|
+
for (const [id, e] of live.entries()) {
|
|
52537
52578
|
if (e.firstAt > cutoff)
|
|
52538
52579
|
continue;
|
|
52580
|
+
toDrop.push({ id, e });
|
|
52581
|
+
const key = escChatKey(e.msg);
|
|
52582
|
+
perChatCount.set(key, (perChatCount.get(key) ?? 0) + 1);
|
|
52583
|
+
}
|
|
52584
|
+
let dropped = 0;
|
|
52585
|
+
let posted = 0;
|
|
52586
|
+
for (const { id, e } of toDrop) {
|
|
52539
52587
|
live.delete(id);
|
|
52540
52588
|
appendRecord({ t: "ack", id });
|
|
52541
52589
|
const key = escChatKey(e.msg);
|
|
@@ -52546,7 +52594,7 @@ function createInboundSpool(opts) {
|
|
|
52546
52594
|
const thread = typeof threadRaw === "string" && threadRaw.length > 0 ? threadRaw : undefined;
|
|
52547
52595
|
appendRecord({ t: "esc", chat: e.msg.chatId, thread, at: tNow });
|
|
52548
52596
|
try {
|
|
52549
|
-
onEscalate({ agent: e.agent, msg: e.msg }, { postNotice });
|
|
52597
|
+
onEscalate({ agent: e.agent, msg: e.msg }, { postNotice, droppedCount: perChatCount.get(key) ?? 1 });
|
|
52550
52598
|
} catch (err) {
|
|
52551
52599
|
log(`inbound-spool: onEscalate threw id=${id}: ${err.message}
|
|
52552
52600
|
`);
|
|
@@ -52565,6 +52613,12 @@ function createInboundSpool(opts) {
|
|
|
52565
52613
|
},
|
|
52566
52614
|
liveCount() {
|
|
52567
52615
|
return live.size;
|
|
52616
|
+
},
|
|
52617
|
+
isDegraded() {
|
|
52618
|
+
return degraded;
|
|
52619
|
+
},
|
|
52620
|
+
appendFailureCount() {
|
|
52621
|
+
return consecutiveAppendFailures;
|
|
52568
52622
|
}
|
|
52569
52623
|
};
|
|
52570
52624
|
}
|
|
@@ -52931,12 +52985,23 @@ function createDeliveryQueue() {
|
|
|
52931
52985
|
function trackDelivery(q, key, inbound, now, messageId = null) {
|
|
52932
52986
|
q.pending.set(key, { key, inbound, messageId, lastAttemptAt: now });
|
|
52933
52987
|
}
|
|
52934
|
-
function
|
|
52988
|
+
function extractEnqueueMessageIds(content) {
|
|
52989
|
+
const ids = [];
|
|
52990
|
+
const re = /(?:^|[\s"'])message_id="([^"]+)"/g;
|
|
52991
|
+
let m;
|
|
52992
|
+
while ((m = re.exec(content)) != null)
|
|
52993
|
+
ids.push(m[1]);
|
|
52994
|
+
return ids;
|
|
52995
|
+
}
|
|
52996
|
+
function ackDelivery(q, key, enqueueMessageId = null, enqueueContent = null) {
|
|
52935
52997
|
const entry = q.pending.get(key);
|
|
52936
52998
|
if (!entry)
|
|
52937
52999
|
return false;
|
|
52938
|
-
if (entry.messageId != null
|
|
52939
|
-
|
|
53000
|
+
if (entry.messageId != null) {
|
|
53001
|
+
const matches = entry.messageId === enqueueMessageId || enqueueContent != null && extractEnqueueMessageIds(enqueueContent).includes(entry.messageId);
|
|
53002
|
+
if (!matches)
|
|
53003
|
+
return false;
|
|
53004
|
+
}
|
|
52940
53005
|
q.pending.delete(key);
|
|
52941
53006
|
return true;
|
|
52942
53007
|
}
|
|
@@ -52950,6 +53015,13 @@ function sweep(q, now, timeoutMs) {
|
|
|
52950
53015
|
}
|
|
52951
53016
|
return redeliver;
|
|
52952
53017
|
}
|
|
53018
|
+
function isRedeliverySuspended(key, suspended) {
|
|
53019
|
+
if (suspended.keys.has(key))
|
|
53020
|
+
return true;
|
|
53021
|
+
const idx = key.indexOf(":");
|
|
53022
|
+
const chatId = idx < 0 ? key : key.slice(0, idx);
|
|
53023
|
+
return suspended.chats.has(chatId);
|
|
53024
|
+
}
|
|
52953
53025
|
function forgetDelivery(q, key) {
|
|
52954
53026
|
q.pending.delete(key);
|
|
52955
53027
|
}
|
|
@@ -53426,6 +53498,27 @@ function dispatchOne(effect, ctx) {
|
|
|
53426
53498
|
}
|
|
53427
53499
|
}
|
|
53428
53500
|
|
|
53501
|
+
// gateway/gate-parity-probe.ts
|
|
53502
|
+
function gateParityDivergence(machineInTurn, busyKeysSize) {
|
|
53503
|
+
const busy = busyKeysSize > 0;
|
|
53504
|
+
if (machineInTurn === busy)
|
|
53505
|
+
return "none";
|
|
53506
|
+
if (!machineInTurn && busy)
|
|
53507
|
+
return "busykeys_dangle";
|
|
53508
|
+
return "machine_over_holds";
|
|
53509
|
+
}
|
|
53510
|
+
function isDangerousGateDivergence(d) {
|
|
53511
|
+
return d === "machine_over_holds";
|
|
53512
|
+
}
|
|
53513
|
+
function probeGateParity(machineInTurn, busyKeysSize, log = (line) => process.stderr.write(line)) {
|
|
53514
|
+
const d = gateParityDivergence(machineInTurn, busyKeysSize);
|
|
53515
|
+
if (isDangerousGateDivergence(d)) {
|
|
53516
|
+
log(`gw-trace gate-drift kind=${d} machineInTurn=${machineInTurn} ` + `busyKeys=${busyKeysSize} note=machine-authoritative-gate-holds-while-imperative-idle
|
|
53517
|
+
`);
|
|
53518
|
+
}
|
|
53519
|
+
return machineInTurn;
|
|
53520
|
+
}
|
|
53521
|
+
|
|
53429
53522
|
// gateway/prefix-warmup.ts
|
|
53430
53523
|
var WARMUP_COOLDOWN_MS = 5 * 60000;
|
|
53431
53524
|
var lastWarmupAtPerAgent = new Map;
|
|
@@ -53686,11 +53779,11 @@ function buildSkillProposalApplyInbound(opts) {
|
|
|
53686
53779
|
// ../src/self-improve/skill-proposals.ts
|
|
53687
53780
|
import {
|
|
53688
53781
|
closeSync as closeSync3,
|
|
53689
|
-
existsSync as
|
|
53782
|
+
existsSync as existsSync28,
|
|
53690
53783
|
mkdirSync as mkdirSync20,
|
|
53691
53784
|
openSync as openSync3,
|
|
53692
53785
|
readFileSync as readFileSync22,
|
|
53693
|
-
writeSync as
|
|
53786
|
+
writeSync as writeSync3
|
|
53694
53787
|
} from "node:fs";
|
|
53695
53788
|
import { join as join25 } from "node:path";
|
|
53696
53789
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
@@ -53705,21 +53798,21 @@ function rejectedPath(stateDir) {
|
|
|
53705
53798
|
return join25(stateDir, REJECTED_FILE);
|
|
53706
53799
|
}
|
|
53707
53800
|
function ensureDir2(stateDir) {
|
|
53708
|
-
if (!
|
|
53801
|
+
if (!existsSync28(stateDir)) {
|
|
53709
53802
|
mkdirSync20(stateDir, { recursive: true, mode: 493 });
|
|
53710
53803
|
}
|
|
53711
53804
|
}
|
|
53712
53805
|
function appendLine(path, obj) {
|
|
53713
53806
|
const fd = openSync3(path, "a");
|
|
53714
53807
|
try {
|
|
53715
|
-
|
|
53808
|
+
writeSync3(fd, JSON.stringify(obj) + `
|
|
53716
53809
|
`);
|
|
53717
53810
|
} finally {
|
|
53718
53811
|
closeSync3(fd);
|
|
53719
53812
|
}
|
|
53720
53813
|
}
|
|
53721
53814
|
function readLines(path, isValid2) {
|
|
53722
|
-
if (!
|
|
53815
|
+
if (!existsSync28(path))
|
|
53723
53816
|
return [];
|
|
53724
53817
|
let raw;
|
|
53725
53818
|
try {
|
|
@@ -54352,10 +54445,10 @@ function escapeBody2(s) {
|
|
|
54352
54445
|
}
|
|
54353
54446
|
|
|
54354
54447
|
// gateway/pid-file.ts
|
|
54355
|
-
import { writeFileSync as
|
|
54448
|
+
import { writeFileSync as writeFileSync16, readFileSync as readFileSync23, unlinkSync as unlinkSync9, renameSync as renameSync8 } from "node:fs";
|
|
54356
54449
|
function writePidFile(path, record) {
|
|
54357
54450
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
54358
|
-
|
|
54451
|
+
writeFileSync16(tmp, JSON.stringify(record), "utf-8");
|
|
54359
54452
|
renameSync8(tmp, path);
|
|
54360
54453
|
}
|
|
54361
54454
|
function clearPidFile(path) {
|
|
@@ -54577,10 +54670,10 @@ function safeCount(fn) {
|
|
|
54577
54670
|
}
|
|
54578
54671
|
|
|
54579
54672
|
// gateway/session-marker.ts
|
|
54580
|
-
import { writeFileSync as
|
|
54673
|
+
import { writeFileSync as writeFileSync17, readFileSync as readFileSync25, renameSync as renameSync9, unlinkSync as unlinkSync10 } from "node:fs";
|
|
54581
54674
|
function writeSessionMarker(path, marker) {
|
|
54582
54675
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
54583
|
-
|
|
54676
|
+
writeFileSync17(tmp, JSON.stringify(marker), "utf-8");
|
|
54584
54677
|
renameSync9(tmp, path);
|
|
54585
54678
|
}
|
|
54586
54679
|
function readSessionMarker(path) {
|
|
@@ -54607,11 +54700,11 @@ function shouldFireRestartBanner(input) {
|
|
|
54607
54700
|
}
|
|
54608
54701
|
|
|
54609
54702
|
// gateway/clean-shutdown-marker.ts
|
|
54610
|
-
import { writeFileSync as
|
|
54703
|
+
import { writeFileSync as writeFileSync18, readFileSync as readFileSync26, renameSync as renameSync10, unlinkSync as unlinkSync11 } from "node:fs";
|
|
54611
54704
|
var DEFAULT_MAX_AGE_MS = 60000;
|
|
54612
54705
|
function writeCleanShutdownMarker(path, marker) {
|
|
54613
54706
|
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
54614
|
-
|
|
54707
|
+
writeFileSync18(tmp, JSON.stringify(marker), "utf-8");
|
|
54615
54708
|
renameSync10(tmp, path);
|
|
54616
54709
|
}
|
|
54617
54710
|
function readCleanShutdownMarker(path) {
|
|
@@ -55207,7 +55300,7 @@ function classifyAdminGate(text, myAgentName) {
|
|
|
55207
55300
|
|
|
55208
55301
|
// subagent-watcher.ts
|
|
55209
55302
|
import {
|
|
55210
|
-
existsSync as
|
|
55303
|
+
existsSync as existsSync30,
|
|
55211
55304
|
openSync as openSync5,
|
|
55212
55305
|
readSync,
|
|
55213
55306
|
statSync as statSync9,
|
|
@@ -55408,7 +55501,12 @@ function projectSubagentLine(line, agentId, state4) {
|
|
|
55408
55501
|
if (ct === "tool_use") {
|
|
55409
55502
|
const name = c.name ?? "";
|
|
55410
55503
|
if (name === "Agent" || name === "Task") {
|
|
55411
|
-
events.push({
|
|
55504
|
+
events.push({
|
|
55505
|
+
kind: "sub_agent_nested_spawn",
|
|
55506
|
+
agentId,
|
|
55507
|
+
toolUseId: c.id ?? null,
|
|
55508
|
+
input: c.input ?? undefined
|
|
55509
|
+
});
|
|
55412
55510
|
} else {
|
|
55413
55511
|
events.push({
|
|
55414
55512
|
kind: "sub_agent_tool_use",
|
|
@@ -55657,24 +55755,43 @@ function bumpSubagentActivity(db2, args) {
|
|
|
55657
55755
|
WHERE id = ?
|
|
55658
55756
|
`).run(args.ts, args.id);
|
|
55659
55757
|
}
|
|
55758
|
+
function recordNestedSubagentDispatch(db2, args) {
|
|
55759
|
+
db2.prepare(`
|
|
55760
|
+
INSERT OR IGNORE INTO subagents
|
|
55761
|
+
(id, parent_session_id, parent_turn_key, agent_type, description,
|
|
55762
|
+
background, started_at, last_activity_at, status, jsonl_agent_id,
|
|
55763
|
+
parent_agent_id)
|
|
55764
|
+
VALUES (?, NULL, NULL, ?, ?, ?, ?, ?, 'running', NULL, ?)
|
|
55765
|
+
`).run(args.toolUseId, args.agentType ?? null, args.description ?? null, args.background ? 1 : 0, args.now, args.now, args.parentJsonlAgentId);
|
|
55766
|
+
db2.prepare(`
|
|
55767
|
+
UPDATE subagents
|
|
55768
|
+
SET parent_agent_id = COALESCE(parent_agent_id, ?),
|
|
55769
|
+
parent_turn_key = COALESCE(
|
|
55770
|
+
parent_turn_key,
|
|
55771
|
+
(SELECT p.parent_turn_key FROM subagents p
|
|
55772
|
+
WHERE p.jsonl_agent_id = ? LIMIT 1)
|
|
55773
|
+
)
|
|
55774
|
+
WHERE id = ?
|
|
55775
|
+
`).run(args.parentJsonlAgentId, args.parentJsonlAgentId, args.toolUseId);
|
|
55776
|
+
}
|
|
55660
55777
|
|
|
55661
55778
|
// gateway/turn-active-marker.ts
|
|
55662
55779
|
import {
|
|
55663
55780
|
closeSync as closeSync4,
|
|
55664
|
-
existsSync as
|
|
55781
|
+
existsSync as existsSync29,
|
|
55665
55782
|
mkdirSync as mkdirSync21,
|
|
55666
55783
|
openSync as openSync4,
|
|
55667
55784
|
readFileSync as readFileSync27,
|
|
55668
55785
|
statSync as statSync8,
|
|
55669
55786
|
unlinkSync as unlinkSync12,
|
|
55670
55787
|
utimesSync,
|
|
55671
|
-
writeFileSync as
|
|
55788
|
+
writeFileSync as writeFileSync19
|
|
55672
55789
|
} from "node:fs";
|
|
55673
55790
|
import { join as join26 } from "node:path";
|
|
55674
55791
|
var TURN_ACTIVE_MARKER_FILE = "turn-active.json";
|
|
55675
55792
|
function touchTurnActiveMarker(stateDir) {
|
|
55676
55793
|
const path = join26(stateDir, TURN_ACTIVE_MARKER_FILE);
|
|
55677
|
-
if (!
|
|
55794
|
+
if (!existsSync29(path))
|
|
55678
55795
|
return;
|
|
55679
55796
|
const now = new Date;
|
|
55680
55797
|
try {
|
|
@@ -55692,6 +55809,10 @@ var DEFAULT_RESCAN_MS = 1000;
|
|
|
55692
55809
|
var DEFAULT_STALL_THRESHOLD_MS = 60000;
|
|
55693
55810
|
var DEFAULT_SILENT_SYNTHESIS_STALL_THRESHOLD_MS = 300000;
|
|
55694
55811
|
var DEFAULT_SILENT_STALL_TERMINAL_MS = 300000;
|
|
55812
|
+
var LONG_RUNNING_TOOLS = new Set(["Bash"]);
|
|
55813
|
+
function isLongRunningTool(name) {
|
|
55814
|
+
return name != null && LONG_RUNNING_TOOLS.has(name);
|
|
55815
|
+
}
|
|
55695
55816
|
var DEFAULT_INFLIGHT_PROMOTE_MAX_AGE_MS = 15 * 60000;
|
|
55696
55817
|
var SUBAGENT_RESULT_TEXT_MAX = 3000;
|
|
55697
55818
|
function parseEnvMs(varName) {
|
|
@@ -55706,6 +55827,7 @@ function parseEnvMs(varName) {
|
|
|
55706
55827
|
var DEFAULT_REAPER_TTL_MS = 60 * 60000;
|
|
55707
55828
|
var DEFAULT_REAPER_INTERVAL_MS = 15 * 60000;
|
|
55708
55829
|
var TERMINAL_CLEANUP_GRACE_MS = 30000;
|
|
55830
|
+
var BACKFILL_RETRY_INTERVAL_MS = 3000;
|
|
55709
55831
|
function backfillJsonlAgentId(db2, jsonlPath, agentId, log) {
|
|
55710
55832
|
const metaPath = jsonlPath.replace(/\.jsonl$/, ".meta.json");
|
|
55711
55833
|
let meta;
|
|
@@ -55754,14 +55876,38 @@ function backfillJsonlAgentId(db2, jsonlPath, agentId, log) {
|
|
|
55754
55876
|
db2.prepare("UPDATE subagents SET jsonl_agent_id = ? WHERE id = ?").run(agentId, candidateId);
|
|
55755
55877
|
log?.(`subagent-watcher: backfill linked ${agentId} \u2192 ${candidateId}`);
|
|
55756
55878
|
try {
|
|
55757
|
-
const linkedRow = db2.prepare("SELECT started_at, parent_turn_key FROM subagents WHERE id = ?").get(candidateId);
|
|
55879
|
+
const linkedRow = db2.prepare("SELECT started_at, parent_turn_key, parent_agent_id FROM subagents WHERE id = ?").get(candidateId);
|
|
55758
55880
|
if (linkedRow != null && linkedRow.parent_turn_key == null) {
|
|
55759
|
-
|
|
55760
|
-
|
|
55761
|
-
|
|
55762
|
-
|
|
55763
|
-
|
|
55764
|
-
|
|
55881
|
+
let resolvedKey = null;
|
|
55882
|
+
if (linkedRow.parent_agent_id != null && linkedRow.parent_agent_id.length > 0) {
|
|
55883
|
+
const seen = new Set([agentId]);
|
|
55884
|
+
let cursor = linkedRow.parent_agent_id;
|
|
55885
|
+
for (let hop = 0;hop < 5 && cursor != null && !seen.has(cursor); hop++) {
|
|
55886
|
+
seen.add(cursor);
|
|
55887
|
+
const parentRow = db2.prepare("SELECT parent_turn_key, parent_agent_id FROM subagents WHERE jsonl_agent_id = ? LIMIT 1").get(cursor);
|
|
55888
|
+
if (parentRow == null)
|
|
55889
|
+
break;
|
|
55890
|
+
if (parentRow.parent_turn_key != null && parentRow.parent_turn_key.length > 0) {
|
|
55891
|
+
resolvedKey = parentRow.parent_turn_key;
|
|
55892
|
+
break;
|
|
55893
|
+
}
|
|
55894
|
+
cursor = parentRow.parent_agent_id ?? null;
|
|
55895
|
+
}
|
|
55896
|
+
if (resolvedKey != null) {
|
|
55897
|
+
log?.(`subagent-watcher: backfill parent_turn_key ${candidateId} \u2192 ${resolvedKey} (inherited via nested-parent chain)`);
|
|
55898
|
+
}
|
|
55899
|
+
}
|
|
55900
|
+
if (resolvedKey == null) {
|
|
55901
|
+
const turn = db2.prepare(`SELECT turn_key FROM turns
|
|
55902
|
+
WHERE started_at <= ? AND (ended_at IS NULL OR ended_at >= ?)
|
|
55903
|
+
ORDER BY started_at DESC LIMIT 1`).get(linkedRow.started_at, linkedRow.started_at);
|
|
55904
|
+
if (turn?.turn_key != null) {
|
|
55905
|
+
resolvedKey = turn.turn_key;
|
|
55906
|
+
log?.(`subagent-watcher: backfill parent_turn_key ${candidateId} \u2192 ${resolvedKey}`);
|
|
55907
|
+
}
|
|
55908
|
+
}
|
|
55909
|
+
if (resolvedKey != null) {
|
|
55910
|
+
db2.prepare("UPDATE subagents SET parent_turn_key = ? WHERE id = ?").run(resolvedKey, candidateId);
|
|
55765
55911
|
}
|
|
55766
55912
|
}
|
|
55767
55913
|
} catch (err) {
|
|
@@ -55791,6 +55937,14 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
55791
55937
|
const existing = db2.prepare("SELECT id, background FROM subagents WHERE jsonl_agent_id = ?").get(entry.agentId);
|
|
55792
55938
|
if (existing == null) {
|
|
55793
55939
|
log?.(`subagent-watcher: liveness skip ${entry.agentId} \u2014 row not in DB yet (Phase 2 Pre hook pending)`);
|
|
55940
|
+
if (entry.lastBackfillAttemptAt == null || now - entry.lastBackfillAttemptAt >= BACKFILL_RETRY_INTERVAL_MS) {
|
|
55941
|
+
entry.lastBackfillAttemptAt = now;
|
|
55942
|
+
try {
|
|
55943
|
+
backfillJsonlAgentId(db2, entry.filePath, entry.agentId, log);
|
|
55944
|
+
} catch (bfErr) {
|
|
55945
|
+
log?.(`subagent-watcher: backfill retry error ${entry.agentId}: ${bfErr.message}`);
|
|
55946
|
+
}
|
|
55947
|
+
}
|
|
55794
55948
|
} else {
|
|
55795
55949
|
bumpSubagentActivity(db2, { id: existing.id, ts: now });
|
|
55796
55950
|
isForeground = existing.background === 0;
|
|
@@ -55913,6 +56067,23 @@ function readSubTail(entry, tail, now, onDescriptionUpdate, fs2, log, db2, paren
|
|
|
55913
56067
|
}
|
|
55914
56068
|
}
|
|
55915
56069
|
}
|
|
56070
|
+
} else if (ev.kind === "sub_agent_nested_spawn") {
|
|
56071
|
+
if (db2 != null && ev.toolUseId != null && ev.toolUseId.length > 0) {
|
|
56072
|
+
try {
|
|
56073
|
+
const nestedInput = ev.input ?? {};
|
|
56074
|
+
recordNestedSubagentDispatch(db2, {
|
|
56075
|
+
toolUseId: ev.toolUseId,
|
|
56076
|
+
parentJsonlAgentId: entry.agentId,
|
|
56077
|
+
agentType: typeof nestedInput.subagent_type === "string" ? nestedInput.subagent_type : null,
|
|
56078
|
+
description: typeof nestedInput.description === "string" ? nestedInput.description : null,
|
|
56079
|
+
background: nestedInput.run_in_background === true,
|
|
56080
|
+
now
|
|
56081
|
+
});
|
|
56082
|
+
log?.(`subagent-watcher: nested dispatch recorded parent=${entry.agentId} toolUseId=${ev.toolUseId}`);
|
|
56083
|
+
} catch (dbErr) {
|
|
56084
|
+
log?.(`subagent-watcher: nested dispatch record error ${entry.agentId}: ${dbErr.message}`);
|
|
56085
|
+
}
|
|
56086
|
+
}
|
|
55916
56087
|
} else if (ev.kind === "sub_agent_text") {
|
|
55917
56088
|
entry.lastSummaryLine = clipNarrative(ev.text);
|
|
55918
56089
|
entry.lastResultText = ev.text.trim().slice(0, SUBAGENT_RESULT_TEXT_MAX);
|
|
@@ -55984,7 +56155,7 @@ function startSubagentWatcher(config) {
|
|
|
55984
56155
|
clearTimeout(ref.ref);
|
|
55985
56156
|
});
|
|
55986
56157
|
const fs2 = config.fs ?? {
|
|
55987
|
-
existsSync:
|
|
56158
|
+
existsSync: existsSync30,
|
|
55988
56159
|
readdirSync: readdirSync5,
|
|
55989
56160
|
statSync: statSync9,
|
|
55990
56161
|
openSync: openSync5,
|
|
@@ -56064,6 +56235,15 @@ function startSubagentWatcher(config) {
|
|
|
56064
56235
|
log?.(`subagent-watcher: backfill error for ${agentId}: ${err.message}`);
|
|
56065
56236
|
}
|
|
56066
56237
|
}
|
|
56238
|
+
entry.toolCount = 0;
|
|
56239
|
+
entry.lastTool = null;
|
|
56240
|
+
entry.pendingNarrative = null;
|
|
56241
|
+
tail.cursor = 0;
|
|
56242
|
+
tail.pendingPartial = "";
|
|
56243
|
+
tail.hasEmittedStart = false;
|
|
56244
|
+
readSubTail(entry, tail, n, (desc) => {
|
|
56245
|
+
log?.(`subagent-watcher: description updated for ${agentId}: ${desc}`);
|
|
56246
|
+
}, fs2, log, db2, parentStateDir, config.onUnstall, undefined, config.onProgress);
|
|
56067
56247
|
}
|
|
56068
56248
|
}
|
|
56069
56249
|
if (isHistorical && entry.state === "done") {
|
|
@@ -56172,7 +56352,7 @@ function startSubagentWatcher(config) {
|
|
|
56172
56352
|
if (entry.stallNotified)
|
|
56173
56353
|
continue;
|
|
56174
56354
|
const idleMs = n - entry.lastActivityAt;
|
|
56175
|
-
const threshold = entry.toolCount === 0 ? silentSynthesisStallThresholdMs : stallThresholdMs;
|
|
56355
|
+
const threshold = entry.toolCount === 0 || isLongRunningTool(entry.lastTool?.name) ? silentSynthesisStallThresholdMs : stallThresholdMs;
|
|
56176
56356
|
if (idleMs >= threshold) {
|
|
56177
56357
|
entry.stallNotified = true;
|
|
56178
56358
|
entry.stalledAt = n;
|
|
@@ -56448,7 +56628,7 @@ function determineRestartReason(opts) {
|
|
|
56448
56628
|
init_boot_card();
|
|
56449
56629
|
|
|
56450
56630
|
// gateway/update-announce.ts
|
|
56451
|
-
import { existsSync as
|
|
56631
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync25, openSync as openSync6, closeSync as closeSync6, readFileSync as readFileSync34 } from "node:fs";
|
|
56452
56632
|
import { join as join32 } from "node:path";
|
|
56453
56633
|
import { homedir as homedir12 } from "node:os";
|
|
56454
56634
|
|
|
@@ -56577,7 +56757,7 @@ function readAndFilter(raw, filters, limit) {
|
|
|
56577
56757
|
var DEFAULT_LOOKBACK_MS = 10 * 60 * 1000;
|
|
56578
56758
|
function readLastTerminalUpdateAudit(opts = {}) {
|
|
56579
56759
|
const path = opts.auditLogPath ?? defaultAuditLogPath();
|
|
56580
|
-
const exists = opts.exists ??
|
|
56760
|
+
const exists = opts.exists ?? existsSync35;
|
|
56581
56761
|
const readFile = opts.readFile ?? ((p) => readFileSync34(p, "utf-8"));
|
|
56582
56762
|
if (!exists(path))
|
|
56583
56763
|
return null;
|
|
@@ -56667,7 +56847,7 @@ function maybeRenderUpdateAnnouncement(opts = {}) {
|
|
|
56667
56847
|
|
|
56668
56848
|
// issues-card.ts
|
|
56669
56849
|
init_card_format();
|
|
56670
|
-
import { readFileSync as readFileSync35, writeFileSync as
|
|
56850
|
+
import { readFileSync as readFileSync35, writeFileSync as writeFileSync24 } from "node:fs";
|
|
56671
56851
|
var SEVERITY_EMOJI = {
|
|
56672
56852
|
info: "\u2139\ufe0f",
|
|
56673
56853
|
warn: "\u26a0\ufe0f",
|
|
@@ -56775,7 +56955,7 @@ function readPersistedMessageId(path, log) {
|
|
|
56775
56955
|
}
|
|
56776
56956
|
function writePersistedMessageId(path, messageId, log) {
|
|
56777
56957
|
try {
|
|
56778
|
-
|
|
56958
|
+
writeFileSync24(path, JSON.stringify({ messageId }) + `
|
|
56779
56959
|
`, { mode: 384 });
|
|
56780
56960
|
} catch (err) {
|
|
56781
56961
|
log(`issues-card: persist write failed (${err.message})`);
|
|
@@ -56868,13 +57048,13 @@ function createIssuesCardHandle(opts) {
|
|
|
56868
57048
|
}
|
|
56869
57049
|
|
|
56870
57050
|
// issues-watcher.ts
|
|
56871
|
-
import { existsSync as
|
|
57051
|
+
import { existsSync as existsSync37, statSync as statSync11 } from "node:fs";
|
|
56872
57052
|
import { join as join34 } from "node:path";
|
|
56873
57053
|
|
|
56874
57054
|
// ../src/issues/store.ts
|
|
56875
57055
|
import {
|
|
56876
57056
|
closeSync as closeSync7,
|
|
56877
|
-
existsSync as
|
|
57057
|
+
existsSync as existsSync36,
|
|
56878
57058
|
mkdirSync as mkdirSync26,
|
|
56879
57059
|
openSync as openSync7,
|
|
56880
57060
|
readdirSync as readdirSync7,
|
|
@@ -56882,8 +57062,8 @@ import {
|
|
|
56882
57062
|
renameSync as renameSync13,
|
|
56883
57063
|
statSync as statSync10,
|
|
56884
57064
|
unlinkSync as unlinkSync13,
|
|
56885
|
-
writeFileSync as
|
|
56886
|
-
writeSync as
|
|
57065
|
+
writeFileSync as writeFileSync25,
|
|
57066
|
+
writeSync as writeSync4
|
|
56887
57067
|
} from "node:fs";
|
|
56888
57068
|
import { join as join33 } from "node:path";
|
|
56889
57069
|
import { randomBytes as randomBytes6 } from "node:crypto";
|
|
@@ -56905,7 +57085,7 @@ var ISSUES_FILE = "issues.jsonl";
|
|
|
56905
57085
|
var ISSUES_LOCK = "issues.lock";
|
|
56906
57086
|
function readAll(stateDir) {
|
|
56907
57087
|
const path = join33(stateDir, ISSUES_FILE);
|
|
56908
|
-
if (!
|
|
57088
|
+
if (!existsSync36(path))
|
|
56909
57089
|
return [];
|
|
56910
57090
|
let raw;
|
|
56911
57091
|
try {
|
|
@@ -56941,7 +57121,7 @@ function list(stateDir, opts = {}) {
|
|
|
56941
57121
|
});
|
|
56942
57122
|
}
|
|
56943
57123
|
function resolve6(stateDir, fingerprint, nowFn = Date.now) {
|
|
56944
|
-
if (!
|
|
57124
|
+
if (!existsSync36(join33(stateDir, ISSUES_FILE)))
|
|
56945
57125
|
return 0;
|
|
56946
57126
|
return withLock(stateDir, () => {
|
|
56947
57127
|
const all = readAll(stateDir);
|
|
@@ -56965,7 +57145,7 @@ function writeAll(stateDir, events) {
|
|
|
56965
57145
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
56966
57146
|
`) + `
|
|
56967
57147
|
`;
|
|
56968
|
-
|
|
57148
|
+
writeFileSync25(tmp, body, "utf-8");
|
|
56969
57149
|
renameSync13(tmp, path);
|
|
56970
57150
|
}
|
|
56971
57151
|
var ORPHAN_TMP_TTL_MS = 60000;
|
|
@@ -57000,7 +57180,7 @@ function withLock(stateDir, fn) {
|
|
|
57000
57180
|
try {
|
|
57001
57181
|
fd = openSync7(lockPath, "wx");
|
|
57002
57182
|
try {
|
|
57003
|
-
|
|
57183
|
+
writeSync4(fd, String(process.pid));
|
|
57004
57184
|
} catch {}
|
|
57005
57185
|
} catch (err) {
|
|
57006
57186
|
const e = err;
|
|
@@ -57126,7 +57306,7 @@ function startIssuesWatcher(opts) {
|
|
|
57126
57306
|
};
|
|
57127
57307
|
}
|
|
57128
57308
|
function defaultSignatureProvider(path) {
|
|
57129
|
-
if (!
|
|
57309
|
+
if (!existsSync37(path))
|
|
57130
57310
|
return null;
|
|
57131
57311
|
try {
|
|
57132
57312
|
const stat = statSync11(path);
|
|
@@ -58127,7 +58307,7 @@ function extractFlowItems(line) {
|
|
|
58127
58307
|
|
|
58128
58308
|
// credits-watch.ts
|
|
58129
58309
|
init_card_format();
|
|
58130
|
-
import { readFileSync as readFileSync37, writeFileSync as
|
|
58310
|
+
import { readFileSync as readFileSync37, writeFileSync as writeFileSync26, existsSync as existsSync38, mkdirSync as mkdirSync27 } from "fs";
|
|
58131
58311
|
import { join as join35 } from "path";
|
|
58132
58312
|
var STATE_FILE = "credits-watch.json";
|
|
58133
58313
|
var DEFAULT_CREDIT_FATAL_REASONS = new Set;
|
|
@@ -58151,7 +58331,7 @@ function emptyCreditState() {
|
|
|
58151
58331
|
}
|
|
58152
58332
|
function readClaudeJsonOverage(claudeConfigDir) {
|
|
58153
58333
|
const path = join35(claudeConfigDir, ".claude.json");
|
|
58154
|
-
if (!
|
|
58334
|
+
if (!existsSync38(path))
|
|
58155
58335
|
return null;
|
|
58156
58336
|
let raw;
|
|
58157
58337
|
try {
|
|
@@ -58234,7 +58414,7 @@ function humanizeReason(reason) {
|
|
|
58234
58414
|
}
|
|
58235
58415
|
function loadCreditState(stateDir) {
|
|
58236
58416
|
const path = join35(stateDir, STATE_FILE);
|
|
58237
|
-
if (!
|
|
58417
|
+
if (!existsSync38(path))
|
|
58238
58418
|
return emptyCreditState();
|
|
58239
58419
|
try {
|
|
58240
58420
|
const raw = readFileSync37(path, "utf-8");
|
|
@@ -58251,12 +58431,12 @@ function loadCreditState(stateDir) {
|
|
|
58251
58431
|
function saveCreditState(stateDir, state4) {
|
|
58252
58432
|
mkdirSync27(stateDir, { recursive: true });
|
|
58253
58433
|
const path = join35(stateDir, STATE_FILE);
|
|
58254
|
-
|
|
58434
|
+
writeFileSync26(path, JSON.stringify(state4, null, 2) + `
|
|
58255
58435
|
`, { mode: 384 });
|
|
58256
58436
|
}
|
|
58257
58437
|
|
|
58258
58438
|
// quota-watch.ts
|
|
58259
|
-
import { readFileSync as readFileSync38, writeFileSync as
|
|
58439
|
+
import { readFileSync as readFileSync38, writeFileSync as writeFileSync27, existsSync as existsSync39, mkdirSync as mkdirSync28 } from "fs";
|
|
58260
58440
|
import { join as join36 } from "path";
|
|
58261
58441
|
init_card_format();
|
|
58262
58442
|
var STATE_FILE2 = "quota-watch.json";
|
|
@@ -58453,7 +58633,7 @@ function buildRecoveryMessage(agentName3, snap) {
|
|
|
58453
58633
|
}
|
|
58454
58634
|
function loadQuotaWatchState(stateDir) {
|
|
58455
58635
|
const path = join36(stateDir, STATE_FILE2);
|
|
58456
|
-
if (!
|
|
58636
|
+
if (!existsSync39(path))
|
|
58457
58637
|
return emptyQuotaWatchState();
|
|
58458
58638
|
try {
|
|
58459
58639
|
const raw = readFileSync38(path, "utf-8");
|
|
@@ -58475,7 +58655,7 @@ function loadQuotaWatchState(stateDir) {
|
|
|
58475
58655
|
function saveQuotaWatchState(stateDir, state4) {
|
|
58476
58656
|
mkdirSync28(stateDir, { recursive: true });
|
|
58477
58657
|
const path = join36(stateDir, STATE_FILE2);
|
|
58478
|
-
|
|
58658
|
+
writeFileSync27(path, JSON.stringify(state4, null, 2) + `
|
|
58479
58659
|
`, { mode: 384 });
|
|
58480
58660
|
}
|
|
58481
58661
|
function patchQuotaWatchState(current, accountLabel, accountState) {
|
|
@@ -58505,27 +58685,27 @@ function maskVaultKey(name) {
|
|
|
58505
58685
|
// gateway/turn-active-marker.ts
|
|
58506
58686
|
import {
|
|
58507
58687
|
closeSync as closeSync8,
|
|
58508
|
-
existsSync as
|
|
58688
|
+
existsSync as existsSync40,
|
|
58509
58689
|
mkdirSync as mkdirSync29,
|
|
58510
58690
|
openSync as openSync8,
|
|
58511
58691
|
readFileSync as readFileSync39,
|
|
58512
58692
|
statSync as statSync12,
|
|
58513
58693
|
unlinkSync as unlinkSync14,
|
|
58514
58694
|
utimesSync as utimesSync2,
|
|
58515
|
-
writeFileSync as
|
|
58695
|
+
writeFileSync as writeFileSync28
|
|
58516
58696
|
} from "node:fs";
|
|
58517
58697
|
import { join as join37 } from "node:path";
|
|
58518
58698
|
var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
|
|
58519
58699
|
function writeTurnActiveMarker(stateDir, marker) {
|
|
58520
58700
|
try {
|
|
58521
58701
|
mkdirSync29(stateDir, { recursive: true });
|
|
58522
|
-
|
|
58702
|
+
writeFileSync28(join37(stateDir, TURN_ACTIVE_MARKER_FILE2), JSON.stringify(marker, null, 2) + `
|
|
58523
58703
|
`, { mode: 384 });
|
|
58524
58704
|
} catch {}
|
|
58525
58705
|
}
|
|
58526
58706
|
function touchTurnActiveMarker2(stateDir) {
|
|
58527
58707
|
const path = join37(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
58528
|
-
if (!
|
|
58708
|
+
if (!existsSync40(path))
|
|
58529
58709
|
return;
|
|
58530
58710
|
const now = new Date;
|
|
58531
58711
|
try {
|
|
@@ -58544,7 +58724,7 @@ function removeTurnActiveMarker(stateDir) {
|
|
|
58544
58724
|
}
|
|
58545
58725
|
function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
58546
58726
|
const path = join37(stateDir, TURN_ACTIVE_MARKER_FILE2);
|
|
58547
|
-
if (!
|
|
58727
|
+
if (!existsSync40(path))
|
|
58548
58728
|
return false;
|
|
58549
58729
|
const now = opts.now ?? Date.now();
|
|
58550
58730
|
try {
|
|
@@ -58584,11 +58764,11 @@ function readTurnActiveMarkerAgeMs(stateDir, now) {
|
|
|
58584
58764
|
}
|
|
58585
58765
|
|
|
58586
58766
|
// ../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 =
|
|
58767
|
+
var VERSION = "0.17.1";
|
|
58768
|
+
var COMMIT_SHA = "d7162c0";
|
|
58769
|
+
var COMMIT_DATE = "2026-07-05T05:44:23Z";
|
|
58770
|
+
var LATEST_PR = 2831;
|
|
58771
|
+
var COMMITS_AHEAD_OF_TAG = 0;
|
|
58592
58772
|
|
|
58593
58773
|
// gateway/boot-version.ts
|
|
58594
58774
|
function formatRelativeAgo(iso) {
|
|
@@ -59630,6 +59810,7 @@ var SCHEMA_SQL = `
|
|
|
59630
59810
|
assistant_reply_preview TEXT,
|
|
59631
59811
|
tool_call_count INTEGER,
|
|
59632
59812
|
interrupt_reason TEXT,
|
|
59813
|
+
resumed_at INTEGER,
|
|
59633
59814
|
created_at INTEGER NOT NULL,
|
|
59634
59815
|
updated_at INTEGER NOT NULL
|
|
59635
59816
|
);
|
|
@@ -59643,12 +59824,15 @@ var PHASE1_MIGRATIONS = [
|
|
|
59643
59824
|
var PHASE2_MIGRATIONS = [
|
|
59644
59825
|
`ALTER TABLE turns ADD COLUMN interrupt_reason TEXT`
|
|
59645
59826
|
];
|
|
59827
|
+
var PHASE3_MIGRATIONS = [
|
|
59828
|
+
`ALTER TABLE turns ADD COLUMN resumed_at INTEGER`
|
|
59829
|
+
];
|
|
59646
59830
|
function applySchema(db2) {
|
|
59647
59831
|
db2.exec("PRAGMA journal_mode = WAL");
|
|
59648
59832
|
db2.exec("PRAGMA synchronous = NORMAL");
|
|
59649
59833
|
db2.exec("PRAGMA busy_timeout = 5000");
|
|
59650
59834
|
db2.exec(SCHEMA_SQL);
|
|
59651
|
-
for (const sql of [...PHASE1_MIGRATIONS, ...PHASE2_MIGRATIONS]) {
|
|
59835
|
+
for (const sql of [...PHASE1_MIGRATIONS, ...PHASE2_MIGRATIONS, ...PHASE3_MIGRATIONS]) {
|
|
59652
59836
|
try {
|
|
59653
59837
|
db2.exec(sql);
|
|
59654
59838
|
} catch (err) {
|
|
@@ -59690,6 +59874,7 @@ function mapRow(row) {
|
|
|
59690
59874
|
assistant_reply_preview: row.assistant_reply_preview,
|
|
59691
59875
|
tool_call_count: row.tool_call_count,
|
|
59692
59876
|
interrupt_reason: row.interrupt_reason,
|
|
59877
|
+
resumed_at: row.resumed_at,
|
|
59693
59878
|
created_at: row.created_at,
|
|
59694
59879
|
updated_at: row.updated_at
|
|
59695
59880
|
};
|
|
@@ -59753,6 +59938,14 @@ var INTERRUPTED_VIA = new Set([
|
|
|
59753
59938
|
"timeout",
|
|
59754
59939
|
"unknown"
|
|
59755
59940
|
]);
|
|
59941
|
+
function markTurnResumed(db2, turnKey, now = Date.now()) {
|
|
59942
|
+
db2.prepare(`
|
|
59943
|
+
UPDATE turns
|
|
59944
|
+
SET resumed_at = ?,
|
|
59945
|
+
updated_at = ?
|
|
59946
|
+
WHERE turn_key = ? AND resumed_at IS NULL
|
|
59947
|
+
`).run(now, now, turnKey);
|
|
59948
|
+
}
|
|
59756
59949
|
function findLatestTurnIfInterrupted(db2) {
|
|
59757
59950
|
const row = db2.prepare(`
|
|
59758
59951
|
SELECT * FROM turns
|
|
@@ -59762,6 +59955,8 @@ function findLatestTurnIfInterrupted(db2) {
|
|
|
59762
59955
|
if (!row)
|
|
59763
59956
|
return null;
|
|
59764
59957
|
const turn = mapRow(row);
|
|
59958
|
+
if (turn.resumed_at != null)
|
|
59959
|
+
return null;
|
|
59765
59960
|
if (turn.ended_at == null)
|
|
59766
59961
|
return turn;
|
|
59767
59962
|
if (turn.ended_via != null && INTERRUPTED_VIA.has(turn.ended_via))
|
|
@@ -59885,7 +60080,8 @@ var SUBAGENTS_SCHEMA_SQL = `
|
|
|
59885
60080
|
ended_at INTEGER,
|
|
59886
60081
|
status TEXT NOT NULL,
|
|
59887
60082
|
result_summary TEXT,
|
|
59888
|
-
jsonl_agent_id TEXT
|
|
60083
|
+
jsonl_agent_id TEXT,
|
|
60084
|
+
parent_agent_id TEXT
|
|
59889
60085
|
);
|
|
59890
60086
|
CREATE INDEX IF NOT EXISTS subagents_turn ON subagents(parent_turn_key);
|
|
59891
60087
|
CREATE INDEX IF NOT EXISTS subagents_status ON subagents(status);
|
|
@@ -59897,6 +60093,10 @@ function applySubagentsSchema(db2) {
|
|
|
59897
60093
|
if (!hasJsonlId) {
|
|
59898
60094
|
db2.exec("ALTER TABLE subagents ADD COLUMN jsonl_agent_id TEXT");
|
|
59899
60095
|
}
|
|
60096
|
+
const hasParentAgentId = cols.some((c) => c.name === "parent_agent_id");
|
|
60097
|
+
if (!hasParentAgentId) {
|
|
60098
|
+
db2.exec("ALTER TABLE subagents ADD COLUMN parent_agent_id TEXT");
|
|
60099
|
+
}
|
|
59900
60100
|
db2.exec("CREATE INDEX IF NOT EXISTS subagents_jsonl_id ON subagents(jsonl_agent_id)");
|
|
59901
60101
|
}
|
|
59902
60102
|
function mapSubagentRow(row) {
|
|
@@ -59912,19 +60112,38 @@ function mapSubagentRow(row) {
|
|
|
59912
60112
|
ended_at: row.ended_at,
|
|
59913
60113
|
status: row.status,
|
|
59914
60114
|
result_summary: row.result_summary,
|
|
59915
|
-
jsonl_agent_id: row.jsonl_agent_id
|
|
60115
|
+
jsonl_agent_id: row.jsonl_agent_id,
|
|
60116
|
+
parent_agent_id: row.parent_agent_id ?? null
|
|
59916
60117
|
};
|
|
59917
60118
|
}
|
|
59918
60119
|
function getSubagentByJsonlId(db2, jsonlAgentId) {
|
|
59919
60120
|
const row = db2.prepare("SELECT * FROM subagents WHERE jsonl_agent_id = ?").get(jsonlAgentId);
|
|
59920
60121
|
return row ? mapSubagentRow(row) : null;
|
|
59921
60122
|
}
|
|
60123
|
+
function resolveSubagentOriginTurnKey(db2, jsonlAgentId, maxHops = 5) {
|
|
60124
|
+
const seen = new Set;
|
|
60125
|
+
let currentJsonlId = jsonlAgentId;
|
|
60126
|
+
for (let hop = 0;hop <= maxHops && currentJsonlId != null; hop++) {
|
|
60127
|
+
if (seen.has(currentJsonlId))
|
|
60128
|
+
return null;
|
|
60129
|
+
seen.add(currentJsonlId);
|
|
60130
|
+
const row = db2.prepare("SELECT parent_turn_key, parent_agent_id FROM subagents WHERE jsonl_agent_id = ? LIMIT 1").get(currentJsonlId);
|
|
60131
|
+
if (row == null)
|
|
60132
|
+
return null;
|
|
60133
|
+
if (row.parent_turn_key != null && row.parent_turn_key.length > 0)
|
|
60134
|
+
return row.parent_turn_key;
|
|
60135
|
+
currentJsonlId = row.parent_agent_id ?? null;
|
|
60136
|
+
}
|
|
60137
|
+
return null;
|
|
60138
|
+
}
|
|
59922
60139
|
|
|
59923
60140
|
// gateway/worker-feed-dispatch.ts
|
|
59924
60141
|
function resolveWorkerFeedDispatch(sub, watcherDescription) {
|
|
59925
60142
|
return {
|
|
59926
60143
|
isBackground: sub?.background ?? false,
|
|
59927
|
-
feedDescription: (sub?.description ?? "") || watcherDescription
|
|
60144
|
+
feedDescription: (sub?.description ?? "") || watcherDescription,
|
|
60145
|
+
hasRow: sub != null,
|
|
60146
|
+
isNested: sub?.parent_agent_id != null
|
|
59928
60147
|
};
|
|
59929
60148
|
}
|
|
59930
60149
|
|
|
@@ -59943,35 +60162,19 @@ function isOrphanSubagentStatusEnabled(envVal) {
|
|
|
59943
60162
|
return envVal !== "0";
|
|
59944
60163
|
}
|
|
59945
60164
|
|
|
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
|
-
}
|
|
60165
|
+
// status-query-telemetry.ts
|
|
60166
|
+
function deriveStatusQueryTelemetry(s) {
|
|
60167
|
+
const idle = !s.turnActive && s.runningWorkers <= 0 && s.workerFeedSize <= 0 && s.pendingAsync <= 0;
|
|
60168
|
+
const stage = idle ? "idle" : s.turnActive ? "turn-active" : "background-work";
|
|
60169
|
+
return { stage, idle };
|
|
60170
|
+
}
|
|
60171
|
+
function formatStatusQueryLine(agentName3, chatId, thread, s, t) {
|
|
60172
|
+
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}
|
|
60173
|
+
`;
|
|
60174
|
+
}
|
|
60175
|
+
function formatStatusQueryUxFailureLine(agentName3, chatId, thread) {
|
|
60176
|
+
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
|
|
60177
|
+
`;
|
|
59975
60178
|
}
|
|
59976
60179
|
|
|
59977
60180
|
// gateway/gateway.ts
|
|
@@ -60277,7 +60480,7 @@ function saveAccess(a) {
|
|
|
60277
60480
|
return;
|
|
60278
60481
|
mkdirSync32(STATE_DIR, { recursive: true, mode: 448 });
|
|
60279
60482
|
const tmp = ACCESS_FILE + ".tmp";
|
|
60280
|
-
|
|
60483
|
+
writeFileSync30(tmp, JSON.stringify(a, null, 2) + `
|
|
60281
60484
|
`, { mode: 384 });
|
|
60282
60485
|
renameSync14(tmp, ACCESS_FILE);
|
|
60283
60486
|
}
|
|
@@ -60314,7 +60517,7 @@ try {
|
|
|
60314
60517
|
let markerAgeMs = null;
|
|
60315
60518
|
try {
|
|
60316
60519
|
const markerPath = join40(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
60317
|
-
if (
|
|
60520
|
+
if (existsSync43(markerPath)) {
|
|
60318
60521
|
const st = statSync15(markerPath);
|
|
60319
60522
|
markerAgeMs = Date.now() - st.mtimeMs;
|
|
60320
60523
|
try {
|
|
@@ -60400,13 +60603,13 @@ try {
|
|
|
60400
60603
|
pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
|
|
60401
60604
|
];
|
|
60402
60605
|
const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
|
|
60403
|
-
|
|
60606
|
+
writeFileSync30(pendingEnvTmp, lines.join(`
|
|
60404
60607
|
`) + `
|
|
60405
60608
|
`, { mode: 384 });
|
|
60406
60609
|
renameSync14(pendingEnvTmp, pendingEnvPath);
|
|
60407
60610
|
process.stderr.write(`telegram gateway: pending-turn env written to ${pendingEnvPath} turnKey=${pending2.turn_key} endedVia=${pending2.ended_via ?? "open"}
|
|
60408
60611
|
`);
|
|
60409
|
-
} else if (
|
|
60612
|
+
} else if (existsSync43(pendingEnvPath)) {
|
|
60410
60613
|
rmSync4(pendingEnvPath, { force: true });
|
|
60411
60614
|
process.stderr.write(`telegram gateway: pending-turn env cleared (clean previous shutdown)
|
|
60412
60615
|
`);
|
|
@@ -60424,10 +60627,10 @@ function resolveSubagentOriginChat(agentId) {
|
|
|
60424
60627
|
if (turnsDb == null)
|
|
60425
60628
|
return null;
|
|
60426
60629
|
try {
|
|
60427
|
-
const
|
|
60428
|
-
if (
|
|
60630
|
+
const originKey = resolveSubagentOriginTurnKey(turnsDb, agentId);
|
|
60631
|
+
if (originKey == null)
|
|
60429
60632
|
return null;
|
|
60430
|
-
const turn = getTurnByKey(turnsDb,
|
|
60633
|
+
const turn = getTurnByKey(turnsDb, originKey);
|
|
60431
60634
|
if (turn == null || turn.chat_id.length === 0)
|
|
60432
60635
|
return null;
|
|
60433
60636
|
const threadNum = turn.thread_id != null && turn.thread_id.length > 0 ? Number(turn.thread_id) : NaN;
|
|
@@ -60506,6 +60709,7 @@ var chatAvailableReactions = new Map;
|
|
|
60506
60709
|
var chatProbesInFlight = new Set;
|
|
60507
60710
|
var activeTurnStartedAt = new Map;
|
|
60508
60711
|
var claudeBusyKeys = new Set;
|
|
60712
|
+
var claudeBusyKeySince = new Map;
|
|
60509
60713
|
var reactionTransitionCounts = new Map;
|
|
60510
60714
|
var firstTextReplyLogged = new Set;
|
|
60511
60715
|
function markClaudeBusyForInbound(m) {
|
|
@@ -60516,9 +60720,20 @@ function markClaudeBusyForInbound(m) {
|
|
|
60516
60720
|
tid = n;
|
|
60517
60721
|
}
|
|
60518
60722
|
const key = chatKey2(m.chatId, tid);
|
|
60519
|
-
claudeBusyKeys.
|
|
60723
|
+
markBusyKeyLockstep(claudeBusyKeys, claudeBusyKeySince, key, Date.now());
|
|
60520
60724
|
return key;
|
|
60521
60725
|
}
|
|
60726
|
+
var _busyOrphanTtlRaw = process.env.SWITCHROOM_BUSY_ORPHAN_TTL_MS;
|
|
60727
|
+
var _busyOrphanTtlParsed = _busyOrphanTtlRaw != null && _busyOrphanTtlRaw !== "" ? Number(_busyOrphanTtlRaw) : 360000;
|
|
60728
|
+
var CLAUDE_BUSY_ORPHAN_TTL_MS = Number.isFinite(_busyOrphanTtlParsed) && _busyOrphanTtlParsed > 0 ? _busyOrphanTtlParsed : 360000;
|
|
60729
|
+
function reapOrphanBusyKeysNow(now) {
|
|
60730
|
+
reapOrphanBusyKeys(claudeBusyKeys, claudeBusyKeySince, now, {
|
|
60731
|
+
ttlMs: CLAUDE_BUSY_ORPHAN_TTL_MS,
|
|
60732
|
+
hasPendingDelivery: (key) => deliveryQueue.pending.has(key),
|
|
60733
|
+
log: (msg) => process.stderr.write(`${msg}
|
|
60734
|
+
`)
|
|
60735
|
+
});
|
|
60736
|
+
}
|
|
60522
60737
|
var DELIVERY_CONFIRM_ENABLED = process.env.SWITCHROOM_INBOUND_DELIVERY_CONFIRM !== "0";
|
|
60523
60738
|
var _deliveryTimeoutRaw = process.env.SWITCHROOM_INBOUND_DELIVERY_TIMEOUT_MS;
|
|
60524
60739
|
var _deliveryTimeoutParsed = _deliveryTimeoutRaw != null && _deliveryTimeoutRaw !== "" ? Number(_deliveryTimeoutRaw) : 15000;
|
|
@@ -60572,9 +60787,9 @@ function noteAgentOutputAt(key, ts) {
|
|
|
60572
60787
|
var OBLIGATION_STORE_PATH = join40(STATE_DIR, "obligations.json");
|
|
60573
60788
|
var obligationStoreFs = {
|
|
60574
60789
|
readFileSync: (p) => readFileSync41(p, "utf8"),
|
|
60575
|
-
writeFileSync: (p, d) =>
|
|
60790
|
+
writeFileSync: (p, d) => writeFileSync30(p, d),
|
|
60576
60791
|
renameSync: (a, b) => renameSync14(a, b),
|
|
60577
|
-
existsSync: (p) =>
|
|
60792
|
+
existsSync: (p) => existsSync43(p)
|
|
60578
60793
|
};
|
|
60579
60794
|
var obligationLedger = new ObligationLedger(OBLIGATION_REPRESENT_MAX, {
|
|
60580
60795
|
onChange: STATIC || !OBLIGATION_LEDGER_ENABLED ? undefined : (snapshot) => persistObligations(OBLIGATION_STORE_PATH, obligationStoreFs, snapshot)
|
|
@@ -60608,15 +60823,10 @@ var FEED_LIVENESS_OPEN_MS = (() => {
|
|
|
60608
60823
|
return Number.isFinite(n) && n > 0 ? n : 1200;
|
|
60609
60824
|
})();
|
|
60610
60825
|
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
60826
|
function turnInFlightForGate() {
|
|
60619
|
-
|
|
60827
|
+
if (!isDeliveryCutoverEnabled())
|
|
60828
|
+
return claudeBusyKeys.size > 0;
|
|
60829
|
+
return probeGateParity(isMachineInTurn(), claudeBusyKeys.size);
|
|
60620
60830
|
}
|
|
60621
60831
|
function deliverResumeSyntheticOrBuffer(agent, inbound) {
|
|
60622
60832
|
const decision = decideInboundDelivery({
|
|
@@ -60936,7 +61146,7 @@ var pendingPtyPartial = null;
|
|
|
60936
61146
|
function statusKey(chatId, threadId) {
|
|
60937
61147
|
return chatKey2(chatId, threadId);
|
|
60938
61148
|
}
|
|
60939
|
-
function
|
|
61149
|
+
function streamKey2(chatId, threadId) {
|
|
60940
61150
|
return chatKey2(chatId, threadId);
|
|
60941
61151
|
}
|
|
60942
61152
|
var pendingCrossTurnGate = new Map;
|
|
@@ -61002,6 +61212,7 @@ function purgeReactionTracking(key, endingTurn) {
|
|
|
61002
61212
|
reapQueuedStatus(pqChatId, Number.isFinite(pqThread) ? pqThread : undefined);
|
|
61003
61213
|
}
|
|
61004
61214
|
claudeBusyKeys.delete(key);
|
|
61215
|
+
claudeBusyKeySince.delete(key);
|
|
61005
61216
|
reactionTransitionCounts.delete(key);
|
|
61006
61217
|
firstTextReplyLogged.delete(key);
|
|
61007
61218
|
if (endingTurn != null) {
|
|
@@ -61064,6 +61275,7 @@ function releaseTurnBufferGate(key, endingTurn) {
|
|
|
61064
61275
|
return;
|
|
61065
61276
|
activeTurnStartedAt.delete(key);
|
|
61066
61277
|
claudeBusyKeys.delete(key);
|
|
61278
|
+
claudeBusyKeySince.delete(key);
|
|
61067
61279
|
shadowEmit({ kind: "turnEnd", key, at: Date.now(), outboundEmitted: true });
|
|
61068
61280
|
drainBufferedIfAllowed(endingTurn, "reply-released-gate");
|
|
61069
61281
|
}
|
|
@@ -62024,24 +62236,12 @@ var statusPinChatIds = new Map;
|
|
|
62024
62236
|
var STATUS_PIN_STORE_PATH = join40(STATE_DIR, "status-pins.json");
|
|
62025
62237
|
var statusPinStoreFs = {
|
|
62026
62238
|
readFileSync: (p) => readFileSync41(p, "utf8"),
|
|
62027
|
-
writeFileSync: (p, d) =>
|
|
62239
|
+
writeFileSync: (p, d) => writeFileSync30(p, d),
|
|
62028
62240
|
renameSync: (a, b) => renameSync14(a, b),
|
|
62029
|
-
existsSync: (p) =>
|
|
62241
|
+
existsSync: (p) => existsSync43(p)
|
|
62030
62242
|
};
|
|
62031
62243
|
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
|
-
}
|
|
62244
|
+
var bannerPinPersistEnabled = !STATIC;
|
|
62045
62245
|
function statusPinApi() {
|
|
62046
62246
|
return {
|
|
62047
62247
|
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 +62249,7 @@ function statusPinApi() {
|
|
|
62049
62249
|
};
|
|
62050
62250
|
}
|
|
62051
62251
|
async function statusPinBootCleanup() {
|
|
62052
|
-
if (!statusPinPersistEnabled)
|
|
62252
|
+
if (!statusPinPersistEnabled && !bannerPinPersistEnabled)
|
|
62053
62253
|
return;
|
|
62054
62254
|
const api = statusPinApi();
|
|
62055
62255
|
const { cleared, total } = await runStatusPinBootCleanup({
|
|
@@ -62107,7 +62307,6 @@ async function reconcileStatusPinInner(pinKey, chatId, desired) {
|
|
|
62107
62307
|
pinKey,
|
|
62108
62308
|
chatId,
|
|
62109
62309
|
op,
|
|
62110
|
-
snapshotOthers: () => snapshotStatusPinsExcept(pinKey),
|
|
62111
62310
|
applyPin: runReconcile
|
|
62112
62311
|
});
|
|
62113
62312
|
if (next == null) {
|
|
@@ -62459,14 +62658,35 @@ async function redeliverStrandedInbound(p) {
|
|
|
62459
62658
|
forgetDelivery(deliveryQueue, p.key);
|
|
62460
62659
|
}
|
|
62461
62660
|
}
|
|
62661
|
+
function sweepSuspendedTargets() {
|
|
62662
|
+
const keys = new Set;
|
|
62663
|
+
const chats = new Set;
|
|
62664
|
+
for (const p of pendingPermissions.values()) {
|
|
62665
|
+
for (const c of p.cards) {
|
|
62666
|
+
if (c.threadId != null)
|
|
62667
|
+
keys.add(chatKey2(c.chatId, c.threadId));
|
|
62668
|
+
else
|
|
62669
|
+
chats.add(c.chatId);
|
|
62670
|
+
}
|
|
62671
|
+
}
|
|
62672
|
+
for (const a of pendingAskUser.values()) {
|
|
62673
|
+
if (a.threadId != null)
|
|
62674
|
+
keys.add(chatKey2(a.chatId, a.threadId));
|
|
62675
|
+
else
|
|
62676
|
+
chats.add(a.chatId);
|
|
62677
|
+
}
|
|
62678
|
+
return { keys, chats };
|
|
62679
|
+
}
|
|
62462
62680
|
var _deliveryConfirmSweep = setInterval(() => {
|
|
62463
62681
|
if (!DELIVERY_CONFIRM_ENABLED)
|
|
62464
62682
|
return;
|
|
62465
62683
|
if (currentTurn != null)
|
|
62466
62684
|
return;
|
|
62467
|
-
|
|
62468
|
-
|
|
62685
|
+
reapOrphanBusyKeysNow(Date.now());
|
|
62686
|
+
const suspended = sweepSuspendedTargets();
|
|
62469
62687
|
for (const p of sweep(deliveryQueue, Date.now(), DELIVERY_CONFIRM_TIMEOUT_MS)) {
|
|
62688
|
+
if (isRedeliverySuspended(p.key, suspended))
|
|
62689
|
+
continue;
|
|
62470
62690
|
redeliverStrandedInbound(p);
|
|
62471
62691
|
}
|
|
62472
62692
|
}, DELIVERY_CONFIRM_SWEEP_MS);
|
|
@@ -62490,13 +62710,33 @@ var inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
|
62490
62710
|
fs: {
|
|
62491
62711
|
appendFileSync: (p, d) => appendFileSync6(p, d),
|
|
62492
62712
|
readFileSync: (p) => readFileSync41(p, "utf8"),
|
|
62493
|
-
writeFileSync: (p, d) =>
|
|
62713
|
+
writeFileSync: (p, d) => writeFileSync30(p, d),
|
|
62494
62714
|
renameSync: (a, b) => renameSync14(a, b),
|
|
62495
|
-
existsSync: (p) =>
|
|
62715
|
+
existsSync: (p) => existsSync43(p),
|
|
62496
62716
|
statSizeSync: (p) => statSync15(p).size
|
|
62717
|
+
},
|
|
62718
|
+
onDegraded: (info) => {
|
|
62719
|
+
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}` : ""}
|
|
62720
|
+
`);
|
|
62721
|
+
}
|
|
62722
|
+
});
|
|
62723
|
+
var EVICT_NOTICE_COOLDOWN_MS = 300000;
|
|
62724
|
+
var evictNoticeByChat = new Map;
|
|
62725
|
+
var pendingInboundBuffer = createPendingInboundBuffer({
|
|
62726
|
+
spool: inboundSpool,
|
|
62727
|
+
onEvict: (_agent, evicted) => {
|
|
62728
|
+
const chat = evicted.chatId;
|
|
62729
|
+
const evThread = typeof evicted.meta?.threadId === "string" && evicted.meta.threadId ? Number(evicted.meta.threadId) : undefined;
|
|
62730
|
+
const key = `${chat}:${evThread ?? "-"}`;
|
|
62731
|
+
const last = evictNoticeByChat.get(key);
|
|
62732
|
+
const nowMs2 = Date.now();
|
|
62733
|
+
if (last != null && nowMs2 - last < EVICT_NOTICE_COOLDOWN_MS)
|
|
62734
|
+
return;
|
|
62735
|
+
evictNoticeByChat.set(key, nowMs2);
|
|
62736
|
+
const threadOpts = evThread != null ? { message_thread_id: evThread } : {};
|
|
62737
|
+
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
62738
|
}
|
|
62498
62739
|
});
|
|
62499
|
-
var pendingInboundBuffer = createPendingInboundBuffer({ spool: inboundSpool });
|
|
62500
62740
|
function agentHasInFlightBackgroundWork(now) {
|
|
62501
62741
|
if (countRunningWorkers() > 0)
|
|
62502
62742
|
return true;
|
|
@@ -62546,6 +62786,11 @@ function obligationSweep() {
|
|
|
62546
62786
|
pendingCrossTurnGate.set(o.originTurnId, { sinceMs: o.openedAt });
|
|
62547
62787
|
const attempt = obligationLedger.markRepresented(o.originTurnId);
|
|
62548
62788
|
process.stderr.write(`telegram gateway: obligation re-presented origin=${o.originTurnId} attempt=${attempt}/${OBLIGATION_REPRESENT_MAX}
|
|
62789
|
+
`);
|
|
62790
|
+
return;
|
|
62791
|
+
}
|
|
62792
|
+
if (shouldDeferEscalationForBridge({ bridgeAlive: ipcServer.getClient(agent)?.isAlive() === true })) {
|
|
62793
|
+
process.stderr.write(`telegram gateway: obligation escalation deferred \u2014 bridge down (nudge waits for reconnect) origin=${o.originTurnId}
|
|
62549
62794
|
`);
|
|
62550
62795
|
return;
|
|
62551
62796
|
}
|
|
@@ -62575,6 +62820,15 @@ if (bootResumeInbound != null) {
|
|
|
62575
62820
|
} else {
|
|
62576
62821
|
pendingInboundBuffer.push(bootResumeInbound.agent, bootResumeInbound.msg);
|
|
62577
62822
|
}
|
|
62823
|
+
const resumeTurnKey = bootResumeInbound.msg.meta?.resume_turn_key;
|
|
62824
|
+
if (turnsDb != null && typeof resumeTurnKey === "string" && resumeTurnKey.length > 0) {
|
|
62825
|
+
try {
|
|
62826
|
+
markTurnResumed(turnsDb, resumeTurnKey);
|
|
62827
|
+
} catch (err) {
|
|
62828
|
+
process.stderr.write(`telegram gateway: markTurnResumed failed turnKey=${resumeTurnKey}: ${err.message}
|
|
62829
|
+
`);
|
|
62830
|
+
}
|
|
62831
|
+
}
|
|
62578
62832
|
}
|
|
62579
62833
|
if (inboundSpool != null) {
|
|
62580
62834
|
const replay = inboundSpool.liveEntries();
|
|
@@ -62759,6 +63013,7 @@ var ipcServer = createIpcServer({
|
|
|
62759
63013
|
activeReactionMsgIds,
|
|
62760
63014
|
activeTurnStartedAt,
|
|
62761
63015
|
claudeBusyKeys,
|
|
63016
|
+
claudeBusyKeySince,
|
|
62762
63017
|
activeDraftStreams,
|
|
62763
63018
|
clearActiveReactions: () => {
|
|
62764
63019
|
const ad = resolveAgentDirFromEnv();
|
|
@@ -62800,9 +63055,6 @@ var ipcServer = createIpcServer({
|
|
|
62800
63055
|
if (msg.activeFile)
|
|
62801
63056
|
lastSessionActiveFile = msg.activeFile;
|
|
62802
63057
|
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
63058
|
handleSessionEvent(ev);
|
|
62807
63059
|
toolFlightTracker.onEvent(ev);
|
|
62808
63060
|
if (pendingDeferredInterrupt != null && !toolFlightTracker.isMidToolCall()) {
|
|
@@ -62872,7 +63124,8 @@ var ipcServer = createIpcServer({
|
|
|
62872
63124
|
}), { threadId, chat_id: chatId, verb: "permission_request" }).then((sent) => {
|
|
62873
63125
|
const pend = pendingPermissions.get(requestId);
|
|
62874
63126
|
if (pend && sent && typeof sent.message_id === "number") {
|
|
62875
|
-
|
|
63127
|
+
const landedThreadId = sent.message_thread_id ?? undefined;
|
|
63128
|
+
pend.cards.push({ chatId, messageId: sent.message_id, threadId: landedThreadId });
|
|
62876
63129
|
permCardStore.add({
|
|
62877
63130
|
requestId,
|
|
62878
63131
|
chatId,
|
|
@@ -63383,20 +63636,21 @@ if (!STATIC) {
|
|
|
63383
63636
|
process.stderr.write(`telegram gateway: idle-drain flushed ${r.redelivered}/${r.drained} buffered inbound for ${selfAgent}${r.rebuffered > 0 ? ` (${r.rebuffered} re-buffered)` : ""}
|
|
63384
63637
|
`);
|
|
63385
63638
|
}
|
|
63386
|
-
inboundSpool?.sweepEscalations((e, { postNotice }) => {
|
|
63639
|
+
inboundSpool?.sweepEscalations((e, { postNotice, droppedCount }) => {
|
|
63387
63640
|
const chat = e.msg.chatId;
|
|
63388
63641
|
const escThread = typeof e.msg.meta?.threadId === "string" && e.msg.meta.threadId ? Number(e.msg.meta.threadId) : undefined;
|
|
63389
63642
|
const threadOpts = escThread != null ? { message_thread_id: escThread } : {};
|
|
63390
63643
|
reapQueuedStatus(chat, escThread);
|
|
63391
63644
|
if (!postNotice)
|
|
63392
63645
|
return;
|
|
63393
|
-
|
|
63646
|
+
const n = droppedCount > 0 ? droppedCount : 1;
|
|
63647
|
+
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.`;
|
|
63648
|
+
swallowingApiCall(() => bot.api.sendMessage(chat, noticeText, { ...threadOpts }), { chat_id: chat, verb: "inbound-spool-escalation" });
|
|
63394
63649
|
});
|
|
63395
63650
|
}, IDLE_DRAIN_INTERVAL_MS).unref();
|
|
63396
63651
|
}
|
|
63397
63652
|
var ALLOWED_TOOLS = new Set([
|
|
63398
63653
|
"reply",
|
|
63399
|
-
"stream_reply",
|
|
63400
63654
|
"progress_update",
|
|
63401
63655
|
"react",
|
|
63402
63656
|
"download_attachment",
|
|
@@ -63425,8 +63679,6 @@ async function executeToolCall(tool, args) {
|
|
|
63425
63679
|
switch (tool) {
|
|
63426
63680
|
case "reply":
|
|
63427
63681
|
return executeReply(args);
|
|
63428
|
-
case "stream_reply":
|
|
63429
|
-
return executeStreamReply(args);
|
|
63430
63682
|
case "progress_update":
|
|
63431
63683
|
return executeProgressUpdate(args);
|
|
63432
63684
|
case "react":
|
|
@@ -63895,7 +64147,7 @@ ${url}`;
|
|
|
63895
64147
|
}
|
|
63896
64148
|
}
|
|
63897
64149
|
}
|
|
63898
|
-
const replySKey =
|
|
64150
|
+
const replySKey = streamKey2(chat_id, threadId);
|
|
63899
64151
|
suppressPtyPreview.add(replySKey);
|
|
63900
64152
|
let previewMessageId = null;
|
|
63901
64153
|
const openStream = activeDraftStreams.get(replySKey);
|
|
@@ -64293,221 +64545,6 @@ ${url}`;
|
|
|
64293
64545
|
}
|
|
64294
64546
|
return { content: [{ type: "text", text: result }] };
|
|
64295
64547
|
}
|
|
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
64548
|
async function executeProgressUpdate(args) {
|
|
64512
64549
|
if (!args.chat_id)
|
|
64513
64550
|
throw new Error("progress_update: chat_id is required");
|
|
@@ -64518,6 +64555,7 @@ async function executeProgressUpdate(args) {
|
|
|
64518
64555
|
const threadId = resolveThreadId(chat_id, args.message_thread_id);
|
|
64519
64556
|
const key = statusKey(chat_id, threadId);
|
|
64520
64557
|
assertAllowedChat(chat_id);
|
|
64558
|
+
text2 = redactOutboundText(text2, "progress_update");
|
|
64521
64559
|
if (text2.length > 300) {
|
|
64522
64560
|
text2 = text2.slice(0, 299) + "\u2026";
|
|
64523
64561
|
}
|
|
@@ -64551,38 +64589,6 @@ async function executeProgressUpdate(args) {
|
|
|
64551
64589
|
}
|
|
64552
64590
|
progressUpdateTurnCount.set(key, currentCount + 1);
|
|
64553
64591
|
}
|
|
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
64592
|
const access = loadAccess();
|
|
64587
64593
|
const literalText = (access.parseMode ?? "html") === "text";
|
|
64588
64594
|
const sendOpts = {
|
|
@@ -64764,7 +64770,7 @@ async function publishToTelegraph(text2, shortName, authorName) {
|
|
|
64764
64770
|
const accountPath = join40(STATE_DIR, "telegraph-account.json");
|
|
64765
64771
|
let account = null;
|
|
64766
64772
|
try {
|
|
64767
|
-
if (
|
|
64773
|
+
if (existsSync43(accountPath)) {
|
|
64768
64774
|
const raw = readFileSync41(accountPath, "utf-8");
|
|
64769
64775
|
const parsed = JSON.parse(raw);
|
|
64770
64776
|
if (parsed.shortName && parsed.accessToken) {
|
|
@@ -64785,7 +64791,7 @@ async function publishToTelegraph(text2, shortName, authorName) {
|
|
|
64785
64791
|
account = created.value;
|
|
64786
64792
|
try {
|
|
64787
64793
|
mkdirSync32(STATE_DIR, { recursive: true, mode: 448 });
|
|
64788
|
-
|
|
64794
|
+
writeFileSync30(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
|
|
64789
64795
|
} catch (err) {
|
|
64790
64796
|
process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
|
|
64791
64797
|
`);
|
|
@@ -64830,8 +64836,8 @@ function renderVaultRequestSaveCard(req, agentSlug) {
|
|
|
64830
64836
|
}
|
|
64831
64837
|
lines.push("");
|
|
64832
64838
|
lines.push(`_Tap Save to write to the host vault, Rename to change the key name, or Discard to drop it. The value is held in this chat's gateway memory until you decide._`);
|
|
64833
|
-
return lines.join(`
|
|
64834
|
-
`);
|
|
64839
|
+
return hardenCardBreaks2(lines.join(`
|
|
64840
|
+
`));
|
|
64835
64841
|
}
|
|
64836
64842
|
async function executeVaultRequestSave(args) {
|
|
64837
64843
|
const chat_id = String(args.chat_id ?? "");
|
|
@@ -65227,7 +65233,7 @@ async function executeDownloadAttachment(args) {
|
|
|
65227
65233
|
});
|
|
65228
65234
|
mkdirSync32(INBOX_DIR, { recursive: true, mode: 448 });
|
|
65229
65235
|
assertInsideInbox(INBOX_DIR, dlPath);
|
|
65230
|
-
|
|
65236
|
+
writeFileSync30(dlPath, buf, { mode: 384 });
|
|
65231
65237
|
return { content: [{ type: "text", text: dlPath }] };
|
|
65232
65238
|
}
|
|
65233
65239
|
async function executeEditMessage(args) {
|
|
@@ -65402,7 +65408,7 @@ function resetOrphanedReplyTimeout() {
|
|
|
65402
65408
|
currentSessionChatId: turn.sessionChatId,
|
|
65403
65409
|
capturedTextCount: turn.capturedText.length,
|
|
65404
65410
|
replyCalled: turn.replyCalled,
|
|
65405
|
-
progressCardActive:
|
|
65411
|
+
progressCardActive: false
|
|
65406
65412
|
})) {
|
|
65407
65413
|
turn.orphanedReplyTimeoutId = setTimeout(() => {
|
|
65408
65414
|
const t = currentTurn;
|
|
@@ -65413,7 +65419,7 @@ function resetOrphanedReplyTimeout() {
|
|
|
65413
65419
|
currentSessionChatId: t.sessionChatId,
|
|
65414
65420
|
capturedTextCount: t.capturedText.length,
|
|
65415
65421
|
replyCalled: t.replyCalled,
|
|
65416
|
-
progressCardActive:
|
|
65422
|
+
progressCardActive: false
|
|
65417
65423
|
})) {
|
|
65418
65424
|
const turnKey = statusKey(t.sessionChatId, t.sessionThreadId);
|
|
65419
65425
|
const working = isLegitimatelyWorking(turnKey);
|
|
@@ -65583,7 +65589,7 @@ function openLivenessFeedIfDue(turn) {
|
|
|
65583
65589
|
toolCount: turn.labeledToolCount,
|
|
65584
65590
|
state: "running"
|
|
65585
65591
|
};
|
|
65586
|
-
const rendered = renderActivityFeedWithNested(lines, [], false,
|
|
65592
|
+
const rendered = renderActivityFeedWithNested(lines, [], false, formatStepSuffix2(age), undefined, livenessHeader);
|
|
65587
65593
|
if (rendered == null)
|
|
65588
65594
|
return;
|
|
65589
65595
|
turn.activityPendingRender = rendered;
|
|
@@ -65646,7 +65652,7 @@ function feedHeartbeatTick() {
|
|
|
65646
65652
|
};
|
|
65647
65653
|
const lines = turn.mirrorLines.length > 0 ? turn.mirrorLines : ["Working in background\u2026"];
|
|
65648
65654
|
const elapsed2 = Date.now() - subagentAt;
|
|
65649
|
-
const rendered2 = renderActivityFeedWithNested(lines, [], false,
|
|
65655
|
+
const rendered2 = renderActivityFeedWithNested(lines, [], false, formatStepSuffix2(elapsed2), undefined, livenessHeader);
|
|
65650
65656
|
if (rendered2 == null)
|
|
65651
65657
|
return;
|
|
65652
65658
|
turn.activityPendingRender = rendered2;
|
|
@@ -65671,7 +65677,7 @@ function feedHeartbeatTick() {
|
|
|
65671
65677
|
const elapsed = Date.now() - turn.lastToolLabelAt;
|
|
65672
65678
|
if (elapsed < FEED_HEARTBEAT_MIN_STALE_MS)
|
|
65673
65679
|
return;
|
|
65674
|
-
const rendered = composeTurnActivity(turn, false,
|
|
65680
|
+
const rendered = composeTurnActivity(turn, false, formatStepSuffix2(elapsed));
|
|
65675
65681
|
if (rendered == null)
|
|
65676
65682
|
return;
|
|
65677
65683
|
turn.activityPendingRender = rendered;
|
|
@@ -65799,7 +65805,7 @@ function handleSessionEvent(ev) {
|
|
|
65799
65805
|
rememberRecentTurn(next);
|
|
65800
65806
|
promoteQueuedStatus(ev.chatId, enqThreadIdNum);
|
|
65801
65807
|
if (DELIVERY_CONFIRM_ENABLED) {
|
|
65802
|
-
ackDelivery(deliveryQueue, chatKey2(ev.chatId, ev.threadId != null ? Number(ev.threadId) : null), ev.messageId);
|
|
65808
|
+
ackDelivery(deliveryQueue, chatKey2(ev.chatId, ev.threadId != null ? Number(ev.threadId) : null), ev.messageId, ev.rawContent);
|
|
65803
65809
|
}
|
|
65804
65810
|
shadowEmit({
|
|
65805
65811
|
kind: "turnStart",
|
|
@@ -66149,7 +66155,7 @@ function handleSessionEvent(ev) {
|
|
|
66149
66155
|
unpinProgressCardForChat?.(chatId, threadId);
|
|
66150
66156
|
finalizeStatusReaction(chatId, threadId, "done");
|
|
66151
66157
|
{
|
|
66152
|
-
const sKey =
|
|
66158
|
+
const sKey = streamKey2(chatId, threadId);
|
|
66153
66159
|
const turnDurationMs = turn.startedAt > 0 ? Date.now() - turn.startedAt : 0;
|
|
66154
66160
|
logStreamingEvent({
|
|
66155
66161
|
kind: "turn_end",
|
|
@@ -66194,10 +66200,12 @@ function handleSessionEvent(ev) {
|
|
|
66194
66200
|
}
|
|
66195
66201
|
if (flushDecision.kind === "flush") {
|
|
66196
66202
|
let capturedText = flushDecision.text;
|
|
66203
|
+
capturedText = normalizeParagraphBreaks(repairEscapedWhitespace(capturedText));
|
|
66197
66204
|
const backstopChatId = chatId;
|
|
66198
66205
|
const backstopThreadId = threadId;
|
|
66199
66206
|
const backstopCtrl = ctrl;
|
|
66200
66207
|
capturedText = redactOutboundText(capturedText, "turn_flush");
|
|
66208
|
+
capturedText = stripExcessBold(normalizePunctuation(capturedText));
|
|
66201
66209
|
{
|
|
66202
66210
|
const scrub = scrubVoice(capturedText);
|
|
66203
66211
|
if (scrub.replaced > 0) {
|
|
@@ -66247,7 +66255,8 @@ function handleSessionEvent(ev) {
|
|
|
66247
66255
|
link_preview_options: { is_disabled: true }
|
|
66248
66256
|
};
|
|
66249
66257
|
const limit = RICH_MESSAGE_MAX_CHARS2;
|
|
66250
|
-
const
|
|
66258
|
+
const renderedText = addParagraphSpacers(capturedText);
|
|
66259
|
+
const htmlChunks = splitMarkdownChunks(renderedText, limit);
|
|
66251
66260
|
const sentIds = [];
|
|
66252
66261
|
try {
|
|
66253
66262
|
let firstSendUsedEdit = false;
|
|
@@ -66336,7 +66345,7 @@ function handleSessionEvent(ev) {
|
|
|
66336
66345
|
}
|
|
66337
66346
|
finalizeStatusReaction(chatId, threadId, terminalReason);
|
|
66338
66347
|
{
|
|
66339
|
-
const sKey =
|
|
66348
|
+
const sKey = streamKey2(chatId, threadId);
|
|
66340
66349
|
const turnDurationMs = turn.startedAt > 0 ? Date.now() - turn.startedAt : 0;
|
|
66341
66350
|
logStreamingEvent({
|
|
66342
66351
|
kind: "turn_end",
|
|
@@ -66736,15 +66745,23 @@ async function handleInbound(ctx, text2, downloadImage, attachment, extraAttachm
|
|
|
66736
66745
|
}
|
|
66737
66746
|
if (STATUS_QUERY_RE.test(text2)) {
|
|
66738
66747
|
try {
|
|
66739
|
-
const
|
|
66740
|
-
const
|
|
66741
|
-
const
|
|
66742
|
-
const
|
|
66743
|
-
const
|
|
66744
|
-
|
|
66748
|
+
const thread = messageThreadId != null ? String(messageThreadId) : "none";
|
|
66749
|
+
const key = statusKey(chat_id, messageThreadId);
|
|
66750
|
+
const turnStartedAt = activeTurnStartedAt.get(key);
|
|
66751
|
+
const turnActive = turnStartedAt != null;
|
|
66752
|
+
const surfaces = {
|
|
66753
|
+
turnActive,
|
|
66754
|
+
turnAgeS: turnActive ? Math.max(0, Math.floor((Date.now() - turnStartedAt) / 1000)) : -1,
|
|
66755
|
+
runningWorkers: countRunningWorkers(),
|
|
66756
|
+
workerFeedSize: workerActivityFeed?.size ?? 0,
|
|
66757
|
+
pendingAsync: hasPendingAsyncDispatch(key) ? 1 : 0
|
|
66758
|
+
};
|
|
66759
|
+
const telemetry = deriveStatusQueryTelemetry(surfaces);
|
|
66745
66760
|
const agentName3 = process.env.SWITCHROOM_AGENT_NAME ?? "-";
|
|
66746
|
-
process.stderr.write(
|
|
66747
|
-
|
|
66761
|
+
process.stderr.write(formatStatusQueryLine(agentName3, String(chat_id), thread, surfaces, telemetry));
|
|
66762
|
+
if (telemetry.idle) {
|
|
66763
|
+
process.stderr.write(formatStatusQueryUxFailureLine(agentName3, String(chat_id), thread));
|
|
66764
|
+
}
|
|
66748
66765
|
} catch (err) {
|
|
66749
66766
|
process.stderr.write(`telegram gateway: status-query telemetry failed: ${err.message}
|
|
66750
66767
|
`);
|
|
@@ -67113,7 +67130,7 @@ ${preBlock(write.output)}`;
|
|
|
67113
67130
|
bot.api.setMessageReaction(chat_id, msgId, [{ type: "emoji", emoji: "\uD83D\uDC40" }]).catch(() => {});
|
|
67114
67131
|
logStreamingEvent({ kind: "inbound_ack", chatId: chat_id, messageId: msgId, ackDelayMs: Date.now() - inboundReceivedAt });
|
|
67115
67132
|
} else {
|
|
67116
|
-
const sKey =
|
|
67133
|
+
const sKey = streamKey2(chat_id, messageThreadId);
|
|
67117
67134
|
const priorStream = activeDraftStreams.get(sKey);
|
|
67118
67135
|
if (priorStream && !priorStream.isFinal()) {
|
|
67119
67136
|
activeDraftStreams.delete(sKey);
|
|
@@ -67427,7 +67444,7 @@ async function switchroomReply(ctx, text2, options = {}) {
|
|
|
67427
67444
|
...options.reply_markup ? { reply_markup: options.reply_markup } : {}
|
|
67428
67445
|
};
|
|
67429
67446
|
if (options.html) {
|
|
67430
|
-
await ctx.replyWithRichMessage(richMessage2(text2), replyOpts);
|
|
67447
|
+
await ctx.replyWithRichMessage(richMessage2(hardenCardBreaks2(text2)), replyOpts);
|
|
67431
67448
|
} else {
|
|
67432
67449
|
await ctx.reply(text2, replyOpts);
|
|
67433
67450
|
}
|
|
@@ -67486,7 +67503,7 @@ function writeRestartMarker(marker) {
|
|
|
67486
67503
|
if (!p)
|
|
67487
67504
|
return;
|
|
67488
67505
|
try {
|
|
67489
|
-
|
|
67506
|
+
writeFileSync30(p, JSON.stringify(marker));
|
|
67490
67507
|
lastPlannedRestartAt = Date.now();
|
|
67491
67508
|
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
67509
|
`);
|
|
@@ -67654,7 +67671,7 @@ var _dockerReachable;
|
|
|
67654
67671
|
function isDockerReachable() {
|
|
67655
67672
|
if (_dockerReachable !== undefined)
|
|
67656
67673
|
return _dockerReachable;
|
|
67657
|
-
if (!
|
|
67674
|
+
if (!existsSync43("/var/run/docker.sock")) {
|
|
67658
67675
|
_dockerReachable = false;
|
|
67659
67676
|
return _dockerReachable;
|
|
67660
67677
|
}
|
|
@@ -67676,7 +67693,7 @@ function spawnSwitchroomDetached(args, onFailure) {
|
|
|
67676
67693
|
try {
|
|
67677
67694
|
mkdirSync32(STATE_DIR, { recursive: true });
|
|
67678
67695
|
outFd = openSync9(logPath, "a");
|
|
67679
|
-
|
|
67696
|
+
writeFileSync30(logPath, `
|
|
67680
67697
|
[${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
|
|
67681
67698
|
`, { flag: "a" });
|
|
67682
67699
|
} catch {}
|
|
@@ -68070,7 +68087,7 @@ bot.use(async (ctx, next) => {
|
|
|
68070
68087
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
68071
68088
|
try {
|
|
68072
68089
|
const auditPath = join40(homedir14(), ".switchroom", "vault-audit.log");
|
|
68073
|
-
if (!
|
|
68090
|
+
if (!existsSync43(auditPath))
|
|
68074
68091
|
return [];
|
|
68075
68092
|
const raw = readFileSync41(auditPath, "utf8");
|
|
68076
68093
|
return recentDenialsFromAuditLog(raw, { agentName: agentName3, windowMs, limit });
|
|
@@ -68498,7 +68515,7 @@ function flushAgentHandoff(agentDir) {
|
|
|
68498
68515
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
68499
68516
|
const p = join40(agentDir, fname);
|
|
68500
68517
|
try {
|
|
68501
|
-
if (
|
|
68518
|
+
if (existsSync43(p)) {
|
|
68502
68519
|
unlinkSync16(p);
|
|
68503
68520
|
removed++;
|
|
68504
68521
|
}
|
|
@@ -68554,7 +68571,7 @@ async function handleNewCommand(ctx) {
|
|
|
68554
68571
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
68555
68572
|
if (agentDir != null) {
|
|
68556
68573
|
try {
|
|
68557
|
-
|
|
68574
|
+
writeFileSync30(join40(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
68558
68575
|
`, "utf8");
|
|
68559
68576
|
} catch (err) {
|
|
68560
68577
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -68918,15 +68935,15 @@ bot.command("interrupt", async (ctx) => {
|
|
|
68918
68935
|
});
|
|
68919
68936
|
var lockoutOps = {
|
|
68920
68937
|
readFileSync: (p, enc) => readFileSync41(p, enc),
|
|
68921
|
-
writeFileSync: (p, data, opts) =>
|
|
68922
|
-
existsSync: (p) =>
|
|
68938
|
+
writeFileSync: (p, data, opts) => writeFileSync30(p, data, opts),
|
|
68939
|
+
existsSync: (p) => existsSync43(p),
|
|
68923
68940
|
mkdirSync: (p, opts) => mkdirSync32(p, opts),
|
|
68924
68941
|
joinPath: (...parts) => join40(...parts)
|
|
68925
68942
|
};
|
|
68926
68943
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
68927
68944
|
function isAuthBrokerSocketReachable() {
|
|
68928
68945
|
try {
|
|
68929
|
-
return
|
|
68946
|
+
return existsSync43(resolveAuthBrokerSocketPath2());
|
|
68930
68947
|
} catch {
|
|
68931
68948
|
return false;
|
|
68932
68949
|
}
|
|
@@ -69582,7 +69599,7 @@ async function handleVaultRecentDenialCallback(ctx, data) {
|
|
|
69582
69599
|
const tokenPath = join40(homedir14(), ".switchroom", "agents", agentName3, ".vault-token");
|
|
69583
69600
|
try {
|
|
69584
69601
|
mkdirSync32(join40(homedir14(), ".switchroom", "agents", agentName3), { recursive: true });
|
|
69585
|
-
|
|
69602
|
+
writeFileSync30(tokenPath, token, { mode: 384 });
|
|
69586
69603
|
} catch (err) {
|
|
69587
69604
|
await switchroomReply(ctx, `**Grant created (${escapeHtmlForTg2(id)}) but token write failed:** ${escapeHtmlForTg2(String(err))}
|
|
69588
69605
|
_Recover with: \`switchroom vault grant ${escapeHtmlForTg2(agentName3)} --keys ${escapeHtmlForTg2(keyName)} --duration 30d\` on the host._`, { html: true });
|
|
@@ -69660,7 +69677,7 @@ async function performVaultAccessApproval(ctx, pending2, stageId, senderId, atte
|
|
|
69660
69677
|
const tokenPath = join40(homedir14(), ".switchroom", "agents", pending2.agent, ".vault-token");
|
|
69661
69678
|
try {
|
|
69662
69679
|
mkdirSync32(join40(homedir14(), ".switchroom", "agents", pending2.agent), { recursive: true });
|
|
69663
|
-
|
|
69680
|
+
writeFileSync30(tokenPath, token, { mode: 384 });
|
|
69664
69681
|
} catch (err) {
|
|
69665
69682
|
await switchroomReply(ctx, `**Grant created (${escapeHtmlForTg2(id)}) but token write failed:** ${escapeHtmlForTg2(String(err))}
|
|
69666
69683
|
_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 +70249,7 @@ async function executeGrantWizard(ctx, chatId, state4) {
|
|
|
70232
70249
|
const tokenPath = join40(homedir14(), ".switchroom", "agents", state4.agent, ".vault-token");
|
|
70233
70250
|
try {
|
|
70234
70251
|
mkdirSync32(join40(homedir14(), ".switchroom", "agents", state4.agent), { recursive: true });
|
|
70235
|
-
|
|
70252
|
+
writeFileSync30(tokenPath, token, { mode: 384 });
|
|
70236
70253
|
} catch (err) {
|
|
70237
70254
|
await switchroomReply(ctx, `**Grant created but token write failed:** ${escapeHtmlForTg2(String(err))}`, { html: true });
|
|
70238
70255
|
return;
|
|
@@ -71962,7 +71979,7 @@ bot.on("message:photo", async (ctx) => {
|
|
|
71962
71979
|
});
|
|
71963
71980
|
mkdirSync32(INBOX_DIR, { recursive: true, mode: 448 });
|
|
71964
71981
|
assertInsideInbox(INBOX_DIR, dlPath);
|
|
71965
|
-
|
|
71982
|
+
writeFileSync30(dlPath, buf, { mode: 384 });
|
|
71966
71983
|
return dlPath;
|
|
71967
71984
|
} catch (err) {
|
|
71968
71985
|
const msg = err instanceof Error ? err.message : "unknown error";
|
|
@@ -73152,28 +73169,6 @@ var didOneTimeSetup = false;
|
|
|
73152
73169
|
parentStateDir: STATE_DIR,
|
|
73153
73170
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
73154
73171
|
`),
|
|
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
73172
|
onFinish: ({ agentId, outcome, description, resultText, toolCount, durationMs }) => {
|
|
73178
73173
|
deferredDoneReactions.promote();
|
|
73179
73174
|
let fleetChatId = "";
|
|
@@ -73193,6 +73188,20 @@ var didOneTimeSetup = false;
|
|
|
73193
73188
|
} catch {}
|
|
73194
73189
|
}
|
|
73195
73190
|
const isBackground = dispatch.isBackground;
|
|
73191
|
+
if (dispatch.isNested) {
|
|
73192
|
+
if (workerFeedEnabled) {
|
|
73193
|
+
workerActivityFeed?.finish(agentId, {
|
|
73194
|
+
description: dispatch.feedDescription,
|
|
73195
|
+
lastTool: null,
|
|
73196
|
+
toolCount,
|
|
73197
|
+
latestSummary: resultText,
|
|
73198
|
+
elapsedMs: durationMs,
|
|
73199
|
+
state: outcome === "failed" ? "failed" : "done"
|
|
73200
|
+
});
|
|
73201
|
+
reconcileWorkerPin(agentId, null, false);
|
|
73202
|
+
}
|
|
73203
|
+
return;
|
|
73204
|
+
}
|
|
73196
73205
|
if (!isBackground) {
|
|
73197
73206
|
const turn = currentTurn;
|
|
73198
73207
|
if (turn != null && turn.foregroundSubAgents.has(agentId)) {
|
|
@@ -73302,11 +73311,12 @@ var didOneTimeSetup = false;
|
|
|
73302
73311
|
dispatch = resolveWorkerFeedDispatch(getSubagentByJsonlId(turnsDb, agentId), description);
|
|
73303
73312
|
} catch {}
|
|
73304
73313
|
}
|
|
73305
|
-
const isBackground = dispatch.isBackground;
|
|
73314
|
+
const isBackground = dispatch.isBackground || dispatch.isNested;
|
|
73315
|
+
const stepLine = progressLine != null && progressLine.length > 0 ? progressLine : latestSummary;
|
|
73306
73316
|
if (!isBackground) {
|
|
73307
73317
|
const surface = resolveSubagentStatusSurface({
|
|
73308
73318
|
isBackground: false,
|
|
73309
|
-
liveTurnPresent: currentTurn != null,
|
|
73319
|
+
liveTurnPresent: currentTurn != null && dispatch.hasRow,
|
|
73310
73320
|
workerFeedEnabled,
|
|
73311
73321
|
orphanStatusEnabled
|
|
73312
73322
|
});
|
|
@@ -73317,7 +73327,7 @@ var didOneTimeSetup = false;
|
|
|
73317
73327
|
description: dispatch.feedDescription,
|
|
73318
73328
|
lastTool,
|
|
73319
73329
|
toolCount,
|
|
73320
|
-
latestSummary,
|
|
73330
|
+
latestSummary: stepLine,
|
|
73321
73331
|
elapsedMs,
|
|
73322
73332
|
state: "running"
|
|
73323
73333
|
}, origin?.threadId)?.then(() => reconcileWorkerPin(agentId, wkChat, true));
|
|
@@ -73341,7 +73351,7 @@ var didOneTimeSetup = false;
|
|
|
73341
73351
|
narrative = [];
|
|
73342
73352
|
turn.foregroundSubAgents.set(agentId, narrative);
|
|
73343
73353
|
}
|
|
73344
|
-
if (narrative
|
|
73354
|
+
if (!narrative.includes(child)) {
|
|
73345
73355
|
narrative.push(child);
|
|
73346
73356
|
if (narrative.length > FOREGROUND_SUBAGENT_ACCUM_MAX) {
|
|
73347
73357
|
narrative.splice(0, narrative.length - FOREGROUND_SUBAGENT_ACCUM_MAX);
|
|
@@ -73375,7 +73385,7 @@ var didOneTimeSetup = false;
|
|
|
73375
73385
|
description: dispatch.feedDescription,
|
|
73376
73386
|
lastTool,
|
|
73377
73387
|
toolCount,
|
|
73378
|
-
latestSummary,
|
|
73388
|
+
latestSummary: stepLine,
|
|
73379
73389
|
elapsedMs,
|
|
73380
73390
|
state: "running"
|
|
73381
73391
|
}, origin?.threadId)?.then(() => reconcileWorkerPin(agentId, wkChat, true));
|