switchroom 0.19.19 → 0.19.23
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 +2 -1
- package/dist/auth-broker/index.js +121 -1
- package/dist/cli/notion-write-pretool.mjs +2 -1
- package/dist/cli/switchroom.js +2995 -1583
- package/dist/host-control/main.js +122 -2
- package/dist/vault/approvals/kernel-server.js +124 -4
- package/dist/vault/broker/server.js +124 -4
- package/package.json +7 -4
- package/profiles/_base/start.sh.hbs +101 -0
- package/profiles/_shared/agent-self-service.md.hbs +64 -109
- package/profiles/_shared/delegation-golden-rule.md.hbs +5 -5
- package/profiles/_shared/dev-protocol.md.hbs +13 -42
- package/profiles/_shared/execution-discipline.md.hbs +7 -14
- package/profiles/coding/CLAUDE.md.hbs +0 -6
- package/profiles/default/CLAUDE.md.hbs +21 -50
- package/skills/dev-protocol/SKILL.md +90 -107
- package/skills/switchroom-release/SKILL.md +103 -20
- package/telegram-plugin/bunfig.toml +10 -0
- package/telegram-plugin/card-format.ts +92 -3
- package/telegram-plugin/dist/gateway/gateway.js +873 -184
- package/telegram-plugin/edit-flood-fuse.ts +477 -0
- package/telegram-plugin/format.ts +19 -7
- package/telegram-plugin/gateway/backstop-delivery.ts +97 -16
- package/telegram-plugin/gateway/boot-sweep-gate.ts +164 -0
- package/telegram-plugin/gateway/callback-query-handlers.ts +454 -81
- package/telegram-plugin/gateway/captured-answer-resume.ts +46 -17
- package/telegram-plugin/gateway/gateway.ts +75 -63
- package/telegram-plugin/gateway/inbound-interceptors.ts +27 -4
- package/telegram-plugin/gateway/narrative-lane.ts +49 -3
- package/telegram-plugin/gateway/outbound-send-path.ts +8 -1
- package/telegram-plugin/gateway/status-pin-api.ts +145 -0
- package/telegram-plugin/gateway/stream-render.ts +6 -0
- package/telegram-plugin/gateway/turn-record-status.ts +19 -0
- package/telegram-plugin/gateway/turns-jsonl-rotate.ts +65 -0
- package/telegram-plugin/hooks/subagent-tracker-posttool.mjs +325 -45
- package/telegram-plugin/retry-api-call.ts +15 -2
- package/telegram-plugin/send-gate.ts +1 -1
- package/telegram-plugin/status-no-truncate.ts +64 -1
- package/telegram-plugin/status-pin-driver.ts +50 -27
- package/telegram-plugin/status-pin.ts +43 -5
- package/telegram-plugin/tests/activity-card-send-gate.test.ts +275 -0
- package/telegram-plugin/tests/activity-card-wiring.test.ts +16 -7
- package/telegram-plugin/tests/agent-state-dir-preload.test.ts +33 -0
- package/telegram-plugin/tests/backstop-delivery.test.ts +204 -7
- package/telegram-plugin/tests/backstop-readback-probe.test.ts +12 -0
- package/telegram-plugin/tests/boot-pin-sweep-wiring.test.ts +101 -0
- package/telegram-plugin/tests/boot-sweep-gate.test.ts +293 -0
- package/telegram-plugin/tests/boot-version-string.test.ts +0 -0
- package/telegram-plugin/tests/captured-answer-resume.test.ts +104 -0
- package/telegram-plugin/tests/edit-flood-fuse.test.ts +431 -0
- package/telegram-plugin/tests/pinned-card-collapse.test.ts +356 -0
- package/telegram-plugin/tests/status-pin-api.test.ts +178 -0
- package/telegram-plugin/tests/status-pin-boot-recovery.test.ts +94 -11
- package/telegram-plugin/tests/status-pin.test.ts +106 -5
- package/telegram-plugin/tests/subagent-tracker-hooks.test.ts +631 -1
- package/telegram-plugin/tests/tool-activity-summary.test.ts +19 -10
- package/telegram-plugin/tests/turns-jsonl-rotate.test.ts +92 -1
- package/telegram-plugin/tests/vault-approval-posture.test.ts +6 -1
- package/telegram-plugin/tests/vault-passphrase-retry.test.ts +666 -0
- package/telegram-plugin/tests/vault-request-access-unlock-resume.test.ts +42 -21
- package/telegram-plugin/tests/worker-feed-coalesce.test.ts +233 -1
- package/telegram-plugin/tool-activity-summary.ts +85 -13
- package/telegram-plugin/worker-activity-feed.ts +5 -1
- package/vendor/hindsight-memory/scripts/drain_pending.py +304 -34
- package/vendor/hindsight-memory/scripts/lib/pending.py +886 -70
- package/vendor/hindsight-memory/scripts/lib/retain_split.py +71 -13
- package/vendor/hindsight-memory/scripts/recall.py +74 -5
- package/vendor/hindsight-memory/scripts/tests/test_pending_drops.py +1602 -14
- package/vendor/hindsight-memory/scripts/tests/test_pending_failure_class.py +105 -0
- package/vendor/hindsight-memory/scripts/tests/test_pending_wedge.py +300 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_degraded_notice.py +365 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_envelope_strip_telemetry.py +12 -4
- package/vendor/hindsight-memory/scripts/tests/test_recall_transcript_fallback.py +27 -2
- package/vendor/hindsight-memory/scripts/tests/test_retain_split.py +93 -13
- package/vendor/hindsight-memory/tests/test_drain_pending.py +44 -3
- package/vendor/hindsight-memory/tests/test_pending.py +12 -4
|
@@ -7379,7 +7379,17 @@ var init_text_voice_scrub = __esm(() => {
|
|
|
7379
7379
|
});
|
|
7380
7380
|
|
|
7381
7381
|
// card-format.ts
|
|
7382
|
-
function
|
|
7382
|
+
function normalizeLineTail(line, collapseSafe) {
|
|
7383
|
+
let out = line.replace(/[ \t\r]+$/, "");
|
|
7384
|
+
if (!collapseSafe || COLLAPSE_SAFE_SEPARATOR.length === 0)
|
|
7385
|
+
return out;
|
|
7386
|
+
while (out.endsWith(COLLAPSE_SAFE_SEPARATOR)) {
|
|
7387
|
+
out = out.slice(0, -COLLAPSE_SAFE_SEPARATOR.length).replace(/[ \t\r]+$/, "");
|
|
7388
|
+
}
|
|
7389
|
+
return out;
|
|
7390
|
+
}
|
|
7391
|
+
function stackCardLines(lines, opts) {
|
|
7392
|
+
const collapseSafe = opts?.collapseSafe === true;
|
|
7383
7393
|
const pieces = [];
|
|
7384
7394
|
for (let i = 0;i < lines.length; i++) {
|
|
7385
7395
|
const line = lines[i];
|
|
@@ -7389,11 +7399,14 @@ function stackCardLines(lines) {
|
|
|
7389
7399
|
const cur = line.trim();
|
|
7390
7400
|
const next = lines[i + 1].trim();
|
|
7391
7401
|
if (cur === "" || next === "") {
|
|
7402
|
+
if (collapseSafe && cur !== "") {
|
|
7403
|
+
pieces[pieces.length - 1] = normalizeLineTail(line, true) + COLLAPSE_SAFE_SEPARATOR;
|
|
7404
|
+
}
|
|
7392
7405
|
pieces.push(`
|
|
7393
7406
|
`);
|
|
7394
7407
|
continue;
|
|
7395
7408
|
}
|
|
7396
|
-
pieces[pieces.length - 1] = line
|
|
7409
|
+
pieces[pieces.length - 1] = normalizeLineTail(line, collapseSafe) + (collapseSafe ? COLLAPSE_SAFE_SEPARATOR : "");
|
|
7397
7410
|
pieces.push(`
|
|
7398
7411
|
`);
|
|
7399
7412
|
}
|
|
@@ -7439,6 +7452,7 @@ function cleanWorkerResultParagraph(s) {
|
|
|
7439
7452
|
}
|
|
7440
7453
|
return kept.join(" ").replace(/\s+/g, " ").trim();
|
|
7441
7454
|
}
|
|
7455
|
+
var COLLAPSE_SAFE_SEPARATOR = "\u00a0";
|
|
7442
7456
|
var init_card_format = __esm(() => {
|
|
7443
7457
|
init_format();
|
|
7444
7458
|
init_text_voice_scrub();
|
|
@@ -21037,7 +21051,8 @@ var init_schema = __esm(() => {
|
|
|
21037
21051
|
retain: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `retain` LLM op (memory ingestion). Emits " + "`HINDSIGHT_API_RETAIN_LLM_*`. Absent \u2192 uses the global model/provider."),
|
|
21038
21052
|
reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent \u2192 uses global."),
|
|
21039
21053
|
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent \u2192 global.")
|
|
21040
|
-
}).optional().describe("LLM knob for the hindsight container. The flat `provider`/`model` set " + "the global default (backward-compatible); optional `retain`/`reflect`/" + "`consolidation` blocks override individual ops. All fields optional; " + "unset fields fall back to the hard-coded defaults.")
|
|
21054
|
+
}).optional().describe("LLM knob for the hindsight container. The flat `provider`/`model` set " + "the global default (backward-compatible); optional `retain`/`reflect`/" + "`consolidation` blocks override individual ops. All fields optional; " + "unset fields fall back to the hard-coded defaults."),
|
|
21055
|
+
env: exports_external.record(exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean()])).optional().describe("Operator overrides for switchroom's capability-gated Hindsight " + "performance defaults. Only the keys switchroom actually manages are " + "honoured (`HINDSIGHT_PERF_ENV_KEYS` in " + "src/setup/hindsight-perf-defaults.ts: RERANKER_LOCAL_FP16, " + "LLM_MAX_CONCURRENT, RETAIN/CONSOLIDATION_LLM_MAX_CONCURRENT, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT), plus the " + "embedded-PostgreSQL (pg0) sizing keys switchroom manages in " + "src/setup/hindsight-pg-defaults.ts (`HINDSIGHT_PG_ENV_KEYS`: " + "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE, " + "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS \u2014 a postgres size string such " + "as `4GB`, or the sentinel `off` to leave pg0's own default for that " + "one knob). A value set here " + "REPLACES switchroom's default and is emitted even when the gating " + "capability is absent, so an operator can always force a knob. Other " + "`HINDSIGHT_API_*` keys are deliberately IGNORED \u2014 a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
|
|
21041
21056
|
});
|
|
21042
21057
|
MicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
21043
21058
|
microsoft_client_id: exports_external.string().min(1).optional().describe("Microsoft OAuth application (client) ID from Entra portal " + "(literal string or vault reference e.g. " + "'vault:microsoft-oauth-client-id'). OPTIONAL \u2014 omit it to use " + "switchroom's shipped default Microsoft app (zero-config). " + "Set it only to bring your own Entra app (BYO)."),
|
|
@@ -35381,7 +35396,7 @@ function renderAuthLine(state7, agentName3, now = Date.now()) {
|
|
|
35381
35396
|
|
|
35382
35397
|
// gateway/quota-cache.ts
|
|
35383
35398
|
import { existsSync as existsSync45, readFileSync as readFileSync43, writeFileSync as writeFileSync36, mkdirSync as mkdirSync38 } from "fs";
|
|
35384
|
-
import { join as join51, dirname as
|
|
35399
|
+
import { join as join51, dirname as dirname18 } from "path";
|
|
35385
35400
|
function defaultCachePath() {
|
|
35386
35401
|
return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join51(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
|
|
35387
35402
|
}
|
|
@@ -35418,7 +35433,7 @@ function writeQuotaCache(result, opts = {}) {
|
|
|
35418
35433
|
result
|
|
35419
35434
|
};
|
|
35420
35435
|
try {
|
|
35421
|
-
mkdirSync38(
|
|
35436
|
+
mkdirSync38(dirname18(path3), { recursive: true });
|
|
35422
35437
|
writeFileSync36(path3, JSON.stringify(entry, null, 2), { mode: 384 });
|
|
35423
35438
|
} catch {}
|
|
35424
35439
|
}
|
|
@@ -36291,7 +36306,7 @@ var init_boot_probes = __esm(() => {
|
|
|
36291
36306
|
|
|
36292
36307
|
// gateway/boot-issue-cache.ts
|
|
36293
36308
|
import { existsSync as existsSync47, readFileSync as readFileSync45, writeFileSync as writeFileSync37, mkdirSync as mkdirSync39, renameSync as renameSync21 } from "fs";
|
|
36294
|
-
import { dirname as
|
|
36309
|
+
import { dirname as dirname19 } from "path";
|
|
36295
36310
|
function fingerprintProbe(key, r) {
|
|
36296
36311
|
if (r.status === "ok")
|
|
36297
36312
|
return `${key}:ok`;
|
|
@@ -36417,7 +36432,7 @@ function applyAndSave(path3, cache, diff) {
|
|
|
36417
36432
|
}
|
|
36418
36433
|
}
|
|
36419
36434
|
try {
|
|
36420
|
-
mkdirSync39(
|
|
36435
|
+
mkdirSync39(dirname19(path3), { recursive: true });
|
|
36421
36436
|
const tmp = `${path3}.tmp`;
|
|
36422
36437
|
writeFileSync37(tmp, JSON.stringify(next), { mode: 384 });
|
|
36423
36438
|
renameSync21(tmp, path3);
|
|
@@ -36434,7 +36449,7 @@ var init_boot_issue_cache = __esm(() => {
|
|
|
36434
36449
|
// gateway/config-snapshot.ts
|
|
36435
36450
|
import { createHash as createHash6 } from "crypto";
|
|
36436
36451
|
import { existsSync as existsSync48, readFileSync as readFileSync46, writeFileSync as writeFileSync38, mkdirSync as mkdirSync40, renameSync as renameSync22 } from "fs";
|
|
36437
|
-
import { dirname as
|
|
36452
|
+
import { dirname as dirname20 } from "path";
|
|
36438
36453
|
function hashStringArray(items) {
|
|
36439
36454
|
if (!items || items.length === 0)
|
|
36440
36455
|
return null;
|
|
@@ -36532,7 +36547,7 @@ function loadSnapshot(path3, now = Date.now) {
|
|
|
36532
36547
|
}
|
|
36533
36548
|
function persistSnapshot(path3, snapshot) {
|
|
36534
36549
|
try {
|
|
36535
|
-
mkdirSync40(
|
|
36550
|
+
mkdirSync40(dirname20(path3), { recursive: true });
|
|
36536
36551
|
const tmp = `${path3}.tmp`;
|
|
36537
36552
|
writeFileSync38(tmp, JSON.stringify(snapshot), { mode: 384 });
|
|
36538
36553
|
renameSync22(tmp, path3);
|
|
@@ -38714,7 +38729,7 @@ import {
|
|
|
38714
38729
|
unlinkSync as unlinkSync28,
|
|
38715
38730
|
appendFileSync as appendFileSync9
|
|
38716
38731
|
} from "fs";
|
|
38717
|
-
import { homedir as
|
|
38732
|
+
import { homedir as homedir19 } from "os";
|
|
38718
38733
|
import { join as join65, sep as sep4, basename as basename17 } from "path";
|
|
38719
38734
|
|
|
38720
38735
|
// plugin-logger.ts
|
|
@@ -43330,13 +43345,27 @@ async function interceptVault(p, deps) {
|
|
|
43330
43345
|
deps.vaultPassphraseCache.set(p.chat_id, { passphrase, expiresAt: Date.now() + deps.vaultPassphraseTtlMs });
|
|
43331
43346
|
if (p.msgId != null)
|
|
43332
43347
|
await deps.deleteSensitiveMessage(p.chat_id, p.msgId, "vault passphrase");
|
|
43348
|
+
const mismatched = [];
|
|
43349
|
+
let mismatchMsg = "";
|
|
43333
43350
|
for (const item of pendingVault.items) {
|
|
43334
43351
|
const stagedAccess = deps.pendingVaultRequestAccesses.get(item.stageId);
|
|
43335
43352
|
if (!stagedAccess) {
|
|
43336
43353
|
await p.ctx.api.editMessageText(item.cardChatId, item.cardMessageId, richMessage(`\u231b _Vault unlocked, but this access-request card expired or was denied before you replied. Ask the agent to re-issue if still needed._`), { reply_markup: { inline_keyboard: [] } }).catch(() => {});
|
|
43337
43354
|
continue;
|
|
43338
43355
|
}
|
|
43339
|
-
await deps.callbackQueryHandlers().performVaultAccessApproval(p.ctx, stagedAccess, item.stageId, item.senderId, { kind: "passphrase", passphrase });
|
|
43356
|
+
const outcome = await deps.callbackQueryHandlers().performVaultAccessApproval(p.ctx, stagedAccess, item.stageId, item.senderId, { kind: "passphrase", passphrase });
|
|
43357
|
+
if (outcome?.kind === "passphrase-mismatch") {
|
|
43358
|
+
mismatched.push(item);
|
|
43359
|
+
mismatchMsg = outcome.msg;
|
|
43360
|
+
}
|
|
43361
|
+
}
|
|
43362
|
+
if (mismatched.length > 0) {
|
|
43363
|
+
await deps.callbackQueryHandlers().resolveAccessApprovalPassphraseMismatch(p.ctx, {
|
|
43364
|
+
chat_id: p.chat_id,
|
|
43365
|
+
failed: mismatched,
|
|
43366
|
+
priorAttempts: pendingVault.attempts ?? 0,
|
|
43367
|
+
brokerMsg: mismatchMsg
|
|
43368
|
+
});
|
|
43340
43369
|
}
|
|
43341
43370
|
} else if (pendingVault.kind === "grant-wizard" && pendingVault.awaitingCustomDuration) {
|
|
43342
43371
|
const input = p.text.trim();
|
|
@@ -43965,13 +43994,27 @@ async function interceptVault2(p, deps) {
|
|
|
43965
43994
|
deps.vaultPassphraseCache.set(p.chat_id, { passphrase, expiresAt: Date.now() + deps.vaultPassphraseTtlMs });
|
|
43966
43995
|
if (p.msgId != null)
|
|
43967
43996
|
await deps.deleteSensitiveMessage(p.chat_id, p.msgId, "vault passphrase");
|
|
43997
|
+
const mismatched = [];
|
|
43998
|
+
let mismatchMsg = "";
|
|
43968
43999
|
for (const item of pendingVault.items) {
|
|
43969
44000
|
const stagedAccess = deps.pendingVaultRequestAccesses.get(item.stageId);
|
|
43970
44001
|
if (!stagedAccess) {
|
|
43971
44002
|
await p.ctx.api.editMessageText(item.cardChatId, item.cardMessageId, richMessage(`\u231b _Vault unlocked, but this access-request card expired or was denied before you replied. Ask the agent to re-issue if still needed._`), { reply_markup: { inline_keyboard: [] } }).catch(() => {});
|
|
43972
44003
|
continue;
|
|
43973
44004
|
}
|
|
43974
|
-
await deps.callbackQueryHandlers().performVaultAccessApproval(p.ctx, stagedAccess, item.stageId, item.senderId, { kind: "passphrase", passphrase });
|
|
44005
|
+
const outcome = await deps.callbackQueryHandlers().performVaultAccessApproval(p.ctx, stagedAccess, item.stageId, item.senderId, { kind: "passphrase", passphrase });
|
|
44006
|
+
if (outcome?.kind === "passphrase-mismatch") {
|
|
44007
|
+
mismatched.push(item);
|
|
44008
|
+
mismatchMsg = outcome.msg;
|
|
44009
|
+
}
|
|
44010
|
+
}
|
|
44011
|
+
if (mismatched.length > 0) {
|
|
44012
|
+
await deps.callbackQueryHandlers().resolveAccessApprovalPassphraseMismatch(p.ctx, {
|
|
44013
|
+
chat_id: p.chat_id,
|
|
44014
|
+
failed: mismatched,
|
|
44015
|
+
priorAttempts: pendingVault.attempts ?? 0,
|
|
44016
|
+
brokerMsg: mismatchMsg
|
|
44017
|
+
});
|
|
43975
44018
|
}
|
|
43976
44019
|
} else if (pendingVault.kind === "grant-wizard" && pendingVault.awaitingCustomDuration) {
|
|
43977
44020
|
const input = p.text.trim();
|
|
@@ -46435,6 +46478,7 @@ var WORKER_HISTORY_MAX = 6;
|
|
|
46435
46478
|
var STATUS_LINE_MAX = 200;
|
|
46436
46479
|
var STATUS_CARD_CHAR_BUDGET = RICH_MESSAGE_MAX_CHARS;
|
|
46437
46480
|
var NESTED_PREFIX = " \u21b3 ";
|
|
46481
|
+
var WORKER_STEP_INDENT = "\u2800\u2800\u2800";
|
|
46438
46482
|
|
|
46439
46483
|
// hooks/tool-label-pretool.mjs
|
|
46440
46484
|
import { readFileSync as readFileSync9, mkdirSync as mkdirSync10, appendFileSync as appendFileSync2, existsSync as existsSync9 } from "node:fs";
|
|
@@ -46858,16 +46902,16 @@ function escapeStepLine(raw) {
|
|
|
46858
46902
|
const cleaned = stripMarkdown(raw).replace(/\s+/g, " ").trim();
|
|
46859
46903
|
return escapeMarkdown(truncate(cleaned, STATUS_LINE_MAX));
|
|
46860
46904
|
}
|
|
46861
|
-
function renderStepFeed(out, steps, allDone, liveSuffix = "", window2 = STATUS_ROLLING_LINES) {
|
|
46905
|
+
function renderStepFeed(out, steps, allDone, liveSuffix = "", window2 = STATUS_ROLLING_LINES, indent = "") {
|
|
46862
46906
|
if (steps.length === 0)
|
|
46863
46907
|
return;
|
|
46864
46908
|
const shown = steps.slice(-Math.max(1, window2));
|
|
46865
46909
|
const hidden = steps.length - shown.length;
|
|
46866
46910
|
if (hidden > 0)
|
|
46867
|
-
out.push(
|
|
46911
|
+
out.push(`${indent}_\u2713 +${hidden} earlier\u2026_`);
|
|
46868
46912
|
const lastIdx = shown.length - 1;
|
|
46869
46913
|
shown.forEach((s, i) => {
|
|
46870
|
-
out.push(!allDone && i === lastIdx ?
|
|
46914
|
+
out.push(!allDone && i === lastIdx ? `${indent}**\u2192 ${s}${liveSuffix}**` : `${indent}~~_\u2713 ${s}_~~`);
|
|
46871
46915
|
});
|
|
46872
46916
|
}
|
|
46873
46917
|
function renderStatusCard(opts) {
|
|
@@ -46907,7 +46951,7 @@ function renderStatusCard(opts) {
|
|
|
46907
46951
|
}
|
|
46908
46952
|
if (out.length === 0)
|
|
46909
46953
|
return null;
|
|
46910
|
-
const joined = stackCardLines(out);
|
|
46954
|
+
const joined = stackCardLines(out, { collapseSafe: true });
|
|
46911
46955
|
if (joined.length <= STATUS_CARD_CHAR_BUDGET)
|
|
46912
46956
|
return joined;
|
|
46913
46957
|
return fitCardToBudget(opts, headerLines);
|
|
@@ -46942,7 +46986,7 @@ function fitCardToBudget(opts, headerLines) {
|
|
|
46942
46986
|
const lastIdx = shown.length - 1;
|
|
46943
46987
|
shown.forEach((esc, i) => lines2.push(buildBullet(esc, i === lastIdx)));
|
|
46944
46988
|
lines2.push(...footerLines);
|
|
46945
|
-
const candidate = stackCardLines(lines2);
|
|
46989
|
+
const candidate = stackCardLines(lines2, { collapseSafe: true });
|
|
46946
46990
|
if (candidate.length <= STATUS_CARD_CHAR_BUDGET)
|
|
46947
46991
|
return candidate;
|
|
46948
46992
|
}
|
|
@@ -46963,7 +47007,7 @@ function fitCardToBudget(opts, headerLines) {
|
|
|
46963
47007
|
lines.push(parentMarker);
|
|
46964
47008
|
lines.push(newestLine);
|
|
46965
47009
|
lines.push(...footerLines);
|
|
46966
|
-
return stackCardLines(lines);
|
|
47010
|
+
return stackCardLines(lines, { collapseSafe: true });
|
|
46967
47011
|
}
|
|
46968
47012
|
function renderActivityFeed(lines, final = false, liveSuffix = "", stepCount, header) {
|
|
46969
47013
|
if (lines.length === 0 && header == null)
|
|
@@ -47016,6 +47060,13 @@ function combinedHistoryDepth(w) {
|
|
|
47016
47060
|
return Math.min(WORKER_HISTORY_MAX, Math.max(MIN_WORKER_DEPTH, 7 - w));
|
|
47017
47061
|
}
|
|
47018
47062
|
var workerHistoryDepth = combinedHistoryDepth;
|
|
47063
|
+
function glanceLine(rows) {
|
|
47064
|
+
const oldestMs = rows.reduce((m, r) => Math.max(m, r.elapsedMs), 0);
|
|
47065
|
+
const tools = rows.reduce((n, r) => n + r.toolCount, 0);
|
|
47066
|
+
const tok = rows.reduce((n, r) => n + (r.totalTokens ?? 0), 0);
|
|
47067
|
+
const toolWord = tools === 1 ? "tool" : "tools";
|
|
47068
|
+
return `\uD83D\uDEE0 **Workers** \u00b7 _${rows.length} running \u00b7 oldest ${formatFeedElapsed(oldestMs)}` + ` \u00b7 ${tools} ${toolWord}${tokenSegment(tok)}_`;
|
|
47069
|
+
}
|
|
47019
47070
|
function renderCombinedWorkerFeed(rows, opts) {
|
|
47020
47071
|
if (rows.length === 0)
|
|
47021
47072
|
return null;
|
|
@@ -47038,22 +47089,22 @@ function renderCombinedWorkerFeed(rows, opts) {
|
|
|
47038
47089
|
const shown = rows.slice(0, visibleCount);
|
|
47039
47090
|
const hidden = rows.length - shown.length;
|
|
47040
47091
|
const depth = combinedHistoryDepth(shown.length);
|
|
47041
|
-
const chrome = [
|
|
47092
|
+
const chrome = [glanceLine(rows)];
|
|
47042
47093
|
const bodyOut = [];
|
|
47043
47094
|
for (const r of shown) {
|
|
47044
47095
|
bodyOut.push(rowHeader(r));
|
|
47045
47096
|
const hist = rowHistory(r);
|
|
47046
47097
|
if (hist.length === 0) {
|
|
47047
|
-
bodyOut.push(
|
|
47098
|
+
bodyOut.push(`${WORKER_STEP_INDENT}\u2192 _starting\u2026_`);
|
|
47048
47099
|
continue;
|
|
47049
47100
|
}
|
|
47050
47101
|
const esc = hist.slice(-depth).map(escapeStepLine);
|
|
47051
|
-
renderStepFeed(bodyOut, esc, false, "", depth);
|
|
47102
|
+
renderStepFeed(bodyOut, esc, false, "", depth, WORKER_STEP_INDENT);
|
|
47052
47103
|
}
|
|
47053
47104
|
const out = [...chrome, ...bodyOut];
|
|
47054
47105
|
if (hidden > 0)
|
|
47055
47106
|
out.push(`_+${hidden} more working\u2026_`);
|
|
47056
|
-
return { body: stackCardLines(out), bodyLines: bodyOut.length };
|
|
47107
|
+
return { body: stackCardLines(out, { collapseSafe: true }), bodyLines: bodyOut.length };
|
|
47057
47108
|
};
|
|
47058
47109
|
let visible = Math.min(rows.length, maxRows);
|
|
47059
47110
|
let { body, bodyLines } = compose(visible);
|
|
@@ -47074,6 +47125,10 @@ function appendActivityLabel(lines, label) {
|
|
|
47074
47125
|
}
|
|
47075
47126
|
|
|
47076
47127
|
// send-gate.ts
|
|
47128
|
+
var systemClock = {
|
|
47129
|
+
now: () => Date.now(),
|
|
47130
|
+
sleep: (ms) => new Promise((r) => setTimeout(r, ms))
|
|
47131
|
+
};
|
|
47077
47132
|
var SEND_GATE_SHED = Symbol.for("switchroom.send-gate.shed");
|
|
47078
47133
|
function isSendGateShed(value) {
|
|
47079
47134
|
return value === SEND_GATE_SHED;
|
|
@@ -47131,7 +47186,7 @@ function renderWorkerActivity(v, liveSuffix = "") {
|
|
|
47131
47186
|
return `\uD83D\uDEE0 **Worker** \u00b7 _starting\u2026_`;
|
|
47132
47187
|
}
|
|
47133
47188
|
if (!finished && steps.length === 0) {
|
|
47134
|
-
return `${card}
|
|
47189
|
+
return `${card}${COLLAPSE_SAFE_SEPARATOR}
|
|
47135
47190
|
_starting\u2026_`;
|
|
47136
47191
|
}
|
|
47137
47192
|
return card;
|
|
@@ -48032,6 +48087,16 @@ function errorDescription(err) {
|
|
|
48032
48087
|
function isPinRightsError(err) {
|
|
48033
48088
|
return errorDescription(err).includes("not enough rights");
|
|
48034
48089
|
}
|
|
48090
|
+
function isUnpinTerminalError(err) {
|
|
48091
|
+
if (isPinRightsError(err))
|
|
48092
|
+
return true;
|
|
48093
|
+
if (err != null && typeof err === "object") {
|
|
48094
|
+
const code = err.error_code;
|
|
48095
|
+
if (typeof code === "number")
|
|
48096
|
+
return code >= 400 && code < 500 && code !== 429;
|
|
48097
|
+
}
|
|
48098
|
+
return false;
|
|
48099
|
+
}
|
|
48035
48100
|
|
|
48036
48101
|
class PinRightsCache {
|
|
48037
48102
|
blocked = new Set;
|
|
@@ -48055,18 +48120,20 @@ async function reconcilePin(args) {
|
|
|
48055
48120
|
if (action.kind === "noop")
|
|
48056
48121
|
return args.prevState;
|
|
48057
48122
|
if (action.kind === "unpin") {
|
|
48058
|
-
if (
|
|
48059
|
-
|
|
48060
|
-
|
|
48061
|
-
|
|
48062
|
-
|
|
48063
|
-
|
|
48064
|
-
|
|
48065
|
-
|
|
48066
|
-
|
|
48067
|
-
|
|
48068
|
-
|
|
48123
|
+
if (args.rightsCache?.isBlocked(args.chatId))
|
|
48124
|
+
return null;
|
|
48125
|
+
try {
|
|
48126
|
+
await args.api.unpinChatMessage(args.chatId, action.messageId);
|
|
48127
|
+
} catch (err) {
|
|
48128
|
+
if (args.rightsCache && isPinRightsError(err)) {
|
|
48129
|
+
const firstTime = args.rightsCache.block(args.chatId);
|
|
48130
|
+
if (firstTime)
|
|
48131
|
+
args.onPinRightsDisabled?.(args.chatId);
|
|
48132
|
+
} else {
|
|
48133
|
+
args.onError?.("unpin", err);
|
|
48069
48134
|
}
|
|
48135
|
+
if (!isUnpinTerminalError(err))
|
|
48136
|
+
return args.prevState;
|
|
48070
48137
|
}
|
|
48071
48138
|
return null;
|
|
48072
48139
|
}
|
|
@@ -48498,8 +48565,7 @@ init_rich_send();
|
|
|
48498
48565
|
var import_grammy4 = __toESM(require_mod2(), 1);
|
|
48499
48566
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
48500
48567
|
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync9 } from "fs";
|
|
48501
|
-
import {
|
|
48502
|
-
import { join as join13 } from "path";
|
|
48568
|
+
import { dirname as dirname5 } from "path";
|
|
48503
48569
|
init_client();
|
|
48504
48570
|
|
|
48505
48571
|
// gateway/vault-grant-inbound-builders.ts
|
|
@@ -49532,6 +49598,48 @@ var _deprecationSeen = new Set;
|
|
|
49532
49598
|
|
|
49533
49599
|
// gateway/callback-query-handlers.ts
|
|
49534
49600
|
var AUTH_REFRESH_THROTTLE_MS = 5000;
|
|
49601
|
+
var MAX_VAULT_PASSPHRASE_ATTEMPTS = 3;
|
|
49602
|
+
function isPassphraseMismatchBrokerError(msg) {
|
|
49603
|
+
const m = msg.toLowerCase();
|
|
49604
|
+
if (!m.includes("passphrase"))
|
|
49605
|
+
return false;
|
|
49606
|
+
return m.includes("does not match") || m.includes("mismatch");
|
|
49607
|
+
}
|
|
49608
|
+
function buildAccessPassphrasePromptText(opts) {
|
|
49609
|
+
const header = `**\uD83D\uDEA8\uD83D\uDD10 ACTION NEEDED: passphrase required**`;
|
|
49610
|
+
if (opts.kind === "retry") {
|
|
49611
|
+
const plural = opts.retryRemaining === 1 ? "attempt" : "attempts";
|
|
49612
|
+
return `${header}
|
|
49613
|
+
|
|
49614
|
+
` + `Wrong passphrase. ${opts.retryRemaining} ${plural} remaining.
|
|
49615
|
+
` + `Type your vault passphrase again as your **next message**.
|
|
49616
|
+
` + (opts.itemCount > 1 ? `One entry covers **${opts.itemCount}** pending approvals in this chat.
|
|
49617
|
+
` : ``) + `
|
|
49618
|
+
_We delete the passphrase message the moment we read it._`;
|
|
49619
|
+
}
|
|
49620
|
+
if (opts.variant === "batch") {
|
|
49621
|
+
return `${header}
|
|
49622
|
+
|
|
49623
|
+
` + `Type your vault passphrase as your **next message**.
|
|
49624
|
+
` + `One entry covers **${opts.itemCount}** pending approvals in this chat, no re-type per card.
|
|
49625
|
+
|
|
49626
|
+
` + `_We delete the passphrase message the moment we read it._`;
|
|
49627
|
+
}
|
|
49628
|
+
if (opts.variant === "admin-only") {
|
|
49629
|
+
return `${header}
|
|
49630
|
+
|
|
49631
|
+
` + `\`${opts.key}\` is an **admin-only credential**.
|
|
49632
|
+
` + `Type your vault passphrase as your **next message** to mint the grant for **${opts.agentEscaped}**.
|
|
49633
|
+
|
|
49634
|
+
` + `_The passphrase is what proves it's you. An agent can never mint this key on its own. We delete the passphrase message the moment we read it._`;
|
|
49635
|
+
}
|
|
49636
|
+
return `${header}
|
|
49637
|
+
|
|
49638
|
+
` + `Your vault is locked.
|
|
49639
|
+
` + `Reply with your passphrase as your **next message** to unlock and mint the grant for **${opts.agentEscaped}**.
|
|
49640
|
+
|
|
49641
|
+
` + `_Mint authority stays operator-only: the broker only accepts the grant when the passphrase matches. We delete the passphrase message the moment we read it._`;
|
|
49642
|
+
}
|
|
49535
49643
|
function createCallbackQueryHandlers(deps) {
|
|
49536
49644
|
const {
|
|
49537
49645
|
loadAccess,
|
|
@@ -49574,6 +49682,10 @@ function createCallbackQueryHandlers(deps) {
|
|
|
49574
49682
|
} = deps;
|
|
49575
49683
|
const bot = deps.bot;
|
|
49576
49684
|
const lockedBot = deps.lockedBot;
|
|
49685
|
+
const mintGrantViaBroker2 = deps.brokerMintGrant ?? mintGrantViaBroker;
|
|
49686
|
+
const listViaBroker2 = deps.brokerList ?? listViaBroker;
|
|
49687
|
+
const listGrantsViaBroker2 = deps.brokerListGrants ?? listGrantsViaBroker;
|
|
49688
|
+
const vaultTokenFilePath2 = deps.brokerVaultTokenFilePath ?? vaultTokenFilePath;
|
|
49577
49689
|
async function handleVaultRecentDenialCallback(ctx, data) {
|
|
49578
49690
|
const senderId = String(ctx.from?.id ?? "");
|
|
49579
49691
|
const access = loadAccess();
|
|
@@ -49596,7 +49708,7 @@ function createCallbackQueryHandlers(deps) {
|
|
|
49596
49708
|
return;
|
|
49597
49709
|
}
|
|
49598
49710
|
await ctx.answerCallbackQuery({ text: "\u23f3 Minting 30-day read grant\u2026" }).catch(() => {});
|
|
49599
|
-
const result = await
|
|
49711
|
+
const result = await mintGrantViaBroker2({
|
|
49600
49712
|
agent: agentName,
|
|
49601
49713
|
keys: [keyName],
|
|
49602
49714
|
ttl_seconds: 30 * 24 * 60 * 60,
|
|
@@ -49611,9 +49723,9 @@ function createCallbackQueryHandlers(deps) {
|
|
|
49611
49723
|
return;
|
|
49612
49724
|
}
|
|
49613
49725
|
const { token, id } = result;
|
|
49614
|
-
const tokenPath =
|
|
49726
|
+
const tokenPath = vaultTokenFilePath2(agentName);
|
|
49615
49727
|
try {
|
|
49616
|
-
mkdirSync13(
|
|
49728
|
+
mkdirSync13(dirname5(tokenPath), { recursive: true });
|
|
49617
49729
|
writeFileSync9(tokenPath, token, { mode: 384 });
|
|
49618
49730
|
} catch (err) {
|
|
49619
49731
|
await switchroomReply(ctx, `**Grant created (${escapeHtmlForTg2(id)}) but token write failed:** ` + `${escapeHtmlForTg2(String(err))}
|
|
@@ -49630,25 +49742,93 @@ function createCallbackQueryHandlers(deps) {
|
|
|
49630
49742
|
newText: baseText ? `${baseText}${statusLine}` : statusLine
|
|
49631
49743
|
});
|
|
49632
49744
|
}
|
|
49745
|
+
async function editResolvedCard(ctx, target, messageId, markdown, label) {
|
|
49746
|
+
if (messageId > 0) {
|
|
49747
|
+
try {
|
|
49748
|
+
await robustApiCall(() => ctx.api.editMessageText(target.chat_id, messageId, richMessage(markdown), {
|
|
49749
|
+
reply_markup: { inline_keyboard: [] }
|
|
49750
|
+
}), { chat_id: target.chat_id, verb: `vault_request_access.${label}_edit` });
|
|
49751
|
+
return;
|
|
49752
|
+
} catch (err) {
|
|
49753
|
+
process.stderr.write(`telegram gateway: vault card resolution edit FAILED (${label}) ` + `chat=${target.chat_id} msg=${messageId}: ${String(err)} \u2014 sending fallback message
|
|
49754
|
+
`);
|
|
49755
|
+
}
|
|
49756
|
+
}
|
|
49757
|
+
try {
|
|
49758
|
+
await retryWithThreadFallback(robustApiCall, (tid) => lockedBot.api.sendRichMessage(target.chat_id, richMessage(markdown), {
|
|
49759
|
+
...tid != null && Number.isFinite(tid) ? { message_thread_id: tid } : {}
|
|
49760
|
+
}), {
|
|
49761
|
+
threadId: target.threadId,
|
|
49762
|
+
chat_id: target.chat_id,
|
|
49763
|
+
verb: `vault_request_access.${label}_fallback`
|
|
49764
|
+
});
|
|
49765
|
+
} catch (err) {
|
|
49766
|
+
process.stderr.write(`telegram gateway: vault card resolution FALLBACK SEND failed (${label}) ` + `chat=${target.chat_id}: ${String(err)}
|
|
49767
|
+
`);
|
|
49768
|
+
}
|
|
49769
|
+
}
|
|
49770
|
+
async function sendAccessPassphrasePrompt(target, spec) {
|
|
49771
|
+
const promptText = buildAccessPassphrasePromptText(spec);
|
|
49772
|
+
await retryWithThreadFallback(robustApiCall, (tid) => lockedBot.api.sendRichMessage(target.chat_id, richMessage(promptText), {
|
|
49773
|
+
...tid != null && Number.isFinite(tid) ? { message_thread_id: tid } : {}
|
|
49774
|
+
}), {
|
|
49775
|
+
threadId: target.threadId,
|
|
49776
|
+
chat_id: target.chat_id,
|
|
49777
|
+
verb: "vault_request_access.passphrase_prompt"
|
|
49778
|
+
}).catch((err) => {
|
|
49779
|
+
process.stderr.write(`telegram gateway: vault passphrase prompt send FAILED chat=${target.chat_id} ` + `kind=${spec.kind}: ${String(err)}
|
|
49780
|
+
`);
|
|
49781
|
+
});
|
|
49782
|
+
}
|
|
49783
|
+
async function resolveAccessApprovalPassphraseMismatch(ctx, args) {
|
|
49784
|
+
const { chat_id, failed, priorAttempts, brokerMsg } = args;
|
|
49785
|
+
if (failed.length === 0)
|
|
49786
|
+
return;
|
|
49787
|
+
vaultPassphraseCache.delete(chat_id);
|
|
49788
|
+
const attempts = priorAttempts + 1;
|
|
49789
|
+
const remaining = MAX_VAULT_PASSPHRASE_ATTEMPTS - attempts;
|
|
49790
|
+
const threadId = failed.find((it) => it.threadId != null)?.threadId;
|
|
49791
|
+
process.stderr.write(`telegram gateway: vault_request_access passphrase mismatch chat=${chat_id} ` + `stages=${failed.map((f) => f.stageId).join(",")} attempts=${attempts}/${MAX_VAULT_PASSPHRASE_ATTEMPTS}
|
|
49792
|
+
`);
|
|
49793
|
+
if (remaining > 0) {
|
|
49794
|
+
const open = pendingVaultOps.get(chat_id);
|
|
49795
|
+
const carried = open?.kind === "passphrase-for-access-approve" ? open.items.filter((it) => !failed.some((f) => f.stageId === it.stageId)) : [];
|
|
49796
|
+
const requeued = [...failed, ...carried];
|
|
49797
|
+
pendingVaultOps.set(chat_id, {
|
|
49798
|
+
kind: "passphrase-for-access-approve",
|
|
49799
|
+
items: requeued,
|
|
49800
|
+
attempts,
|
|
49801
|
+
startedAt: Date.now()
|
|
49802
|
+
});
|
|
49803
|
+
await sendAccessPassphrasePrompt({ chat_id, ...threadId != null ? { threadId } : {} }, { kind: "retry", retryRemaining: remaining, itemCount: requeued.length });
|
|
49804
|
+
return;
|
|
49805
|
+
}
|
|
49806
|
+
for (const item of failed) {
|
|
49807
|
+
pendingVaultRequestAccesses.delete(item.stageId);
|
|
49808
|
+
pendingCardStore.remove(item.stageId);
|
|
49809
|
+
await editResolvedCard(ctx, { chat_id: item.cardChatId, ...item.threadId != null ? { threadId: item.threadId } : {} }, item.cardMessageId, `\u274c **Too many wrong passphrase attempts** (${MAX_VAULT_PASSPHRASE_ATTEMPTS}). ` + `This request was cancelled \u2014 ask the agent to re-issue it.
|
|
49810
|
+
` + `_Broker: ${escapeHtmlForTg2(brokerMsg)}_`, "passphrase_lockout");
|
|
49811
|
+
}
|
|
49812
|
+
}
|
|
49633
49813
|
async function performVaultAccessApproval(ctx, pending, stageId, senderId, attestation) {
|
|
49634
49814
|
const brokerAuthOpts = attestation.kind === "passphrase" ? { passphrase: attestation.passphrase } : { attest_via_posture: true };
|
|
49635
49815
|
if (pending.scope === "read") {
|
|
49636
49816
|
try {
|
|
49637
|
-
const visible = await
|
|
49817
|
+
const visible = await listViaBroker2();
|
|
49638
49818
|
if (visible !== null && visible.includes(pending.key)) {
|
|
49639
49819
|
pendingVaultRequestAccesses.delete(stageId);
|
|
49640
49820
|
pendingCardStore.remove(stageId);
|
|
49641
49821
|
if (pending.card_message_id != null) {
|
|
49642
|
-
await ctx
|
|
49822
|
+
await editResolvedCard(ctx, pending, pending.card_message_id, `\u2139\ufe0f **${escapeHtmlForTg2(pending.agent)}** already has standing-ACL access to ` + `\`${pending.key}\` (schedule.secrets[]). ` + `**No grant minted** \u2014 a token would shadow the standing ACL. ` + `The agent can read it directly.`, "standing_acl");
|
|
49643
49823
|
}
|
|
49644
|
-
return;
|
|
49824
|
+
return { kind: "ok" };
|
|
49645
49825
|
}
|
|
49646
49826
|
} catch {}
|
|
49647
49827
|
}
|
|
49648
49828
|
let existingReadKeys = [];
|
|
49649
49829
|
let existingWriteKeys = [];
|
|
49650
49830
|
if (pending.scope === "read" || pending.scope === "write") {
|
|
49651
|
-
const list = await
|
|
49831
|
+
const list = await listGrantsViaBroker2(pending.agent, brokerAuthOpts);
|
|
49652
49832
|
if (list.kind === "ok") {
|
|
49653
49833
|
const now = Math.floor(Date.now() / 1000);
|
|
49654
49834
|
const active = list.grants.filter((g) => g.expires_at === null || g.expires_at > now).sort((a, b) => {
|
|
@@ -49677,28 +49857,31 @@ function createCallbackQueryHandlers(deps) {
|
|
|
49677
49857
|
...writeKeys.size > 0 ? { write_keys: Array.from(writeKeys) } : {},
|
|
49678
49858
|
...brokerAuthOpts
|
|
49679
49859
|
};
|
|
49680
|
-
const result = await
|
|
49860
|
+
const result = await mintGrantViaBroker2(mintArgs);
|
|
49681
49861
|
if (result.kind === "unreachable") {
|
|
49682
49862
|
await switchroomReply(ctx, `\uD83D\uDD34 Broker unreachable: ${escapeHtmlForTg2(result.msg)}`, { html: true });
|
|
49683
|
-
return;
|
|
49863
|
+
return { kind: "failed", msg: result.msg };
|
|
49684
49864
|
}
|
|
49685
49865
|
if (result.kind === "error") {
|
|
49866
|
+
if (attestation.kind === "passphrase" && isPassphraseMismatchBrokerError(result.msg)) {
|
|
49867
|
+
return { kind: "passphrase-mismatch", msg: result.msg };
|
|
49868
|
+
}
|
|
49686
49869
|
pendingVaultRequestAccesses.delete(stageId);
|
|
49687
49870
|
pendingCardStore.remove(stageId);
|
|
49688
49871
|
if (pending.card_message_id != null) {
|
|
49689
|
-
await ctx
|
|
49872
|
+
await editResolvedCard(ctx, pending, pending.card_message_id, `**mint_grant failed:** ${escapeHtmlForTg2(result.msg)}`, "mint_failed");
|
|
49690
49873
|
}
|
|
49691
|
-
return;
|
|
49874
|
+
return { kind: "failed", msg: result.msg };
|
|
49692
49875
|
}
|
|
49693
49876
|
const { token, id } = result;
|
|
49694
|
-
const tokenPath =
|
|
49877
|
+
const tokenPath = vaultTokenFilePath2(pending.agent);
|
|
49695
49878
|
try {
|
|
49696
|
-
mkdirSync13(
|
|
49879
|
+
mkdirSync13(dirname5(tokenPath), { recursive: true });
|
|
49697
49880
|
writeFileSync9(tokenPath, token, { mode: 384 });
|
|
49698
49881
|
} catch (err) {
|
|
49699
49882
|
await switchroomReply(ctx, `**Grant created (${escapeHtmlForTg2(id)}) but token write failed:** ` + `${escapeHtmlForTg2(String(err))}
|
|
49700
49883
|
` + `_Recover with: \`switchroom vault grant ${escapeHtmlForTg2(pending.agent)} ` + `--keys ${escapeHtmlForTg2(pending.key)} --duration ${Math.round(pending.ttl_seconds / 86400)}d\` on the host._`, { html: true });
|
|
49701
|
-
return;
|
|
49884
|
+
return { kind: "failed", msg: String(err) };
|
|
49702
49885
|
}
|
|
49703
49886
|
pendingVaultRequestAccesses.delete(stageId);
|
|
49704
49887
|
pendingCardStore.remove(stageId);
|
|
@@ -49707,7 +49890,7 @@ function createCallbackQueryHandlers(deps) {
|
|
|
49707
49890
|
const reasonNormalized = normalizeGrantReason(pending.reason);
|
|
49708
49891
|
const footer = getVaultApprovalAuthMode() === "telegram-id" ? `
|
|
49709
49892
|
_Approver verified by Telegram identity \u2014 broker auto-unlocked at startup._` : "";
|
|
49710
|
-
await ctx
|
|
49893
|
+
await editResolvedCard(ctx, pending, pending.card_message_id, buildVaultGrantApprovedCardText({
|
|
49711
49894
|
agentEscaped: escapeHtmlForTg2(pending.agent),
|
|
49712
49895
|
scope: pending.scope,
|
|
49713
49896
|
key: pending.key,
|
|
@@ -49715,7 +49898,7 @@ _Approver verified by Telegram identity \u2014 broker auto-unlocked at startup._
|
|
|
49715
49898
|
grantId: id,
|
|
49716
49899
|
reasonEscaped: reasonNormalized.length > 0 ? escapeHtmlForTg2(reasonNormalized) : undefined,
|
|
49717
49900
|
footer
|
|
49718
|
-
})
|
|
49901
|
+
}), "grant_approved");
|
|
49719
49902
|
}
|
|
49720
49903
|
const synthetic = buildVaultGrantApprovedInbound({
|
|
49721
49904
|
ctx: {
|
|
@@ -49733,6 +49916,7 @@ _Approver verified by Telegram identity \u2014 broker auto-unlocked at startup._
|
|
|
49733
49916
|
const delivered = deliverResumeSyntheticOrBuffer(pending.agent, synthetic);
|
|
49734
49917
|
process.stderr.write(`telegram gateway: vault_grant_approved injection agent=${pending.agent} ` + `key=${pending.key} stage=${stageId} delivered=${delivered}
|
|
49735
49918
|
`);
|
|
49919
|
+
return { kind: "ok" };
|
|
49736
49920
|
}
|
|
49737
49921
|
async function handleSkillProposalCallback(ctx, data) {
|
|
49738
49922
|
const senderId = String(ctx.from?.id ?? "");
|
|
@@ -50025,40 +50209,53 @@ _Approver verified by Telegram identity \u2014 broker auto-unlocked at startup._
|
|
|
50025
50209
|
stageId,
|
|
50026
50210
|
cardChatId: pending.chat_id,
|
|
50027
50211
|
cardMessageId: pending.card_message_id,
|
|
50028
|
-
senderId
|
|
50212
|
+
senderId,
|
|
50213
|
+
...pending.threadId != null ? { threadId: pending.threadId } : {}
|
|
50029
50214
|
};
|
|
50030
50215
|
const items = existing?.kind === "passphrase-for-access-approve" ? [...existing.items.filter((it) => it.stageId !== stageId), newItem] : [newItem];
|
|
50031
50216
|
pendingVaultOps.set(pending.chat_id, {
|
|
50032
50217
|
kind: "passphrase-for-access-approve",
|
|
50033
50218
|
items,
|
|
50219
|
+
...existing?.kind === "passphrase-for-access-approve" && existing.attempts ? { attempts: existing.attempts } : {},
|
|
50034
50220
|
startedAt: existing?.kind === "passphrase-for-access-approve" ? existing.startedAt : Date.now()
|
|
50035
50221
|
});
|
|
50036
50222
|
const joiningBatch = items.length > 1;
|
|
50037
50223
|
await ctx.answerCallbackQuery({ text: joiningBatch ? `\uD83D\uDD10 Queued \u2014 one passphrase covers ${items.length} cards` : "\uD83D\uDD10 Send your passphrase\u2026" }).catch(() => {});
|
|
50038
50224
|
await ctx.api.editMessageText(pending.chat_id, pending.card_message_id, richMessage(`\uD83D\uDD10 _Approved \u2014 waiting for your vault passphrase. See the prompt below._`), { reply_markup: { inline_keyboard: [] } }).catch(() => {});
|
|
50039
|
-
|
|
50040
|
-
|
|
50041
|
-
|
|
50042
|
-
|
|
50043
|
-
|
|
50044
|
-
|
|
50045
|
-
|
|
50046
|
-
|
|
50047
|
-
|
|
50048
|
-
|
|
50049
|
-
` + `_The passphrase is what proves it's you. An agent can never mint this key on its own. We delete the passphrase message the moment we read it._` : `**\u26a0\ufe0f\uD83D\uDD10 ACTION NEEDED: passphrase required**
|
|
50050
|
-
|
|
50051
|
-
` + `Your vault is locked.
|
|
50052
|
-
` + `Reply with your passphrase as your **next message** to unlock and mint the grant for **${escapeHtmlForTg2(pending.agent)}**.
|
|
50053
|
-
|
|
50054
|
-
` + `_Mint authority stays operator-only: the broker only accepts the grant when the passphrase matches. We delete the passphrase message the moment we read it._`;
|
|
50055
|
-
await retryWithThreadFallback(robustApiCall, (tid) => lockedBot.api.sendRichMessage(pending.chat_id, richMessage(promptText), {
|
|
50056
|
-
...tid != null && Number.isFinite(tid) ? { message_thread_id: tid } : {}
|
|
50057
|
-
}), { threadId: pending.threadId, chat_id: pending.chat_id, verb: "vault_request_access.passphrase_prompt" }).catch(() => {});
|
|
50225
|
+
await sendAccessPassphrasePrompt({
|
|
50226
|
+
chat_id: pending.chat_id,
|
|
50227
|
+
...pending.threadId != null ? { threadId: pending.threadId } : {}
|
|
50228
|
+
}, {
|
|
50229
|
+
kind: "first",
|
|
50230
|
+
variant: joiningBatch ? "batch" : isAdminOnly ? "admin-only" : "locked",
|
|
50231
|
+
itemCount: items.length,
|
|
50232
|
+
agentEscaped: escapeHtmlForTg2(pending.agent),
|
|
50233
|
+
key: pending.key
|
|
50234
|
+
});
|
|
50058
50235
|
return;
|
|
50059
50236
|
}
|
|
50060
50237
|
await ctx.answerCallbackQuery({ text: "\u23f3 Minting grant\u2026" }).catch(() => {});
|
|
50061
|
-
await performVaultAccessApproval(ctx, pending, stageId, senderId, {
|
|
50238
|
+
const outcome = await performVaultAccessApproval(ctx, pending, stageId, senderId, {
|
|
50239
|
+
kind: "passphrase",
|
|
50240
|
+
passphrase: cached.passphrase
|
|
50241
|
+
});
|
|
50242
|
+
if (outcome.kind === "passphrase-mismatch") {
|
|
50243
|
+
const openOp = pendingVaultOps.get(pending.chat_id);
|
|
50244
|
+
await resolveAccessApprovalPassphraseMismatch(ctx, {
|
|
50245
|
+
chat_id: pending.chat_id,
|
|
50246
|
+
failed: [
|
|
50247
|
+
{
|
|
50248
|
+
stageId,
|
|
50249
|
+
cardChatId: pending.chat_id,
|
|
50250
|
+
cardMessageId: pending.card_message_id ?? 0,
|
|
50251
|
+
senderId,
|
|
50252
|
+
...pending.threadId != null ? { threadId: pending.threadId } : {}
|
|
50253
|
+
}
|
|
50254
|
+
],
|
|
50255
|
+
priorAttempts: openOp?.kind === "passphrase-for-access-approve" ? openOp.attempts ?? 0 : 0,
|
|
50256
|
+
brokerMsg: outcome.msg
|
|
50257
|
+
});
|
|
50258
|
+
}
|
|
50062
50259
|
return;
|
|
50063
50260
|
}
|
|
50064
50261
|
await ctx.answerCallbackQuery({ text: "Unknown action" }).catch(() => {});
|
|
@@ -50399,7 +50596,7 @@ Which agent?`, { html: true, reply_markup: kb });
|
|
|
50399
50596
|
});
|
|
50400
50597
|
}
|
|
50401
50598
|
async function grantWizardStep2(ctx, chatId, agent, wizardMsgId) {
|
|
50402
|
-
const keys = await
|
|
50599
|
+
const keys = await listViaBroker2();
|
|
50403
50600
|
if (!keys) {
|
|
50404
50601
|
await switchroomReply(ctx, "\uD83D\uDD34 Broker is not running (or unreachable). Cannot list vault keys.", { html: true });
|
|
50405
50602
|
pendingVaultOps.delete(chatId);
|
|
@@ -50490,7 +50687,7 @@ ${keyList}`,
|
|
|
50490
50687
|
} catch {
|
|
50491
50688
|
return;
|
|
50492
50689
|
}
|
|
50493
|
-
const result = await
|
|
50690
|
+
const result = await mintGrantViaBroker2({
|
|
50494
50691
|
agent: state.agent,
|
|
50495
50692
|
keys: state.selectedKeys,
|
|
50496
50693
|
ttl_seconds: state.ttlSeconds ?? null,
|
|
@@ -50505,9 +50702,9 @@ ${keyList}`,
|
|
|
50505
50702
|
return;
|
|
50506
50703
|
}
|
|
50507
50704
|
const { token, id } = result;
|
|
50508
|
-
const tokenPath =
|
|
50705
|
+
const tokenPath = vaultTokenFilePath2(state.agent);
|
|
50509
50706
|
try {
|
|
50510
|
-
mkdirSync13(
|
|
50707
|
+
mkdirSync13(dirname5(tokenPath), { recursive: true });
|
|
50511
50708
|
writeFileSync9(tokenPath, token, { mode: 384 });
|
|
50512
50709
|
} catch (err) {
|
|
50513
50710
|
await switchroomReply(ctx, `**Grant created but token write failed:** ${escapeHtmlForTg2(String(err))}`, { html: true });
|
|
@@ -50534,7 +50731,7 @@ ${keyList}`,
|
|
|
50534
50731
|
const revokeMatch = /^vg:revoke:(.+)$/.exec(data);
|
|
50535
50732
|
if (revokeMatch) {
|
|
50536
50733
|
const grantId = revokeMatch[1];
|
|
50537
|
-
const result = await
|
|
50734
|
+
const result = await listGrantsViaBroker2(undefined);
|
|
50538
50735
|
if (result.kind !== "ok") {
|
|
50539
50736
|
await ctx.answerCallbackQuery({ text: "Broker unreachable." }).catch(() => {});
|
|
50540
50737
|
return;
|
|
@@ -50983,6 +51180,7 @@ ${trimmed.replace(/```/g, "`\u200b``")}
|
|
|
50983
51180
|
return {
|
|
50984
51181
|
handleVaultRecentDenialCallback,
|
|
50985
51182
|
performVaultAccessApproval,
|
|
51183
|
+
resolveAccessApprovalPassphraseMismatch,
|
|
50986
51184
|
handleSkillProposalCallback,
|
|
50987
51185
|
handleMentalModelProposeCallback,
|
|
50988
51186
|
handleVaultRequestAccessCallback,
|
|
@@ -51955,7 +52153,7 @@ function pickRecoveredPermissionOrigin(recentTurns, now, maxAgeMs) {
|
|
|
51955
52153
|
|
|
51956
52154
|
// gateway/approval-hold.ts
|
|
51957
52155
|
import { mkdirSync as mkdirSync14, writeFileSync as writeFileSync11, readFileSync as readFileSync13, unlinkSync as unlinkSync9, chmodSync as chmodSync4, lstatSync as lstatSync2 } from "node:fs";
|
|
51958
|
-
import { join as join16, dirname as
|
|
52156
|
+
import { join as join16, dirname as dirname6 } from "node:path";
|
|
51959
52157
|
var BLOCKED_APPROVAL_FILE_MODE = 420;
|
|
51960
52158
|
var BLOCKED_APPROVAL_DIR_MODE = 1023;
|
|
51961
52159
|
var HELD_RETRY_BACKOFF_MS = 60000;
|
|
@@ -51988,7 +52186,7 @@ function createBlockedApprovalStore(dir, agent, fallbackDir) {
|
|
|
51988
52186
|
const fallback = fallbackDir != null ? join16(fallbackDir, "blocked-approval.json") : null;
|
|
51989
52187
|
let active = primary;
|
|
51990
52188
|
function tryWrite(target, body, dirMode) {
|
|
51991
|
-
const parent =
|
|
52189
|
+
const parent = dirname6(target);
|
|
51992
52190
|
try {
|
|
51993
52191
|
mkdirSync14(parent, { recursive: true });
|
|
51994
52192
|
try {
|
|
@@ -62658,9 +62856,249 @@ async function retryWithThreadFallback2(retry, send, opts) {
|
|
|
62658
62856
|
}
|
|
62659
62857
|
}
|
|
62660
62858
|
|
|
62859
|
+
// edit-flood-fuse.ts
|
|
62860
|
+
var EDIT_METHODS = new Set([
|
|
62861
|
+
"editMessageText",
|
|
62862
|
+
"editMessageCaption",
|
|
62863
|
+
"editMessageMedia",
|
|
62864
|
+
"editMessageReplyMarkup",
|
|
62865
|
+
"editMessageLiveLocation",
|
|
62866
|
+
"editMessageChecklist"
|
|
62867
|
+
]);
|
|
62868
|
+
var SEND_METHODS = new Set([
|
|
62869
|
+
"sendMessage",
|
|
62870
|
+
"sendPhoto",
|
|
62871
|
+
"sendDocument",
|
|
62872
|
+
"sendMediaGroup",
|
|
62873
|
+
"sendAnimation",
|
|
62874
|
+
"sendVideo",
|
|
62875
|
+
"sendVoice",
|
|
62876
|
+
"sendAudio",
|
|
62877
|
+
"sendSticker",
|
|
62878
|
+
"sendLocation",
|
|
62879
|
+
"forwardMessage",
|
|
62880
|
+
"forwardMessages",
|
|
62881
|
+
"copyMessage",
|
|
62882
|
+
"copyMessages",
|
|
62883
|
+
"sendRichMessage"
|
|
62884
|
+
]);
|
|
62885
|
+
var EDIT_FLOOD_FUSE_DEFAULTS = {
|
|
62886
|
+
perMessageMaxPerWindow: 20,
|
|
62887
|
+
perMessageWindowMs: 60000,
|
|
62888
|
+
perChatEditMaxPerWindow: 30,
|
|
62889
|
+
perChatSendMaxPerWindow: 25,
|
|
62890
|
+
perChatWindowMs: 60000,
|
|
62891
|
+
maxDeferMs: 30000,
|
|
62892
|
+
tightenFactor: 0.5,
|
|
62893
|
+
tightenMs: 600000
|
|
62894
|
+
};
|
|
62895
|
+
var DROPPED_RESULT = true;
|
|
62896
|
+
function createEditFloodFuse(config = {}) {
|
|
62897
|
+
const enabled2 = config.enabled ?? true;
|
|
62898
|
+
const clock = config.clock ?? systemClock;
|
|
62899
|
+
const D = EDIT_FLOOD_FUSE_DEFAULTS;
|
|
62900
|
+
const perMessageMax = config.perMessageMaxPerWindow ?? D.perMessageMaxPerWindow;
|
|
62901
|
+
const perMessageWindowMs = config.perMessageWindowMs ?? D.perMessageWindowMs;
|
|
62902
|
+
const perChatEditMax = config.perChatEditMaxPerWindow ?? D.perChatEditMaxPerWindow;
|
|
62903
|
+
const perChatSendMax = config.perChatSendMaxPerWindow ?? D.perChatSendMaxPerWindow;
|
|
62904
|
+
const perChatWindowMs = config.perChatWindowMs ?? D.perChatWindowMs;
|
|
62905
|
+
const maxDeferMs = config.maxDeferMs ?? D.maxDeferMs;
|
|
62906
|
+
const tightenFactor = config.tightenFactor ?? D.tightenFactor;
|
|
62907
|
+
const tightenMs = config.tightenMs ?? D.tightenMs;
|
|
62908
|
+
const onTrip = config.onTrip;
|
|
62909
|
+
const windows = new Map;
|
|
62910
|
+
const counters = { deferred: 0, dropped: 0, superseded: 0, floodObserved: 0 };
|
|
62911
|
+
let tightenedUntil = 0;
|
|
62912
|
+
function isTightened(now) {
|
|
62913
|
+
return tightenedUntil > now;
|
|
62914
|
+
}
|
|
62915
|
+
function ceiling(base, now) {
|
|
62916
|
+
if (!isTightened(now))
|
|
62917
|
+
return base;
|
|
62918
|
+
return Math.max(1, Math.floor(base * tightenFactor));
|
|
62919
|
+
}
|
|
62920
|
+
function win(key) {
|
|
62921
|
+
let w = windows.get(key);
|
|
62922
|
+
if (w === undefined) {
|
|
62923
|
+
w = { ts: [], waiter: null, inflight: 0 };
|
|
62924
|
+
windows.set(key, w);
|
|
62925
|
+
}
|
|
62926
|
+
return w;
|
|
62927
|
+
}
|
|
62928
|
+
function prune(w, now, windowMs) {
|
|
62929
|
+
const cutoff = now - windowMs;
|
|
62930
|
+
while (w.ts.length > 0 && w.ts[0] <= cutoff)
|
|
62931
|
+
w.ts.shift();
|
|
62932
|
+
}
|
|
62933
|
+
let sinceEvict = 0;
|
|
62934
|
+
function evict(now) {
|
|
62935
|
+
if (windows.size < 4096)
|
|
62936
|
+
return;
|
|
62937
|
+
if (++sinceEvict < 1024)
|
|
62938
|
+
return;
|
|
62939
|
+
sinceEvict = 0;
|
|
62940
|
+
const widest = Math.max(perMessageWindowMs, perChatWindowMs);
|
|
62941
|
+
for (const [k, w] of windows) {
|
|
62942
|
+
if (w.waiter === null && w.inflight === 0 && (w.ts.length === 0 || w.ts[w.ts.length - 1] <= now - widest)) {
|
|
62943
|
+
windows.delete(k);
|
|
62944
|
+
}
|
|
62945
|
+
}
|
|
62946
|
+
}
|
|
62947
|
+
function waitFor(w, now, windowMs, max) {
|
|
62948
|
+
prune(w, now, windowMs);
|
|
62949
|
+
if (w.ts.length < max)
|
|
62950
|
+
return 0;
|
|
62951
|
+
return Math.max(1, w.ts[0] + windowMs - now);
|
|
62952
|
+
}
|
|
62953
|
+
function payloadKeys(payload) {
|
|
62954
|
+
const p = payload ?? {};
|
|
62955
|
+
const chat = p.chat_id != null ? String(p.chat_id) : null;
|
|
62956
|
+
const msg = p.message_id != null ? String(p.message_id) : null;
|
|
62957
|
+
return { chat, msg };
|
|
62958
|
+
}
|
|
62959
|
+
function noteFlood(now) {
|
|
62960
|
+
counters.floodObserved++;
|
|
62961
|
+
tightenedUntil = now + tightenMs;
|
|
62962
|
+
}
|
|
62963
|
+
function looksLikeFlood(err) {
|
|
62964
|
+
const e = err;
|
|
62965
|
+
if (e != null && typeof e === "object") {
|
|
62966
|
+
if (e.error_code === 429)
|
|
62967
|
+
return true;
|
|
62968
|
+
if (e.parameters != null && typeof e.parameters.retry_after === "number")
|
|
62969
|
+
return true;
|
|
62970
|
+
}
|
|
62971
|
+
const msg = err instanceof Error ? err.message : String(err ?? "");
|
|
62972
|
+
return /too many requests/i.test(msg) || /retry[ _-]?after/i.test(msg);
|
|
62973
|
+
}
|
|
62974
|
+
function unreserve(key, at) {
|
|
62975
|
+
const w = windows.get(key);
|
|
62976
|
+
if (w === undefined)
|
|
62977
|
+
return;
|
|
62978
|
+
const i = w.ts.lastIndexOf(at);
|
|
62979
|
+
if (i >= 0)
|
|
62980
|
+
w.ts.splice(i, 1);
|
|
62981
|
+
}
|
|
62982
|
+
async function awaitRoom(key, windowMs, max, method, mode, dropGuard) {
|
|
62983
|
+
const w = win(key);
|
|
62984
|
+
const deadline = clock.now() + maxDeferMs;
|
|
62985
|
+
let counted = false;
|
|
62986
|
+
for (;; ) {
|
|
62987
|
+
const now = clock.now();
|
|
62988
|
+
const wait = waitFor(w, now, windowMs, ceiling(max, now));
|
|
62989
|
+
if (wait === 0) {
|
|
62990
|
+
w.ts.push(now);
|
|
62991
|
+
return now;
|
|
62992
|
+
}
|
|
62993
|
+
if (now >= deadline) {
|
|
62994
|
+
if (mode !== "release" && (dropGuard === undefined || dropGuard())) {
|
|
62995
|
+
counters.dropped++;
|
|
62996
|
+
onTrip?.({ method, key, action: "dropped" });
|
|
62997
|
+
return null;
|
|
62998
|
+
}
|
|
62999
|
+
w.ts.push(now);
|
|
63000
|
+
return now;
|
|
63001
|
+
}
|
|
63002
|
+
if (!counted) {
|
|
63003
|
+
counters.deferred++;
|
|
63004
|
+
counted = true;
|
|
63005
|
+
onTrip?.({ method, key, action: "deferred" });
|
|
63006
|
+
}
|
|
63007
|
+
let killed = false;
|
|
63008
|
+
if (mode === "supersede") {
|
|
63009
|
+
w.waiter?.kill();
|
|
63010
|
+
const superseded = new Promise((resolve6) => {
|
|
63011
|
+
w.waiter = { kill: () => {
|
|
63012
|
+
killed = true;
|
|
63013
|
+
resolve6();
|
|
63014
|
+
} };
|
|
63015
|
+
});
|
|
63016
|
+
await Promise.race([clock.sleep(Math.min(wait, deadline - now)), superseded]);
|
|
63017
|
+
if (killed) {
|
|
63018
|
+
counters.superseded++;
|
|
63019
|
+
onTrip?.({ method, key, action: "superseded" });
|
|
63020
|
+
return null;
|
|
63021
|
+
}
|
|
63022
|
+
w.waiter = null;
|
|
63023
|
+
} else {
|
|
63024
|
+
await clock.sleep(Math.min(wait, deadline - now));
|
|
63025
|
+
}
|
|
63026
|
+
}
|
|
63027
|
+
}
|
|
63028
|
+
async function apply(method, payload, next) {
|
|
63029
|
+
if (!enabled2)
|
|
63030
|
+
return next();
|
|
63031
|
+
const isEdit = EDIT_METHODS.has(method);
|
|
63032
|
+
const isSend = SEND_METHODS.has(method);
|
|
63033
|
+
if (!isEdit && !isSend)
|
|
63034
|
+
return runObserved(next);
|
|
63035
|
+
const { chat, msg } = payloadKeys(payload);
|
|
63036
|
+
if (chat == null)
|
|
63037
|
+
return runObserved(next);
|
|
63038
|
+
const now = clock.now();
|
|
63039
|
+
evict(now);
|
|
63040
|
+
if (isEdit) {
|
|
63041
|
+
if (msg == null)
|
|
63042
|
+
return runObserved(next);
|
|
63043
|
+
const msgKey = `m:${chat}:${msg}`;
|
|
63044
|
+
const mw = win(msgKey);
|
|
63045
|
+
mw.inflight++;
|
|
63046
|
+
try {
|
|
63047
|
+
const msgSlot = await awaitRoom(msgKey, perMessageWindowMs, perMessageMax, method, "supersede");
|
|
63048
|
+
if (msgSlot === null)
|
|
63049
|
+
return DROPPED_RESULT;
|
|
63050
|
+
const chatKey3 = `ce:${chat}`;
|
|
63051
|
+
const chatSlot = await awaitRoom(chatKey3, perChatWindowMs, perChatEditMax, method, "drop", () => mw.inflight > 1);
|
|
63052
|
+
if (chatSlot === null) {
|
|
63053
|
+
unreserve(msgKey, msgSlot);
|
|
63054
|
+
return DROPPED_RESULT;
|
|
63055
|
+
}
|
|
63056
|
+
return runObserved(next);
|
|
63057
|
+
} finally {
|
|
63058
|
+
mw.inflight--;
|
|
63059
|
+
}
|
|
63060
|
+
}
|
|
63061
|
+
const chatKey2 = `cs:${chat}`;
|
|
63062
|
+
await awaitRoom(chatKey2, perChatWindowMs, perChatSendMax, method, "release");
|
|
63063
|
+
return runObserved(next);
|
|
63064
|
+
}
|
|
63065
|
+
async function runObserved(next) {
|
|
63066
|
+
try {
|
|
63067
|
+
const res = await next();
|
|
63068
|
+
const r = res;
|
|
63069
|
+
if (r != null && typeof r === "object" && r.ok === false && r.error_code === 429) {
|
|
63070
|
+
noteFlood(clock.now());
|
|
63071
|
+
}
|
|
63072
|
+
return res;
|
|
63073
|
+
} catch (err) {
|
|
63074
|
+
if (looksLikeFlood(err))
|
|
63075
|
+
noteFlood(clock.now());
|
|
63076
|
+
throw err;
|
|
63077
|
+
}
|
|
63078
|
+
}
|
|
63079
|
+
function stats() {
|
|
63080
|
+
const now = clock.now();
|
|
63081
|
+
return {
|
|
63082
|
+
enabled: enabled2,
|
|
63083
|
+
deferred: counters.deferred,
|
|
63084
|
+
dropped: counters.dropped,
|
|
63085
|
+
superseded: counters.superseded,
|
|
63086
|
+
floodObserved: counters.floodObserved,
|
|
63087
|
+
tightened: isTightened(now),
|
|
63088
|
+
perMessageCeiling: ceiling(perMessageMax, now)
|
|
63089
|
+
};
|
|
63090
|
+
}
|
|
63091
|
+
return { apply, stats };
|
|
63092
|
+
}
|
|
63093
|
+
function installEditFloodFuse(bot, config = {}) {
|
|
63094
|
+
const fuse = createEditFloodFuse(config);
|
|
63095
|
+
bot.api.config.use(async (prev, method, payload, signal) => fuse.apply(method, payload, () => prev(method, payload, signal)));
|
|
63096
|
+
return fuse;
|
|
63097
|
+
}
|
|
63098
|
+
|
|
62661
63099
|
// send-gate.ts
|
|
62662
63100
|
import { createHash } from "node:crypto";
|
|
62663
|
-
var
|
|
63101
|
+
var systemClock2 = {
|
|
62664
63102
|
now: () => Date.now(),
|
|
62665
63103
|
sleep: (ms) => new Promise((r) => setTimeout(r, ms))
|
|
62666
63104
|
};
|
|
@@ -62761,7 +63199,7 @@ var SEND_GATE_DEFAULTS = {
|
|
|
62761
63199
|
};
|
|
62762
63200
|
function createSendGate(config) {
|
|
62763
63201
|
const enabled2 = config.enabled;
|
|
62764
|
-
const clock = config.clock ??
|
|
63202
|
+
const clock = config.clock ?? systemClock2;
|
|
62765
63203
|
const globalPerSec = config.globalPerSec ?? SEND_GATE_DEFAULTS.globalPerSec;
|
|
62766
63204
|
const globalBurst = config.globalBurst ?? SEND_GATE_DEFAULTS.globalBurst;
|
|
62767
63205
|
const perChatPerSec = config.perChatPerSec ?? SEND_GATE_DEFAULTS.perChatPerSec;
|
|
@@ -63491,7 +63929,7 @@ import {
|
|
|
63491
63929
|
unlinkSync as unlinkSync10,
|
|
63492
63930
|
renameSync as renameSync9
|
|
63493
63931
|
} from "node:fs";
|
|
63494
|
-
import { dirname as
|
|
63932
|
+
import { dirname as dirname7, join as join17 } from "node:path";
|
|
63495
63933
|
var FLOOD_STATE_FILE = "flood-wait.json";
|
|
63496
63934
|
var FLOOD_STATE_MODE = 420;
|
|
63497
63935
|
function floodStatePath(stateDir) {
|
|
@@ -63544,7 +63982,7 @@ function readFloodState(path2) {
|
|
|
63544
63982
|
function writeFloodState(path2, state, log = (l) => process.stderr.write(l)) {
|
|
63545
63983
|
const payload = JSON.stringify(state);
|
|
63546
63984
|
try {
|
|
63547
|
-
mkdirSync15(
|
|
63985
|
+
mkdirSync15(dirname7(path2), { recursive: true });
|
|
63548
63986
|
} catch {}
|
|
63549
63987
|
try {
|
|
63550
63988
|
writeFileSync12(path2, payload, { mode: FLOOD_STATE_MODE });
|
|
@@ -63675,7 +64113,7 @@ function readFloodWindows(path2, now, log = (l) => process.stderr.write(l)) {
|
|
|
63675
64113
|
}
|
|
63676
64114
|
function writeFloodWindow(path2, record, now) {
|
|
63677
64115
|
try {
|
|
63678
|
-
mkdirSync15(
|
|
64116
|
+
mkdirSync15(dirname7(path2), { recursive: true });
|
|
63679
64117
|
const existing = readFloodWindows(path2, now);
|
|
63680
64118
|
const byScope = new Map;
|
|
63681
64119
|
for (const r of existing)
|
|
@@ -63850,7 +64288,7 @@ function __resetAllForTests() {
|
|
|
63850
64288
|
}
|
|
63851
64289
|
|
|
63852
64290
|
// ../node_modules/.bun/posthog-node@5.29.2/node_modules/posthog-node/dist/extensions/error-tracking/modifiers/module.node.mjs
|
|
63853
|
-
import { dirname as
|
|
64291
|
+
import { dirname as dirname8, posix, sep as sep3 } from "path";
|
|
63854
64292
|
function createModulerModifier() {
|
|
63855
64293
|
const getModuleFromFileName = createGetModuleFromFilename();
|
|
63856
64294
|
return async (frames) => {
|
|
@@ -63859,7 +64297,7 @@ function createModulerModifier() {
|
|
|
63859
64297
|
return frames;
|
|
63860
64298
|
};
|
|
63861
64299
|
}
|
|
63862
|
-
function createGetModuleFromFilename(basePath = process.argv[1] ?
|
|
64300
|
+
function createGetModuleFromFilename(basePath = process.argv[1] ? dirname8(process.argv[1]) : process.cwd(), isWindows = sep3 === "\\") {
|
|
63863
64301
|
const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath;
|
|
63864
64302
|
return (filename) => {
|
|
63865
64303
|
if (!filename)
|
|
@@ -68291,7 +68729,7 @@ class PostHog extends PostHogBackendClient {
|
|
|
68291
68729
|
|
|
68292
68730
|
// analytics-posthog.ts
|
|
68293
68731
|
import { existsSync as existsSync13, mkdirSync as mkdirSync16, readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "node:fs";
|
|
68294
|
-
import { dirname as
|
|
68732
|
+
import { dirname as dirname9, join as join19 } from "node:path";
|
|
68295
68733
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
68296
68734
|
var DEFAULT_KEY = "phc_qKY87cKWZm6ZyCtk7LcRd2cU8Sg42u7Ywhui5stYCegd";
|
|
68297
68735
|
var DEFAULT_HOST = "https://us.i.posthog.com";
|
|
@@ -68330,7 +68768,7 @@ function getDistinctId() {
|
|
|
68330
68768
|
const id = randomUUID2();
|
|
68331
68769
|
cachedDistinctId = id;
|
|
68332
68770
|
try {
|
|
68333
|
-
mkdirSync16(
|
|
68771
|
+
mkdirSync16(dirname9(fallbackPath), { recursive: true });
|
|
68334
68772
|
writeFileSync13(fallbackPath, id, "utf-8");
|
|
68335
68773
|
} catch {}
|
|
68336
68774
|
return id;
|
|
@@ -68403,11 +68841,11 @@ function installGlobalErrorHandlers() {
|
|
|
68403
68841
|
|
|
68404
68842
|
// runtime-metrics.ts
|
|
68405
68843
|
import { mkdirSync as mkdirSync18, appendFileSync as appendFileSync3 } from "node:fs";
|
|
68406
|
-
import { dirname as
|
|
68844
|
+
import { dirname as dirname11, join as join21 } from "node:path";
|
|
68407
68845
|
|
|
68408
68846
|
// analytics-posthog.ts
|
|
68409
68847
|
import { existsSync as existsSync14, mkdirSync as mkdirSync17, readFileSync as readFileSync16, writeFileSync as writeFileSync14 } from "node:fs";
|
|
68410
|
-
import { dirname as
|
|
68848
|
+
import { dirname as dirname10, join as join20 } from "node:path";
|
|
68411
68849
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
68412
68850
|
var DEFAULT_KEY2 = "phc_qKY87cKWZm6ZyCtk7LcRd2cU8Sg42u7Ywhui5stYCegd";
|
|
68413
68851
|
var DEFAULT_HOST2 = "https://us.i.posthog.com";
|
|
@@ -68445,7 +68883,7 @@ function getDistinctId2() {
|
|
|
68445
68883
|
const id = randomUUID3();
|
|
68446
68884
|
cachedDistinctId2 = id;
|
|
68447
68885
|
try {
|
|
68448
|
-
mkdirSync17(
|
|
68886
|
+
mkdirSync17(dirname10(fallbackPath), { recursive: true });
|
|
68449
68887
|
writeFileSync14(fallbackPath, id, "utf-8");
|
|
68450
68888
|
} catch {}
|
|
68451
68889
|
return id;
|
|
@@ -68500,7 +68938,7 @@ function resolveJsonlPath() {
|
|
|
68500
68938
|
function appendJsonl(line) {
|
|
68501
68939
|
const path2 = resolveJsonlPath();
|
|
68502
68940
|
try {
|
|
68503
|
-
mkdirSync18(
|
|
68941
|
+
mkdirSync18(dirname11(path2), { recursive: true });
|
|
68504
68942
|
appendFileSync3(path2, line + `
|
|
68505
68943
|
`, "utf-8");
|
|
68506
68944
|
} catch (err) {
|
|
@@ -69169,8 +69607,8 @@ function __resetAllForTests3() {
|
|
|
69169
69607
|
|
|
69170
69608
|
// silent-end.ts
|
|
69171
69609
|
import { existsSync as existsSync16, readFileSync as readFileSync17, writeFileSync as writeFileSync15, unlinkSync as unlinkSync11, mkdirSync as mkdirSync19 } from "node:fs";
|
|
69172
|
-
import { dirname as
|
|
69173
|
-
import { homedir as
|
|
69610
|
+
import { dirname as dirname12, join as join22 } from "node:path";
|
|
69611
|
+
import { homedir as homedir5 } from "node:os";
|
|
69174
69612
|
var SILENT_END_MAX_RETRIES = 2;
|
|
69175
69613
|
var SILENT_END_STALE_RECORD_MAX_AGE_MS = 30 * 60000;
|
|
69176
69614
|
function resolveStateDir(deps) {
|
|
@@ -69179,7 +69617,7 @@ function resolveStateDir(deps) {
|
|
|
69179
69617
|
const env = process.env.TELEGRAM_STATE_DIR;
|
|
69180
69618
|
if (env != null && env !== "")
|
|
69181
69619
|
return env;
|
|
69182
|
-
const home2 = process.env.HOME ??
|
|
69620
|
+
const home2 = process.env.HOME ?? homedir5();
|
|
69183
69621
|
return join22(home2, ".claude", "channels", "telegram");
|
|
69184
69622
|
}
|
|
69185
69623
|
function resolveStatePath2(deps) {
|
|
@@ -69212,7 +69650,7 @@ function writeSilentEndState(args, deps) {
|
|
|
69212
69650
|
timestamp: Date.now()
|
|
69213
69651
|
};
|
|
69214
69652
|
try {
|
|
69215
|
-
mkdirSync19(
|
|
69653
|
+
mkdirSync19(dirname12(statePath), { recursive: true });
|
|
69216
69654
|
writeFileSync15(statePath, JSON.stringify(state3), "utf8");
|
|
69217
69655
|
emitLog(deps, `silent-end: wrote state file turnKey=${args.turnKey} retryCount=${retryCount}
|
|
69218
69656
|
`);
|
|
@@ -69301,7 +69739,7 @@ function resolveAnswerLaneConfig(input) {
|
|
|
69301
69739
|
}
|
|
69302
69740
|
|
|
69303
69741
|
// session-tail.ts
|
|
69304
|
-
import { homedir as
|
|
69742
|
+
import { homedir as homedir6 } from "os";
|
|
69305
69743
|
import { basename as basename8, join as join24 } from "path";
|
|
69306
69744
|
|
|
69307
69745
|
// operator-events.ts
|
|
@@ -69704,7 +70142,7 @@ var OPERATOR_ACTIONABLE_KINDS = new Set([
|
|
|
69704
70142
|
function sanitizeCwdToProjectName(cwd) {
|
|
69705
70143
|
return cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
69706
70144
|
}
|
|
69707
|
-
function getProjectsDirForCwd(cwd = process.cwd(), claudeHome = process.env.CLAUDE_CONFIG_DIR ?? join24(
|
|
70145
|
+
function getProjectsDirForCwd(cwd = process.cwd(), claudeHome = process.env.CLAUDE_CONFIG_DIR ?? join24(homedir6(), ".claude")) {
|
|
69708
70146
|
return join24(claudeHome, "projects", sanitizeCwdToProjectName(cwd));
|
|
69709
70147
|
}
|
|
69710
70148
|
function parseChannelMeta(content3) {
|
|
@@ -71048,7 +71486,7 @@ function defaultAddAccount(label, credentials, opts) {
|
|
|
71048
71486
|
// ../src/auth/broker/client.ts
|
|
71049
71487
|
init_protocol2();
|
|
71050
71488
|
import * as net3 from "node:net";
|
|
71051
|
-
import { homedir as
|
|
71489
|
+
import { homedir as homedir7 } from "node:os";
|
|
71052
71490
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
71053
71491
|
import { join as join26 } from "node:path";
|
|
71054
71492
|
var DEFAULT_TIMEOUT_MS3 = 5000;
|
|
@@ -71060,7 +71498,7 @@ function reviveDate2(v) {
|
|
|
71060
71498
|
const d = new Date(v);
|
|
71061
71499
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
71062
71500
|
}
|
|
71063
|
-
function operatorSocketPath2(home2 =
|
|
71501
|
+
function operatorSocketPath2(home2 = homedir7()) {
|
|
71064
71502
|
return join26(home2, ".switchroom", "state", "auth-broker-operator", "sock");
|
|
71065
71503
|
}
|
|
71066
71504
|
function resolveAuthBrokerSocketPath2(opts) {
|
|
@@ -71629,7 +72067,7 @@ function resolveExhaustUntil(resetAtMs, now = Date.now()) {
|
|
|
71629
72067
|
// gateway/auth-add-flow.ts
|
|
71630
72068
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
71631
72069
|
import { existsSync as existsSync22, mkdirSync as mkdirSync23, readFileSync as readFileSync21, readdirSync as readdirSync6, rmSync as rmSync5, statSync as statSync9, writeFileSync as writeFileSync19 } from "node:fs";
|
|
71632
|
-
import { homedir as
|
|
72070
|
+
import { homedir as homedir8 } from "node:os";
|
|
71633
72071
|
import { join as join27 } from "node:path";
|
|
71634
72072
|
import { randomBytes as randomBytes8 } from "node:crypto";
|
|
71635
72073
|
|
|
@@ -71735,7 +72173,7 @@ function makeAuthAddTmuxOps(tmuxBin = "tmux") {
|
|
|
71735
72173
|
};
|
|
71736
72174
|
}
|
|
71737
72175
|
var pendingAuthAddFlows = new Map;
|
|
71738
|
-
function pickScratchDir(label, home2 =
|
|
72176
|
+
function pickScratchDir(label, home2 = homedir8()) {
|
|
71739
72177
|
const suffix = randomBytes8(8).toString("hex");
|
|
71740
72178
|
return join27(home2, ".switchroom", "accounts", ".in-progress", `${label}-${suffix}`);
|
|
71741
72179
|
}
|
|
@@ -71786,7 +72224,7 @@ async function startAccountAuthSession(label, opts = {}) {
|
|
|
71786
72224
|
if (process.env.SWITCHROOM_TMUX_SUPERVISOR !== "1" && !opts.tmuxOps) {
|
|
71787
72225
|
throw new Error('tmux supervisor required for /auth add: SWITCHROOM_TMUX_SUPERVISOR is not set to "1". ' + "Legacy pipe-based setup-token is unsupported (setup-token writes to /dev/tty, not stdout/stderr).");
|
|
71788
72226
|
}
|
|
71789
|
-
const home2 = opts.home ??
|
|
72227
|
+
const home2 = opts.home ?? homedir8();
|
|
71790
72228
|
const urlTimeoutMs = opts.urlTimeoutMs ?? 30000;
|
|
71791
72229
|
const agentName3 = opts.agentName ?? process.env.SWITCHROOM_AGENT_NAME ?? "gateway";
|
|
71792
72230
|
const tmux = opts.tmuxOps ?? makeAuthAddTmuxOps(opts.tmuxBin);
|
|
@@ -74756,7 +75194,7 @@ import {
|
|
|
74756
75194
|
appendFileSync as appendFileSync4
|
|
74757
75195
|
} from "node:fs";
|
|
74758
75196
|
import { join as join29 } from "node:path";
|
|
74759
|
-
import { homedir as
|
|
75197
|
+
import { homedir as homedir9 } from "node:os";
|
|
74760
75198
|
function sha256Hex(s) {
|
|
74761
75199
|
return createHash3("sha256").update(s, "utf8").digest("hex");
|
|
74762
75200
|
}
|
|
@@ -74766,7 +75204,7 @@ function resolveStateDir2(explicit) {
|
|
|
74766
75204
|
const env = process.env.TELEGRAM_STATE_DIR;
|
|
74767
75205
|
if (env != null && env !== "")
|
|
74768
75206
|
return env;
|
|
74769
|
-
const home2 = process.env.HOME ??
|
|
75207
|
+
const home2 = process.env.HOME ?? homedir9();
|
|
74770
75208
|
return join29(home2, ".claude", "channels", "telegram");
|
|
74771
75209
|
}
|
|
74772
75210
|
function resolveOutboxDir(stateDir) {
|
|
@@ -75620,8 +76058,8 @@ function monotonicNowMs2() {
|
|
|
75620
76058
|
|
|
75621
76059
|
// silent-end.ts
|
|
75622
76060
|
import { existsSync as existsSync26, readFileSync as readFileSync24, writeFileSync as writeFileSync22, unlinkSync as unlinkSync13, mkdirSync as mkdirSync27 } from "node:fs";
|
|
75623
|
-
import { dirname as
|
|
75624
|
-
import { homedir as
|
|
76061
|
+
import { dirname as dirname13, join as join31 } from "node:path";
|
|
76062
|
+
import { homedir as homedir10 } from "node:os";
|
|
75625
76063
|
var SILENT_END_MAX_RETRIES2 = 2;
|
|
75626
76064
|
var SILENT_END_STALE_RECORD_MAX_AGE_MS2 = 30 * 60000;
|
|
75627
76065
|
function silentEndFallbackText(turnDurationMs) {
|
|
@@ -75634,7 +76072,7 @@ function resolveStateDir3(deps) {
|
|
|
75634
76072
|
const env = process.env.TELEGRAM_STATE_DIR;
|
|
75635
76073
|
if (env != null && env !== "")
|
|
75636
76074
|
return env;
|
|
75637
|
-
const home2 = process.env.HOME ??
|
|
76075
|
+
const home2 = process.env.HOME ?? homedir10();
|
|
75638
76076
|
return join31(home2, ".claude", "channels", "telegram");
|
|
75639
76077
|
}
|
|
75640
76078
|
function resolveStatePath3(deps) {
|
|
@@ -75667,7 +76105,7 @@ function writeSilentEndState2(args, deps) {
|
|
|
75667
76105
|
timestamp: Date.now()
|
|
75668
76106
|
};
|
|
75669
76107
|
try {
|
|
75670
|
-
mkdirSync27(
|
|
76108
|
+
mkdirSync27(dirname13(statePath), { recursive: true });
|
|
75671
76109
|
writeFileSync22(statePath, JSON.stringify(state3), "utf8");
|
|
75672
76110
|
emitLog2(deps, `silent-end: wrote state file turnKey=${args.turnKey} retryCount=${retryCount}
|
|
75673
76111
|
`);
|
|
@@ -75939,7 +76377,9 @@ function createBackstopReadBack(w) {
|
|
|
75939
76377
|
};
|
|
75940
76378
|
try {
|
|
75941
76379
|
const r = await w.gate(() => w.editMessageText(messageId, body, editApiOpts), gateOpts);
|
|
75942
|
-
|
|
76380
|
+
if (w.isShed(r) || r === undefined)
|
|
76381
|
+
return "ambiguous";
|
|
76382
|
+
return "exists";
|
|
75943
76383
|
} catch (err) {
|
|
75944
76384
|
return classifyReadBackError(err);
|
|
75945
76385
|
}
|
|
@@ -77261,7 +77701,7 @@ init_rich_send();
|
|
|
77261
77701
|
|
|
77262
77702
|
// runtime-metrics.ts
|
|
77263
77703
|
import { mkdirSync as mkdirSync29, appendFileSync as appendFileSync6 } from "node:fs";
|
|
77264
|
-
import { dirname as
|
|
77704
|
+
import { dirname as dirname14, join as join33 } from "node:path";
|
|
77265
77705
|
function resolveJsonlPath2() {
|
|
77266
77706
|
const override = process.env.SWITCHROOM_RUNTIME_METRICS_PATH;
|
|
77267
77707
|
if (override && override.trim() !== "")
|
|
@@ -77272,7 +77712,7 @@ function resolveJsonlPath2() {
|
|
|
77272
77712
|
function appendJsonl2(line) {
|
|
77273
77713
|
const path2 = resolveJsonlPath2();
|
|
77274
77714
|
try {
|
|
77275
|
-
mkdirSync29(
|
|
77715
|
+
mkdirSync29(dirname14(path2), { recursive: true });
|
|
77276
77716
|
appendFileSync6(path2, line + `
|
|
77277
77717
|
`, "utf-8");
|
|
77278
77718
|
} catch (err) {
|
|
@@ -78687,6 +79127,7 @@ function handleSessionEvent(deps, ev) {
|
|
|
78687
79127
|
sentIds = delivery.sentIds;
|
|
78688
79128
|
chunkCount = delivery.chunkCount;
|
|
78689
79129
|
delivered = delivery.delivered;
|
|
79130
|
+
turn.landedUnconfirmed = delivery.landedUnconfirmed;
|
|
78690
79131
|
outboundDedup.record(backstopChatId, backstopThreadId, capturedText, Date.now(), getCurrentTurn()?.registryKey ?? null);
|
|
78691
79132
|
if (sentIds.length > 0) {
|
|
78692
79133
|
flushedTurnSupersede.record(backstopChatId, backstopThreadId, { turnId: turn.turnId, messageIds: sentIds, text: capturedText }, Date.now());
|
|
@@ -79230,7 +79671,16 @@ function createNarrativeLane(deps) {
|
|
|
79230
79671
|
reconcileStatusPin(`fg:${statusKey(chat, thread)}`, chat, { pinned: true, messageId: sent.message_id });
|
|
79231
79672
|
} else {
|
|
79232
79673
|
const id = turn.activityMessageId;
|
|
79233
|
-
await robustApiCall(() => bot.api.editMessageText(chat, id, richMessage(html), {}), {
|
|
79674
|
+
const editRes = await robustApiCall(() => bot.api.editMessageText(chat, id, richMessage(html), {}), {
|
|
79675
|
+
chat_id: chat,
|
|
79676
|
+
...thread != null ? { threadId: thread } : {},
|
|
79677
|
+
verb: "activity-summary.edit",
|
|
79678
|
+
priorityClass: "cosmetic",
|
|
79679
|
+
messageId: id,
|
|
79680
|
+
editPayload: html
|
|
79681
|
+
});
|
|
79682
|
+
if (isSendGateShed(editRes))
|
|
79683
|
+
break;
|
|
79234
79684
|
}
|
|
79235
79685
|
turn.activityLastSentRender = target;
|
|
79236
79686
|
} catch (err) {
|
|
@@ -79431,7 +79881,14 @@ function createNarrativeLane(deps) {
|
|
|
79431
79881
|
if (finalHtml == null)
|
|
79432
79882
|
return;
|
|
79433
79883
|
try {
|
|
79434
|
-
await robustApiCall(() => bot.api.editMessageText(chat, id, richMessage(finalHtml), {}), {
|
|
79884
|
+
await robustApiCall(() => bot.api.editMessageText(chat, id, richMessage(finalHtml), {}), {
|
|
79885
|
+
chat_id: chat,
|
|
79886
|
+
...thread != null ? { threadId: thread } : {},
|
|
79887
|
+
verb: "activity-summary.finalize",
|
|
79888
|
+
priorityClass: "useful",
|
|
79889
|
+
messageId: id,
|
|
79890
|
+
editPayload: finalHtml
|
|
79891
|
+
});
|
|
79435
79892
|
} catch (err) {
|
|
79436
79893
|
const msg = err instanceof Error ? err.message : String(err);
|
|
79437
79894
|
const low = msg.toLowerCase();
|
|
@@ -79738,12 +80195,12 @@ class AnswerReadyFlushController {
|
|
|
79738
80195
|
}
|
|
79739
80196
|
|
|
79740
80197
|
// agent-dir.ts
|
|
79741
|
-
import { dirname as
|
|
80198
|
+
import { dirname as dirname15 } from "node:path";
|
|
79742
80199
|
function resolveAgentDirFromEnv() {
|
|
79743
80200
|
const state6 = process.env.TELEGRAM_STATE_DIR;
|
|
79744
80201
|
if (!state6 || state6.trim().length === 0)
|
|
79745
80202
|
return null;
|
|
79746
|
-
return
|
|
80203
|
+
return dirname15(state6);
|
|
79747
80204
|
}
|
|
79748
80205
|
|
|
79749
80206
|
// active-reactions.ts
|
|
@@ -82450,7 +82907,7 @@ async function discoverModels(agentName3, opts = {}) {
|
|
|
82450
82907
|
}
|
|
82451
82908
|
|
|
82452
82909
|
// ../src/agents/scaffold.ts
|
|
82453
|
-
import { dirname as
|
|
82910
|
+
import { dirname as dirname16, isAbsolute, join as join42, relative, resolve as resolve8 } from "node:path";
|
|
82454
82911
|
init_atomic();
|
|
82455
82912
|
|
|
82456
82913
|
// ../src/agents/agent-uid.ts
|
|
@@ -82469,7 +82926,7 @@ init_timezone();
|
|
|
82469
82926
|
|
|
82470
82927
|
// ../src/cli/agent-config.ts
|
|
82471
82928
|
import { join as join40 } from "node:path";
|
|
82472
|
-
import { homedir as
|
|
82929
|
+
import { homedir as homedir11 } from "node:os";
|
|
82473
82930
|
|
|
82474
82931
|
// ../src/cli/helpers.ts
|
|
82475
82932
|
init_loader();
|
|
@@ -82489,7 +82946,7 @@ var WEBKITE_VAULT_KEYS = new Set([
|
|
|
82489
82946
|
init_overlay_loader();
|
|
82490
82947
|
|
|
82491
82948
|
// ../src/cli/agent-config.ts
|
|
82492
|
-
var AUDIT_ROOT = join40(
|
|
82949
|
+
var AUDIT_ROOT = join40(homedir11(), ".switchroom", "audit");
|
|
82493
82950
|
|
|
82494
82951
|
// ../src/agents/profiles.ts
|
|
82495
82952
|
var import_handlebars = __toESM(require_lib(), 1);
|
|
@@ -82527,6 +82984,116 @@ for (const name of SHARED_FRAGMENTS) {
|
|
|
82527
82984
|
}
|
|
82528
82985
|
}
|
|
82529
82986
|
|
|
82987
|
+
// ../src/litellm/timeout-budget.ts
|
|
82988
|
+
var LITELLM_ROUTER_MARGIN_S = 10;
|
|
82989
|
+
var LITELLM_TIMEOUT_TIERS = {
|
|
82990
|
+
interactive: {
|
|
82991
|
+
group: "gpt-oss-20b",
|
|
82992
|
+
localTimeoutS: 90,
|
|
82993
|
+
fallbackGroup: "gpt-oss-20b-openrouter",
|
|
82994
|
+
fallbackTimeoutS: 60
|
|
82995
|
+
},
|
|
82996
|
+
retain: {
|
|
82997
|
+
group: "gpt-oss-20b-retain",
|
|
82998
|
+
localTimeoutS: 200,
|
|
82999
|
+
fallbackGroup: "gpt-oss-20b-retain-openrouter",
|
|
83000
|
+
fallbackTimeoutS: 90
|
|
83001
|
+
},
|
|
83002
|
+
consolidation: {
|
|
83003
|
+
group: "gpt-oss-20b-consolidation",
|
|
83004
|
+
localTimeoutS: 200,
|
|
83005
|
+
fallbackGroup: "gpt-oss-20b-consolidation-openrouter",
|
|
83006
|
+
fallbackTimeoutS: 90
|
|
83007
|
+
}
|
|
83008
|
+
};
|
|
83009
|
+
function litellmChainSeconds(tier) {
|
|
83010
|
+
assertPositive(tier.localTimeoutS, `${tier.group} localTimeoutS`);
|
|
83011
|
+
if (tier.fallbackGroup === null)
|
|
83012
|
+
return tier.localTimeoutS;
|
|
83013
|
+
assertPositive(tier.fallbackTimeoutS, `${tier.fallbackGroup} fallbackTimeoutS`);
|
|
83014
|
+
return tier.localTimeoutS + tier.fallbackTimeoutS;
|
|
83015
|
+
}
|
|
83016
|
+
function minimumClientBudgetSeconds(tier, marginS = LITELLM_ROUTER_MARGIN_S) {
|
|
83017
|
+
return litellmChainSeconds(tier) + marginS;
|
|
83018
|
+
}
|
|
83019
|
+
function clientBudgetSeconds(tier, floorS = 0, marginS = LITELLM_ROUTER_MARGIN_S) {
|
|
83020
|
+
return Math.max(Math.ceil(floorS), minimumClientBudgetSeconds(tier, marginS));
|
|
83021
|
+
}
|
|
83022
|
+
function assertPositive(value, label) {
|
|
83023
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
83024
|
+
throw new Error(`litellm timeout budget: ${label} must be a positive number, got ${value}`);
|
|
83025
|
+
}
|
|
83026
|
+
}
|
|
83027
|
+
|
|
83028
|
+
// ../src/setup/host-capabilities.ts
|
|
83029
|
+
init_paths();
|
|
83030
|
+
|
|
83031
|
+
// ../src/setup/hindsight-perf-defaults.ts
|
|
83032
|
+
var HINDSIGHT_RERANKER_MAX_CANDIDATES_FOR_DERIVATION = 150;
|
|
83033
|
+
var HINDSIGHT_DEFAULT_RECALL_MAX_CANDIDATES_PER_SOURCE = Math.ceil(HINDSIGHT_RERANKER_MAX_CANDIDATES_FOR_DERIVATION * 0.4);
|
|
83034
|
+
var HINDSIGHT_DEFAULT_LINK_EXPANSION_PER_ENTITY_LIMIT = 50;
|
|
83035
|
+
var HINDSIGHT_DEFAULT_LINK_EXPANSION_TIMEOUT_S = 2;
|
|
83036
|
+
var HINDSIGHT_DEFAULT_LLM_REASONING_EFFORT = "low";
|
|
83037
|
+
var HINDSIGHT_DEFAULT_LLM_MAX_CONCURRENT = 4;
|
|
83038
|
+
var HINDSIGHT_DEFAULT_RETAIN_LLM_MAX_CONCURRENT = 1;
|
|
83039
|
+
var HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_MAX_CONCURRENT = 1;
|
|
83040
|
+
var HINDSIGHT_DEFAULT_RERANKER_LOCAL_FP16 = "true";
|
|
83041
|
+
var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
83042
|
+
[
|
|
83043
|
+
"HINDSIGHT_API_RECALL_MAX_CANDIDATES_PER_SOURCE",
|
|
83044
|
+
String(HINDSIGHT_DEFAULT_RECALL_MAX_CANDIDATES_PER_SOURCE)
|
|
83045
|
+
],
|
|
83046
|
+
[
|
|
83047
|
+
"HINDSIGHT_API_LINK_EXPANSION_PER_ENTITY_LIMIT",
|
|
83048
|
+
String(HINDSIGHT_DEFAULT_LINK_EXPANSION_PER_ENTITY_LIMIT)
|
|
83049
|
+
],
|
|
83050
|
+
[
|
|
83051
|
+
"HINDSIGHT_API_LINK_EXPANSION_TIMEOUT",
|
|
83052
|
+
String(HINDSIGHT_DEFAULT_LINK_EXPANSION_TIMEOUT_S)
|
|
83053
|
+
],
|
|
83054
|
+
["HINDSIGHT_API_LLM_REASONING_EFFORT", HINDSIGHT_DEFAULT_LLM_REASONING_EFFORT]
|
|
83055
|
+
];
|
|
83056
|
+
var HINDSIGHT_PERF_DEFAULTS_GPU = [
|
|
83057
|
+
["HINDSIGHT_API_RERANKER_LOCAL_FP16", HINDSIGHT_DEFAULT_RERANKER_LOCAL_FP16]
|
|
83058
|
+
];
|
|
83059
|
+
var HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM = [
|
|
83060
|
+
["HINDSIGHT_API_LLM_MAX_CONCURRENT", String(HINDSIGHT_DEFAULT_LLM_MAX_CONCURRENT)],
|
|
83061
|
+
[
|
|
83062
|
+
"HINDSIGHT_API_RETAIN_LLM_MAX_CONCURRENT",
|
|
83063
|
+
String(HINDSIGHT_DEFAULT_RETAIN_LLM_MAX_CONCURRENT)
|
|
83064
|
+
],
|
|
83065
|
+
[
|
|
83066
|
+
"HINDSIGHT_API_CONSOLIDATION_LLM_MAX_CONCURRENT",
|
|
83067
|
+
String(HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_MAX_CONCURRENT)
|
|
83068
|
+
]
|
|
83069
|
+
];
|
|
83070
|
+
var HINDSIGHT_PERF_ENV_KEYS = new Set([
|
|
83071
|
+
...HINDSIGHT_PERF_DEFAULTS_UNGATED,
|
|
83072
|
+
...HINDSIGHT_PERF_DEFAULTS_GPU,
|
|
83073
|
+
...HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM
|
|
83074
|
+
].map(([k]) => k));
|
|
83075
|
+
|
|
83076
|
+
// ../src/setup/hindsight-pg-defaults.ts
|
|
83077
|
+
var HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION = 8 * 1024;
|
|
83078
|
+
var HINDSIGHT_PG_APP_ANON_MIB = 2560;
|
|
83079
|
+
var HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB = 2048;
|
|
83080
|
+
var HINDSIGHT_PG_SHARED_BUFFERS_BUDGET_MIB = HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION - HINDSIGHT_PG_APP_ANON_MIB - HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB;
|
|
83081
|
+
var HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB = 1536;
|
|
83082
|
+
var HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB = 4096;
|
|
83083
|
+
function pgMib(mib) {
|
|
83084
|
+
return `${mib}MB`;
|
|
83085
|
+
}
|
|
83086
|
+
var HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE_ENV = "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE";
|
|
83087
|
+
var HINDSIGHT_PG_SHARED_BUFFERS_ENV = "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS";
|
|
83088
|
+
var HINDSIGHT_PG_DEFAULTS = [
|
|
83089
|
+
[
|
|
83090
|
+
HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE_ENV,
|
|
83091
|
+
pgMib(HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB)
|
|
83092
|
+
],
|
|
83093
|
+
[HINDSIGHT_PG_SHARED_BUFFERS_ENV, pgMib(HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB)]
|
|
83094
|
+
];
|
|
83095
|
+
var HINDSIGHT_PG_ENV_KEYS = new Set(HINDSIGHT_PG_DEFAULTS.map(([k]) => k));
|
|
83096
|
+
|
|
82530
83097
|
// ../src/setup/hindsight.ts
|
|
82531
83098
|
var HINDSIGHT_DEFAULT_API_PORT = 18888;
|
|
82532
83099
|
var HINDSIGHT_DEFAULT_MCP_URL = `http://127.0.0.1:${HINDSIGHT_DEFAULT_API_PORT}/mcp/`;
|
|
@@ -82536,6 +83103,18 @@ var HINDSIGHT_IMAGE_REPO = "ghcr.io/switchroom/switchroom-hindsight";
|
|
|
82536
83103
|
var HINDSIGHT_IMAGE = `${HINDSIGHT_IMAGE_REPO}:latest`;
|
|
82537
83104
|
var HINDSIGHT_BROKER_SOCK_VOLUME = `auth-broker-${HINDSIGHT_CONSUMER_NAME}-sock`;
|
|
82538
83105
|
var HINDSIGHT_CREDS_MIRROR_VOLUME = `consumer-creds-${HINDSIGHT_CONSUMER_NAME}`;
|
|
83106
|
+
var HINDSIGHT_DEFAULT_RETAIN_MAX_COMPLETION_TOKENS = 16384;
|
|
83107
|
+
var HINDSIGHT_RETAIN_MIN_TOKENS_PER_SECOND = 80.6;
|
|
83108
|
+
function hindsightRetainLlmTimeoutSeconds(maxCompletionTokens = HINDSIGHT_DEFAULT_RETAIN_MAX_COMPLETION_TOKENS, tokensPerSecond = HINDSIGHT_RETAIN_MIN_TOKENS_PER_SECOND) {
|
|
83109
|
+
if (!(maxCompletionTokens > 0) || !(tokensPerSecond > 0)) {
|
|
83110
|
+
throw new Error(`hindsight retain budget: maxCompletionTokens (${maxCompletionTokens}) and ` + `tokensPerSecond (${tokensPerSecond}) must both be positive`);
|
|
83111
|
+
}
|
|
83112
|
+
return Math.ceil(maxCompletionTokens / tokensPerSecond);
|
|
83113
|
+
}
|
|
83114
|
+
function hindsightRetainClientTimeoutSeconds() {
|
|
83115
|
+
return clientBudgetSeconds(LITELLM_TIMEOUT_TIERS.retain, hindsightRetainLlmTimeoutSeconds());
|
|
83116
|
+
}
|
|
83117
|
+
var HINDSIGHT_RETAIN_CLIENT_DEADLINE_S = hindsightRetainClientTimeoutSeconds() + LITELLM_ROUTER_MARGIN_S;
|
|
82539
83118
|
var HINDSIGHT_HEALTHCHECK_PY = 'import urllib.request,sys; sys.exit(0 if urllib.request.urlopen("http://localhost:8888/health",timeout=4).getcode()==200 else 1)';
|
|
82540
83119
|
var HINDSIGHT_HEALTHCHECK_CMD = `python3 -c '${HINDSIGHT_HEALTHCHECK_PY}'`;
|
|
82541
83120
|
var DOCKER_PROBE_TIMEOUT_MS = 60 * 1000;
|
|
@@ -83149,7 +83728,7 @@ init_merge();
|
|
|
83149
83728
|
init_timezone();
|
|
83150
83729
|
var import_yaml4 = __toESM(require_dist(), 1);
|
|
83151
83730
|
import { readFileSync as readFileSync32, existsSync as existsSync35 } from "node:fs";
|
|
83152
|
-
import { homedir as
|
|
83731
|
+
import { homedir as homedir12 } from "node:os";
|
|
83153
83732
|
import { resolve as resolve9 } from "node:path";
|
|
83154
83733
|
|
|
83155
83734
|
class ConfigError2 extends Error {
|
|
@@ -83206,7 +83785,7 @@ function coerceLegacyGoogleWorkspaceKeys2(parsed, filePath) {
|
|
|
83206
83785
|
}
|
|
83207
83786
|
function findConfigFile2(startDir) {
|
|
83208
83787
|
const envPath = process.env.SWITCHROOM_CONFIG;
|
|
83209
|
-
const home2 =
|
|
83788
|
+
const home2 = homedir12();
|
|
83210
83789
|
const userDir = resolve9(home2, ".switchroom");
|
|
83211
83790
|
const searchPaths = [
|
|
83212
83791
|
envPath ? resolve9(envPath) : null,
|
|
@@ -84156,6 +84735,94 @@ function shouldSweepChatAtBoot(chatId) {
|
|
|
84156
84735
|
return n < 0;
|
|
84157
84736
|
}
|
|
84158
84737
|
|
|
84738
|
+
// gateway/boot-sweep-gate.ts
|
|
84739
|
+
function createBootSweepGate(args) {
|
|
84740
|
+
let armed = false;
|
|
84741
|
+
let ready = false;
|
|
84742
|
+
let started = false;
|
|
84743
|
+
const maybeRun = () => {
|
|
84744
|
+
if (started || !armed || !ready)
|
|
84745
|
+
return;
|
|
84746
|
+
started = true;
|
|
84747
|
+
(async () => {
|
|
84748
|
+
try {
|
|
84749
|
+
await args.run();
|
|
84750
|
+
} catch (err) {
|
|
84751
|
+
args.onError?.(err);
|
|
84752
|
+
}
|
|
84753
|
+
})();
|
|
84754
|
+
};
|
|
84755
|
+
return {
|
|
84756
|
+
arm() {
|
|
84757
|
+
armed = true;
|
|
84758
|
+
maybeRun();
|
|
84759
|
+
},
|
|
84760
|
+
botReady() {
|
|
84761
|
+
ready = true;
|
|
84762
|
+
maybeRun();
|
|
84763
|
+
},
|
|
84764
|
+
hasRun() {
|
|
84765
|
+
return started;
|
|
84766
|
+
}
|
|
84767
|
+
};
|
|
84768
|
+
}
|
|
84769
|
+
async function step(name, fn, log) {
|
|
84770
|
+
try {
|
|
84771
|
+
await fn();
|
|
84772
|
+
} catch (err) {
|
|
84773
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
84774
|
+
try {
|
|
84775
|
+
log(`telegram gateway: boot pin sweep step '${name}' failed: ${msg}
|
|
84776
|
+
`);
|
|
84777
|
+
} catch {}
|
|
84778
|
+
}
|
|
84779
|
+
}
|
|
84780
|
+
async function runBootPinSweepSteps(deps) {
|
|
84781
|
+
const log = deps.log ?? ((l) => process.stderr.write(l));
|
|
84782
|
+
let dmChatIds = [];
|
|
84783
|
+
await step("dm-chat-scan", async () => {
|
|
84784
|
+
dmChatIds = deps.scanDmChatIds();
|
|
84785
|
+
}, log);
|
|
84786
|
+
await step("status-pin-cleanup", deps.statusPinCleanup, log);
|
|
84787
|
+
await step("activity-card-reaper", deps.activityCardReaper, log);
|
|
84788
|
+
await step("queued-card-reaper", deps.queuedCardReaper, log);
|
|
84789
|
+
await step("enable-dm-sweep", async () => deps.enableDmSweep(), log);
|
|
84790
|
+
for (const id of dmChatIds)
|
|
84791
|
+
await step(`dm-sweep:${id}`, () => deps.sweepDm(id), log);
|
|
84792
|
+
}
|
|
84793
|
+
|
|
84794
|
+
// gateway/status-pin-api.ts
|
|
84795
|
+
class BotNotReadyError extends Error {
|
|
84796
|
+
constructor(what) {
|
|
84797
|
+
super(`STATUS_PIN_BOT_NOT_READY: ${what} used before initGatewayBot() assigned ` + `lockedBot \u2014 the boot sweep must run behind the boot-sweep gate`);
|
|
84798
|
+
this.name = "BotNotReadyError";
|
|
84799
|
+
}
|
|
84800
|
+
}
|
|
84801
|
+
function assertBotReady(bot, what) {
|
|
84802
|
+
if (bot == null)
|
|
84803
|
+
throw new BotNotReadyError(what);
|
|
84804
|
+
return bot;
|
|
84805
|
+
}
|
|
84806
|
+
|
|
84807
|
+
class SendGateShedError extends Error {
|
|
84808
|
+
constructor(verb) {
|
|
84809
|
+
super(`STATUS_PIN_SEND_SHED: ${verb} was shed by the send gate and never reached Telegram`);
|
|
84810
|
+
this.name = "SendGateShedError";
|
|
84811
|
+
}
|
|
84812
|
+
}
|
|
84813
|
+
function assertLanded(result, verb) {
|
|
84814
|
+
if (result === SEND_GATE_SHED)
|
|
84815
|
+
throw new SendGateShedError(verb);
|
|
84816
|
+
return result;
|
|
84817
|
+
}
|
|
84818
|
+
function createStatusPinApi(getBot, robust) {
|
|
84819
|
+
const call = (verb, fn, chatId) => robust(() => fn(assertBotReady(getBot(), verb)), { chat_id: chatId, verb }).then((r) => assertLanded(r, verb));
|
|
84820
|
+
return {
|
|
84821
|
+
pinChatMessage: (chat_id, message_id, opts) => call("status-pin.pin", (bot) => bot.api.pinChatMessage(chat_id, message_id, opts), String(chat_id)),
|
|
84822
|
+
unpinChatMessage: (chat_id, message_id) => call("status-pin.unpin", (bot) => bot.api.unpinChatMessage(chat_id, message_id), String(chat_id))
|
|
84823
|
+
};
|
|
84824
|
+
}
|
|
84825
|
+
|
|
84159
84826
|
// gateway/dm-pin-sweep.ts
|
|
84160
84827
|
function isDmChatId(chatId) {
|
|
84161
84828
|
const n = Number(chatId);
|
|
@@ -84339,7 +85006,7 @@ function startWebhookIngestServer(opts) {
|
|
|
84339
85006
|
// ../src/web/webhook-gateway-record.ts
|
|
84340
85007
|
import { appendFileSync as appendFileSync8, mkdirSync as mkdirSync35 } from "fs";
|
|
84341
85008
|
import { join as join47 } from "path";
|
|
84342
|
-
import { homedir as
|
|
85009
|
+
import { homedir as homedir14 } from "os";
|
|
84343
85010
|
|
|
84344
85011
|
// ../src/web/webhook-handler.ts
|
|
84345
85012
|
import { appendFileSync as appendFileSync7, existsSync as existsSync40, mkdirSync as mkdirSync33, readFileSync as readFileSync34, writeFileSync as writeFileSync30 } from "fs";
|
|
@@ -84493,21 +85160,21 @@ function sweepStaleParks(lockPath) {
|
|
|
84493
85160
|
} catch {}
|
|
84494
85161
|
}
|
|
84495
85162
|
function fsyncAndMeasure(snapshotPath, logPath, tag) {
|
|
84496
|
-
let
|
|
85163
|
+
let step2 = "open";
|
|
84497
85164
|
try {
|
|
84498
85165
|
const fd = fs2.openSync(snapshotPath, "r");
|
|
84499
85166
|
try {
|
|
84500
|
-
|
|
85167
|
+
step2 = "fsync";
|
|
84501
85168
|
fs2.fsyncSync(fd);
|
|
84502
|
-
|
|
85169
|
+
step2 = "measure";
|
|
84503
85170
|
const size = fs2.fstatSync(fd).size;
|
|
84504
|
-
|
|
85171
|
+
step2 = "close";
|
|
84505
85172
|
return size;
|
|
84506
85173
|
} finally {
|
|
84507
85174
|
fs2.closeSync(fd);
|
|
84508
85175
|
}
|
|
84509
85176
|
} catch (err) {
|
|
84510
|
-
process.stderr.write(`[${tag}] ERROR: could not ${
|
|
85177
|
+
process.stderr.write(`[${tag}] ERROR: could not ${step2} snapshot ${snapshotPath}; leaving active log ${logPath} intact to avoid data loss: ${err.message}
|
|
84511
85178
|
`);
|
|
84512
85179
|
return null;
|
|
84513
85180
|
}
|
|
@@ -84729,7 +85396,7 @@ var throttleIssueWindow = new Map;
|
|
|
84729
85396
|
// ../src/web/webhook-dispatch.ts
|
|
84730
85397
|
import { existsSync as existsSync41, mkdirSync as mkdirSync34, readFileSync as readFileSync35, writeFileSync as writeFileSync31 } from "fs";
|
|
84731
85398
|
import { join as join46 } from "path";
|
|
84732
|
-
import { homedir as
|
|
85399
|
+
import { homedir as homedir13 } from "os";
|
|
84733
85400
|
|
|
84734
85401
|
// ../src/agent-scheduler/ipc-client.ts
|
|
84735
85402
|
import { createConnection as createConnection2 } from "node:net";
|
|
@@ -85106,7 +85773,7 @@ async function defaultInject(socketPath, agentName3, inbound) {
|
|
|
85106
85773
|
}
|
|
85107
85774
|
function injectWebhookInbound(agent, prompt, ctx, deps = {}) {
|
|
85108
85775
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
85109
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join46(
|
|
85776
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join46(homedir13(), ".switchroom", "agents", a));
|
|
85110
85777
|
const now = (deps.now ?? Date.now)();
|
|
85111
85778
|
const socketPath = join46(resolveAgentDir(agent), "telegram", "gateway.sock");
|
|
85112
85779
|
const inbound = {
|
|
@@ -85180,7 +85847,7 @@ function evaluateDispatch(args, deps = {}) {
|
|
|
85180
85847
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
85181
85848
|
const now = (deps.now ?? Date.now)();
|
|
85182
85849
|
const nowDate = deps.nowDate ?? (() => new Date(now));
|
|
85183
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join46(
|
|
85850
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join46(homedir13(), ".switchroom", "agents", a));
|
|
85184
85851
|
const cooldownStore = deps.cooldownStore ?? createFileCooldownStore(resolveAgentDir);
|
|
85185
85852
|
if (!DISPATCH_SOURCES.includes(args.source))
|
|
85186
85853
|
return 0;
|
|
@@ -85258,7 +85925,7 @@ var CONSOLIDATION_COMPLETED_EVENT = "consolidation.completed";
|
|
|
85258
85925
|
function recordWebhookEvent(rec, deps = {}) {
|
|
85259
85926
|
const log = deps.log ?? ((s) => process.stderr.write(s));
|
|
85260
85927
|
const now = rec.ts || (deps.now ?? Date.now)();
|
|
85261
|
-
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join47(
|
|
85928
|
+
const resolveAgentDir = deps.resolveAgentDir ?? ((a) => join47(homedir14(), ".switchroom", "agents", a));
|
|
85262
85929
|
const dedupStore = deps.dedupStore ?? createFileDedupStore(resolveAgentDir);
|
|
85263
85930
|
const agent = rec.agent;
|
|
85264
85931
|
const telegramDir = join47(resolveAgentDir(agent), "telegram");
|
|
@@ -86999,10 +87666,10 @@ function createCapturedResumeDispatcher(ports) {
|
|
|
86999
87666
|
if (delivered) {
|
|
87000
87667
|
ports.obligationLedger.close(o.originTurnId);
|
|
87001
87668
|
ports.backstopDeliveryLedger.clear(o.originTurnId);
|
|
87002
|
-
stderr(`telegram gateway: captured-answer resume delivered \u2014 origin=${o.originTurnId} ` + `${sentIds.length}
|
|
87669
|
+
stderr(`telegram gateway: captured-answer resume delivered \u2014 origin=${o.originTurnId} ` + `${sentIds.length} message id(s) landed; obligation closed
|
|
87003
87670
|
`);
|
|
87004
87671
|
} else {
|
|
87005
|
-
stderr(`telegram gateway: captured-answer resume partial \u2014 origin=${o.originTurnId} ` + `tail still not
|
|
87672
|
+
stderr(`telegram gateway: captured-answer resume partial \u2014 origin=${o.originTurnId} ` + `tail still not landed; left OPEN for retry
|
|
87006
87673
|
`);
|
|
87007
87674
|
}
|
|
87008
87675
|
} catch (err) {
|
|
@@ -87875,6 +88542,14 @@ function resolveAnswerThreadId(input) {
|
|
|
87875
88542
|
|
|
87876
88543
|
// gateway/turns-jsonl-rotate.ts
|
|
87877
88544
|
var TURNS_JSONL_MAX_BYTES = 5 * 1024 * 1024;
|
|
88545
|
+
var DEFAULT_AGENT_STATE_DIR = "/state/agent";
|
|
88546
|
+
function resolveAgentStateDir(env = process.env) {
|
|
88547
|
+
const dir = env.SWITCHROOM_AGENT_STATE_DIR?.trim();
|
|
88548
|
+
return dir != null && dir !== "" ? dir.replace(/\/+$/, "") : DEFAULT_AGENT_STATE_DIR;
|
|
88549
|
+
}
|
|
88550
|
+
function resolveTurnsJsonlPath(env = process.env) {
|
|
88551
|
+
return `${resolveAgentStateDir(env)}/turns.jsonl`;
|
|
88552
|
+
}
|
|
87878
88553
|
function maybeRotate(path3, fs3, maxBytes = TURNS_JSONL_MAX_BYTES) {
|
|
87879
88554
|
const size = fs3.statSize(path3);
|
|
87880
88555
|
if (size == null || size < maxBytes)
|
|
@@ -87903,7 +88578,8 @@ function buildTurnRecord(turn, endedAt) {
|
|
|
87903
88578
|
duration_ms: turn.startedAt > 0 ? endedAt - turn.startedAt : 0,
|
|
87904
88579
|
tools: turn.toolCallCount ?? 0,
|
|
87905
88580
|
status: computeTurnStatus(turn),
|
|
87906
|
-
turn_id: turn.turnId
|
|
88581
|
+
turn_id: turn.turnId,
|
|
88582
|
+
...turn.landedUnconfirmed != null && turn.landedUnconfirmed > 0 ? { landed_unconfirmed: turn.landedUnconfirmed } : {}
|
|
87907
88583
|
};
|
|
87908
88584
|
}
|
|
87909
88585
|
|
|
@@ -88085,8 +88761,16 @@ async function runBackstopDelivery(ledger, turnId, chunks, cardMessageId, deps,
|
|
|
88085
88761
|
break;
|
|
88086
88762
|
}
|
|
88087
88763
|
const sentIds = ledger.sentIds(turnId);
|
|
88088
|
-
const
|
|
88764
|
+
const confirmed = ledger.allConfirmed(turnId, chunkCount);
|
|
88765
|
+
const allLanded = chunkCount > 0 && ledger.unsentIndices(turnId, chunkCount).length === 0;
|
|
88766
|
+
const delivered = allLanded && backstopReceiptIds(sentIds, cardMessageId).length > 0;
|
|
88089
88767
|
const exhausted = !delivered;
|
|
88768
|
+
const confirmedSet = new Set(ledger.confirmedIds(turnId));
|
|
88769
|
+
const landedUnconfirmedIds = sentIds.filter((id) => !confirmedSet.has(id));
|
|
88770
|
+
if (delivered && !confirmed) {
|
|
88771
|
+
stderr(`telegram gateway: backstop delivery landed-unconfirmed for turn ${turnId} \u2014 ` + `every chunk returned a fresh message id but the read-back probe was ` + `inconclusive for ${landedUnconfirmedIds.length} of ${sentIds.length} landed ` + `id(s); counting it delivered (an ambiguous probe is not a failure)
|
|
88772
|
+
`);
|
|
88773
|
+
}
|
|
88090
88774
|
if (deps.recordOutbound && sentIds.length > 0) {
|
|
88091
88775
|
const texts = [];
|
|
88092
88776
|
const ids = [];
|
|
@@ -88098,7 +88782,7 @@ async function runBackstopDelivery(ledger, turnId, chunks, cardMessageId, deps,
|
|
|
88098
88782
|
}
|
|
88099
88783
|
deps.recordOutbound(ids, texts);
|
|
88100
88784
|
}
|
|
88101
|
-
return { sentIds, chunkCount, delivered, attempts, exhausted };
|
|
88785
|
+
return { sentIds, chunkCount, delivered, confirmed, landedUnconfirmedIds, attempts, exhausted };
|
|
88102
88786
|
}
|
|
88103
88787
|
|
|
88104
88788
|
// gateway/inbound-delivery-confirm.ts
|
|
@@ -93053,9 +93737,9 @@ import {
|
|
|
93053
93737
|
renameSync as renameSync20
|
|
93054
93738
|
} from "node:fs";
|
|
93055
93739
|
import { join as join50, resolve as resolve10 } from "node:path";
|
|
93056
|
-
import { homedir as
|
|
93740
|
+
import { homedir as homedir15 } from "node:os";
|
|
93057
93741
|
function registryDir() {
|
|
93058
|
-
return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ?? join50(
|
|
93742
|
+
return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ?? join50(homedir15(), ".switchroom", "worktrees"));
|
|
93059
93743
|
}
|
|
93060
93744
|
function recordPath(id) {
|
|
93061
93745
|
return join50(registryDir(), `${id}.json`);
|
|
@@ -93238,17 +93922,17 @@ init_boot_card();
|
|
|
93238
93922
|
// gateway/update-announce.ts
|
|
93239
93923
|
import { existsSync as existsSync49, mkdirSync as mkdirSync41, openSync as openSync10, closeSync as closeSync10, readFileSync as readFileSync49 } from "node:fs";
|
|
93240
93924
|
import { join as join55 } from "node:path";
|
|
93241
|
-
import { homedir as
|
|
93925
|
+
import { homedir as homedir17 } from "node:os";
|
|
93242
93926
|
|
|
93243
93927
|
// ../src/host-control/audit-reader.ts
|
|
93244
|
-
import { homedir as
|
|
93928
|
+
import { homedir as homedir16 } from "node:os";
|
|
93245
93929
|
import { join as join54 } from "node:path";
|
|
93246
93930
|
|
|
93247
93931
|
// ../src/host-control/audit-rotation-config.ts
|
|
93248
93932
|
var DEFAULT_HOSTD_AUDIT_MAX_BYTES = 32 * 1024 * 1024;
|
|
93249
93933
|
|
|
93250
93934
|
// ../src/host-control/audit-reader.ts
|
|
93251
|
-
function defaultAuditLogPath(home2 =
|
|
93935
|
+
function defaultAuditLogPath(home2 = homedir16()) {
|
|
93252
93936
|
return join54(home2, ".switchroom", "host-control-audit.log");
|
|
93253
93937
|
}
|
|
93254
93938
|
var AUDIT_READ_WINDOW_BYTES = 4 * 1024 * 1024;
|
|
@@ -93435,7 +94119,7 @@ function renderUpdateOutcomeLine(entry) {
|
|
|
93435
94119
|
`);
|
|
93436
94120
|
}
|
|
93437
94121
|
function claimUpdateAnnouncement(requestId, opts = {}) {
|
|
93438
|
-
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join55(
|
|
94122
|
+
const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join55(homedir17(), ".switchroom");
|
|
93439
94123
|
const dir = join55(stateDir, "update-announced");
|
|
93440
94124
|
try {
|
|
93441
94125
|
mkdirSync41(dir, { recursive: true });
|
|
@@ -95737,10 +96421,10 @@ function startOutboxSweep(deps) {
|
|
|
95737
96421
|
}
|
|
95738
96422
|
|
|
95739
96423
|
// ../src/build-info.ts
|
|
95740
|
-
var VERSION2 = "0.19.
|
|
95741
|
-
var COMMIT_SHA = "
|
|
95742
|
-
var COMMIT_DATE = "2026-07-
|
|
95743
|
-
var LATEST_PR =
|
|
96424
|
+
var VERSION2 = "0.19.23";
|
|
96425
|
+
var COMMIT_SHA = "56e3c01e";
|
|
96426
|
+
var COMMIT_DATE = "2026-07-26T13:24:39Z";
|
|
96427
|
+
var LATEST_PR = 3711;
|
|
95744
96428
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
95745
96429
|
|
|
95746
96430
|
// gateway/boot-version.ts
|
|
@@ -95817,12 +96501,12 @@ init_protocol();
|
|
|
95817
96501
|
init_peercred();
|
|
95818
96502
|
import * as net5 from "node:net";
|
|
95819
96503
|
import * as fs3 from "node:fs";
|
|
95820
|
-
import { homedir as
|
|
96504
|
+
import { homedir as homedir18 } from "node:os";
|
|
95821
96505
|
import { join as join63 } from "node:path";
|
|
95822
96506
|
var DEFAULT_TIMEOUT_MS4 = 2000;
|
|
95823
96507
|
var UNLOCK_TIMEOUT_MS = 30000;
|
|
95824
|
-
var LEGACY_SOCKET_PATH2 = join63(
|
|
95825
|
-
var OPERATOR_SOCKET_PATH2 = join63(
|
|
96508
|
+
var LEGACY_SOCKET_PATH2 = join63(homedir18(), ".switchroom", "vault-broker.sock");
|
|
96509
|
+
var OPERATOR_SOCKET_PATH2 = join63(homedir18(), ".switchroom", "broker-operator", "sock");
|
|
95826
96510
|
function defaultBrokerSocketPath2() {
|
|
95827
96511
|
if (fs3.existsSync(OPERATOR_SOCKET_PATH2))
|
|
95828
96512
|
return OPERATOR_SOCKET_PATH2;
|
|
@@ -97596,11 +98280,11 @@ if (isGatewayMain) {
|
|
|
97596
98280
|
shutdownAnalytics();
|
|
97597
98281
|
});
|
|
97598
98282
|
}
|
|
97599
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join65(
|
|
98283
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join65(homedir19(), ".claude", "channels", "telegram");
|
|
97600
98284
|
var permCardStore = createPermissionCardStore(STATE_DIR);
|
|
97601
98285
|
var BLOCKED_APPROVALS_DIR = process.env.SWITCHROOM_BLOCKED_APPROVALS_DIR ?? "/state/blocked-approvals";
|
|
97602
98286
|
var AGENT_NAME = process.env.SWITCHROOM_AGENT_NAME ?? "agent";
|
|
97603
|
-
var blockedApprovalStore = createBlockedApprovalStore(BLOCKED_APPROVALS_DIR, AGENT_NAME,
|
|
98287
|
+
var blockedApprovalStore = createBlockedApprovalStore(BLOCKED_APPROVALS_DIR, AGENT_NAME, resolveAgentStateDir());
|
|
97604
98288
|
function reconcileBlockedApprovals() {
|
|
97605
98289
|
const oldest = selectOldestHeld(pendingPermissions);
|
|
97606
98290
|
if (oldest == null) {
|
|
@@ -98381,7 +99065,8 @@ async function deliverAnswer(args) {
|
|
|
98381
99065
|
sentIds: result.sentIds,
|
|
98382
99066
|
chunkCount: result.chunkCount,
|
|
98383
99067
|
delivered: result.delivered,
|
|
98384
|
-
exhausted: result.exhausted
|
|
99068
|
+
exhausted: result.exhausted,
|
|
99069
|
+
landedUnconfirmed: result.landedUnconfirmedIds.length
|
|
98385
99070
|
};
|
|
98386
99071
|
}
|
|
98387
99072
|
var chatAvailableReactions = new Map;
|
|
@@ -98853,9 +99538,9 @@ function maybePostBusyAck(gateDecision, chatId, threadId) {
|
|
|
98853
99538
|
const inFlight = currentTurn;
|
|
98854
99539
|
const inFlightKey = inFlight != null ? statusKey(inFlight.sessionChatId, inFlight.sessionThreadId) : key;
|
|
98855
99540
|
const now = Date.now();
|
|
98856
|
-
const
|
|
99541
|
+
const step2 = longestInFlightTool(inFlightKey, now);
|
|
98857
99542
|
const midToolCall = toolFlightTracker.isMidToolCall();
|
|
98858
|
-
const stepAgeMs =
|
|
99543
|
+
const stepAgeMs = step2?.durationMs ?? null;
|
|
98859
99544
|
const alreadyAcked = queuedStatusMsgIds.has(key) || busyAckPostedKeys.has(key);
|
|
98860
99545
|
const fire = shouldPostBusyAck({ gateDecision, midToolCall, stepAgeMs, alreadyAcked });
|
|
98861
99546
|
if (!fire) {
|
|
@@ -98879,10 +99564,10 @@ function maybePostBusyAck(gateDecision, chatId, threadId) {
|
|
|
98879
99564
|
busyAckPostedKeys.add(key);
|
|
98880
99565
|
const text5 = formatBusyAckText({
|
|
98881
99566
|
gateDecision,
|
|
98882
|
-
toolName:
|
|
98883
|
-
toolLabel:
|
|
99567
|
+
toolName: step2?.name ?? null,
|
|
99568
|
+
toolLabel: step2?.label ?? null
|
|
98884
99569
|
});
|
|
98885
|
-
process.stderr.write(`telegram gateway: mid-flight busy ack chat=${chatId} thread=${threadId ?? "-"} decision=${gateDecision} step=${
|
|
99570
|
+
process.stderr.write(`telegram gateway: mid-flight busy ack chat=${chatId} thread=${threadId ?? "-"} decision=${gateDecision} step=${step2?.name ?? "-"} step_age_ms=${step2?.durationMs ?? "-"}
|
|
98886
99571
|
`);
|
|
98887
99572
|
postBusyAck(chatId, threadId, text5);
|
|
98888
99573
|
}
|
|
@@ -99208,7 +99893,7 @@ function snapshotContextOccupancy() {
|
|
|
99208
99893
|
} catch {
|
|
99209
99894
|
cap = null;
|
|
99210
99895
|
}
|
|
99211
|
-
const stateDir =
|
|
99896
|
+
const stateDir = resolveAgentStateDir();
|
|
99212
99897
|
writeContextOccupancySnapshot(stateDir, buildContextOccupancy(occupancy, cap, Date.now()));
|
|
99213
99898
|
} catch {}
|
|
99214
99899
|
}
|
|
@@ -99220,10 +99905,11 @@ function emitTurnRecord(turn, endedAt) {
|
|
|
99220
99905
|
toolCallCount: turn.toolCallCount ?? 0,
|
|
99221
99906
|
turnId: turn.turnId,
|
|
99222
99907
|
finalAnswerDelivered: turn.finalAnswerDelivered,
|
|
99223
|
-
deliveryOutcome: turn.deliveryOutcome
|
|
99908
|
+
deliveryOutcome: turn.deliveryOutcome,
|
|
99909
|
+
landedUnconfirmed: turn.landedUnconfirmed
|
|
99224
99910
|
}, endedAt)) + `
|
|
99225
99911
|
`;
|
|
99226
|
-
const turnsPath =
|
|
99912
|
+
const turnsPath = resolveTurnsJsonlPath();
|
|
99227
99913
|
maybeRotate(turnsPath, {
|
|
99228
99914
|
statSize: (p) => {
|
|
99229
99915
|
try {
|
|
@@ -100842,10 +101528,7 @@ var TOOL_PIN_TTL_MS = (() => {
|
|
|
100842
101528
|
return Number.isFinite(v) && v > 0 ? v : 604800000;
|
|
100843
101529
|
})();
|
|
100844
101530
|
function statusPinApi() {
|
|
100845
|
-
return
|
|
100846
|
-
pinChatMessage: (chat_id, message_id, opts) => robustApiCall(() => lockedBot.api.pinChatMessage(chat_id, message_id, opts), { chat_id: String(chat_id), verb: "status-pin.pin" }),
|
|
100847
|
-
unpinChatMessage: (chat_id, message_id) => robustApiCall(() => lockedBot.api.unpinChatMessage(chat_id, message_id), { chat_id: String(chat_id), verb: "status-pin.unpin" })
|
|
100848
|
-
};
|
|
101531
|
+
return createStatusPinApi(() => lockedBot, robustApiCall);
|
|
100849
101532
|
}
|
|
100850
101533
|
async function statusPinBootCleanup() {
|
|
100851
101534
|
if (!statusPinPersistEnabled && !bannerPinPersistEnabled && !toolPinPersistEnabled)
|
|
@@ -101165,25 +101848,25 @@ var dmPinSweeper = createDmPinSweeper({
|
|
|
101165
101848
|
eligible: () => dmPinSweepEligible,
|
|
101166
101849
|
log: (line) => process.stderr.write(line)
|
|
101167
101850
|
});
|
|
101168
|
-
|
|
101169
|
-
|
|
101170
|
-
|
|
101171
|
-
dmChatIds = collectDmChatIdsFromStores({
|
|
101851
|
+
function runBootPinCleanupAndDmSweep() {
|
|
101852
|
+
return runBootPinSweepSteps({
|
|
101853
|
+
scanDmChatIds: () => collectDmChatIdsFromStores({
|
|
101172
101854
|
statusPins: statusPinPersistEnabled || bannerPinPersistEnabled || toolPinPersistEnabled ? loadStatusPins(STATUS_PIN_STORE_PATH, statusPinStoreFs) : [],
|
|
101173
101855
|
activityCards: activityCardPersistEnabled ? loadActivityCards2(ACTIVITY_CARD_STORE_PATH, activityCardStoreFs) : [],
|
|
101174
101856
|
queuedCards: queuedCardPersistEnabled ? loadQueuedCards(QUEUED_CARD_STORE_PATH, queuedCardStoreFs) : []
|
|
101175
|
-
})
|
|
101176
|
-
|
|
101177
|
-
|
|
101178
|
-
|
|
101179
|
-
|
|
101180
|
-
|
|
101181
|
-
|
|
101182
|
-
|
|
101183
|
-
|
|
101184
|
-
|
|
101185
|
-
await dmPinSweeper.sweep(id);
|
|
101857
|
+
}),
|
|
101858
|
+
statusPinCleanup: statusPinBootCleanup,
|
|
101859
|
+
activityCardReaper: activityCardBootReaper,
|
|
101860
|
+
queuedCardReaper: queuedCardBootReaper,
|
|
101861
|
+
enableDmSweep: () => {
|
|
101862
|
+
dmPinSweepEligible = true;
|
|
101863
|
+
},
|
|
101864
|
+
sweepDm: (id) => dmPinSweeper.sweep(id),
|
|
101865
|
+
log: (line) => process.stderr.write(line)
|
|
101866
|
+
});
|
|
101186
101867
|
}
|
|
101868
|
+
var bootPinSweepGate = createBootSweepGate({ run: runBootPinCleanupAndDmSweep, onError: (err) => process.stderr.write(`telegram gateway: boot pin cleanup / DM sweep failed: ${err.message}
|
|
101869
|
+
`) });
|
|
101187
101870
|
var progressDriver = null;
|
|
101188
101871
|
var unpinProgressCardForChat = null;
|
|
101189
101872
|
var getPinnedProgressCardMessageId = null;
|
|
@@ -101255,7 +101938,7 @@ if (isGatewayMain) {
|
|
|
101255
101938
|
if (carrierAgentDir != null)
|
|
101256
101939
|
consumeSessionModelCarrierOnHealthyBoot(carrierAgentDir);
|
|
101257
101940
|
}
|
|
101258
|
-
|
|
101941
|
+
bootPinSweepGate.arm();
|
|
101259
101942
|
} catch (err) {
|
|
101260
101943
|
process.stderr.write(`telegram gateway: boot.lock_acquire_failed err=${err.message} agent=${SWITCHROOM_AGENT_NAME}
|
|
101261
101944
|
`);
|
|
@@ -101268,7 +101951,7 @@ if (isGatewayMain) {
|
|
|
101268
101951
|
if (carrierAgentDir != null)
|
|
101269
101952
|
consumeSessionModelCarrierOnHealthyBoot(carrierAgentDir);
|
|
101270
101953
|
}
|
|
101271
|
-
|
|
101954
|
+
bootPinSweepGate.arm();
|
|
101272
101955
|
} catch (writeErr) {
|
|
101273
101956
|
process.stderr.write(`telegram gateway: writePidFile failed: ${writeErr}
|
|
101274
101957
|
`);
|
|
@@ -105097,7 +105780,7 @@ ${preBlock(formatSwitchroomOutput(detail))}`, { html: true });
|
|
|
105097
105780
|
}
|
|
105098
105781
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
105099
105782
|
try {
|
|
105100
|
-
const auditPath = join65(
|
|
105783
|
+
const auditPath = join65(homedir19(), ".switchroom", "vault-audit.log");
|
|
105101
105784
|
if (!existsSync58(auditPath))
|
|
105102
105785
|
return [];
|
|
105103
105786
|
const raw = readFileSync58(auditPath, "utf8");
|
|
@@ -108608,6 +109291,11 @@ async function initGatewayBot() {
|
|
|
108608
109291
|
bot = new import_grammy16.Bot(TOKEN);
|
|
108609
109292
|
installTgPostLogger(bot);
|
|
108610
109293
|
installRichMarkdownGuard(bot);
|
|
109294
|
+
installEditFloodFuse(bot, {
|
|
109295
|
+
enabled: process.env.SWITCHROOM_EDIT_FUSE !== "0",
|
|
109296
|
+
onTrip: (i) => process.stderr.write(`edit-flood-fuse ${i.action} method=${i.method} key=${i.key}
|
|
109297
|
+
`)
|
|
109298
|
+
});
|
|
108611
109299
|
installUpdateTap(bot, (line) => process.stderr.write(line));
|
|
108612
109300
|
bot.api.config.use(async (prev, method, payload, signal) => {
|
|
108613
109301
|
try {
|
|
@@ -108698,6 +109386,7 @@ async function initGatewayBot() {
|
|
|
108698
109386
|
MENTAL_MODEL_PROPOSE_MAX_PER_WINDOW
|
|
108699
109387
|
});
|
|
108700
109388
|
registerGatewayHandlers(bot, {});
|
|
109389
|
+
bootPinSweepGate.botReady();
|
|
108701
109390
|
}
|
|
108702
109391
|
var didOneTimeSetup = false;
|
|
108703
109392
|
async function startGateway() {
|
|
@@ -108960,7 +109649,7 @@ async function startGateway() {
|
|
|
108960
109649
|
return;
|
|
108961
109650
|
}
|
|
108962
109651
|
})();
|
|
108963
|
-
const resolvedAgentDirForBootCard = agentDir ?? join65(
|
|
109652
|
+
const resolvedAgentDirForBootCard = agentDir ?? join65(homedir19(), ".switchroom", "agents", agentSlug);
|
|
108964
109653
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
108965
109654
|
agentName: agentDisplayName,
|
|
108966
109655
|
agentSlug,
|