switchroom 0.19.47 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-scheduler/index.js +22 -1
- package/dist/auth-broker/index.js +26 -2
- package/dist/buzz-gateway/index.js +9207 -0
- package/dist/cli/notion-write-pretool.mjs +22 -1
- package/dist/cli/switchroom.js +91 -9
- package/dist/host-control/main.js +27 -3
- package/dist/vault/approvals/kernel-server.js +26 -2
- package/dist/vault/broker/server.js +26 -2
- package/package.json +4 -3
- package/profiles/_base/start.sh.hbs +78 -1
- package/profiles/default/CLAUDE.md.hbs +1 -1
- package/skills/dev-protocol/SKILL.md +30 -1
- package/skills/switchroom-architecture/SKILL.md +5 -0
- package/skills/switchroom-cli/SKILL.md +1 -1
- package/telegram-plugin/dist/bridge/bridge.js +7 -4
- package/telegram-plugin/dist/gateway/gateway.js +1156 -247
- package/telegram-plugin/dist/server.js +7 -4
- package/telegram-plugin/gateway/boot-briefing-builder.ts +458 -0
- package/telegram-plugin/gateway/boot-briefing-wiring.ts +170 -0
- package/telegram-plugin/gateway/buzz-mirror.ts +329 -0
- package/telegram-plugin/gateway/buzz-type-guards.ts +34 -0
- package/telegram-plugin/gateway/channel-route.ts +272 -0
- package/telegram-plugin/gateway/gateway.ts +73 -81
- package/telegram-plugin/gateway/inbound-spool.ts +33 -1
- package/telegram-plugin/gateway/ipc-protocol.ts +81 -2
- package/telegram-plugin/gateway/ipc-server.ts +197 -2
- package/telegram-plugin/gateway/outbound-send-path.ts +37 -1
- package/telegram-plugin/gateway/pending-turn-env.ts +61 -0
- package/telegram-plugin/gateway/stream-render.ts +21 -0
- package/telegram-plugin/gateway/subagent-handback-marker.ts +12 -0
- package/telegram-plugin/gateway/user-failure-notices.ts +172 -0
- package/telegram-plugin/history.ts +15 -0
- package/telegram-plugin/llm-error-present.ts +9 -4
- package/telegram-plugin/model-unavailable.ts +4 -0
- package/telegram-plugin/operator-events.fixtures.json +12 -12
- package/telegram-plugin/operator-events.ts +81 -9
- package/telegram-plugin/session-tail.ts +7 -1
- package/telegram-plugin/tests/boot-briefing-builder.test.ts +604 -0
- package/telegram-plugin/tests/buzz-mirror.test.ts +242 -0
- package/telegram-plugin/tests/buzz-origin-stamp-gate.test.ts +159 -0
- package/telegram-plugin/tests/channel-route.test.ts +306 -0
- package/telegram-plugin/tests/inbound-spool.test.ts +47 -0
- package/telegram-plugin/tests/ipc-server-buzz-dedup.test.ts +124 -0
- package/telegram-plugin/tests/ipc-server-buzz-peer.test.ts +269 -0
- package/telegram-plugin/tests/operator-events-session-tail.test.ts +63 -0
- package/telegram-plugin/tests/operator-events.test.ts +71 -7
- package/telegram-plugin/tests/user-failure-notices.test.ts +165 -0
- package/telegram-plugin/voice-normalize-text.ts +5 -0
- package/vendor/hindsight-memory/scripts/directive_verify.py +4 -0
- package/vendor/hindsight-memory/scripts/recall.py +7 -2
|
@@ -11591,7 +11591,7 @@ __export(exports_client, {
|
|
|
11591
11591
|
});
|
|
11592
11592
|
import * as net from "node:net";
|
|
11593
11593
|
import { homedir as homedir2 } from "node:os";
|
|
11594
|
-
import { randomUUID } from "node:crypto";
|
|
11594
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
11595
11595
|
import { join as join3 } from "node:path";
|
|
11596
11596
|
function reviveDate(v) {
|
|
11597
11597
|
if (v == null)
|
|
@@ -11649,7 +11649,7 @@ class AuthBrokerClient {
|
|
|
11649
11649
|
async getCredentials(provider, account) {
|
|
11650
11650
|
const base = {
|
|
11651
11651
|
v: PROTOCOL_VERSION,
|
|
11652
|
-
id:
|
|
11652
|
+
id: randomUUID2(),
|
|
11653
11653
|
op: "get-credentials"
|
|
11654
11654
|
};
|
|
11655
11655
|
let req = base;
|
|
@@ -11663,7 +11663,7 @@ class AuthBrokerClient {
|
|
|
11663
11663
|
async listState() {
|
|
11664
11664
|
const data = await this.send({
|
|
11665
11665
|
v: PROTOCOL_VERSION,
|
|
11666
|
-
id:
|
|
11666
|
+
id: randomUUID2(),
|
|
11667
11667
|
op: "list-state"
|
|
11668
11668
|
});
|
|
11669
11669
|
return data;
|
|
@@ -11671,7 +11671,7 @@ class AuthBrokerClient {
|
|
|
11671
11671
|
async listGoogleAccounts() {
|
|
11672
11672
|
const data = await this.send({
|
|
11673
11673
|
v: PROTOCOL_VERSION,
|
|
11674
|
-
id:
|
|
11674
|
+
id: randomUUID2(),
|
|
11675
11675
|
op: "list-google-accounts"
|
|
11676
11676
|
});
|
|
11677
11677
|
return data;
|
|
@@ -11679,7 +11679,7 @@ class AuthBrokerClient {
|
|
|
11679
11679
|
async listMicrosoftAccounts() {
|
|
11680
11680
|
const data = await this.send({
|
|
11681
11681
|
v: PROTOCOL_VERSION,
|
|
11682
|
-
id:
|
|
11682
|
+
id: randomUUID2(),
|
|
11683
11683
|
op: "list-microsoft-accounts"
|
|
11684
11684
|
});
|
|
11685
11685
|
return data;
|
|
@@ -11687,7 +11687,7 @@ class AuthBrokerClient {
|
|
|
11687
11687
|
async probeQuota(accounts, timeoutMs, forceLive) {
|
|
11688
11688
|
const data = await this.send({
|
|
11689
11689
|
v: PROTOCOL_VERSION,
|
|
11690
|
-
id:
|
|
11690
|
+
id: randomUUID2(),
|
|
11691
11691
|
op: "probe-quota",
|
|
11692
11692
|
accounts: [...accounts],
|
|
11693
11693
|
...timeoutMs !== undefined ? { timeoutMs } : {},
|
|
@@ -11705,7 +11705,7 @@ class AuthBrokerClient {
|
|
|
11705
11705
|
async getExternalSpend(forceLive) {
|
|
11706
11706
|
const data = await this.send({
|
|
11707
11707
|
v: PROTOCOL_VERSION,
|
|
11708
|
-
id:
|
|
11708
|
+
id: randomUUID2(),
|
|
11709
11709
|
op: "get-external-spend",
|
|
11710
11710
|
...forceLive ? { forceLive: true } : {}
|
|
11711
11711
|
});
|
|
@@ -11714,21 +11714,21 @@ class AuthBrokerClient {
|
|
|
11714
11714
|
async setActive(account) {
|
|
11715
11715
|
const data = await this.send({
|
|
11716
11716
|
v: PROTOCOL_VERSION,
|
|
11717
|
-
id:
|
|
11717
|
+
id: randomUUID2(),
|
|
11718
11718
|
op: "set-active",
|
|
11719
11719
|
account
|
|
11720
11720
|
});
|
|
11721
11721
|
return data;
|
|
11722
11722
|
}
|
|
11723
11723
|
async markExhausted(until) {
|
|
11724
|
-
const req = until !== undefined ? { v: PROTOCOL_VERSION, id:
|
|
11724
|
+
const req = until !== undefined ? { v: PROTOCOL_VERSION, id: randomUUID2(), op: "mark-exhausted", until } : { v: PROTOCOL_VERSION, id: randomUUID2(), op: "mark-exhausted" };
|
|
11725
11725
|
const data = await this.send(req);
|
|
11726
11726
|
return data;
|
|
11727
11727
|
}
|
|
11728
11728
|
async markThrottled(until) {
|
|
11729
11729
|
const data = await this.send({
|
|
11730
11730
|
v: PROTOCOL_VERSION,
|
|
11731
|
-
id:
|
|
11731
|
+
id: randomUUID2(),
|
|
11732
11732
|
op: "mark-throttled",
|
|
11733
11733
|
until
|
|
11734
11734
|
});
|
|
@@ -11737,7 +11737,7 @@ class AuthBrokerClient {
|
|
|
11737
11737
|
async claimNotification(key, windowMs) {
|
|
11738
11738
|
const data = await this.send({
|
|
11739
11739
|
v: PROTOCOL_VERSION,
|
|
11740
|
-
id:
|
|
11740
|
+
id: randomUUID2(),
|
|
11741
11741
|
op: "claim-notification",
|
|
11742
11742
|
key,
|
|
11743
11743
|
windowMs
|
|
@@ -11747,7 +11747,7 @@ class AuthBrokerClient {
|
|
|
11747
11747
|
async refreshAccount(account) {
|
|
11748
11748
|
const data = await this.send({
|
|
11749
11749
|
v: PROTOCOL_VERSION,
|
|
11750
|
-
id:
|
|
11750
|
+
id: randomUUID2(),
|
|
11751
11751
|
op: "refresh-account",
|
|
11752
11752
|
account
|
|
11753
11753
|
});
|
|
@@ -11756,7 +11756,7 @@ class AuthBrokerClient {
|
|
|
11756
11756
|
async addAccount(label, credentials, replace, provider) {
|
|
11757
11757
|
const base = {
|
|
11758
11758
|
v: PROTOCOL_VERSION,
|
|
11759
|
-
id:
|
|
11759
|
+
id: randomUUID2(),
|
|
11760
11760
|
op: "add-account",
|
|
11761
11761
|
label,
|
|
11762
11762
|
credentials
|
|
@@ -11769,7 +11769,7 @@ class AuthBrokerClient {
|
|
|
11769
11769
|
async rmAccount(label, provider) {
|
|
11770
11770
|
const base = {
|
|
11771
11771
|
v: PROTOCOL_VERSION,
|
|
11772
|
-
id:
|
|
11772
|
+
id: randomUUID2(),
|
|
11773
11773
|
op: "rm-account",
|
|
11774
11774
|
label
|
|
11775
11775
|
};
|
|
@@ -11780,7 +11780,7 @@ class AuthBrokerClient {
|
|
|
11780
11780
|
async setOverride(agent, account) {
|
|
11781
11781
|
const data = await this.send({
|
|
11782
11782
|
v: PROTOCOL_VERSION,
|
|
11783
|
-
id:
|
|
11783
|
+
id: randomUUID2(),
|
|
11784
11784
|
op: "set-override",
|
|
11785
11785
|
agent,
|
|
11786
11786
|
account
|
|
@@ -21547,7 +21547,7 @@ var init_observation_scopes = __esm(() => {
|
|
|
21547
21547
|
});
|
|
21548
21548
|
|
|
21549
21549
|
// ../src/config/schema.ts
|
|
21550
|
-
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, ObservationScopeStrategySchema, AntiConfabulationDirectiveSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleServiceTokenSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
21550
|
+
var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, ObservationScopeStrategySchema, AntiConfabulationDirectiveSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, BuzzChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleServiceTokenSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
|
|
21551
21551
|
var init_schema = __esm(() => {
|
|
21552
21552
|
init_zod();
|
|
21553
21553
|
init_observation_scopes();
|
|
@@ -21673,6 +21673,8 @@ var init_schema = __esm(() => {
|
|
|
21673
21673
|
profile: exports_external.string().optional().describe("Memory profile bank this agent's curated memory defaults key off \u2014 " + "the built-in disposition + observations_mission in PROFILE_MEMORY_DEFAULTS. " + "Decouples the memory profile from `extends` (the filesystem persona " + "profile), so an agent on `extends: default` can opt into the `coding` " + "memory bundle via `memory.profile: coding` without inheriting the coding " + "persona. Resolution: memory.profile \u2192 extends \u2192 DEFAULT_PROFILE (see " + "resolveMemoryProfile). Unset \u21d2 byte-identical to keying off `extends`."),
|
|
21674
21674
|
bank_mission: exports_external.string().optional().describe("Bank-level mission statement used during recall to contextualize " + "results. NOTE: this is an alias for the Hindsight engine's " + "`reflect_mission` field (verified live: switchroom's bank_mission " + "lands in `config.reflect_mission`). Prefer `reflect_mission` going " + "forward; `bank_mission` is retained for back-compat. If both are " + "set, `reflect_mission` wins. Cascade: override."),
|
|
21675
21675
|
reflect_mission: exports_external.string().optional().describe("Mission/context steering Hindsight Reflect operations (the bank's " + "'who am I / what matters' framing applied during recall). The " + "engine-accurate name for what `bank_mission` sets. Cascade: override."),
|
|
21676
|
+
reflect_budget: exports_external.enum(["low", "mid", "high"]).optional().describe("Thinking/retrieval budget injected into reflect MCP calls when the " + "caller omits budget. Unset \u21d2 shim default (mid). Explicit per-call " + "budget always wins. Higher = better recall on fuzzy queries, more " + "backend latency/compute. stdio-shim transport only (ignored under " + "memory.config.mcp_transport: http). Cascade: override (per-agent " + "wins over default)."),
|
|
21677
|
+
reflect_max_tokens: exports_external.number().int().positive().max(8192).optional().describe("Token cap injected into reflect MCP calls when caller omits " + "max_tokens. Unset \u21d2 shim default 1024. Values much above ~2048 risk " + "exceeding Claude Code's MCP output cap, silently dropping the " + "payload \u2014 raise deliberately. Explicit per-call values always win. " + "stdio-shim transport only. Cascade: override."),
|
|
21676
21678
|
retain_mission: exports_external.string().optional().describe("Instructions for the fact extraction LLM during retain. Cascade: override."),
|
|
21677
21679
|
mental_models: exports_external.array(exports_external.object({
|
|
21678
21680
|
name: exports_external.string().min(1).describe("Stable model name (identity key for idempotent ensure). Two " + "declarations with the same name in one agent are rejected."),
|
|
@@ -21792,6 +21794,7 @@ var init_schema = __esm(() => {
|
|
|
21792
21794
|
max_turns_in_briefing: exports_external.number().int().positive().optional().describe("Cap on recent user/assistant turn pairs fed to the summarizer."),
|
|
21793
21795
|
resume_mode: exports_external.enum(["auto", "continue", "handoff", "none"]).optional().describe("How to resume the next session. 'handoff' (default as of #362) " + "never passes --continue; a fresh Claude starts each restart and " + "reads a briefing assembled from recent Telegram messages, Hindsight " + "recall, and today's daily memory file. 'auto' uses --continue when " + "the latest JSONL is smaller than resume_max_bytes, else falls back " + "to the handoff briefing. 'continue' always passes --continue. " + "'none' starts completely fresh every time."),
|
|
21794
21796
|
resume_max_bytes: exports_external.number().int().positive().optional().describe("Byte threshold above which 'auto' mode falls back to handoff " + "instead of --continue. Default 2_000_000 (~2MB). Large transcripts " + "can blow out the context window even with prefix caching, and " + "--continue replay is known-fragile at scale."),
|
|
21797
|
+
briefing: exports_external.enum(["gateway", "legacy"]).optional().describe("Which mechanism assembles the fresh-session reorientation briefing " + "(default 'legacy'). 'legacy' keeps today's behaviour: the Stop-hook " + ".handoff.md and/or bin/handoff-briefing.sh, injected via " + "--append-system-prompt. 'gateway' moves it to a gateway boot-time " + "builder sourced from the durable history.db (crash-independent, " + "surface-scoped, token-budgeted) and injects it as a synthetic " + '<channel source="boot_briefing"> inbound over the durable spool \u2014 ' + "keeping the system-prompt prefix stable for cross-session prompt " + "caching. Suppressed automatically when resume_mode is " + "'continue'/'auto' (the transcript may be replayed) and on a /reset " + "force-fresh boot. Threaded to the gateway as " + "SWITCHROOM_SESSION_BRIEFING."),
|
|
21795
21798
|
boot_resume: exports_external.enum(["always", "in-flight", "never"]).optional().describe("How the gateway auto-resumes a turn that was IN FLIGHT when the " + "agent restarted. 'in-flight' (default) resumes genuinely " + "interrupted work even after a deliberate/operator restart \u2014 a " + "sanctioned restart landing mid-turn no longer silently drops the " + "work. 'always' forces resume unconditionally (same as the " + "SWITCHROOM_BOOT_RESUME_ALWAYS=1 escape hatch). 'never' is the " + "quota-saving posture: don't auto-replay work across a clean " + "restart \u2014 but the user is STILL sent a passive notice of what was " + "in flight (silence is never used). Independent of the at-most-once " + "resume ledger and the bounded resume-chain loop-guard, which always " + "apply. Threaded to the gateway as SWITCHROOM_BOOT_RESUME."),
|
|
21796
21799
|
session_retention_max_count: exports_external.number().int().nonnegative().optional().describe("Session-JSONL retention (issue #2792): keep at most this many " + "newest session transcripts under .claude/projects; older ones " + "past both this count and the age bound are pruned by the Stop " + "hook. The newest sessions (and the handoff source) are always " + "kept. Default 20; set 0 to disable the count bound."),
|
|
21797
21800
|
session_retention_max_age_days: exports_external.number().int().nonnegative().optional().describe("Session-JSONL retention (issue #2792): prune session transcripts " + "older than this many days (a file is deleted only when it is BOTH " + "over the count bound and older than this). Default 30; set 0 to " + "disable the age bound.")
|
|
@@ -21924,8 +21927,24 @@ var init_schema = __esm(() => {
|
|
|
21924
21927
|
}
|
|
21925
21928
|
return tg;
|
|
21926
21929
|
});
|
|
21930
|
+
BuzzChannelSchema = exports_external.object({
|
|
21931
|
+
enabled: exports_external.boolean().default(false).describe("Master switch for the per-agent Buzz sidecar. Default false \u2014 the " + "channel ships dark; start.sh forks the sidecar only when true."),
|
|
21932
|
+
relay_url: exports_external.string().regex(/^wss?:\/\//, "relay_url must be a ws:// or wss:// URL").describe("CANONICAL WebSocket URL of the closed Buzz relay \u2014 the exact string " + "the relay expects in the NIP-42 `relay` auth tag (e.g. " + "'ws://127.0.0.1:3000'). A live probe proved the relay validates this " + "tag as an exact string match against its own URL BEFORE the " + "membership check, so it is the relay's advertised identity, NOT " + "necessarily the address the sidecar dials. Set relay_dial_url when " + "the reachable address differs (a docker-network IP)."),
|
|
21933
|
+
relay_dial_url: exports_external.string().regex(/^wss?:\/\//, "relay_dial_url must be a ws:// or wss:// URL").optional().describe("Reachable ws:// / wss:// address the sidecar DIALS when it differs " + "from the canonical relay_url (e.g. a docker-network IP the relay's " + "own 127.0.0.1 can't stand in for). The NIP-42 auth tag still uses " + "relay_url. Defaults to relay_url when unset."),
|
|
21934
|
+
relay_host: exports_external.string().regex(/^(\[[0-9a-fA-F:]+\]|[^\s/?#:@]+)(:\d+)?$/, "relay_host must be a bare host[:port] authority \u2014 no scheme, path, or userinfo (e.g. '127.0.0.1:3000')").describe("REQUIRED HTTP Host header authority sent verbatim on the WS upgrade " + "(e.g. '127.0.0.1:3000', port included). The relay resolves its " + "community from this header before the upgrade and returns HTTP 404 if " + "it is missing/wrong, so it must match the relay's configured " + "authority and is deployment config, never derived from the dial URL."),
|
|
21935
|
+
nsec_vault_key: exports_external.string().default("buzz/{agent}-nsec").describe("Vault KEY NAME for the agent's Nostr secret key. Broker-fetched " + "in-process at sidecar boot; NEVER resolved into env or logged. " + "'{agent}' is substituted with the agent name."),
|
|
21936
|
+
operator_pubkey: exports_external.string().regex(/^(npub1[02-9ac-hj-np-z]{58}|[0-9a-f]{64})$/, "operator_pubkey must be a bech32 npub or 64-char hex pubkey").describe("The operator's Nostr pubkey (npub or hex). Always in the effective " + "inbound allowlist \u2014 the fail-closed default is operator-only."),
|
|
21937
|
+
authorized_pubkeys: exports_external.array(exports_external.string()).default([]).describe("Additional pubkeys (npub or hex) whose signed events may become " + "turns. Effective allowlist = this \u222a {operator_pubkey}. Empty by " + "default (operator-only)."),
|
|
21938
|
+
mirror: exports_external.enum(["both", "origin", "off"]).default("both").describe("Cross-surface mirror mode. 'both' answers on the origin channel AND " + "mirrors a copy to the other; 'off' is a true kill-switch that disables " + "the channel in BOTH directions (the inbound sidecar exits idle). " + "Phase 2b (S2): 'origin' is DEFERRED \u2014 the hub's mirror hook lives only " + "in sendReply, so 'origin' cannot be honored soundly; a configured " + "'origin' is degraded to 'off' (dark) at runtime by both the sidecar " + "config loader and the hub (channel-route.ts parseConfiguredMirrorMode). " + "Only 'both' and 'off' ship live in 2b."),
|
|
21939
|
+
chat_id: exports_external.string().min(1, "chat_id must be a non-empty Telegram chat id").describe("Telegram chat id an injected Buzz turn is routed to. Phase 1 is " + "inbound-only, so the agent's reply lands here on Telegram (the " + "authoritative surface); in later phases this is the chat the Buzz " + "turn's Telegram copy maps to. Required \u2014 the sidecar refuses to run " + "live without it (BUZZ_CHAT_ID)."),
|
|
21940
|
+
default_channel_id: exports_external.string().describe("Relay-minted group UUID (the NIP-29 `h` tag) the sidecar subscribes " + "to and stamps on injected turns."),
|
|
21941
|
+
channel_map: exports_external.record(exports_external.string(), exports_external.string()).default({}).describe("Optional map of extra group UUIDs \u2192 friendly labels."),
|
|
21942
|
+
pubkey_names: exports_external.record(exports_external.string(), exports_external.string()).default({}).describe("Optional petnames: hex/npub pubkey \u2192 display name, used to label " + "the sender on injected turns."),
|
|
21943
|
+
pinned_relay_digest: exports_external.string().optional().describe("Pinned relay image digest (M4). The compat-check warns on mismatch; " + "advisory in Phase 1.")
|
|
21944
|
+
}).strict();
|
|
21927
21945
|
ChannelsSchema = exports_external.object({
|
|
21928
|
-
telegram: TelegramChannelSchema
|
|
21946
|
+
telegram: TelegramChannelSchema,
|
|
21947
|
+
buzz: BuzzChannelSchema.optional()
|
|
21929
21948
|
}).optional();
|
|
21930
21949
|
TIMEZONE_REGEX = /^UTC$|^[A-Z][A-Za-z0-9_+-]+(\/[A-Z][A-Za-z0-9_+-]+){1,2}$/;
|
|
21931
21950
|
ApproverIdSchema = exports_external.union([exports_external.number(), exports_external.string().regex(/^\d+$/)]);
|
|
@@ -22164,6 +22183,8 @@ var init_schema = __esm(() => {
|
|
|
22164
22183
|
}).optional(),
|
|
22165
22184
|
bank_mission: exports_external.string().optional(),
|
|
22166
22185
|
reflect_mission: exports_external.string().optional(),
|
|
22186
|
+
reflect_budget: exports_external.enum(["low", "mid", "high"]).optional(),
|
|
22187
|
+
reflect_max_tokens: exports_external.number().int().positive().max(8192).optional(),
|
|
22167
22188
|
retain_mission: exports_external.string().optional(),
|
|
22168
22189
|
observations_mission: exports_external.string().optional(),
|
|
22169
22190
|
disposition: exports_external.object({
|
|
@@ -29754,6 +29775,7 @@ __export(exports_history, {
|
|
|
29754
29775
|
hasOutboundDeliveredSince: () => hasOutboundDeliveredSince2,
|
|
29755
29776
|
getRecentOutboundCount: () => getRecentOutboundCount,
|
|
29756
29777
|
getLatestInboundMessageId: () => getLatestInboundMessageId2,
|
|
29778
|
+
getHistoryDbForBriefing: () => getHistoryDbForBriefing,
|
|
29757
29779
|
deliveryTextMatch: () => deliveryTextMatch2,
|
|
29758
29780
|
deleteFromHistory: () => deleteFromHistory2,
|
|
29759
29781
|
checkpointWal: () => checkpointWal2,
|
|
@@ -29897,6 +29919,9 @@ function verifyHistoryWritable2() {
|
|
|
29897
29919
|
} catch {}
|
|
29898
29920
|
}
|
|
29899
29921
|
}
|
|
29922
|
+
function getHistoryDbForBriefing() {
|
|
29923
|
+
return db2;
|
|
29924
|
+
}
|
|
29900
29925
|
function _resetForTests() {
|
|
29901
29926
|
if (db2 != null) {
|
|
29902
29927
|
db2.close();
|
|
@@ -38217,7 +38242,7 @@ __export(exports_tmux2, {
|
|
|
38217
38242
|
captureAgentPane: () => captureAgentPane2
|
|
38218
38243
|
});
|
|
38219
38244
|
import { execFileSync as execFileSync8 } from "node:child_process";
|
|
38220
|
-
import { chmodSync as chmodSync13, mkdirSync as mkdirSync50, readdirSync as readdirSync15, statSync as statSync22, unlinkSync as unlinkSync30, writeFileSync as
|
|
38245
|
+
import { chmodSync as chmodSync13, mkdirSync as mkdirSync50, readdirSync as readdirSync15, statSync as statSync22, unlinkSync as unlinkSync30, writeFileSync as writeFileSync51 } from "node:fs";
|
|
38221
38246
|
import { resolve as resolve12 } from "node:path";
|
|
38222
38247
|
function captureAgentPane2(opts) {
|
|
38223
38248
|
const { agentName: agentName3, agentDir, reason } = opts;
|
|
@@ -38268,7 +38293,7 @@ function captureAgentPane2(opts) {
|
|
|
38268
38293
|
` + `
|
|
38269
38294
|
`;
|
|
38270
38295
|
try {
|
|
38271
|
-
|
|
38296
|
+
writeFileSync51(outPath, Buffer.concat([Buffer.from(header, "utf8"), body]), {
|
|
38272
38297
|
mode: 384
|
|
38273
38298
|
});
|
|
38274
38299
|
} catch (err) {
|
|
@@ -39152,7 +39177,7 @@ __export(exports_materialize_bot_token, {
|
|
|
39152
39177
|
materializeBotToken: () => materializeBotToken,
|
|
39153
39178
|
BotTokenMaterializeError: () => BotTokenMaterializeError
|
|
39154
39179
|
});
|
|
39155
|
-
import { existsSync as
|
|
39180
|
+
import { existsSync as existsSync59 } from "node:fs";
|
|
39156
39181
|
function pickConfiguredToken(config, agentName3) {
|
|
39157
39182
|
if (agentName3) {
|
|
39158
39183
|
const agent = config.agents?.[agentName3];
|
|
@@ -39166,7 +39191,7 @@ function tryDirectVaultRead4(ref, config, passphrase) {
|
|
|
39166
39191
|
if (!passphrase)
|
|
39167
39192
|
return null;
|
|
39168
39193
|
const vaultPath = resolvePath(config.vault?.path ?? "~/.switchroom/vault.enc");
|
|
39169
|
-
if (!
|
|
39194
|
+
if (!existsSync59(vaultPath))
|
|
39170
39195
|
return null;
|
|
39171
39196
|
try {
|
|
39172
39197
|
const secrets = openVault(passphrase, vaultPath);
|
|
@@ -39321,18 +39346,18 @@ var import_runner3 = __toESM(require_mod3(), 1);
|
|
|
39321
39346
|
import { randomBytes as randomBytes13, createHash as createHash8 } from "crypto";
|
|
39322
39347
|
import { execFileSync as execFileSync9, execSync as execSync2, spawn as spawn2 } from "child_process";
|
|
39323
39348
|
import {
|
|
39324
|
-
readFileSync as
|
|
39325
|
-
writeFileSync as
|
|
39349
|
+
readFileSync as readFileSync62,
|
|
39350
|
+
writeFileSync as writeFileSync52,
|
|
39326
39351
|
mkdirSync as mkdirSync51,
|
|
39327
39352
|
readdirSync as readdirSync16,
|
|
39328
|
-
rmSync as
|
|
39353
|
+
rmSync as rmSync9,
|
|
39329
39354
|
statSync as statSync23,
|
|
39330
|
-
renameSync as
|
|
39355
|
+
renameSync as renameSync28,
|
|
39331
39356
|
realpathSync as realpathSync5,
|
|
39332
39357
|
chmodSync as chmodSync14,
|
|
39333
39358
|
openSync as openSync15,
|
|
39334
39359
|
closeSync as closeSync15,
|
|
39335
|
-
existsSync as
|
|
39360
|
+
existsSync as existsSync60,
|
|
39336
39361
|
unlinkSync as unlinkSync31,
|
|
39337
39362
|
appendFileSync as appendFileSync9
|
|
39338
39363
|
} from "fs";
|
|
@@ -39388,7 +39413,7 @@ function fsyncPathSync(path) {
|
|
|
39388
39413
|
|
|
39389
39414
|
// gateway/gateway.ts
|
|
39390
39415
|
import { homedir as homedir20 } from "os";
|
|
39391
|
-
import { join as
|
|
39416
|
+
import { join as join70, sep as sep4, basename as basename17 } from "path";
|
|
39392
39417
|
|
|
39393
39418
|
// plugin-logger.ts
|
|
39394
39419
|
import { appendFileSync, mkdirSync, renameSync as renameSync2, statSync, existsSync } from "fs";
|
|
@@ -42275,6 +42300,225 @@ function resolveReplyOwnerTurnWith(lookups, liveTurn, chatId, args) {
|
|
|
42275
42300
|
return { turn: winnerId != null ? byId.get(winnerId) ?? null : null, tier, candidates };
|
|
42276
42301
|
}
|
|
42277
42302
|
|
|
42303
|
+
// gateway/buzz-mirror.ts
|
|
42304
|
+
import { randomUUID } from "crypto";
|
|
42305
|
+
|
|
42306
|
+
// gateway/channel-route.ts
|
|
42307
|
+
var TELEGRAM_ONLY = Object.freeze({ originChannel: "telegram" });
|
|
42308
|
+
var OUTER_LAST_SOURCE = /<channel[^>]*\bsource="([^"]+)"/;
|
|
42309
|
+
var OUTER_OPEN_TAG = /<channel[^>]*>/;
|
|
42310
|
+
function unescapeXmlAttr(s) {
|
|
42311
|
+
return s.replace(/"/g, '"').replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
42312
|
+
}
|
|
42313
|
+
function readAttr(openTag, name) {
|
|
42314
|
+
const m = openTag.match(new RegExp(`\\b${name}="([^"]*)"`));
|
|
42315
|
+
if (m == null)
|
|
42316
|
+
return null;
|
|
42317
|
+
const value = unescapeXmlAttr(m[1]);
|
|
42318
|
+
return value.length > 0 ? value : null;
|
|
42319
|
+
}
|
|
42320
|
+
function parseChannelOrigin(rawContent) {
|
|
42321
|
+
if (typeof rawContent !== "string")
|
|
42322
|
+
return TELEGRAM_ONLY;
|
|
42323
|
+
const sourceMatch = rawContent.match(OUTER_LAST_SOURCE);
|
|
42324
|
+
if (sourceMatch == null || sourceMatch[1] !== "buzz")
|
|
42325
|
+
return TELEGRAM_ONLY;
|
|
42326
|
+
const openMatch = rawContent.match(OUTER_OPEN_TAG);
|
|
42327
|
+
if (openMatch == null)
|
|
42328
|
+
return TELEGRAM_ONLY;
|
|
42329
|
+
const openTag = openMatch[0];
|
|
42330
|
+
const channelId = readAttr(openTag, "buzz_channel_id");
|
|
42331
|
+
const eventId = readAttr(openTag, "buzz_event_id");
|
|
42332
|
+
const threadRoot = readAttr(openTag, "buzz_thread_root");
|
|
42333
|
+
if (channelId == null || eventId == null || threadRoot == null) {
|
|
42334
|
+
process.stderr.write("telegram gateway: buzz-origin turn missing coordinate " + `(channel_id=${channelId != null} event_id=${eventId != null} ` + `thread_root=${threadRoot != null}) \u2014 routing as telegram
|
|
42335
|
+
`);
|
|
42336
|
+
return TELEGRAM_ONLY;
|
|
42337
|
+
}
|
|
42338
|
+
return { originChannel: "buzz", buzzCoords: { channelId, eventId, threadRoot } };
|
|
42339
|
+
}
|
|
42340
|
+
function resolveRoute(originChannel, mode, buzzEnabled) {
|
|
42341
|
+
const buzzLive = buzzEnabled && mode !== "off";
|
|
42342
|
+
const primary = originChannel === "buzz" && buzzLive ? "buzz" : "telegram";
|
|
42343
|
+
let mirrors = [];
|
|
42344
|
+
if (buzzLive && mode === "both") {
|
|
42345
|
+
mirrors = originChannel === "telegram" ? ["buzz"] : ["telegram"];
|
|
42346
|
+
}
|
|
42347
|
+
return { primary, mirrors };
|
|
42348
|
+
}
|
|
42349
|
+
function parseConfiguredMirrorMode(raw) {
|
|
42350
|
+
if (raw === undefined)
|
|
42351
|
+
return "both";
|
|
42352
|
+
if (raw === "both")
|
|
42353
|
+
return "both";
|
|
42354
|
+
return "off";
|
|
42355
|
+
}
|
|
42356
|
+
function isBuzzThreadedPublishSafe(input) {
|
|
42357
|
+
return input.ownerEchoed || !input.hasRecentDifferentOriginTurn;
|
|
42358
|
+
}
|
|
42359
|
+
function isBuzzTurnRoutingEnabled(env = process.env) {
|
|
42360
|
+
return env.SWITCHROOM_BUZZ_TURN_ROUTING !== "0";
|
|
42361
|
+
}
|
|
42362
|
+
|
|
42363
|
+
// gateway/buzz-mirror.ts
|
|
42364
|
+
var CORRECTION_DEBOUNCE_MS = 30000;
|
|
42365
|
+
var MAX_TRACKED = 4096;
|
|
42366
|
+
|
|
42367
|
+
class BuzzMirror {
|
|
42368
|
+
cfg;
|
|
42369
|
+
log;
|
|
42370
|
+
sender = null;
|
|
42371
|
+
pending = new Map;
|
|
42372
|
+
pendingOrder = [];
|
|
42373
|
+
msgToBuzz = new Map;
|
|
42374
|
+
msgOrder = [];
|
|
42375
|
+
correctionTimers = new Map;
|
|
42376
|
+
constructor(cfg) {
|
|
42377
|
+
this.cfg = cfg;
|
|
42378
|
+
this.log = cfg.log ?? (() => {});
|
|
42379
|
+
}
|
|
42380
|
+
attachSender(sender) {
|
|
42381
|
+
this.sender = sender;
|
|
42382
|
+
}
|
|
42383
|
+
evict(map, order) {
|
|
42384
|
+
while (order.length > MAX_TRACKED) {
|
|
42385
|
+
const k = order.shift();
|
|
42386
|
+
if (k !== undefined)
|
|
42387
|
+
map.delete(k);
|
|
42388
|
+
}
|
|
42389
|
+
}
|
|
42390
|
+
mirrorReplyDelivered(input) {
|
|
42391
|
+
try {
|
|
42392
|
+
const route = resolveRoute(input.ownerOriginChannel, this.cfg.mode, true);
|
|
42393
|
+
const buzzInRoute = route.primary === "buzz" || route.mirrors.includes("buzz");
|
|
42394
|
+
if (!buzzInRoute)
|
|
42395
|
+
return;
|
|
42396
|
+
let channelId;
|
|
42397
|
+
let replyToEventId;
|
|
42398
|
+
let threadRootId;
|
|
42399
|
+
if (input.ownerOriginChannel === "buzz" && input.ownerBuzzCoords) {
|
|
42400
|
+
if (!isBuzzThreadedPublishSafe({
|
|
42401
|
+
ownerEchoed: input.ownerEchoed,
|
|
42402
|
+
hasRecentDifferentOriginTurn: input.hasRecentDifferentOriginTurn
|
|
42403
|
+
})) {
|
|
42404
|
+
this.log("buzz-mirror: S1 guard blocked a threaded publish on an ambiguous " + "owner binding (un-echoed reply + a recent different-origin turn) " + "\u2014 delivered Telegram-only");
|
|
42405
|
+
return;
|
|
42406
|
+
}
|
|
42407
|
+
channelId = input.ownerBuzzCoords.channelId;
|
|
42408
|
+
replyToEventId = input.ownerBuzzCoords.eventId;
|
|
42409
|
+
threadRootId = input.ownerBuzzCoords.threadRoot;
|
|
42410
|
+
} else {
|
|
42411
|
+
if (!this.cfg.defaultChannelId)
|
|
42412
|
+
return;
|
|
42413
|
+
channelId = this.cfg.defaultChannelId;
|
|
42414
|
+
}
|
|
42415
|
+
this.publish({
|
|
42416
|
+
channelId,
|
|
42417
|
+
replyToEventId,
|
|
42418
|
+
threadRootId,
|
|
42419
|
+
payload: { kind: "message", text: input.scrubbedText }
|
|
42420
|
+
}, input.telegramMessageKeys);
|
|
42421
|
+
} catch (err) {
|
|
42422
|
+
this.log(`buzz-mirror: mirrorReplyDelivered threw (ignored): ${String(err)}`);
|
|
42423
|
+
}
|
|
42424
|
+
}
|
|
42425
|
+
mirrorCorrection(input) {
|
|
42426
|
+
try {
|
|
42427
|
+
const target = this.msgToBuzz.get(input.telegramMessageKey);
|
|
42428
|
+
if (!target)
|
|
42429
|
+
return;
|
|
42430
|
+
const existing = this.correctionTimers.get(input.telegramMessageKey);
|
|
42431
|
+
if (existing)
|
|
42432
|
+
clearTimeout(existing);
|
|
42433
|
+
const timer = setTimeout(() => {
|
|
42434
|
+
this.correctionTimers.delete(input.telegramMessageKey);
|
|
42435
|
+
const t = this.msgToBuzz.get(input.telegramMessageKey);
|
|
42436
|
+
if (!t)
|
|
42437
|
+
return;
|
|
42438
|
+
this.publish({
|
|
42439
|
+
channelId: t.channelId,
|
|
42440
|
+
replyToEventId: t.eventId,
|
|
42441
|
+
threadRootId: t.eventId,
|
|
42442
|
+
payload: {
|
|
42443
|
+
kind: "correction",
|
|
42444
|
+
text: input.scrubbedText,
|
|
42445
|
+
targetEventId: t.eventId
|
|
42446
|
+
}
|
|
42447
|
+
}, []);
|
|
42448
|
+
}, CORRECTION_DEBOUNCE_MS);
|
|
42449
|
+
if (typeof timer.unref === "function") {
|
|
42450
|
+
timer.unref();
|
|
42451
|
+
}
|
|
42452
|
+
this.correctionTimers.set(input.telegramMessageKey, timer);
|
|
42453
|
+
} catch (err) {
|
|
42454
|
+
this.log(`buzz-mirror: mirrorCorrection threw (ignored): ${String(err)}`);
|
|
42455
|
+
}
|
|
42456
|
+
}
|
|
42457
|
+
onPublishResult(msg) {
|
|
42458
|
+
const p = this.pending.get(msg.correlationId);
|
|
42459
|
+
this.pending.delete(msg.correlationId);
|
|
42460
|
+
if (!p)
|
|
42461
|
+
return;
|
|
42462
|
+
if (!msg.ok || !msg.eventId) {
|
|
42463
|
+
this.log(`buzz-mirror: publish failed (correlationId=${msg.correlationId.slice(0, 8)}` + `${msg.error ? ` error=${msg.error}` : ""}) \u2014 Telegram copy already delivered`);
|
|
42464
|
+
return;
|
|
42465
|
+
}
|
|
42466
|
+
for (const key of p.telegramMessageKeys) {
|
|
42467
|
+
this.msgToBuzz.set(key, { eventId: msg.eventId, channelId: p.channelId });
|
|
42468
|
+
this.msgOrder.push(key);
|
|
42469
|
+
}
|
|
42470
|
+
this.evict(this.msgToBuzz, this.msgOrder);
|
|
42471
|
+
}
|
|
42472
|
+
publish(fields, telegramMessageKeys) {
|
|
42473
|
+
if (!this.sender) {
|
|
42474
|
+
this.log("buzz-mirror: no Buzz peer connected \u2014 mirror dropped (Telegram copy delivered)");
|
|
42475
|
+
return;
|
|
42476
|
+
}
|
|
42477
|
+
const correlationId = randomUUID();
|
|
42478
|
+
const msg = {
|
|
42479
|
+
type: "outbound_to_buzz",
|
|
42480
|
+
correlationId,
|
|
42481
|
+
agentName: this.cfg.agentName,
|
|
42482
|
+
...fields
|
|
42483
|
+
};
|
|
42484
|
+
const sent = this.sender(msg);
|
|
42485
|
+
if (!sent) {
|
|
42486
|
+
this.log("buzz-mirror: Buzz peer send returned false \u2014 mirror dropped (Telegram copy delivered)");
|
|
42487
|
+
return;
|
|
42488
|
+
}
|
|
42489
|
+
this.pending.set(correlationId, {
|
|
42490
|
+
channelId: fields.channelId,
|
|
42491
|
+
telegramMessageKeys
|
|
42492
|
+
});
|
|
42493
|
+
this.pendingOrder.push(correlationId);
|
|
42494
|
+
this.evict(this.pending, this.pendingOrder);
|
|
42495
|
+
}
|
|
42496
|
+
}
|
|
42497
|
+
var singleton = null;
|
|
42498
|
+
function initBuzzMirror(cfg) {
|
|
42499
|
+
singleton = new BuzzMirror(cfg);
|
|
42500
|
+
return singleton;
|
|
42501
|
+
}
|
|
42502
|
+
function getBuzzMirror() {
|
|
42503
|
+
return singleton;
|
|
42504
|
+
}
|
|
42505
|
+
function maybeBootBuzzMirror(sender, env = process.env) {
|
|
42506
|
+
if (env.BUZZ_ENABLED !== "1" && env.BUZZ_ENABLED !== "true")
|
|
42507
|
+
return null;
|
|
42508
|
+
const mode = parseConfiguredMirrorMode(env.BUZZ_MIRROR);
|
|
42509
|
+
if (mode !== "both")
|
|
42510
|
+
return null;
|
|
42511
|
+
const bm = initBuzzMirror({
|
|
42512
|
+
mode,
|
|
42513
|
+
agentName: env.SWITCHROOM_AGENT_NAME?.trim() ?? "",
|
|
42514
|
+
defaultChannelId: env.BUZZ_CHANNEL_IDS?.trim() ?? "",
|
|
42515
|
+
log: (m) => process.stderr.write(`telegram gateway: buzz-mirror \u2014 ${m}
|
|
42516
|
+
`)
|
|
42517
|
+
});
|
|
42518
|
+
bm.attachSender(sender);
|
|
42519
|
+
return bm;
|
|
42520
|
+
}
|
|
42521
|
+
|
|
42278
42522
|
// gateway/subagent-reply-authority.ts
|
|
42279
42523
|
var SUB_AGENT_KIND_PREFIX = "sub_agent_";
|
|
42280
42524
|
|
|
@@ -70706,7 +70950,7 @@ class PostHog extends PostHogBackendClient {
|
|
|
70706
70950
|
// analytics-posthog.ts
|
|
70707
70951
|
import { existsSync as existsSync13, mkdirSync as mkdirSync17, readFileSync as readFileSync16, writeFileSync as writeFileSync14 } from "node:fs";
|
|
70708
70952
|
import { dirname as dirname11, join as join21 } from "node:path";
|
|
70709
|
-
import { randomUUID as
|
|
70953
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
70710
70954
|
var DEFAULT_KEY = "phc_qKY87cKWZm6ZyCtk7LcRd2cU8Sg42u7Ywhui5stYCegd";
|
|
70711
70955
|
var DEFAULT_HOST = "https://us.i.posthog.com";
|
|
70712
70956
|
var client = null;
|
|
@@ -70741,7 +70985,7 @@ function getDistinctId() {
|
|
|
70741
70985
|
}
|
|
70742
70986
|
}
|
|
70743
70987
|
} catch {}
|
|
70744
|
-
const id =
|
|
70988
|
+
const id = randomUUID3();
|
|
70745
70989
|
cachedDistinctId = id;
|
|
70746
70990
|
try {
|
|
70747
70991
|
mkdirSync17(dirname11(fallbackPath), { recursive: true });
|
|
@@ -70822,7 +71066,7 @@ import { dirname as dirname13, join as join23 } from "node:path";
|
|
|
70822
71066
|
// analytics-posthog.ts
|
|
70823
71067
|
import { existsSync as existsSync14, mkdirSync as mkdirSync18, readFileSync as readFileSync17, writeFileSync as writeFileSync15 } from "node:fs";
|
|
70824
71068
|
import { dirname as dirname12, join as join22 } from "node:path";
|
|
70825
|
-
import { randomUUID as
|
|
71069
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
70826
71070
|
var DEFAULT_KEY2 = "phc_qKY87cKWZm6ZyCtk7LcRd2cU8Sg42u7Ywhui5stYCegd";
|
|
70827
71071
|
var DEFAULT_HOST2 = "https://us.i.posthog.com";
|
|
70828
71072
|
var client2 = null;
|
|
@@ -70856,7 +71100,7 @@ function getDistinctId2() {
|
|
|
70856
71100
|
}
|
|
70857
71101
|
}
|
|
70858
71102
|
} catch {}
|
|
70859
|
-
const id =
|
|
71103
|
+
const id = randomUUID4();
|
|
70860
71104
|
cachedDistinctId2 = id;
|
|
70861
71105
|
try {
|
|
70862
71106
|
mkdirSync18(dirname12(fallbackPath), { recursive: true });
|
|
@@ -71909,6 +72153,8 @@ function detectModelUnavailable(stderr) {
|
|
|
71909
72153
|
"socket hang up",
|
|
71910
72154
|
"request timed out",
|
|
71911
72155
|
"connection refused",
|
|
72156
|
+
"connection closed",
|
|
72157
|
+
"mid-response",
|
|
71912
72158
|
"getaddrinfo"
|
|
71913
72159
|
];
|
|
71914
72160
|
if (networkSignals.some((s) => lower.includes(s))) {
|
|
@@ -72139,6 +72385,7 @@ var OPERATOR_EVENT_KINDS = [
|
|
|
72139
72385
|
"mcp-dependency-blocked",
|
|
72140
72386
|
"quota-exhausted",
|
|
72141
72387
|
"rate-limited",
|
|
72388
|
+
"transport-transient",
|
|
72142
72389
|
"agent-crashed",
|
|
72143
72390
|
"agent-restarted-unexpectedly",
|
|
72144
72391
|
"unknown-4xx",
|
|
@@ -72151,12 +72398,12 @@ function classifyClaudeError(raw) {
|
|
|
72151
72398
|
try {
|
|
72152
72399
|
return classifyInner(raw);
|
|
72153
72400
|
} catch {
|
|
72154
|
-
return "unknown-
|
|
72401
|
+
return "unknown-5xx";
|
|
72155
72402
|
}
|
|
72156
72403
|
}
|
|
72157
72404
|
function classifyInner(raw) {
|
|
72158
72405
|
if (raw == null)
|
|
72159
|
-
return "unknown-
|
|
72406
|
+
return "unknown-5xx";
|
|
72160
72407
|
const obj = typeof raw === "object" ? raw : {};
|
|
72161
72408
|
const errorType = extractString(obj, "error_type") ?? extractString(obj, "type") ?? extractString(getNestedObj(obj, "error"), "type") ?? "";
|
|
72162
72409
|
const errorCode = extractString(obj, "code") ?? extractString(getNestedObj(obj, "error"), "code") ?? "";
|
|
@@ -72201,13 +72448,16 @@ ${message}`;
|
|
|
72201
72448
|
if (errorType === "agent-restarted-unexpectedly" || errorCode === "agent-restarted-unexpectedly") {
|
|
72202
72449
|
return "agent-restarted-unexpectedly";
|
|
72203
72450
|
}
|
|
72451
|
+
if ((status == null || status >= 500) && (errorType === "server_error" || errorCode === "server_error" || sdkCode === "server_error" || errorType === "api_error" || errorCode === "api_error" || sdkCode === "api_error")) {
|
|
72452
|
+
return "transport-transient";
|
|
72453
|
+
}
|
|
72204
72454
|
if (status != null) {
|
|
72205
72455
|
if (status >= 400 && status < 500)
|
|
72206
72456
|
return "unknown-4xx";
|
|
72207
72457
|
if (status >= 500 && status < 600)
|
|
72208
72458
|
return "unknown-5xx";
|
|
72209
72459
|
}
|
|
72210
|
-
return "unknown-
|
|
72460
|
+
return "unknown-5xx";
|
|
72211
72461
|
}
|
|
72212
72462
|
function extractString(obj, key) {
|
|
72213
72463
|
const v = obj[key];
|
|
@@ -73593,7 +73843,7 @@ function defaultAddAccount(label, credentials, opts) {
|
|
|
73593
73843
|
init_protocol();
|
|
73594
73844
|
import * as net3 from "node:net";
|
|
73595
73845
|
import { homedir as homedir7 } from "node:os";
|
|
73596
|
-
import { randomUUID as
|
|
73846
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
73597
73847
|
import { join as join28 } from "node:path";
|
|
73598
73848
|
var DEFAULT_TIMEOUT_MS3 = 5000;
|
|
73599
73849
|
function reviveDate2(v) {
|
|
@@ -73667,7 +73917,7 @@ class AuthBrokerClient2 {
|
|
|
73667
73917
|
async getCredentials(provider, account) {
|
|
73668
73918
|
const base = {
|
|
73669
73919
|
v: PROTOCOL_VERSION,
|
|
73670
|
-
id:
|
|
73920
|
+
id: randomUUID5(),
|
|
73671
73921
|
op: "get-credentials"
|
|
73672
73922
|
};
|
|
73673
73923
|
let req = base;
|
|
@@ -73681,7 +73931,7 @@ class AuthBrokerClient2 {
|
|
|
73681
73931
|
async listState() {
|
|
73682
73932
|
const data = await this.send({
|
|
73683
73933
|
v: PROTOCOL_VERSION,
|
|
73684
|
-
id:
|
|
73934
|
+
id: randomUUID5(),
|
|
73685
73935
|
op: "list-state"
|
|
73686
73936
|
});
|
|
73687
73937
|
return data;
|
|
@@ -73689,7 +73939,7 @@ class AuthBrokerClient2 {
|
|
|
73689
73939
|
async listGoogleAccounts() {
|
|
73690
73940
|
const data = await this.send({
|
|
73691
73941
|
v: PROTOCOL_VERSION,
|
|
73692
|
-
id:
|
|
73942
|
+
id: randomUUID5(),
|
|
73693
73943
|
op: "list-google-accounts"
|
|
73694
73944
|
});
|
|
73695
73945
|
return data;
|
|
@@ -73697,7 +73947,7 @@ class AuthBrokerClient2 {
|
|
|
73697
73947
|
async listMicrosoftAccounts() {
|
|
73698
73948
|
const data = await this.send({
|
|
73699
73949
|
v: PROTOCOL_VERSION,
|
|
73700
|
-
id:
|
|
73950
|
+
id: randomUUID5(),
|
|
73701
73951
|
op: "list-microsoft-accounts"
|
|
73702
73952
|
});
|
|
73703
73953
|
return data;
|
|
@@ -73705,7 +73955,7 @@ class AuthBrokerClient2 {
|
|
|
73705
73955
|
async probeQuota(accounts, timeoutMs, forceLive) {
|
|
73706
73956
|
const data = await this.send({
|
|
73707
73957
|
v: PROTOCOL_VERSION,
|
|
73708
|
-
id:
|
|
73958
|
+
id: randomUUID5(),
|
|
73709
73959
|
op: "probe-quota",
|
|
73710
73960
|
accounts: [...accounts],
|
|
73711
73961
|
...timeoutMs !== undefined ? { timeoutMs } : {},
|
|
@@ -73723,7 +73973,7 @@ class AuthBrokerClient2 {
|
|
|
73723
73973
|
async getExternalSpend(forceLive) {
|
|
73724
73974
|
const data = await this.send({
|
|
73725
73975
|
v: PROTOCOL_VERSION,
|
|
73726
|
-
id:
|
|
73976
|
+
id: randomUUID5(),
|
|
73727
73977
|
op: "get-external-spend",
|
|
73728
73978
|
...forceLive ? { forceLive: true } : {}
|
|
73729
73979
|
});
|
|
@@ -73732,21 +73982,21 @@ class AuthBrokerClient2 {
|
|
|
73732
73982
|
async setActive(account) {
|
|
73733
73983
|
const data = await this.send({
|
|
73734
73984
|
v: PROTOCOL_VERSION,
|
|
73735
|
-
id:
|
|
73985
|
+
id: randomUUID5(),
|
|
73736
73986
|
op: "set-active",
|
|
73737
73987
|
account
|
|
73738
73988
|
});
|
|
73739
73989
|
return data;
|
|
73740
73990
|
}
|
|
73741
73991
|
async markExhausted(until) {
|
|
73742
|
-
const req = until !== undefined ? { v: PROTOCOL_VERSION, id:
|
|
73992
|
+
const req = until !== undefined ? { v: PROTOCOL_VERSION, id: randomUUID5(), op: "mark-exhausted", until } : { v: PROTOCOL_VERSION, id: randomUUID5(), op: "mark-exhausted" };
|
|
73743
73993
|
const data = await this.send(req);
|
|
73744
73994
|
return data;
|
|
73745
73995
|
}
|
|
73746
73996
|
async markThrottled(until) {
|
|
73747
73997
|
const data = await this.send({
|
|
73748
73998
|
v: PROTOCOL_VERSION,
|
|
73749
|
-
id:
|
|
73999
|
+
id: randomUUID5(),
|
|
73750
74000
|
op: "mark-throttled",
|
|
73751
74001
|
until
|
|
73752
74002
|
});
|
|
@@ -73755,7 +74005,7 @@ class AuthBrokerClient2 {
|
|
|
73755
74005
|
async claimNotification(key, windowMs) {
|
|
73756
74006
|
const data = await this.send({
|
|
73757
74007
|
v: PROTOCOL_VERSION,
|
|
73758
|
-
id:
|
|
74008
|
+
id: randomUUID5(),
|
|
73759
74009
|
op: "claim-notification",
|
|
73760
74010
|
key,
|
|
73761
74011
|
windowMs
|
|
@@ -73765,7 +74015,7 @@ class AuthBrokerClient2 {
|
|
|
73765
74015
|
async refreshAccount(account) {
|
|
73766
74016
|
const data = await this.send({
|
|
73767
74017
|
v: PROTOCOL_VERSION,
|
|
73768
|
-
id:
|
|
74018
|
+
id: randomUUID5(),
|
|
73769
74019
|
op: "refresh-account",
|
|
73770
74020
|
account
|
|
73771
74021
|
});
|
|
@@ -73774,7 +74024,7 @@ class AuthBrokerClient2 {
|
|
|
73774
74024
|
async addAccount(label, credentials, replace2, provider) {
|
|
73775
74025
|
const base = {
|
|
73776
74026
|
v: PROTOCOL_VERSION,
|
|
73777
|
-
id:
|
|
74027
|
+
id: randomUUID5(),
|
|
73778
74028
|
op: "add-account",
|
|
73779
74029
|
label,
|
|
73780
74030
|
credentials
|
|
@@ -73787,7 +74037,7 @@ class AuthBrokerClient2 {
|
|
|
73787
74037
|
async rmAccount(label, provider) {
|
|
73788
74038
|
const base = {
|
|
73789
74039
|
v: PROTOCOL_VERSION,
|
|
73790
|
-
id:
|
|
74040
|
+
id: randomUUID5(),
|
|
73791
74041
|
op: "rm-account",
|
|
73792
74042
|
label
|
|
73793
74043
|
};
|
|
@@ -73798,7 +74048,7 @@ class AuthBrokerClient2 {
|
|
|
73798
74048
|
async setOverride(agent, account) {
|
|
73799
74049
|
const data = await this.send({
|
|
73800
74050
|
v: PROTOCOL_VERSION,
|
|
73801
|
-
id:
|
|
74051
|
+
id: randomUUID5(),
|
|
73802
74052
|
op: "set-override",
|
|
73803
74053
|
agent,
|
|
73804
74054
|
account
|
|
@@ -75357,10 +75607,21 @@ function renderOperatorEvent(ev) {
|
|
|
75357
75607
|
`),
|
|
75358
75608
|
keyboard: {
|
|
75359
75609
|
inline_keyboard: [
|
|
75360
|
-
[
|
|
75361
|
-
|
|
75362
|
-
|
|
75363
|
-
|
|
75610
|
+
[{ text: "\u274c Dismiss", callback_data: `op:dismiss:${encodeURIComponent(ev.agent)}` }]
|
|
75611
|
+
]
|
|
75612
|
+
}
|
|
75613
|
+
};
|
|
75614
|
+
case "transport-transient":
|
|
75615
|
+
return {
|
|
75616
|
+
text: [
|
|
75617
|
+
`\uD83D\uDD0C **Transient transport error** for **${agent}**.`,
|
|
75618
|
+
detail ? `\`${detail}\`` : "",
|
|
75619
|
+
`A mid-response stream to Anthropic dropped. Will retry automatically.`
|
|
75620
|
+
].filter(Boolean).join(`
|
|
75621
|
+
`),
|
|
75622
|
+
keyboard: {
|
|
75623
|
+
inline_keyboard: [
|
|
75624
|
+
[{ text: "\u274c Dismiss", callback_data: `op:dismiss:${encodeURIComponent(ev.agent)}` }]
|
|
75364
75625
|
]
|
|
75365
75626
|
}
|
|
75366
75627
|
};
|
|
@@ -75758,6 +76019,69 @@ function recordOperatorEvent(event, now = Date.now()) {
|
|
|
75758
76019
|
store2.set(event.agent, { event, storedAt: now });
|
|
75759
76020
|
}
|
|
75760
76021
|
|
|
76022
|
+
// gateway/user-failure-notices.ts
|
|
76023
|
+
init_format();
|
|
76024
|
+
var ESCALATION_WINDOW_MS = 10 * 60000;
|
|
76025
|
+
var ESCALATION_THRESHOLD = 3;
|
|
76026
|
+
var transportEscalation = new Map;
|
|
76027
|
+
function noteTransportTransientAndShouldEscalate(agent, now, opts) {
|
|
76028
|
+
const windowMs = opts?.windowMs ?? ESCALATION_WINDOW_MS;
|
|
76029
|
+
const threshold = opts?.threshold ?? ESCALATION_THRESHOLD;
|
|
76030
|
+
const recent = (transportEscalation.get(agent) ?? []).filter((t) => now - t < windowMs);
|
|
76031
|
+
recent.push(now);
|
|
76032
|
+
if (recent.length >= threshold) {
|
|
76033
|
+
transportEscalation.set(agent, []);
|
|
76034
|
+
return true;
|
|
76035
|
+
}
|
|
76036
|
+
transportEscalation.set(agent, recent);
|
|
76037
|
+
return false;
|
|
76038
|
+
}
|
|
76039
|
+
function renderTransportEscalationCard(agent) {
|
|
76040
|
+
const a = escapeMarkdown(agent);
|
|
76041
|
+
return {
|
|
76042
|
+
text: [
|
|
76043
|
+
`\uD83D\uDD0C **Repeated stream failures** reaching Anthropic for **${a}**.`,
|
|
76044
|
+
`3+ mid-response aborts within ~10 min. Turns retry automatically; if this persists, Anthropic's API may be degraded.`
|
|
76045
|
+
].join(`
|
|
76046
|
+
`),
|
|
76047
|
+
keyboard: {
|
|
76048
|
+
inline_keyboard: [
|
|
76049
|
+
[{ text: "\u274c Dismiss", callback_data: `op:dismiss:${encodeURIComponent(agent)}` }]
|
|
76050
|
+
]
|
|
76051
|
+
}
|
|
76052
|
+
};
|
|
76053
|
+
}
|
|
76054
|
+
function emitTransportTransientEvent(event, deps) {
|
|
76055
|
+
const now = deps.now();
|
|
76056
|
+
deps.record(event);
|
|
76057
|
+
const escalate = noteTransportTransientAndShouldEscalate(event.agent, now);
|
|
76058
|
+
deps.log(`transport-transient agent=${event.agent} escalate=${escalate} ` + `(no broadcast card; user-notice deferred to turn-end)`);
|
|
76059
|
+
const allowFrom = deps.allowFrom();
|
|
76060
|
+
if (allowFrom.length === 0)
|
|
76061
|
+
return;
|
|
76062
|
+
deps.scheduleUserNotice({
|
|
76063
|
+
chatIds: [...allowFrom],
|
|
76064
|
+
agent: event.agent,
|
|
76065
|
+
kind: event.kind,
|
|
76066
|
+
key: deps.liveTurnKey(),
|
|
76067
|
+
atMs: now
|
|
76068
|
+
});
|
|
76069
|
+
if (escalate) {
|
|
76070
|
+
const card = renderTransportEscalationCard(event.agent);
|
|
76071
|
+
deps.send(allowFrom[0], card.text, card.keyboard);
|
|
76072
|
+
}
|
|
76073
|
+
}
|
|
76074
|
+
function flushDeferredUserNotices(turnDeliveredReply, turnKey3, deps) {
|
|
76075
|
+
const notices = deps.resolveNotices(turnDeliveredReply, turnKey3);
|
|
76076
|
+
if (notices.length === 0)
|
|
76077
|
+
return;
|
|
76078
|
+
for (const notice of notices) {
|
|
76079
|
+
deps.log(`user-notice flush (turn died reply-less) agent=${notice.agent} ` + `kind=${notice.kind} chats=${notice.chatIds.length}`);
|
|
76080
|
+
for (const chatId of notice.chatIds)
|
|
76081
|
+
deps.send(chatId, notice.text);
|
|
76082
|
+
}
|
|
76083
|
+
}
|
|
76084
|
+
|
|
75761
76085
|
// throttle-tier.ts
|
|
75762
76086
|
init_card_format();
|
|
75763
76087
|
init_quota_check();
|
|
@@ -75902,9 +76226,6 @@ function classifyKindAndSource(text4) {
|
|
|
75902
76226
|
if (claudeKind === "rate-limited") {
|
|
75903
76227
|
return { kind: "rate_limit", source: "anthropic" };
|
|
75904
76228
|
}
|
|
75905
|
-
if (claudeKind === "unknown-5xx") {
|
|
75906
|
-
return { kind: "overload_529", source: "anthropic" };
|
|
75907
|
-
}
|
|
75908
76229
|
return { kind: "unknown", source: "anthropic" };
|
|
75909
76230
|
}
|
|
75910
76231
|
function providerById(id) {
|
|
@@ -76177,6 +76498,8 @@ function detectModelUnavailable2(stderr) {
|
|
|
76177
76498
|
"socket hang up",
|
|
76178
76499
|
"request timed out",
|
|
76179
76500
|
"connection refused",
|
|
76501
|
+
"connection closed",
|
|
76502
|
+
"mid-response",
|
|
76180
76503
|
"getaddrinfo"
|
|
76181
76504
|
];
|
|
76182
76505
|
if (networkSignals.some((s) => lower.includes(s))) {
|
|
@@ -78294,6 +78617,146 @@ function resolveChatIdFallback(rawChatId, access, turnSessionChatId, lastKnownCh
|
|
|
78294
78617
|
}
|
|
78295
78618
|
return { chatId: rawChatId, tier: "raw" };
|
|
78296
78619
|
}
|
|
78620
|
+
|
|
78621
|
+
// gateway/buzz-mirror.ts
|
|
78622
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
78623
|
+
var CORRECTION_DEBOUNCE_MS2 = 30000;
|
|
78624
|
+
var MAX_TRACKED2 = 4096;
|
|
78625
|
+
|
|
78626
|
+
class BuzzMirror2 {
|
|
78627
|
+
cfg;
|
|
78628
|
+
log;
|
|
78629
|
+
sender = null;
|
|
78630
|
+
pending = new Map;
|
|
78631
|
+
pendingOrder = [];
|
|
78632
|
+
msgToBuzz = new Map;
|
|
78633
|
+
msgOrder = [];
|
|
78634
|
+
correctionTimers = new Map;
|
|
78635
|
+
constructor(cfg) {
|
|
78636
|
+
this.cfg = cfg;
|
|
78637
|
+
this.log = cfg.log ?? (() => {});
|
|
78638
|
+
}
|
|
78639
|
+
attachSender(sender) {
|
|
78640
|
+
this.sender = sender;
|
|
78641
|
+
}
|
|
78642
|
+
evict(map, order) {
|
|
78643
|
+
while (order.length > MAX_TRACKED2) {
|
|
78644
|
+
const k = order.shift();
|
|
78645
|
+
if (k !== undefined)
|
|
78646
|
+
map.delete(k);
|
|
78647
|
+
}
|
|
78648
|
+
}
|
|
78649
|
+
mirrorReplyDelivered(input) {
|
|
78650
|
+
try {
|
|
78651
|
+
const route = resolveRoute(input.ownerOriginChannel, this.cfg.mode, true);
|
|
78652
|
+
const buzzInRoute = route.primary === "buzz" || route.mirrors.includes("buzz");
|
|
78653
|
+
if (!buzzInRoute)
|
|
78654
|
+
return;
|
|
78655
|
+
let channelId;
|
|
78656
|
+
let replyToEventId;
|
|
78657
|
+
let threadRootId;
|
|
78658
|
+
if (input.ownerOriginChannel === "buzz" && input.ownerBuzzCoords) {
|
|
78659
|
+
if (!isBuzzThreadedPublishSafe({
|
|
78660
|
+
ownerEchoed: input.ownerEchoed,
|
|
78661
|
+
hasRecentDifferentOriginTurn: input.hasRecentDifferentOriginTurn
|
|
78662
|
+
})) {
|
|
78663
|
+
this.log("buzz-mirror: S1 guard blocked a threaded publish on an ambiguous " + "owner binding (un-echoed reply + a recent different-origin turn) " + "\u2014 delivered Telegram-only");
|
|
78664
|
+
return;
|
|
78665
|
+
}
|
|
78666
|
+
channelId = input.ownerBuzzCoords.channelId;
|
|
78667
|
+
replyToEventId = input.ownerBuzzCoords.eventId;
|
|
78668
|
+
threadRootId = input.ownerBuzzCoords.threadRoot;
|
|
78669
|
+
} else {
|
|
78670
|
+
if (!this.cfg.defaultChannelId)
|
|
78671
|
+
return;
|
|
78672
|
+
channelId = this.cfg.defaultChannelId;
|
|
78673
|
+
}
|
|
78674
|
+
this.publish({
|
|
78675
|
+
channelId,
|
|
78676
|
+
replyToEventId,
|
|
78677
|
+
threadRootId,
|
|
78678
|
+
payload: { kind: "message", text: input.scrubbedText }
|
|
78679
|
+
}, input.telegramMessageKeys);
|
|
78680
|
+
} catch (err) {
|
|
78681
|
+
this.log(`buzz-mirror: mirrorReplyDelivered threw (ignored): ${String(err)}`);
|
|
78682
|
+
}
|
|
78683
|
+
}
|
|
78684
|
+
mirrorCorrection(input) {
|
|
78685
|
+
try {
|
|
78686
|
+
const target = this.msgToBuzz.get(input.telegramMessageKey);
|
|
78687
|
+
if (!target)
|
|
78688
|
+
return;
|
|
78689
|
+
const existing = this.correctionTimers.get(input.telegramMessageKey);
|
|
78690
|
+
if (existing)
|
|
78691
|
+
clearTimeout(existing);
|
|
78692
|
+
const timer3 = setTimeout(() => {
|
|
78693
|
+
this.correctionTimers.delete(input.telegramMessageKey);
|
|
78694
|
+
const t = this.msgToBuzz.get(input.telegramMessageKey);
|
|
78695
|
+
if (!t)
|
|
78696
|
+
return;
|
|
78697
|
+
this.publish({
|
|
78698
|
+
channelId: t.channelId,
|
|
78699
|
+
replyToEventId: t.eventId,
|
|
78700
|
+
threadRootId: t.eventId,
|
|
78701
|
+
payload: {
|
|
78702
|
+
kind: "correction",
|
|
78703
|
+
text: input.scrubbedText,
|
|
78704
|
+
targetEventId: t.eventId
|
|
78705
|
+
}
|
|
78706
|
+
}, []);
|
|
78707
|
+
}, CORRECTION_DEBOUNCE_MS2);
|
|
78708
|
+
if (typeof timer3.unref === "function") {
|
|
78709
|
+
timer3.unref();
|
|
78710
|
+
}
|
|
78711
|
+
this.correctionTimers.set(input.telegramMessageKey, timer3);
|
|
78712
|
+
} catch (err) {
|
|
78713
|
+
this.log(`buzz-mirror: mirrorCorrection threw (ignored): ${String(err)}`);
|
|
78714
|
+
}
|
|
78715
|
+
}
|
|
78716
|
+
onPublishResult(msg) {
|
|
78717
|
+
const p = this.pending.get(msg.correlationId);
|
|
78718
|
+
this.pending.delete(msg.correlationId);
|
|
78719
|
+
if (!p)
|
|
78720
|
+
return;
|
|
78721
|
+
if (!msg.ok || !msg.eventId) {
|
|
78722
|
+
this.log(`buzz-mirror: publish failed (correlationId=${msg.correlationId.slice(0, 8)}` + `${msg.error ? ` error=${msg.error}` : ""}) \u2014 Telegram copy already delivered`);
|
|
78723
|
+
return;
|
|
78724
|
+
}
|
|
78725
|
+
for (const key of p.telegramMessageKeys) {
|
|
78726
|
+
this.msgToBuzz.set(key, { eventId: msg.eventId, channelId: p.channelId });
|
|
78727
|
+
this.msgOrder.push(key);
|
|
78728
|
+
}
|
|
78729
|
+
this.evict(this.msgToBuzz, this.msgOrder);
|
|
78730
|
+
}
|
|
78731
|
+
publish(fields, telegramMessageKeys) {
|
|
78732
|
+
if (!this.sender) {
|
|
78733
|
+
this.log("buzz-mirror: no Buzz peer connected \u2014 mirror dropped (Telegram copy delivered)");
|
|
78734
|
+
return;
|
|
78735
|
+
}
|
|
78736
|
+
const correlationId = randomUUID6();
|
|
78737
|
+
const msg = {
|
|
78738
|
+
type: "outbound_to_buzz",
|
|
78739
|
+
correlationId,
|
|
78740
|
+
agentName: this.cfg.agentName,
|
|
78741
|
+
...fields
|
|
78742
|
+
};
|
|
78743
|
+
const sent = this.sender(msg);
|
|
78744
|
+
if (!sent) {
|
|
78745
|
+
this.log("buzz-mirror: Buzz peer send returned false \u2014 mirror dropped (Telegram copy delivered)");
|
|
78746
|
+
return;
|
|
78747
|
+
}
|
|
78748
|
+
this.pending.set(correlationId, {
|
|
78749
|
+
channelId: fields.channelId,
|
|
78750
|
+
telegramMessageKeys
|
|
78751
|
+
});
|
|
78752
|
+
this.pendingOrder.push(correlationId);
|
|
78753
|
+
this.evict(this.pending, this.pendingOrder);
|
|
78754
|
+
}
|
|
78755
|
+
}
|
|
78756
|
+
var singleton2 = null;
|
|
78757
|
+
function getBuzzMirror2() {
|
|
78758
|
+
return singleton2;
|
|
78759
|
+
}
|
|
78297
78760
|
// over-ping-safety-net.ts
|
|
78298
78761
|
function decideOverPing(input) {
|
|
78299
78762
|
const substantive = input.substantive === true;
|
|
@@ -78389,7 +78852,9 @@ var INBOUND_SOURCE_CLASSIFICATION = {
|
|
|
78389
78852
|
mental_model_proposal_denied: { decoupledCompletion: false },
|
|
78390
78853
|
mental_model_proposal_failed: { decoupledCompletion: false },
|
|
78391
78854
|
webhook: { decoupledCompletion: false },
|
|
78392
|
-
linear: { decoupledCompletion: false }
|
|
78855
|
+
linear: { decoupledCompletion: false },
|
|
78856
|
+
buzz: { decoupledCompletion: false },
|
|
78857
|
+
boot_briefing: { decoupledCompletion: false }
|
|
78393
78858
|
};
|
|
78394
78859
|
function stampsHandbackMarker(source) {
|
|
78395
78860
|
if (source == null)
|
|
@@ -78999,6 +79464,7 @@ async function sendReply(deps, req) {
|
|
|
78999
79464
|
resolveReplyOwnerTurn,
|
|
79000
79465
|
findTurnByOriginId,
|
|
79001
79466
|
findTurnByQuotedMessageId,
|
|
79467
|
+
findLatestTurnForChat,
|
|
79002
79468
|
resolveAnswerThreadWithLog,
|
|
79003
79469
|
resolveThreadId,
|
|
79004
79470
|
getLatestInboundMessageId: getLatestInboundMessageId2,
|
|
@@ -79732,6 +80198,23 @@ ${url}`;
|
|
|
79732
80198
|
if (shouldJournalReplySiteDelivery({ text: rawText, disableNotification: modelDisableNotification })) {
|
|
79733
80199
|
journalExternalDelivery({ turnNonce: t?.turnId ?? null, text: text4, tgMessageId: sentIds[sentIds.length - 1], replyAlreadyDeliveredThisTurn: true });
|
|
79734
80200
|
}
|
|
80201
|
+
const buzzMirror = getBuzzMirror2();
|
|
80202
|
+
if (buzzMirror !== null) {
|
|
80203
|
+
const { turn: mOwnerTurn, tier: mOwnerTier } = resolveReplyOwnerTurn(turn, chat_id, args);
|
|
80204
|
+
const ownerOrigin = mOwnerTurn?.originChannel ?? "telegram";
|
|
80205
|
+
const ownerTurnId = mOwnerTurn?.turnId ?? null;
|
|
80206
|
+
const ownerEchoed = mOwnerTier === "origin";
|
|
80207
|
+
const latestEnded = findLatestTurnForChat(chat_id, { endedOnly: true });
|
|
80208
|
+
const hasRecentDifferentOriginTurn = [turn, latestEnded].some((c) => c != null && c.turnId !== ownerTurnId && c.originChannel !== ownerOrigin);
|
|
80209
|
+
buzzMirror.mirrorReplyDelivered({
|
|
80210
|
+
scrubbedText: text4,
|
|
80211
|
+
ownerOriginChannel: ownerOrigin,
|
|
80212
|
+
ownerBuzzCoords: mOwnerTurn?.buzzCoords,
|
|
80213
|
+
ownerEchoed,
|
|
80214
|
+
hasRecentDifferentOriginTurn,
|
|
80215
|
+
telegramMessageKeys: sentIds.map((id) => `${chat_id}:${id}`)
|
|
80216
|
+
});
|
|
80217
|
+
}
|
|
79735
80218
|
}
|
|
79736
80219
|
return { content: [{ type: "text", text: result }] };
|
|
79737
80220
|
}
|
|
@@ -81123,6 +81606,8 @@ var QUEUED_CARD_ENABLED = process.env.SWITCHROOM_QUEUED_CARD !== "0";
|
|
|
81123
81606
|
var QUEUED_CARD_HTML = "\u23f3 Queued \u2014 waiting for the current task to finish\u2026";
|
|
81124
81607
|
var QUEUED_CARD_FOLDED_HTML = "\u2705 Folded into the current task.";
|
|
81125
81608
|
var QUEUED_CARD_EXPIRED_HTML = "\u26a0\ufe0f This queued message timed out before it could start.";
|
|
81609
|
+
var BUZZ_ENABLED = process.env.BUZZ_ENABLED === "1" || process.env.BUZZ_ENABLED === "true";
|
|
81610
|
+
var BUZZ_ORIGIN_STAMP_ACTIVE = BUZZ_ENABLED && isBuzzTurnRoutingEnabled();
|
|
81126
81611
|
var parkedTurnStarts = [];
|
|
81127
81612
|
function pruneParkedTurnStarts(now, onDrop) {
|
|
81128
81613
|
for (let i = parkedTurnStarts.length - 1;i >= 0; i--) {
|
|
@@ -81291,6 +81776,7 @@ function beginTurn(deps, ev) {
|
|
|
81291
81776
|
startedAt,
|
|
81292
81777
|
gatewayReceiveAt: startedAt,
|
|
81293
81778
|
role: deriveTurnRole(ev.rawContent),
|
|
81779
|
+
...BUZZ_ORIGIN_STAMP_ACTIVE ? parseChannelOrigin(ev.rawContent) : { originChannel: "telegram" },
|
|
81294
81780
|
...consumedCrossTurnGate != null ? { crossTurnGate: consumedCrossTurnGate } : {},
|
|
81295
81781
|
replyCalled: false,
|
|
81296
81782
|
finalAnswerDelivered: false,
|
|
@@ -86064,6 +86550,9 @@ for (const name of SHARED_FRAGMENTS) {
|
|
|
86064
86550
|
}
|
|
86065
86551
|
}
|
|
86066
86552
|
|
|
86553
|
+
// ../src/memory/scaffold-integration.ts
|
|
86554
|
+
init_merge();
|
|
86555
|
+
|
|
86067
86556
|
// ../src/litellm/timeout-budget.ts
|
|
86068
86557
|
var LITELLM_ROUTER_MARGIN_S = 10;
|
|
86069
86558
|
var LITELLM_TIMEOUT_TIERS = {
|
|
@@ -90522,6 +91011,20 @@ function validateClientMessage(msg) {
|
|
|
90522
91011
|
return false;
|
|
90523
91012
|
return true;
|
|
90524
91013
|
}
|
|
91014
|
+
case "hello_buzz_peer": {
|
|
91015
|
+
return typeof m.agentName === "string" && AGENT_NAME_RE3.test(m.agentName);
|
|
91016
|
+
}
|
|
91017
|
+
case "buzz_publish_result": {
|
|
91018
|
+
if (typeof m.correlationId !== "string" || m.correlationId.length === 0 || m.correlationId.length > 64)
|
|
91019
|
+
return false;
|
|
91020
|
+
if (typeof m.ok !== "boolean")
|
|
91021
|
+
return false;
|
|
91022
|
+
if (m.eventId !== undefined && (typeof m.eventId !== "string" || m.eventId.length > 128))
|
|
91023
|
+
return false;
|
|
91024
|
+
if (m.error !== undefined && (typeof m.error !== "string" || m.error.length > 500))
|
|
91025
|
+
return false;
|
|
91026
|
+
return true;
|
|
91027
|
+
}
|
|
90525
91028
|
default:
|
|
90526
91029
|
return false;
|
|
90527
91030
|
}
|
|
@@ -90550,9 +91053,26 @@ function createIpcServer(options) {
|
|
|
90550
91053
|
onRequestConfigFinalize,
|
|
90551
91054
|
onRolloutStatusPost,
|
|
90552
91055
|
onRolloutStatusEdit,
|
|
91056
|
+
onBuzzPublishResult,
|
|
90553
91057
|
log = () => {},
|
|
90554
91058
|
heartbeatTimeoutMs = 30000
|
|
90555
91059
|
} = options;
|
|
91060
|
+
let buzzPeerClient = null;
|
|
91061
|
+
const BUZZ_INJECT_RING_MAX = 1024;
|
|
91062
|
+
const buzzInjectSeen = new Set;
|
|
91063
|
+
const buzzInjectOrder = [];
|
|
91064
|
+
const buzzInjectIsDuplicate = (eventId) => {
|
|
91065
|
+
if (buzzInjectSeen.has(eventId))
|
|
91066
|
+
return true;
|
|
91067
|
+
buzzInjectSeen.add(eventId);
|
|
91068
|
+
buzzInjectOrder.push(eventId);
|
|
91069
|
+
if (buzzInjectOrder.length > BUZZ_INJECT_RING_MAX) {
|
|
91070
|
+
const evicted = buzzInjectOrder.shift();
|
|
91071
|
+
if (evicted !== undefined)
|
|
91072
|
+
buzzInjectSeen.delete(evicted);
|
|
91073
|
+
}
|
|
91074
|
+
return false;
|
|
91075
|
+
};
|
|
90556
91076
|
try {
|
|
90557
91077
|
renameSync17(socketPath, socketPath + ".bak");
|
|
90558
91078
|
} catch {}
|
|
@@ -90571,6 +91091,8 @@ function createIpcServer(options) {
|
|
|
90571
91091
|
if (client3.topicId != null && topicIndex.get(client3.topicId) === client3) {
|
|
90572
91092
|
topicIndex.delete(client3.topicId);
|
|
90573
91093
|
}
|
|
91094
|
+
if (buzzPeerClient === client3)
|
|
91095
|
+
buzzPeerClient = null;
|
|
90574
91096
|
loggedLegacyUpdatePlaceholder.delete(client3.id);
|
|
90575
91097
|
onClientDisconnected(client3);
|
|
90576
91098
|
log(`client disconnected: ${client3.id} (agent=${client3.agentName})`);
|
|
@@ -90626,10 +91148,19 @@ function createIpcServer(options) {
|
|
|
90626
91148
|
if (onPtyPartial)
|
|
90627
91149
|
onPtyPartial(client3, msg);
|
|
90628
91150
|
break;
|
|
90629
|
-
case "inject_inbound":
|
|
91151
|
+
case "inject_inbound": {
|
|
91152
|
+
const injectMsg = msg;
|
|
91153
|
+
const injMeta = injectMsg.inbound?.meta;
|
|
91154
|
+
if (injMeta && injMeta.source === "buzz" && typeof injMeta.buzz_event_id === "string") {
|
|
91155
|
+
if (buzzInjectIsDuplicate(injMeta.buzz_event_id)) {
|
|
91156
|
+
log(`inject_inbound: dropped duplicate buzz event ${injMeta.buzz_event_id.slice(0, 12)} (hub dedup ring)`);
|
|
91157
|
+
break;
|
|
91158
|
+
}
|
|
91159
|
+
}
|
|
90630
91160
|
if (onInjectInbound)
|
|
90631
|
-
onInjectInbound(client3,
|
|
91161
|
+
onInjectInbound(client3, injectMsg);
|
|
90632
91162
|
break;
|
|
91163
|
+
}
|
|
90633
91164
|
case "send_outbound":
|
|
90634
91165
|
if (onSendOutbound)
|
|
90635
91166
|
onSendOutbound(client3, msg);
|
|
@@ -90773,6 +91304,17 @@ function createIpcServer(options) {
|
|
|
90773
91304
|
}
|
|
90774
91305
|
}
|
|
90775
91306
|
break;
|
|
91307
|
+
case "hello_buzz_peer":
|
|
91308
|
+
handleHelloBuzzPeer(client3, msg);
|
|
91309
|
+
break;
|
|
91310
|
+
case "buzz_publish_result":
|
|
91311
|
+
if (!client3.isBuzzPeer) {
|
|
91312
|
+
log(`SECURITY: rejecting buzz_publish_result from non-peer connection ` + `(agent=${client3.agentName ?? "anonymous"} id=${client3.id}) \u2014 only the ` + `registered Buzz publish peer may report publish outcomes; dropped`);
|
|
91313
|
+
break;
|
|
91314
|
+
}
|
|
91315
|
+
if (onBuzzPublishResult)
|
|
91316
|
+
onBuzzPublishResult(client3, msg);
|
|
91317
|
+
break;
|
|
90776
91318
|
case "update_placeholder":
|
|
90777
91319
|
if (!loggedLegacyUpdatePlaceholder.has(client3.id)) {
|
|
90778
91320
|
loggedLegacyUpdatePlaceholder.add(client3.id);
|
|
@@ -90783,7 +91325,39 @@ function createIpcServer(options) {
|
|
|
90783
91325
|
log(`unknown IPC message type from client ${client3.id}: ${msg.type}`);
|
|
90784
91326
|
}
|
|
90785
91327
|
}
|
|
91328
|
+
function handleHelloBuzzPeer(client3, msg) {
|
|
91329
|
+
if (client3.agentName !== null || client3.isBuzzPeer) {
|
|
91330
|
+
log(`rejecting hello_buzz_peer: connection already has a role ` + `(agent=${client3.agentName ?? "none"} isBuzzPeer=${client3.isBuzzPeer}) \u2014 close+drop client=${client3.id}`);
|
|
91331
|
+
try {
|
|
91332
|
+
client3.close();
|
|
91333
|
+
} catch {}
|
|
91334
|
+
return;
|
|
91335
|
+
}
|
|
91336
|
+
if (buzzPeerClient && buzzPeerClient !== client3 && buzzPeerClient.isAlive()) {
|
|
91337
|
+
log(`SECURITY: rejecting hello_buzz_peer \u2014 a LIVE Buzz peer is already ` + `connected (live_id=${buzzPeerClient.id} rejected_id=${client3.id}); ` + `refusing displacement, close+drop`);
|
|
91338
|
+
try {
|
|
91339
|
+
client3.close();
|
|
91340
|
+
} catch {}
|
|
91341
|
+
return;
|
|
91342
|
+
}
|
|
91343
|
+
if (buzzPeerClient && buzzPeerClient !== client3) {
|
|
91344
|
+
log(`hello_buzz_peer: replacing prior (dead) buzz peer (prior_id=${buzzPeerClient.id} new_id=${client3.id})`);
|
|
91345
|
+
try {
|
|
91346
|
+
buzzPeerClient.close();
|
|
91347
|
+
} catch {}
|
|
91348
|
+
}
|
|
91349
|
+
client3.isBuzzPeer = true;
|
|
91350
|
+
buzzPeerClient = client3;
|
|
91351
|
+
log(`registered buzz publish peer for agent=${msg.agentName} id=${client3.id}`);
|
|
91352
|
+
}
|
|
90786
91353
|
function handleRegister(client3, msg) {
|
|
91354
|
+
if (client3.isBuzzPeer) {
|
|
91355
|
+
log(`rejecting register: connection is the Buzz publish peer, not an agent bridge ` + `(close+drop client=${client3.id})`);
|
|
91356
|
+
try {
|
|
91357
|
+
client3.close();
|
|
91358
|
+
} catch {}
|
|
91359
|
+
return;
|
|
91360
|
+
}
|
|
90787
91361
|
if (msg.agentName === "default") {
|
|
90788
91362
|
log(`rejecting register: agentName="default" \u2014 anonymous bridges are not allowed (close+drop client=${client3.id})`);
|
|
90789
91363
|
try {
|
|
@@ -90823,6 +91397,7 @@ function createIpcServer(options) {
|
|
|
90823
91397
|
id;
|
|
90824
91398
|
agentName = null;
|
|
90825
91399
|
topicId = null;
|
|
91400
|
+
isBuzzPeer = false;
|
|
90826
91401
|
lastHeartbeat = Date.now();
|
|
90827
91402
|
_socket;
|
|
90828
91403
|
_closed = false;
|
|
@@ -90932,6 +91507,12 @@ function createIpcServer(options) {
|
|
|
90932
91507
|
clientCount() {
|
|
90933
91508
|
return clients.size;
|
|
90934
91509
|
},
|
|
91510
|
+
sendToBuzzPeer(msg) {
|
|
91511
|
+
if (!buzzPeerClient || !buzzPeerClient.isAlive())
|
|
91512
|
+
return false;
|
|
91513
|
+
buzzPeerClient.send(msg);
|
|
91514
|
+
return true;
|
|
91515
|
+
},
|
|
90935
91516
|
async close() {
|
|
90936
91517
|
if (watchdogTimer !== null) {
|
|
90937
91518
|
clearInterval(watchdogTimer);
|
|
@@ -92393,6 +92974,9 @@ function spoolId(msg) {
|
|
|
92393
92974
|
if ((msg.meta?.source === "resume_interrupted" || msg.meta?.source === "resume_watchdog_timeout" || msg.meta?.source === "resume_deferred") && typeof msg.meta?.resume_turn_key === "string" && msg.meta.resume_turn_key.length > 0) {
|
|
92394
92975
|
return `s:resume:${msg.meta.resume_turn_key}`;
|
|
92395
92976
|
}
|
|
92977
|
+
if (msg.meta?.source === "boot_briefing") {
|
|
92978
|
+
return `s:boot-briefing:${msg.chatId}`;
|
|
92979
|
+
}
|
|
92396
92980
|
if (msg.meta?.source === "cron" && typeof msg.meta?.replay_fire_ms === "string" && msg.meta.replay_fire_ms.length > 0) {
|
|
92397
92981
|
const idx = typeof msg.meta?.schedule_index === "string" && msg.meta.schedule_index.length > 0 ? msg.meta.schedule_index : "-";
|
|
92398
92982
|
return `s:cron-replay:${msg.chatId}:${idx}:${msg.meta.replay_fire_ms}`;
|
|
@@ -92565,8 +93149,17 @@ function createInboundSpool(opts) {
|
|
|
92565
93149
|
return {
|
|
92566
93150
|
put(agent, msg) {
|
|
92567
93151
|
const id = spoolId(msg);
|
|
92568
|
-
|
|
93152
|
+
const existing = live.get(id);
|
|
93153
|
+
if (existing != null) {
|
|
93154
|
+
if (msg.meta?.source === "boot_briefing") {
|
|
93155
|
+
existing.agent = agent;
|
|
93156
|
+
existing.msg = msg;
|
|
93157
|
+
appendRecord({ t: "put", id, agent, msg, firstAt: existing.firstAt });
|
|
93158
|
+
maybeCompact();
|
|
93159
|
+
return true;
|
|
93160
|
+
}
|
|
92569
93161
|
return false;
|
|
93162
|
+
}
|
|
92570
93163
|
const firstAt = now();
|
|
92571
93164
|
live.set(id, { agent, msg, firstAt });
|
|
92572
93165
|
appendRecord({ t: "put", id, agent, msg, firstAt });
|
|
@@ -95283,7 +95876,7 @@ import {
|
|
|
95283
95876
|
writeSync as writeSync6
|
|
95284
95877
|
} from "node:fs";
|
|
95285
95878
|
import { join as join50 } from "node:path";
|
|
95286
|
-
import { randomUUID as
|
|
95879
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
95287
95880
|
var PROPOSALS_FILE2 = "skill-proposals.jsonl";
|
|
95288
95881
|
var REJECTED_FILE2 = "skill-proposals-rejected.jsonl";
|
|
95289
95882
|
var REJECTION_TTL_MS2 = 90 * 24 * 60 * 60 * 1000;
|
|
@@ -95386,7 +95979,7 @@ function enqueueProposal(stateDir, input, opts = {}) {
|
|
|
95386
95979
|
const now = opts.now ?? Date.now;
|
|
95387
95980
|
ensureDir3(stateDir);
|
|
95388
95981
|
const proposal = {
|
|
95389
|
-
id:
|
|
95982
|
+
id: randomUUID7(),
|
|
95390
95983
|
created_at: new Date(now()).toISOString(),
|
|
95391
95984
|
status: "pending",
|
|
95392
95985
|
...input
|
|
@@ -96775,7 +97368,7 @@ ${errStr}`) ? "rate-limited" : "quota-exhausted" : classifyClaudeError({ type: e
|
|
|
96775
97368
|
const raw = embeddedError ?? obj;
|
|
96776
97369
|
const kind = classifyClaudeError(embeddedError ?? obj);
|
|
96777
97370
|
const detail = extractDetailMessage(embeddedError) ?? extractDetailMessage(obj) ?? String(type ?? "");
|
|
96778
|
-
const transient = kind === "rate-limited";
|
|
97371
|
+
const transient = kind === "rate-limited" || kind === "transport-transient";
|
|
96779
97372
|
const retry = extractRetryState(obj);
|
|
96780
97373
|
const terminal = !transient ? true : retry.retryAttempt != null && retry.maxRetries != null ? retry.retryAttempt >= retry.maxRetries : isErrorLine;
|
|
96781
97374
|
return { kind, raw, detail, transient, terminal };
|
|
@@ -100653,7 +101246,7 @@ function startGatewayHeartbeat(stateDir, intervalMs = GATEWAY_HEARTBEAT_INTERVAL
|
|
|
100653
101246
|
}
|
|
100654
101247
|
|
|
100655
101248
|
// gateway/boot-beacon.ts
|
|
100656
|
-
import { randomUUID as
|
|
101249
|
+
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
100657
101250
|
import {
|
|
100658
101251
|
closeSync as closeSync14,
|
|
100659
101252
|
fsyncSync as fsyncSync5,
|
|
@@ -100787,7 +101380,7 @@ function writeBootBeaconFile(stateDir, beacon) {
|
|
|
100787
101380
|
return false;
|
|
100788
101381
|
}
|
|
100789
101382
|
}
|
|
100790
|
-
var GATEWAY_BOOT_ID =
|
|
101383
|
+
var GATEWAY_BOOT_ID = randomUUID8();
|
|
100791
101384
|
function tickBootBeacon(stateDir, bootId = GATEWAY_BOOT_ID) {
|
|
100792
101385
|
try {
|
|
100793
101386
|
return writeBootBeaconFile(stateDir, buildBootBeacon({
|
|
@@ -101140,10 +101733,10 @@ function startOutboxSweep(deps) {
|
|
|
101140
101733
|
}
|
|
101141
101734
|
|
|
101142
101735
|
// ../src/build-info.ts
|
|
101143
|
-
var VERSION2 = "0.
|
|
101144
|
-
var COMMIT_SHA = "
|
|
101145
|
-
var COMMIT_DATE = "2026-08-
|
|
101146
|
-
var LATEST_PR =
|
|
101736
|
+
var VERSION2 = "0.20.0";
|
|
101737
|
+
var COMMIT_SHA = "86eaf06d";
|
|
101738
|
+
var COMMIT_DATE = "2026-08-03T03:02:51Z";
|
|
101739
|
+
var LATEST_PR = 4259;
|
|
101147
101740
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
101148
101741
|
|
|
101149
101742
|
// gateway/boot-version.ts
|
|
@@ -102526,8 +103119,338 @@ function selectResumeBuilder(endedVia, opts) {
|
|
|
102526
103119
|
return kind;
|
|
102527
103120
|
}
|
|
102528
103121
|
|
|
103122
|
+
// gateway/boot-briefing-wiring.ts
|
|
103123
|
+
init_history();
|
|
103124
|
+
import { existsSync as existsSync56, readFileSync as readFileSync59, writeFileSync as writeFileSync48 } from "node:fs";
|
|
103125
|
+
import { join as join68 } from "node:path";
|
|
103126
|
+
|
|
103127
|
+
// gateway/resume-inbound-builder.ts
|
|
103128
|
+
function humanizeElapsed2(ms) {
|
|
103129
|
+
if (!Number.isFinite(ms) || ms < 0)
|
|
103130
|
+
return "an unknown amount of time";
|
|
103131
|
+
const sec = Math.round(ms / 1000);
|
|
103132
|
+
if (sec < 45)
|
|
103133
|
+
return "moments";
|
|
103134
|
+
const min = Math.round(sec / 60);
|
|
103135
|
+
if (min < 60)
|
|
103136
|
+
return `~${min} min`;
|
|
103137
|
+
const hr = Math.round(min / 60);
|
|
103138
|
+
if (hr < 24)
|
|
103139
|
+
return `~${hr}h`;
|
|
103140
|
+
const days = Math.round(hr / 24);
|
|
103141
|
+
return `~${days} day${days === 1 ? "" : "s"}`;
|
|
103142
|
+
}
|
|
103143
|
+
var RESUME_SYNTHETIC_PROMPT_PREFIX2 = "You just restarted.";
|
|
103144
|
+
|
|
103145
|
+
// gateway/boot-briefing-builder.ts
|
|
103146
|
+
var BOOT_BRIEFING_SOURCE = "boot_briefing";
|
|
103147
|
+
var BRIEFING_CHAR_BUDGET = 7000;
|
|
103148
|
+
var BRIEFING_PER_MESSAGE_MAX_CHARS = 400;
|
|
103149
|
+
var BRIEFING_PRIMARY_DEPTH = 15;
|
|
103150
|
+
var BRIEFING_ACTIVE_WINDOW_MS = 48 * 60 * 60 * 1000;
|
|
103151
|
+
var BRIEFING_MAX_SURFACES = 8;
|
|
103152
|
+
var BRIEFING_TTL_MS = 60 * 60 * 1000;
|
|
103153
|
+
var HISTORY_SELFCHECK_CHAT = "__history_selfcheck__";
|
|
103154
|
+
function excludeWindowFromResumeInbound(msg) {
|
|
103155
|
+
if (msg == null)
|
|
103156
|
+
return null;
|
|
103157
|
+
const chatId = msg.meta?.chat_id;
|
|
103158
|
+
const startedAt = Number(msg.meta?.started_at);
|
|
103159
|
+
if (typeof chatId !== "string" || chatId.length === 0)
|
|
103160
|
+
return null;
|
|
103161
|
+
if (!Number.isFinite(startedAt) || startedAt <= 0)
|
|
103162
|
+
return null;
|
|
103163
|
+
const threadRaw = msg.meta?.message_thread_id;
|
|
103164
|
+
const threadNum = threadRaw != null && threadRaw !== "" ? Number(threadRaw) : null;
|
|
103165
|
+
return {
|
|
103166
|
+
chatId,
|
|
103167
|
+
threadId: threadNum != null && Number.isFinite(threadNum) ? threadNum : null,
|
|
103168
|
+
sinceMs: startedAt
|
|
103169
|
+
};
|
|
103170
|
+
}
|
|
103171
|
+
function sameSurface(a, b) {
|
|
103172
|
+
return a.chatId === b.chatId && (a.threadId ?? null) === (b.threadId ?? null);
|
|
103173
|
+
}
|
|
103174
|
+
function collectBriefingSurfaces(db3, opts) {
|
|
103175
|
+
const activeWindowMs = opts.activeWindowMs ?? BRIEFING_ACTIVE_WINDOW_MS;
|
|
103176
|
+
const primaryDepth = opts.primaryDepth ?? BRIEFING_PRIMARY_DEPTH;
|
|
103177
|
+
const maxSurfaces = opts.maxSurfaces ?? BRIEFING_MAX_SURFACES;
|
|
103178
|
+
const cutoffSec = Math.floor((opts.nowMs - activeWindowMs) / 1000);
|
|
103179
|
+
try {
|
|
103180
|
+
const surfaceRows = db3.prepare(`SELECT chat_id, thread_id, MAX(ts) AS last_ts
|
|
103181
|
+
FROM messages
|
|
103182
|
+
WHERE role IN ('user','assistant')
|
|
103183
|
+
AND ts >= ?
|
|
103184
|
+
AND chat_id <> ?
|
|
103185
|
+
GROUP BY chat_id, thread_id
|
|
103186
|
+
ORDER BY last_ts DESC
|
|
103187
|
+
LIMIT ?`).all(cutoffSec, HISTORY_SELFCHECK_CHAT, maxSurfaces);
|
|
103188
|
+
const out = [];
|
|
103189
|
+
for (let i = 0;i < surfaceRows.length; i++) {
|
|
103190
|
+
const s = surfaceRows[i];
|
|
103191
|
+
const depth = i === 0 ? primaryDepth : 1;
|
|
103192
|
+
const threadClause = s.thread_id == null ? "thread_id IS NULL" : "thread_id = ?";
|
|
103193
|
+
const params = [s.chat_id];
|
|
103194
|
+
if (s.thread_id != null)
|
|
103195
|
+
params.push(s.thread_id);
|
|
103196
|
+
params.push(depth);
|
|
103197
|
+
const msgRows = db3.prepare(`SELECT role, user, ts, text
|
|
103198
|
+
FROM messages
|
|
103199
|
+
WHERE chat_id = ? AND ${threadClause}
|
|
103200
|
+
AND role IN ('user','assistant')
|
|
103201
|
+
ORDER BY ts DESC, message_id DESC
|
|
103202
|
+
LIMIT ?`).all(...params);
|
|
103203
|
+
msgRows.reverse();
|
|
103204
|
+
let messages = msgRows.map((r) => ({
|
|
103205
|
+
role: r.role,
|
|
103206
|
+
user: r.user ?? null,
|
|
103207
|
+
ts: r.ts,
|
|
103208
|
+
text: r.text ?? ""
|
|
103209
|
+
}));
|
|
103210
|
+
const ex = opts.exclude;
|
|
103211
|
+
if (ex != null && sameSurface({ chatId: s.chat_id, threadId: s.thread_id ?? null }, ex)) {
|
|
103212
|
+
const sinceSec = Math.floor(ex.sinceMs / 1000);
|
|
103213
|
+
messages = messages.filter((m) => m.ts < sinceSec);
|
|
103214
|
+
}
|
|
103215
|
+
if (messages.length === 0)
|
|
103216
|
+
continue;
|
|
103217
|
+
out.push({
|
|
103218
|
+
chatId: s.chat_id,
|
|
103219
|
+
threadId: s.thread_id ?? null,
|
|
103220
|
+
lastTs: s.last_ts,
|
|
103221
|
+
messages
|
|
103222
|
+
});
|
|
103223
|
+
}
|
|
103224
|
+
return out;
|
|
103225
|
+
} catch {
|
|
103226
|
+
return [];
|
|
103227
|
+
}
|
|
103228
|
+
}
|
|
103229
|
+
function truncateOneLine(s, max) {
|
|
103230
|
+
const t = s.replace(/\s+/g, " ").trim();
|
|
103231
|
+
if (t.length <= max)
|
|
103232
|
+
return t;
|
|
103233
|
+
const points = Array.from(t);
|
|
103234
|
+
if (points.length <= max)
|
|
103235
|
+
return t;
|
|
103236
|
+
return points.slice(0, max - 1).join("").trimEnd() + "\u2026";
|
|
103237
|
+
}
|
|
103238
|
+
function surfaceLabel(s) {
|
|
103239
|
+
return s.threadId != null ? `chat ${s.chatId}, topic ${s.threadId}` : `chat ${s.chatId}`;
|
|
103240
|
+
}
|
|
103241
|
+
function renderMessageLine(m, nowMs3, perMessageMax) {
|
|
103242
|
+
const label = m.role === "user" ? m.user && m.user.trim() ? m.user.trim() : "user" : "you";
|
|
103243
|
+
const age = humanizeElapsed2(Math.max(0, nowMs3 - m.ts * 1000));
|
|
103244
|
+
return `- [${age} ago] ${label}: ${truncateOneLine(m.text, perMessageMax)}`;
|
|
103245
|
+
}
|
|
103246
|
+
function renderBootBriefing(surfaces, opts) {
|
|
103247
|
+
if (surfaces.length === 0)
|
|
103248
|
+
return "";
|
|
103249
|
+
const charBudget = opts.charBudget ?? BRIEFING_CHAR_BUDGET;
|
|
103250
|
+
const perMessageMax = opts.perMessageMax ?? BRIEFING_PER_MESSAGE_MAX_CHARS;
|
|
103251
|
+
const reasonClause = opts.restartReason && opts.restartReason.trim() ? ` The previous session ended via: ${truncateOneLine(opts.restartReason, 120)}.` : "";
|
|
103252
|
+
const header = `${RESUME_SYNTHETIC_PROMPT_PREFIX2} This is an automatic boot briefing assembled ` + `from your durable message history \u2014 context to reorient you, NOT a new user ` + `request.${reasonClause} Read it, then: if nothing in it is unfinished or owed, ` + `do NOT message the user (end the turn with NO_REPLY); if something was clearly ` + `left unfinished or owed, briefly pick it up. The full history is available via ` + `get_recent_messages.`;
|
|
103253
|
+
const primary = surfaces[0];
|
|
103254
|
+
const primaryTitle = `## Active conversation \u2014 ${surfaceLabel(primary)} ` + `(last active ${humanizeElapsed2(Math.max(0, opts.nowMs - primary.lastTs * 1000))} ago)`;
|
|
103255
|
+
const primaryLines = primary.messages.map((m) => renderMessageLine(m, opts.nowMs, perMessageMax));
|
|
103256
|
+
const assemble = (lines, secondaries2) => {
|
|
103257
|
+
const parts = [header, "", primaryTitle, ...lines];
|
|
103258
|
+
if (secondaries2.length > 0) {
|
|
103259
|
+
parts.push("", "## Other recent surfaces (active in the last 48h)", ...secondaries2);
|
|
103260
|
+
}
|
|
103261
|
+
return parts.join(`
|
|
103262
|
+
`);
|
|
103263
|
+
};
|
|
103264
|
+
const kept = [...primaryLines];
|
|
103265
|
+
while (kept.length > 1 && assemble(kept, []).length > charBudget) {
|
|
103266
|
+
kept.shift();
|
|
103267
|
+
}
|
|
103268
|
+
if (assemble(kept, []).length > charBudget) {
|
|
103269
|
+
return assemble(kept, []).slice(0, charBudget);
|
|
103270
|
+
}
|
|
103271
|
+
const secondaries = [];
|
|
103272
|
+
for (const s of surfaces.slice(1)) {
|
|
103273
|
+
const last = s.messages[s.messages.length - 1];
|
|
103274
|
+
const block = `- ${surfaceLabel(s)} \u2014 last active ` + `${humanizeElapsed2(Math.max(0, opts.nowMs - s.lastTs * 1000))} ago:
|
|
103275
|
+
` + ` ${renderMessageLine(last, opts.nowMs, perMessageMax).slice(2)}`;
|
|
103276
|
+
if (assemble(kept, [...secondaries, block]).length > charBudget)
|
|
103277
|
+
break;
|
|
103278
|
+
secondaries.push(block);
|
|
103279
|
+
}
|
|
103280
|
+
return assemble(kept, secondaries);
|
|
103281
|
+
}
|
|
103282
|
+
function readRestartBreadcrumb(opts) {
|
|
103283
|
+
let reason = null;
|
|
103284
|
+
if (opts.restartReasonPath) {
|
|
103285
|
+
try {
|
|
103286
|
+
const raw = opts.readFile(opts.restartReasonPath);
|
|
103287
|
+
const first = raw.split(`
|
|
103288
|
+
`)[0]?.replace(/\r/g, "").trim();
|
|
103289
|
+
if (first)
|
|
103290
|
+
reason = first;
|
|
103291
|
+
} catch {}
|
|
103292
|
+
}
|
|
103293
|
+
const envVia = opts.env.SWITCHROOM_PENDING_ENDED_VIA;
|
|
103294
|
+
if (typeof envVia === "string" && envVia.trim().length > 0)
|
|
103295
|
+
reason = envVia.trim();
|
|
103296
|
+
return reason;
|
|
103297
|
+
}
|
|
103298
|
+
function decideBootBriefing(opts) {
|
|
103299
|
+
if (opts.briefingMode !== "gateway")
|
|
103300
|
+
return { build: false, reason: "flag-legacy" };
|
|
103301
|
+
if (opts.forceFreshMarker)
|
|
103302
|
+
return { build: false, reason: "force-fresh" };
|
|
103303
|
+
if (opts.resumeMode === "continue" || opts.resumeMode === "auto") {
|
|
103304
|
+
return { build: false, reason: "transcript-replay-possible" };
|
|
103305
|
+
}
|
|
103306
|
+
return { build: true, reason: "ok" };
|
|
103307
|
+
}
|
|
103308
|
+
function buildBootBriefingInbound(args) {
|
|
103309
|
+
const ts = args.nowMs ?? Date.now();
|
|
103310
|
+
const ttlMs = args.ttlMs ?? BRIEFING_TTL_MS;
|
|
103311
|
+
const meta = {
|
|
103312
|
+
source: BOOT_BRIEFING_SOURCE,
|
|
103313
|
+
chat_id: args.chatId,
|
|
103314
|
+
...args.threadId != null ? { message_thread_id: String(args.threadId) } : {},
|
|
103315
|
+
message_id: String(ts),
|
|
103316
|
+
expiresAt: String(ts + ttlMs)
|
|
103317
|
+
};
|
|
103318
|
+
return {
|
|
103319
|
+
type: "inbound",
|
|
103320
|
+
chatId: args.chatId,
|
|
103321
|
+
...args.threadId != null ? { threadId: args.threadId } : {},
|
|
103322
|
+
messageId: ts,
|
|
103323
|
+
user: "switchroom",
|
|
103324
|
+
userId: 0,
|
|
103325
|
+
ts,
|
|
103326
|
+
text: args.text,
|
|
103327
|
+
meta
|
|
103328
|
+
};
|
|
103329
|
+
}
|
|
103330
|
+
|
|
103331
|
+
// gateway/boot-briefing-wiring.ts
|
|
103332
|
+
function maybeQueueBootBriefing(opts) {
|
|
103333
|
+
const log = opts.log ?? ((l) => process.stderr.write(l));
|
|
103334
|
+
try {
|
|
103335
|
+
const agentDir = opts.stateDir.endsWith("/telegram") ? opts.stateDir.slice(0, -"/telegram".length) : opts.stateDir;
|
|
103336
|
+
const bootId = opts.env.SWITCHROOM_GATEWAY_BOOT_ID;
|
|
103337
|
+
const genMarkerPath = join68(agentDir, ".boot-briefing-generation");
|
|
103338
|
+
if (bootId) {
|
|
103339
|
+
let prevGen = null;
|
|
103340
|
+
try {
|
|
103341
|
+
prevGen = readFileSync59(genMarkerPath, "utf8").trim();
|
|
103342
|
+
} catch {
|
|
103343
|
+
prevGen = null;
|
|
103344
|
+
}
|
|
103345
|
+
if (prevGen === bootId) {
|
|
103346
|
+
log(`telegram gateway: boot-briefing suppressed (supervisor respawn \u2014 this boot generation already briefed)
|
|
103347
|
+
`);
|
|
103348
|
+
return null;
|
|
103349
|
+
}
|
|
103350
|
+
}
|
|
103351
|
+
const markGeneration = () => {
|
|
103352
|
+
if (!bootId)
|
|
103353
|
+
return;
|
|
103354
|
+
try {
|
|
103355
|
+
writeFileSync48(genMarkerPath, `${bootId}
|
|
103356
|
+
`);
|
|
103357
|
+
} catch {}
|
|
103358
|
+
};
|
|
103359
|
+
const forceFresh = opts.env.SWITCHROOM_FORCE_FRESH === "1" || existsSync56(join68(agentDir, ".force-fresh-session"));
|
|
103360
|
+
const decision = decideBootBriefing({
|
|
103361
|
+
briefingMode: opts.env.SWITCHROOM_SESSION_BRIEFING,
|
|
103362
|
+
resumeMode: opts.env.SWITCHROOM_RESUME_MODE,
|
|
103363
|
+
forceFreshMarker: forceFresh
|
|
103364
|
+
});
|
|
103365
|
+
if (!decision.build) {
|
|
103366
|
+
if (decision.reason !== "flag-legacy") {
|
|
103367
|
+
log(`telegram gateway: boot-briefing suppressed (${decision.reason})
|
|
103368
|
+
`);
|
|
103369
|
+
}
|
|
103370
|
+
return null;
|
|
103371
|
+
}
|
|
103372
|
+
const selfAgent = opts.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
103373
|
+
if (!selfAgent)
|
|
103374
|
+
return null;
|
|
103375
|
+
const db3 = getHistoryDbForBriefing();
|
|
103376
|
+
if (db3 == null) {
|
|
103377
|
+
log(`telegram gateway: boot-briefing skipped \u2014 history DB unavailable
|
|
103378
|
+
`);
|
|
103379
|
+
return null;
|
|
103380
|
+
}
|
|
103381
|
+
const nowMs3 = opts.nowMs ?? Date.now();
|
|
103382
|
+
const surfaces = collectBriefingSurfaces(db3, {
|
|
103383
|
+
nowMs: nowMs3,
|
|
103384
|
+
exclude: excludeWindowFromResumeInbound(opts.resumeMsg)
|
|
103385
|
+
});
|
|
103386
|
+
const restartReason = readRestartBreadcrumb({
|
|
103387
|
+
restartReasonPath: join68(agentDir, ".restart-reason"),
|
|
103388
|
+
env: opts.env,
|
|
103389
|
+
readFile: (p) => readFileSync59(p, "utf8")
|
|
103390
|
+
});
|
|
103391
|
+
const text5 = renderBootBriefing(surfaces, { nowMs: nowMs3, restartReason });
|
|
103392
|
+
if (!text5) {
|
|
103393
|
+
markGeneration();
|
|
103394
|
+
log(`telegram gateway: boot-briefing empty (no recent surfaces) \u2014 nothing queued
|
|
103395
|
+
`);
|
|
103396
|
+
return null;
|
|
103397
|
+
}
|
|
103398
|
+
const primary = surfaces[0];
|
|
103399
|
+
const msg = buildBootBriefingInbound({
|
|
103400
|
+
chatId: primary.chatId,
|
|
103401
|
+
threadId: primary.threadId,
|
|
103402
|
+
text: text5,
|
|
103403
|
+
nowMs: nowMs3
|
|
103404
|
+
});
|
|
103405
|
+
opts.put(selfAgent, msg);
|
|
103406
|
+
markGeneration();
|
|
103407
|
+
log(`telegram gateway: boot-briefing queued chat=${primary.chatId}` + `${primary.threadId != null ? ` thread=${primary.threadId}` : ""} ` + `surfaces=${surfaces.length} chars=${text5.length}
|
|
103408
|
+
`);
|
|
103409
|
+
return msg;
|
|
103410
|
+
} catch (err) {
|
|
103411
|
+
log(`telegram gateway: boot-briefing failed (${err.message}) \u2014 continuing without briefing
|
|
103412
|
+
`);
|
|
103413
|
+
return null;
|
|
103414
|
+
}
|
|
103415
|
+
}
|
|
103416
|
+
|
|
103417
|
+
// gateway/pending-turn-env.ts
|
|
103418
|
+
import { existsSync as existsSync57, renameSync as renameSync26, rmSync as rmSync8, writeFileSync as writeFileSync49 } from "node:fs";
|
|
103419
|
+
import { join as join69 } from "node:path";
|
|
103420
|
+
function writePendingTurnEnv(agentDir, pending, log = (l) => process.stderr.write(l)) {
|
|
103421
|
+
const pendingEnvPath = join69(agentDir, ".pending-turn.env");
|
|
103422
|
+
try {
|
|
103423
|
+
if (pending != null) {
|
|
103424
|
+
const lines = [
|
|
103425
|
+
`SWITCHROOM_PENDING_TURN=true`,
|
|
103426
|
+
`SWITCHROOM_PENDING_TURN_KEY=${pending.turn_key}`,
|
|
103427
|
+
`SWITCHROOM_PENDING_CHAT_ID=${pending.chat_id}`,
|
|
103428
|
+
pending.thread_id != null ? `SWITCHROOM_PENDING_THREAD_ID=${pending.thread_id}` : `SWITCHROOM_PENDING_THREAD_ID=`,
|
|
103429
|
+
pending.last_user_msg_id != null ? `SWITCHROOM_PENDING_USER_MSG_ID=${pending.last_user_msg_id}` : `SWITCHROOM_PENDING_USER_MSG_ID=`,
|
|
103430
|
+
`SWITCHROOM_PENDING_ENDED_VIA=${pending.ended_via ?? "unknown"}`,
|
|
103431
|
+
`SWITCHROOM_PENDING_STARTED_AT=${pending.started_at}`,
|
|
103432
|
+
pending.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
|
|
103433
|
+
];
|
|
103434
|
+
const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
|
|
103435
|
+
writeFileSync49(pendingEnvTmp, lines.join(`
|
|
103436
|
+
`) + `
|
|
103437
|
+
`, { mode: 384 });
|
|
103438
|
+
renameSync26(pendingEnvTmp, pendingEnvPath);
|
|
103439
|
+
log(`telegram gateway: pending-turn env written to ${pendingEnvPath} ` + `turnKey=${pending.turn_key} endedVia=${pending.ended_via ?? "open"}
|
|
103440
|
+
`);
|
|
103441
|
+
} else if (existsSync57(pendingEnvPath)) {
|
|
103442
|
+
rmSync8(pendingEnvPath, { force: true });
|
|
103443
|
+
log(`telegram gateway: pending-turn env cleared (clean previous shutdown)
|
|
103444
|
+
`);
|
|
103445
|
+
}
|
|
103446
|
+
} catch (err) {
|
|
103447
|
+
log(`telegram gateway: pending-turn env write failed (${err.message})
|
|
103448
|
+
`);
|
|
103449
|
+
}
|
|
103450
|
+
}
|
|
103451
|
+
|
|
102529
103452
|
// gateway/bridge-dead-watchdog.ts
|
|
102530
|
-
import { readFileSync as
|
|
103453
|
+
import { readFileSync as readFileSync60, writeFileSync as writeFileSync50, renameSync as renameSync27, unlinkSync as unlinkSync29 } from "node:fs";
|
|
102531
103454
|
|
|
102532
103455
|
// gateway/cron-session.ts
|
|
102533
103456
|
var CRON_IDENTITY_SUFFIX2 = "-cron";
|
|
@@ -102560,7 +103483,7 @@ function readFreshCrashLogTail(path3, opts = {}) {
|
|
|
102560
103483
|
const nowMs3 = opts.nowMs ?? Date.now();
|
|
102561
103484
|
const freshWindowMs = opts.freshWindowMs ?? CRASH_LOG_FRESH_WINDOW_MS;
|
|
102562
103485
|
const maxLines = opts.maxLines ?? CRASH_LOG_TAIL_LINES;
|
|
102563
|
-
const readFile = opts.readFile ?? ((p) =>
|
|
103486
|
+
const readFile = opts.readFile ?? ((p) => readFileSync60(p, "utf8"));
|
|
102564
103487
|
let raw;
|
|
102565
103488
|
try {
|
|
102566
103489
|
raw = readFile(path3);
|
|
@@ -102581,13 +103504,13 @@ function readFreshCrashLogTail(path3, opts = {}) {
|
|
|
102581
103504
|
}
|
|
102582
103505
|
function writeBridgeDeadEscalationMarker(path3, marker) {
|
|
102583
103506
|
const tmp = `${path3}.tmp-${process.pid}-${Date.now()}`;
|
|
102584
|
-
|
|
102585
|
-
|
|
103507
|
+
writeFileSync50(tmp, JSON.stringify(marker), "utf8");
|
|
103508
|
+
renameSync27(tmp, path3);
|
|
102586
103509
|
}
|
|
102587
103510
|
function consumeBridgeDeadEscalationMarker(path3, nowMs3 = Date.now(), maxAgeMs = ESCALATION_MARKER_MAX_AGE_MS) {
|
|
102588
103511
|
let marker = null;
|
|
102589
103512
|
try {
|
|
102590
|
-
const parsed = JSON.parse(
|
|
103513
|
+
const parsed = JSON.parse(readFileSync60(path3, "utf8"));
|
|
102591
103514
|
if (typeof parsed.ts === "number" && Number.isFinite(parsed.ts) && typeof parsed.reason === "string") {
|
|
102592
103515
|
const age = nowMs3 - parsed.ts;
|
|
102593
103516
|
if (age >= 0 && age < maxAgeMs) {
|
|
@@ -102740,7 +103663,7 @@ function createBridgeDeadWatchdog(opts) {
|
|
|
102740
103663
|
}
|
|
102741
103664
|
|
|
102742
103665
|
// gateway/boot-probes.ts
|
|
102743
|
-
import { readFileSync as
|
|
103666
|
+
import { readFileSync as readFileSync61, readdirSync as readdirSync14, existsSync as existsSync58 } from "fs";
|
|
102744
103667
|
init_quota_cache();
|
|
102745
103668
|
init_generation_stamp();
|
|
102746
103669
|
init_quota_check();
|
|
@@ -102749,7 +103672,7 @@ import { promisify as promisify2 } from "util";
|
|
|
102749
103672
|
var execFile2 = promisify2(execFileCb2);
|
|
102750
103673
|
var realProcFs2 = {
|
|
102751
103674
|
readdir: (p) => readdirSync14(p),
|
|
102752
|
-
readFile: (p) =>
|
|
103675
|
+
readFile: (p) => readFileSync61(p, "utf-8")
|
|
102753
103676
|
};
|
|
102754
103677
|
function findAgentProcessInContainer2(fs4 = realProcFs2) {
|
|
102755
103678
|
let entries;
|
|
@@ -102999,7 +103922,7 @@ if (isGatewayMain) {
|
|
|
102999
103922
|
shutdownAnalytics();
|
|
103000
103923
|
});
|
|
103001
103924
|
}
|
|
103002
|
-
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ??
|
|
103925
|
+
var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join70(homedir20(), ".claude", "channels", "telegram");
|
|
103003
103926
|
var permCardStore = createPermissionCardStore(STATE_DIR);
|
|
103004
103927
|
var BLOCKED_APPROVALS_DIR = process.env.SWITCHROOM_BLOCKED_APPROVALS_DIR ?? "/state/blocked-approvals";
|
|
103005
103928
|
var AGENT_NAME = process.env.SWITCHROOM_AGENT_NAME ?? "agent";
|
|
@@ -103038,7 +103961,7 @@ function alwaysAllowDrainDeps() {
|
|
|
103038
103961
|
return {
|
|
103039
103962
|
readConfigText: () => {
|
|
103040
103963
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
103041
|
-
return
|
|
103964
|
+
return readFileSync62(cfgPath, "utf8");
|
|
103042
103965
|
},
|
|
103043
103966
|
resolveAllowList: (_configText, agentName3) => {
|
|
103044
103967
|
const cfg = loadConfig2();
|
|
@@ -103099,11 +104022,11 @@ function scheduleAlwaysAllowPersistDrain() {
|
|
|
103099
104022
|
}, ALWAYS_ALLOW_DRAIN_INTERVAL_MS);
|
|
103100
104023
|
timer3.unref?.();
|
|
103101
104024
|
}
|
|
103102
|
-
var ACCESS_FILE =
|
|
103103
|
-
var APPROVED_DIR =
|
|
103104
|
-
var ENV_FILE =
|
|
103105
|
-
var INBOX_DIR =
|
|
103106
|
-
var PEOPLE_FILE =
|
|
104025
|
+
var ACCESS_FILE = join70(STATE_DIR, "access.json");
|
|
104026
|
+
var APPROVED_DIR = join70(STATE_DIR, "approved");
|
|
104027
|
+
var ENV_FILE = join70(STATE_DIR, ".env");
|
|
104028
|
+
var INBOX_DIR = join70(STATE_DIR, "inbox");
|
|
104029
|
+
var PEOPLE_FILE = join70(STATE_DIR, "people.json");
|
|
103107
104030
|
function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
|
|
103108
104031
|
const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
|
|
103109
104032
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
|
|
@@ -103168,7 +104091,7 @@ function formatBootVersion() {
|
|
|
103168
104091
|
}
|
|
103169
104092
|
try {
|
|
103170
104093
|
chmodSync14(ENV_FILE, 384);
|
|
103171
|
-
for (const line of
|
|
104094
|
+
for (const line of readFileSync62(ENV_FILE, "utf8").split(`
|
|
103172
104095
|
`)) {
|
|
103173
104096
|
const m = line.match(/^(\w+)=(.*)$/);
|
|
103174
104097
|
if (m && process.env[m[1]] === undefined)
|
|
@@ -103189,7 +104112,7 @@ var bot;
|
|
|
103189
104112
|
var lastGetUpdatesHeartbeatMs = Date.now();
|
|
103190
104113
|
var GRAMMY_VERSION = (() => {
|
|
103191
104114
|
try {
|
|
103192
|
-
const raw =
|
|
104115
|
+
const raw = readFileSync62(new URL("../../node_modules/grammy/package.json", import.meta.url), "utf8");
|
|
103193
104116
|
return JSON.parse(raw).version ?? "unknown";
|
|
103194
104117
|
} catch {
|
|
103195
104118
|
return "unknown";
|
|
@@ -103233,7 +104156,7 @@ function assertSendable(f) {
|
|
|
103233
104156
|
} catch {
|
|
103234
104157
|
throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
|
|
103235
104158
|
}
|
|
103236
|
-
const inbox =
|
|
104159
|
+
const inbox = join70(stateReal, "inbox");
|
|
103237
104160
|
if (real.startsWith(stateReal + sep4) && !real.startsWith(inbox + sep4)) {
|
|
103238
104161
|
throw new Error(`refusing to send channel state: ${f}`);
|
|
103239
104162
|
}
|
|
@@ -103252,7 +104175,7 @@ function assertSendable(f) {
|
|
|
103252
104175
|
}
|
|
103253
104176
|
function readAccessFile() {
|
|
103254
104177
|
try {
|
|
103255
|
-
const raw =
|
|
104178
|
+
const raw = readFileSync62(ACCESS_FILE, "utf8");
|
|
103256
104179
|
const parsed = JSON.parse(raw);
|
|
103257
104180
|
const allowFrom = validateStringArray("allowFrom", parsed.allowFrom ?? []);
|
|
103258
104181
|
const groups = {};
|
|
@@ -103292,7 +104215,7 @@ function readAccessFile() {
|
|
|
103292
104215
|
if (err.code === "ENOENT")
|
|
103293
104216
|
return defaultAccess();
|
|
103294
104217
|
try {
|
|
103295
|
-
|
|
104218
|
+
renameSync28(ACCESS_FILE, `${ACCESS_FILE}.corrupt-${Date.now()}`);
|
|
103296
104219
|
} catch {}
|
|
103297
104220
|
process.stderr.write(`telegram gateway: access.json is corrupt, moved aside. Starting fresh.
|
|
103298
104221
|
`);
|
|
@@ -103314,7 +104237,7 @@ function loadAccess() {
|
|
|
103314
104237
|
}
|
|
103315
104238
|
function readPeopleFile() {
|
|
103316
104239
|
try {
|
|
103317
|
-
const raw =
|
|
104240
|
+
const raw = readFileSync62(PEOPLE_FILE, "utf8");
|
|
103318
104241
|
const parsed = JSON.parse(raw);
|
|
103319
104242
|
if (!Array.isArray(parsed.entries))
|
|
103320
104243
|
return [];
|
|
@@ -103338,9 +104261,9 @@ function saveAccess(a) {
|
|
|
103338
104261
|
return;
|
|
103339
104262
|
mkdirSync51(STATE_DIR, { recursive: true, mode: 448 });
|
|
103340
104263
|
const tmp = ACCESS_FILE + ".tmp";
|
|
103341
|
-
|
|
104264
|
+
writeFileSync52(tmp, JSON.stringify(a, null, 2) + `
|
|
103342
104265
|
`, { mode: 384 });
|
|
103343
|
-
|
|
104266
|
+
renameSync28(tmp, ACCESS_FILE);
|
|
103344
104267
|
}
|
|
103345
104268
|
function pruneExpired(a) {
|
|
103346
104269
|
const now = Date.now();
|
|
@@ -103358,7 +104281,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
|
|
|
103358
104281
|
if (isGatewayMain && HISTORY_ENABLED) {
|
|
103359
104282
|
try {
|
|
103360
104283
|
initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
|
|
103361
|
-
process.stderr.write(`telegram gateway: history capture enabled at ${
|
|
104284
|
+
process.stderr.write(`telegram gateway: history capture enabled at ${join70(STATE_DIR, "history.db")}
|
|
103362
104285
|
`);
|
|
103363
104286
|
} catch (err) {
|
|
103364
104287
|
process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
|
|
@@ -103377,12 +104300,12 @@ if (isGatewayMain)
|
|
|
103377
104300
|
let markerTurnKey = null;
|
|
103378
104301
|
let markerAgeMs = null;
|
|
103379
104302
|
try {
|
|
103380
|
-
const markerPath =
|
|
103381
|
-
if (
|
|
104303
|
+
const markerPath = join70(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
|
|
104304
|
+
if (existsSync60(markerPath)) {
|
|
103382
104305
|
const st = statSync23(markerPath);
|
|
103383
104306
|
markerAgeMs = Date.now() - st.mtimeMs;
|
|
103384
104307
|
try {
|
|
103385
|
-
const payload = JSON.parse(
|
|
104308
|
+
const payload = JSON.parse(readFileSync62(markerPath, "utf8"));
|
|
103386
104309
|
if (typeof payload.turnKey === "string" && payload.turnKey.length > 0) {
|
|
103387
104310
|
markerTurnKey = payload.turnKey;
|
|
103388
104311
|
}
|
|
@@ -103402,10 +104325,10 @@ if (isGatewayMain)
|
|
|
103402
104325
|
process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)` + `${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
|
|
103403
104326
|
`);
|
|
103404
104327
|
} else {
|
|
103405
|
-
process.stderr.write(`telegram gateway: turn-registry initialized at ${
|
|
104328
|
+
process.stderr.write(`telegram gateway: turn-registry initialized at ${join70(agentDir, "telegram", "registry.db")}
|
|
103406
104329
|
`);
|
|
103407
104330
|
}
|
|
103408
|
-
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(
|
|
104331
|
+
const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join70(STATE_DIR, "bridge-dead-escalation.json"));
|
|
103409
104332
|
if (bridgeDeadMarker != null) {
|
|
103410
104333
|
bridgeDeadPriorStreak = bridgeDeadMarker.count ?? 1;
|
|
103411
104334
|
process.stderr.write(`telegram gateway: boot: prior restart was a bridge-dead escalation (reason=${bridgeDeadMarker.reason}` + `, consecutive=${bridgeDeadPriorStreak}` + `${bridgeDeadMarker.crashTail ? `, crashTail=${bridgeDeadMarker.crashTail}` : ""})
|
|
@@ -103418,7 +104341,7 @@ if (isGatewayMain)
|
|
|
103418
104341
|
const pending2 = findLatestTurnIfInterrupted(turnsDb);
|
|
103419
104342
|
const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
103420
104343
|
if (pending2 != null && selfAgent) {
|
|
103421
|
-
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
104344
|
+
const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join70(STATE_DIR, "clean-shutdown.json");
|
|
103422
104345
|
const bootResumeCleanMarker = readCleanShutdownMarker(bootResumeMarkerPath);
|
|
103423
104346
|
const bootResumeForceAlways = process.env.SWITCHROOM_BOOT_RESUME_ALWAYS === "1";
|
|
103424
104347
|
const bootResumeMode = parseBootResumeMode(process.env.SWITCHROOM_BOOT_RESUME);
|
|
@@ -103531,35 +104454,7 @@ if (isGatewayMain)
|
|
|
103531
104454
|
`);
|
|
103532
104455
|
}
|
|
103533
104456
|
}
|
|
103534
|
-
|
|
103535
|
-
try {
|
|
103536
|
-
if (pending2 != null) {
|
|
103537
|
-
const lines = [
|
|
103538
|
-
`SWITCHROOM_PENDING_TURN=true`,
|
|
103539
|
-
`SWITCHROOM_PENDING_TURN_KEY=${pending2.turn_key}`,
|
|
103540
|
-
`SWITCHROOM_PENDING_CHAT_ID=${pending2.chat_id}`,
|
|
103541
|
-
pending2.thread_id != null ? `SWITCHROOM_PENDING_THREAD_ID=${pending2.thread_id}` : `SWITCHROOM_PENDING_THREAD_ID=`,
|
|
103542
|
-
pending2.last_user_msg_id != null ? `SWITCHROOM_PENDING_USER_MSG_ID=${pending2.last_user_msg_id}` : `SWITCHROOM_PENDING_USER_MSG_ID=`,
|
|
103543
|
-
`SWITCHROOM_PENDING_ENDED_VIA=${pending2.ended_via ?? "unknown"}`,
|
|
103544
|
-
`SWITCHROOM_PENDING_STARTED_AT=${pending2.started_at}`,
|
|
103545
|
-
pending2.interrupt_reason != null ? `SWITCHROOM_PENDING_INTERRUPT_REASON=${pending2.interrupt_reason}` : `SWITCHROOM_PENDING_INTERRUPT_REASON=`
|
|
103546
|
-
];
|
|
103547
|
-
const pendingEnvTmp = `${pendingEnvPath}.tmp-${process.pid}`;
|
|
103548
|
-
writeFileSync50(pendingEnvTmp, lines.join(`
|
|
103549
|
-
`) + `
|
|
103550
|
-
`, { mode: 384 });
|
|
103551
|
-
renameSync27(pendingEnvTmp, pendingEnvPath);
|
|
103552
|
-
process.stderr.write(`telegram gateway: pending-turn env written to ${pendingEnvPath} turnKey=${pending2.turn_key} endedVia=${pending2.ended_via ?? "open"}
|
|
103553
|
-
`);
|
|
103554
|
-
} else if (existsSync58(pendingEnvPath)) {
|
|
103555
|
-
rmSync8(pendingEnvPath, { force: true });
|
|
103556
|
-
process.stderr.write(`telegram gateway: pending-turn env cleared (clean previous shutdown)
|
|
103557
|
-
`);
|
|
103558
|
-
}
|
|
103559
|
-
} catch (err) {
|
|
103560
|
-
process.stderr.write(`telegram gateway: pending-turn env write failed (${err.message})
|
|
103561
|
-
`);
|
|
103562
|
-
}
|
|
104457
|
+
writePendingTurnEnv(agentDir, pending2);
|
|
103563
104458
|
} catch (err) {
|
|
103564
104459
|
process.stderr.write(`telegram gateway: turn-registry init failed (${err.message}) \u2014 turn tracking disabled
|
|
103565
104460
|
`);
|
|
@@ -103659,11 +104554,11 @@ function checkApprovals() {
|
|
|
103659
104554
|
return;
|
|
103660
104555
|
}
|
|
103661
104556
|
for (const senderId of files) {
|
|
103662
|
-
const file =
|
|
103663
|
-
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() =>
|
|
104557
|
+
const file = join70(APPROVED_DIR, senderId);
|
|
104558
|
+
bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync9(file, { force: true }), (err) => {
|
|
103664
104559
|
process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
|
|
103665
104560
|
`);
|
|
103666
|
-
|
|
104561
|
+
rmSync9(file, { force: true });
|
|
103667
104562
|
});
|
|
103668
104563
|
}
|
|
103669
104564
|
}
|
|
@@ -103837,12 +104732,12 @@ function noteAgentOutputAt(key, ts) {
|
|
|
103837
104732
|
lastAgentOutputAt.delete(oldest);
|
|
103838
104733
|
}
|
|
103839
104734
|
}
|
|
103840
|
-
var OBLIGATION_STORE_PATH =
|
|
104735
|
+
var OBLIGATION_STORE_PATH = join70(STATE_DIR, "obligations.json");
|
|
103841
104736
|
var obligationStoreFs = {
|
|
103842
|
-
readFileSync: (p) =>
|
|
103843
|
-
writeFileSync: (p, d) =>
|
|
103844
|
-
renameSync: (a, b) =>
|
|
103845
|
-
existsSync: (p) =>
|
|
104737
|
+
readFileSync: (p) => readFileSync62(p, "utf8"),
|
|
104738
|
+
writeFileSync: (p, d) => writeFileSync52(p, d),
|
|
104739
|
+
renameSync: (a, b) => renameSync28(a, b),
|
|
104740
|
+
existsSync: (p) => existsSync60(p),
|
|
103846
104741
|
fsyncFileSync: fsyncPathSync,
|
|
103847
104742
|
fsyncDirSync: fsyncPathSync,
|
|
103848
104743
|
unlinkSync: unlinkSync31
|
|
@@ -104603,7 +105498,7 @@ function emitTurnRecord(turn, endedAt) {
|
|
|
104603
105498
|
return;
|
|
104604
105499
|
}
|
|
104605
105500
|
},
|
|
104606
|
-
rename: (from, to) =>
|
|
105501
|
+
rename: (from, to) => renameSync28(from, to)
|
|
104607
105502
|
});
|
|
104608
105503
|
appendFileSync9(turnsPath, rec);
|
|
104609
105504
|
} catch {}
|
|
@@ -105923,6 +106818,10 @@ var inboundCoalescer = createInboundCoalescer({
|
|
|
105923
106818
|
function emitGatewayOperatorEvent(event) {
|
|
105924
106819
|
const { agent, kind } = event;
|
|
105925
106820
|
event = { ...event, detail: redactOutboundText(event.detail, "operator_event") };
|
|
106821
|
+
if (kind === "transport-transient") {
|
|
106822
|
+
emitTransportTransientEvent(event, userFailureNoticeDeps());
|
|
106823
|
+
return;
|
|
106824
|
+
}
|
|
105926
106825
|
let throttleEscalation = null;
|
|
105927
106826
|
let escalationFired = false;
|
|
105928
106827
|
let rateLimitedCooldownConsulted = false;
|
|
@@ -106077,40 +106976,37 @@ function emitGatewayOperatorEvent(event) {
|
|
|
106077
106976
|
});
|
|
106078
106977
|
}
|
|
106079
106978
|
if (userNoticeChats.length > 0) {
|
|
106080
|
-
const
|
|
106081
|
-
const noticeKey =
|
|
106082
|
-
|
|
106083
|
-
chatIds: userNoticeChats,
|
|
106084
|
-
text: renderUserFacingFailureNotice(),
|
|
106085
|
-
agent,
|
|
106086
|
-
kind,
|
|
106087
|
-
atMs: Date.now(),
|
|
106088
|
-
key: noticeKey
|
|
106089
|
-
});
|
|
106979
|
+
const noticeDeps = userFailureNoticeDeps();
|
|
106980
|
+
const noticeKey = noticeDeps.liveTurnKey();
|
|
106981
|
+
noticeDeps.scheduleUserNotice({ chatIds: userNoticeChats, agent, kind, key: noticeKey, atMs: Date.now() });
|
|
106090
106982
|
process.stderr.write(`telegram gateway: operator-event user-notice deferred to turn-end agent=${agent} kind=${kind} chats=${userNoticeChats.length} topic=${noticeKey ?? "-"}
|
|
106091
106983
|
`);
|
|
106092
106984
|
}
|
|
106093
106985
|
}
|
|
106986
|
+
function userFailureNoticeDeps() {
|
|
106987
|
+
return {
|
|
106988
|
+
now: () => Date.now(),
|
|
106989
|
+
allowFrom: () => loadAccess().allowFrom,
|
|
106990
|
+
liveTurnKey: () => currentTurn != null ? statusKey(currentTurn.sessionChatId, currentTurn.sessionThreadId) : undefined,
|
|
106991
|
+
record: (e) => {
|
|
106992
|
+
try {
|
|
106993
|
+
recordOperatorEvent(e);
|
|
106994
|
+
} catch {}
|
|
106995
|
+
},
|
|
106996
|
+
scheduleUserNotice: (i) => pendingUserNoticeGate.schedule({ ...i, text: renderUserFacingFailureNotice() }),
|
|
106997
|
+
resolveNotices: (delivered, key) => pendingUserNoticeGate.resolveTurnEnd(key, delivered),
|
|
106998
|
+
send: (chat_id, text5, keyboard) => {
|
|
106999
|
+
const thread = topicForRecipient({ recipientChatId: chat_id, resolvedTopic: resolveAgentOutboundTopic({ kind: "compact-watchdog" }), supergroupChatId: resolveAgentSupergroupChatId() });
|
|
107000
|
+
const opts = { ...keyboard ? { reply_markup: keyboard } : {}, ...thread != null ? { message_thread_id: thread } : {} };
|
|
107001
|
+
bot.api.sendRichMessage(chat_id, richMessage2(text5), opts).catch((e) => process.stderr.write(`telegram gateway: user-failure-notice send to ${chat_id} failed: ${e}
|
|
107002
|
+
`));
|
|
107003
|
+
},
|
|
107004
|
+
log: (m) => process.stderr.write(`telegram gateway: ${m}
|
|
107005
|
+
`)
|
|
107006
|
+
};
|
|
107007
|
+
}
|
|
106094
107008
|
function flushPendingUserFailureNotices(turnDeliveredReply, turnKey3) {
|
|
106095
|
-
|
|
106096
|
-
if (notices.length === 0)
|
|
106097
|
-
return;
|
|
106098
|
-
const noticeTopic = resolveAgentOutboundTopic({ kind: "compact-watchdog" });
|
|
106099
|
-
const noticeSupergroup = resolveAgentSupergroupChatId();
|
|
106100
|
-
for (const notice of notices) {
|
|
106101
|
-
process.stderr.write(`telegram gateway: user-notice flush (turn died reply-less) agent=${notice.agent} kind=${notice.kind} chats=${notice.chatIds.length}
|
|
106102
|
-
`);
|
|
106103
|
-
for (const chat_id of notice.chatIds) {
|
|
106104
|
-
const thread = topicForRecipient({ recipientChatId: chat_id, resolvedTopic: noticeTopic, supergroupChatId: noticeSupergroup });
|
|
106105
|
-
const opts = {
|
|
106106
|
-
...thread != null ? { message_thread_id: thread } : {}
|
|
106107
|
-
};
|
|
106108
|
-
bot.api.sendRichMessage(chat_id, richMessage2(notice.text), opts).catch((e) => {
|
|
106109
|
-
process.stderr.write(`telegram gateway: user-notice send to ${chat_id} failed agent=${notice.agent} kind=${notice.kind}: ${e}
|
|
106110
|
-
`);
|
|
106111
|
-
});
|
|
106112
|
-
}
|
|
106113
|
-
}
|
|
107009
|
+
flushDeferredUserNotices(turnDeliveredReply, turnKey3, userFailureNoticeDeps());
|
|
106114
107010
|
}
|
|
106115
107011
|
function postLegacyBanner(chatId, threadId, ackMessageId, ageSec, site) {
|
|
106116
107012
|
const text5 = `\uD83C\uDF9B\uFE0F Switchroom restarted \u2014 ready. (took ~${ageSec}s)`;
|
|
@@ -106166,28 +107062,28 @@ var PIN_STATUS_WHILE_WORKING = (() => {
|
|
|
106166
107062
|
})();
|
|
106167
107063
|
var statusPinClaims = new Map;
|
|
106168
107064
|
var statusPinRightsCache = new PinRightsCache2;
|
|
106169
|
-
var STATUS_PIN_STORE_PATH =
|
|
107065
|
+
var STATUS_PIN_STORE_PATH = join70(STATE_DIR, "status-pins.json");
|
|
106170
107066
|
var statusPinStoreFs = {
|
|
106171
|
-
readFileSync: (p) =>
|
|
106172
|
-
writeFileSync: (p, d) =>
|
|
106173
|
-
renameSync: (a, b) =>
|
|
106174
|
-
existsSync: (p) =>
|
|
107067
|
+
readFileSync: (p) => readFileSync62(p, "utf8"),
|
|
107068
|
+
writeFileSync: (p, d) => writeFileSync52(p, d),
|
|
107069
|
+
renameSync: (a, b) => renameSync28(a, b),
|
|
107070
|
+
existsSync: (p) => existsSync60(p)
|
|
106175
107071
|
};
|
|
106176
107072
|
var statusPinPersistEnabled = !STATIC && PIN_STATUS_WHILE_WORKING;
|
|
106177
|
-
var ACTIVITY_CARD_STORE_PATH =
|
|
107073
|
+
var ACTIVITY_CARD_STORE_PATH = join70(STATE_DIR, "activity-cards-pending.json");
|
|
106178
107074
|
var activityCardStoreFs = {
|
|
106179
|
-
readFileSync: (p) =>
|
|
106180
|
-
writeFileSync: (p, d) =>
|
|
106181
|
-
renameSync: (a, b) =>
|
|
106182
|
-
existsSync: (p) =>
|
|
107075
|
+
readFileSync: (p) => readFileSync62(p, "utf8"),
|
|
107076
|
+
writeFileSync: (p, d) => writeFileSync52(p, d),
|
|
107077
|
+
renameSync: (a, b) => renameSync28(a, b),
|
|
107078
|
+
existsSync: (p) => existsSync60(p)
|
|
106183
107079
|
};
|
|
106184
107080
|
var activityCardPersistEnabled = !STATIC;
|
|
106185
|
-
var QUEUED_CARD_STORE_PATH =
|
|
107081
|
+
var QUEUED_CARD_STORE_PATH = join70(STATE_DIR, "queued-cards-pending.json");
|
|
106186
107082
|
var queuedCardStoreFs = {
|
|
106187
|
-
readFileSync: (p) =>
|
|
106188
|
-
writeFileSync: (p, d) =>
|
|
106189
|
-
renameSync: (a, b) =>
|
|
106190
|
-
existsSync: (p) =>
|
|
107083
|
+
readFileSync: (p) => readFileSync62(p, "utf8"),
|
|
107084
|
+
writeFileSync: (p, d) => writeFileSync52(p, d),
|
|
107085
|
+
renameSync: (a, b) => renameSync28(a, b),
|
|
107086
|
+
existsSync: (p) => existsSync60(p)
|
|
106191
107087
|
};
|
|
106192
107088
|
var queuedCardPersistEnabled = !STATIC;
|
|
106193
107089
|
function persistQueuedCard(key, chatId, threadId, messageId) {
|
|
@@ -106474,7 +107370,7 @@ async function unpinAllStatusPins() {
|
|
|
106474
107370
|
}
|
|
106475
107371
|
}
|
|
106476
107372
|
var stalePinSweepEligible = false;
|
|
106477
|
-
var STALE_PIN_SWEEP_STORE_PATH =
|
|
107373
|
+
var STALE_PIN_SWEEP_STORE_PATH = join70(STATE_DIR, "stale-pin-sweep.json");
|
|
106478
107374
|
var stalePinSweeper = createGatewayStalePinSweeper({
|
|
106479
107375
|
telegram: { handle: () => lockedBot, call: robustApiCall },
|
|
106480
107376
|
claims: () => statusPinClaims.values(),
|
|
@@ -106483,9 +107379,9 @@ var stalePinSweeper = createGatewayStalePinSweeper({
|
|
|
106483
107379
|
store: {
|
|
106484
107380
|
path: STALE_PIN_SWEEP_STORE_PATH,
|
|
106485
107381
|
fs: {
|
|
106486
|
-
readFileSync: (p) =>
|
|
107382
|
+
readFileSync: (p) => readFileSync62(p, "utf-8"),
|
|
106487
107383
|
writeFileSync: (p, data) => atomicWriteFileSync(p, data, 384),
|
|
106488
|
-
existsSync: (p) =>
|
|
107384
|
+
existsSync: (p) => existsSync60(p)
|
|
106489
107385
|
}
|
|
106490
107386
|
},
|
|
106491
107387
|
allowUnpinAllForumTopic: process.env.SWITCHROOM_PIN_SWEEP_UNPIN_ALL_TOPIC == null ? undefined : process.env.SWITCHROOM_PIN_SWEEP_UNPIN_ALL_TOPIC === "1"
|
|
@@ -106515,12 +107411,12 @@ var getPinnedProgressCardMessageId = null;
|
|
|
106515
107411
|
var completeProgressCardTurn = null;
|
|
106516
107412
|
var subagentWatcher = null;
|
|
106517
107413
|
var workerActivityFeed = null;
|
|
106518
|
-
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
107414
|
+
var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join70(STATE_DIR, "gateway.sock");
|
|
106519
107415
|
if (isGatewayMain)
|
|
106520
107416
|
mkdirSync51(STATE_DIR, { recursive: true, mode: 448 });
|
|
106521
|
-
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ??
|
|
106522
|
-
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ??
|
|
106523
|
-
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ??
|
|
107417
|
+
var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join70(STATE_DIR, "gateway.pid.json");
|
|
107418
|
+
var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join70(STATE_DIR, "gateway-session.json");
|
|
107419
|
+
var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join70(STATE_DIR, "clean-shutdown.json");
|
|
106524
107420
|
var GATEWAY_STARTED_AT_MS = Date.now();
|
|
106525
107421
|
var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
|
|
106526
107422
|
var activeBootCard = null;
|
|
@@ -106549,7 +107445,7 @@ function ensureIssuesCard(chatId, threadId) {
|
|
|
106549
107445
|
bot: botApi,
|
|
106550
107446
|
log: (msg) => process.stderr.write(`telegram gateway: ${msg}
|
|
106551
107447
|
`),
|
|
106552
|
-
persistPath:
|
|
107448
|
+
persistPath: join70(stateDir, "issues-card.json")
|
|
106553
107449
|
});
|
|
106554
107450
|
activeIssuesWatcher = startIssuesWatcher({
|
|
106555
107451
|
stateDir,
|
|
@@ -106754,13 +107650,13 @@ if (isGatewayMain)
|
|
|
106754
107650
|
var inboundSpool;
|
|
106755
107651
|
if (isGatewayMain)
|
|
106756
107652
|
inboundSpool = STATIC ? undefined : createInboundSpool({
|
|
106757
|
-
path:
|
|
107653
|
+
path: join70(STATE_DIR, "inbound-spool.jsonl"),
|
|
106758
107654
|
fs: {
|
|
106759
107655
|
appendFileSync: (p, d) => appendFileSync9(p, d),
|
|
106760
|
-
readFileSync: (p) =>
|
|
106761
|
-
writeFileSync: (p, d) =>
|
|
106762
|
-
renameSync: (a, b) =>
|
|
106763
|
-
existsSync: (p) =>
|
|
107656
|
+
readFileSync: (p) => readFileSync62(p, "utf8"),
|
|
107657
|
+
writeFileSync: (p, d) => writeFileSync52(p, d),
|
|
107658
|
+
renameSync: (a, b) => renameSync28(a, b),
|
|
107659
|
+
existsSync: (p) => existsSync60(p),
|
|
106764
107660
|
statSizeSync: (p) => statSync23(p).size,
|
|
106765
107661
|
fsyncFileSync: fsyncPathSync,
|
|
106766
107662
|
fsyncDirSync: fsyncPathSync
|
|
@@ -106824,13 +107720,13 @@ async function maybeRedeliverUndeliveredAnswer() {
|
|
|
106824
107720
|
let transcriptText;
|
|
106825
107721
|
try {
|
|
106826
107722
|
const projectsDir = getProjectsDirForCwd();
|
|
106827
|
-
const path3 =
|
|
106828
|
-
if (!
|
|
107723
|
+
const path3 = join70(projectsDir, `${sessionId}.jsonl`);
|
|
107724
|
+
if (!existsSync60(path3)) {
|
|
106829
107725
|
process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript not found for turnKey=${turn.turn_key} session=${sessionId} (${path3}); skipping
|
|
106830
107726
|
`);
|
|
106831
107727
|
return;
|
|
106832
107728
|
}
|
|
106833
|
-
transcriptText =
|
|
107729
|
+
transcriptText = readFileSync62(path3, "utf8");
|
|
106834
107730
|
} catch (err) {
|
|
106835
107731
|
process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript read failed turnKey=${turn.turn_key}: ${err.message}
|
|
106836
107732
|
`);
|
|
@@ -106900,6 +107796,14 @@ function obligationSweep() {
|
|
|
106900
107796
|
if (isGatewayMain && !STATIC && OBLIGATION_LEDGER_ENABLED) {
|
|
106901
107797
|
setInterval(obligationSweep, OBLIGATION_SWEEP_MS).unref();
|
|
106902
107798
|
}
|
|
107799
|
+
if (isGatewayMain && HISTORY_ENABLED) {
|
|
107800
|
+
maybeQueueBootBriefing({
|
|
107801
|
+
env: process.env,
|
|
107802
|
+
stateDir: STATE_DIR,
|
|
107803
|
+
resumeMsg: bootResumeInbound?.msg ?? null,
|
|
107804
|
+
put: (agent, msg) => inboundSpool != null ? inboundSpool.put(agent, msg) : pendingInboundBuffer.push(agent, msg)
|
|
107805
|
+
});
|
|
107806
|
+
}
|
|
106903
107807
|
if (isGatewayMain && bootResumeInbound != null) {
|
|
106904
107808
|
if (inboundSpool != null) {
|
|
106905
107809
|
inboundSpool.put(bootResumeInbound.agent, bootResumeInbound.msg);
|
|
@@ -106989,8 +107893,8 @@ var bridgeDeadWatchdog = createBridgeDeadWatchdog({
|
|
|
106989
107893
|
isSessionAlive: () => findAgentProcessInContainer2()?.comm === "claude",
|
|
106990
107894
|
isShuttingDown: () => shuttingDown,
|
|
106991
107895
|
escalate: (reason) => triggerSelfRestart(process.env.SWITCHROOM_AGENT_NAME ?? "", reason, 1500),
|
|
106992
|
-
crashLogPath:
|
|
106993
|
-
markerPath:
|
|
107896
|
+
crashLogPath: join70(STATE_DIR, "bridge-crash.log"),
|
|
107897
|
+
markerPath: join70(STATE_DIR, "bridge-dead-escalation.json"),
|
|
106994
107898
|
log: (line) => process.stderr.write(`${line}
|
|
106995
107899
|
`),
|
|
106996
107900
|
priorStreak: bridgeDeadPriorStreak,
|
|
@@ -107096,8 +108000,8 @@ if (isGatewayMain)
|
|
|
107096
108000
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
107097
108001
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
107098
108002
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
107099
|
-
configSnapshotPath:
|
|
107100
|
-
bootCardStatePath:
|
|
108003
|
+
configSnapshotPath: join70(resolvedAgentDirForCard, ".config-snapshot.json"),
|
|
108004
|
+
bootCardStatePath: join70(resolvedAgentDirForCard, ".boot-card-msgid.json"),
|
|
107101
108005
|
floodStatePath: FLOOD_STATE_PATH,
|
|
107102
108006
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
107103
108007
|
}, ackMsgId).then((handle) => {
|
|
@@ -107746,9 +108650,12 @@ if (isGatewayMain)
|
|
|
107746
108650
|
process.stderr.write(`telegram gateway: post_skill_proposal agent=${msg.agentName} chat=${msg.chatId} proposal=${proposal.id} slug=${proposal.skill_slug} new=${proposal.is_new}
|
|
107747
108651
|
`);
|
|
107748
108652
|
},
|
|
108653
|
+
onBuzzPublishResult: (_c, m) => getBuzzMirror()?.onPublishResult(m),
|
|
107749
108654
|
log: (msg) => process.stderr.write(`telegram gateway: ipc \u2014 ${msg}
|
|
107750
108655
|
`)
|
|
107751
108656
|
});
|
|
108657
|
+
if (isGatewayMain)
|
|
108658
|
+
maybeBootBuzzMirror((msg) => ipcServer.sendToBuzzPeer(msg));
|
|
107752
108659
|
if (isGatewayMain)
|
|
107753
108660
|
(() => {
|
|
107754
108661
|
try {
|
|
@@ -107774,7 +108681,7 @@ if (isGatewayMain)
|
|
|
107774
108681
|
const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
|
|
107775
108682
|
if (Number.isInteger(receiverUid))
|
|
107776
108683
|
allowedUids.push(receiverUid);
|
|
107777
|
-
const socketPath =
|
|
108684
|
+
const socketPath = join70(STATE_DIR, "webhook.sock");
|
|
107778
108685
|
const webhookInject = (agentName3, inbound) => {
|
|
107779
108686
|
const msg = inbound;
|
|
107780
108687
|
const delivered = ipcServer.sendToAgent(agentName3, msg);
|
|
@@ -108025,9 +108932,9 @@ function redactOutboundText(text5, site) {
|
|
|
108025
108932
|
var VOICE_OUT_DEFAULT_CHUNK_CHARS = 600;
|
|
108026
108933
|
var VOICE_OUT_HARD_CHUNK_CAP = 4096;
|
|
108027
108934
|
var voiceOnDemandCache = new VoiceOnDemandCache({
|
|
108028
|
-
persistPath:
|
|
108935
|
+
persistPath: join70(STATE_DIR, "voice-ondemand.json")
|
|
108029
108936
|
});
|
|
108030
|
-
var VOICE_CACHE_DIR =
|
|
108937
|
+
var VOICE_CACHE_DIR = join70(STATE_DIR, "voice-cache");
|
|
108031
108938
|
var voicePreSynthQueue = new PreSynthQueue({
|
|
108032
108939
|
runJob: async (job) => {
|
|
108033
108940
|
const sidecarToken = await materializeSidecarToken2();
|
|
@@ -108183,6 +109090,7 @@ function gatewaySendReplyDeps() {
|
|
|
108183
109090
|
resolveReplyOwnerTurn,
|
|
108184
109091
|
findTurnByOriginId,
|
|
108185
109092
|
findTurnByQuotedMessageId,
|
|
109093
|
+
findLatestTurnForChat,
|
|
108186
109094
|
resolveAnswerThreadWithLog,
|
|
108187
109095
|
resolveThreadId,
|
|
108188
109096
|
getLatestInboundMessageId,
|
|
@@ -108434,11 +109342,11 @@ async function executeSendGif(rawArgs) {
|
|
|
108434
109342
|
};
|
|
108435
109343
|
}
|
|
108436
109344
|
async function publishToTelegraph(text5, shortName, authorName) {
|
|
108437
|
-
const accountPath =
|
|
109345
|
+
const accountPath = join70(STATE_DIR, "telegraph-account.json");
|
|
108438
109346
|
let account = null;
|
|
108439
109347
|
try {
|
|
108440
|
-
if (
|
|
108441
|
-
const raw =
|
|
109348
|
+
if (existsSync60(accountPath)) {
|
|
109349
|
+
const raw = readFileSync62(accountPath, "utf-8");
|
|
108442
109350
|
const parsed = JSON.parse(raw);
|
|
108443
109351
|
if (parsed.shortName && parsed.accessToken) {
|
|
108444
109352
|
account = parsed;
|
|
@@ -108458,7 +109366,7 @@ async function publishToTelegraph(text5, shortName, authorName) {
|
|
|
108458
109366
|
account = created.value;
|
|
108459
109367
|
try {
|
|
108460
109368
|
mkdirSync51(STATE_DIR, { recursive: true, mode: 448 });
|
|
108461
|
-
|
|
109369
|
+
writeFileSync52(accountPath, JSON.stringify(account, null, 2), { mode: 384 });
|
|
108462
109370
|
} catch (err) {
|
|
108463
109371
|
process.stderr.write(`telegram gateway: telegraph cache write failed: ${err.message}
|
|
108464
109372
|
`);
|
|
@@ -108574,7 +109482,7 @@ _The secret was NOT saved. The agent can re-request with \`request_secret\`._`,
|
|
|
108574
109482
|
}
|
|
108575
109483
|
function readLiveSwitchroomConfigText() {
|
|
108576
109484
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
108577
|
-
return
|
|
109485
|
+
return readFileSync62(cfgPath, "utf8");
|
|
108578
109486
|
}
|
|
108579
109487
|
async function executeReact(args) {
|
|
108580
109488
|
if (!args.chat_id)
|
|
@@ -108615,7 +109523,7 @@ async function executeDownloadAttachment(args) {
|
|
|
108615
109523
|
});
|
|
108616
109524
|
mkdirSync51(INBOX_DIR, { recursive: true, mode: 448 });
|
|
108617
109525
|
assertInsideInbox2(INBOX_DIR, dlPath);
|
|
108618
|
-
|
|
109526
|
+
writeFileSync52(dlPath, buf, { mode: 384 });
|
|
108619
109527
|
return { content: [{ type: "text", text: dlPath }] };
|
|
108620
109528
|
}
|
|
108621
109529
|
async function executeEditMessage(args) {
|
|
@@ -108654,6 +109562,7 @@ async function executeEditMessage(args) {
|
|
|
108654
109562
|
`);
|
|
108655
109563
|
}
|
|
108656
109564
|
}
|
|
109565
|
+
getBuzzMirror()?.mirrorCorrection({ telegramMessageKey: `${String(args.chat_id ?? "")}:${Number(args.message_id)}`, scrubbedText: editRawText });
|
|
108657
109566
|
return { content: [{ type: "text", text: `edited (id: ${id})` }] };
|
|
108658
109567
|
}
|
|
108659
109568
|
async function executeSendTyping(args) {
|
|
@@ -109989,14 +110898,14 @@ function restartMarkerPath() {
|
|
|
109989
110898
|
const agentDir = resolveAgentDirFromEnv();
|
|
109990
110899
|
if (!agentDir)
|
|
109991
110900
|
return null;
|
|
109992
|
-
return
|
|
110901
|
+
return join70(agentDir, "restart-pending.json");
|
|
109993
110902
|
}
|
|
109994
110903
|
function writeRestartMarker(marker) {
|
|
109995
110904
|
const p = restartMarkerPath();
|
|
109996
110905
|
if (!p)
|
|
109997
110906
|
return;
|
|
109998
110907
|
try {
|
|
109999
|
-
|
|
110908
|
+
writeFileSync52(p, JSON.stringify(marker));
|
|
110000
110909
|
lastPlannedRestartAt = Date.now();
|
|
110001
110910
|
process.stderr.write(`telegram gateway: restart-marker: write chat_id=${marker.chat_id} thread_id=${marker.thread_id ?? "-"} ack=${marker.ack_message_id ?? "-"} path=${p}
|
|
110002
110911
|
`);
|
|
@@ -110015,7 +110924,7 @@ function readRestartMarker() {
|
|
|
110015
110924
|
if (!p)
|
|
110016
110925
|
return null;
|
|
110017
110926
|
try {
|
|
110018
|
-
return JSON.parse(
|
|
110927
|
+
return JSON.parse(readFileSync62(p, "utf8"));
|
|
110019
110928
|
} catch {
|
|
110020
110929
|
return null;
|
|
110021
110930
|
}
|
|
@@ -110025,7 +110934,7 @@ function clearRestartMarker() {
|
|
|
110025
110934
|
if (!p)
|
|
110026
110935
|
return;
|
|
110027
110936
|
try {
|
|
110028
|
-
|
|
110937
|
+
rmSync9(p, { force: true });
|
|
110029
110938
|
process.stderr.write(`telegram gateway: restart-marker: cleared path=${p}
|
|
110030
110939
|
`);
|
|
110031
110940
|
} catch {}
|
|
@@ -110164,7 +111073,7 @@ var _dockerReachable;
|
|
|
110164
111073
|
function isDockerReachable() {
|
|
110165
111074
|
if (_dockerReachable !== undefined)
|
|
110166
111075
|
return _dockerReachable;
|
|
110167
|
-
if (!
|
|
111076
|
+
if (!existsSync60("/var/run/docker.sock")) {
|
|
110168
111077
|
_dockerReachable = false;
|
|
110169
111078
|
return _dockerReachable;
|
|
110170
111079
|
}
|
|
@@ -110181,12 +111090,12 @@ function _resetDockerReachableCache() {
|
|
|
110181
111090
|
}
|
|
110182
111091
|
function spawnSwitchroomDetached(args, onFailure) {
|
|
110183
111092
|
const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
|
|
110184
|
-
const logPath =
|
|
111093
|
+
const logPath = join70(STATE_DIR, "detached-spawn.log");
|
|
110185
111094
|
let outFd = null;
|
|
110186
111095
|
try {
|
|
110187
111096
|
mkdirSync51(STATE_DIR, { recursive: true });
|
|
110188
111097
|
outFd = openSync15(logPath, "a");
|
|
110189
|
-
|
|
111098
|
+
writeFileSync52(logPath, `
|
|
110190
111099
|
[${new Date().toISOString()}] spawn ${SWITCHROOM_CLI} ${fullArgs.join(" ")}
|
|
110191
111100
|
`, { flag: "a" });
|
|
110192
111101
|
} catch {}
|
|
@@ -110212,7 +111121,7 @@ function spawnSwitchroomDetached(args, onFailure) {
|
|
|
110212
111121
|
return;
|
|
110213
111122
|
let tail = "";
|
|
110214
111123
|
try {
|
|
110215
|
-
const full =
|
|
111124
|
+
const full = readFileSync62(logPath, "utf8");
|
|
110216
111125
|
tail = full.split(`
|
|
110217
111126
|
`).slice(-30).join(`
|
|
110218
111127
|
`).trim();
|
|
@@ -110474,10 +111383,10 @@ ${preBlock(formatSwitchroomOutput(detail))}`, { html: true });
|
|
|
110474
111383
|
}
|
|
110475
111384
|
function readRecentDenialsForAgent(agentName3, windowMs, limit) {
|
|
110476
111385
|
try {
|
|
110477
|
-
const auditPath =
|
|
110478
|
-
if (!
|
|
111386
|
+
const auditPath = join70(homedir20(), ".switchroom", "vault-audit.log");
|
|
111387
|
+
if (!existsSync60(auditPath))
|
|
110479
111388
|
return [];
|
|
110480
|
-
const raw =
|
|
111389
|
+
const raw = readFileSync62(auditPath, "utf8");
|
|
110481
111390
|
return recentDenialsFromAuditLog(raw, { agentName: agentName3, windowMs, limit });
|
|
110482
111391
|
} catch {
|
|
110483
111392
|
return [];
|
|
@@ -110528,7 +111437,7 @@ async function buildAgentMetadata(agentName3) {
|
|
|
110528
111437
|
try {
|
|
110529
111438
|
const agentDir = resolveAgentDirFromEnv();
|
|
110530
111439
|
if (agentDir) {
|
|
110531
|
-
const raw =
|
|
111440
|
+
const raw = readFileSync62(join70(agentDir, ".claude", ".claude.json"), "utf8");
|
|
110532
111441
|
claudeJson = JSON.parse(raw);
|
|
110533
111442
|
}
|
|
110534
111443
|
} catch {}
|
|
@@ -110657,7 +111566,7 @@ function buildModelDeps(restartCtx) {
|
|
|
110657
111566
|
try {
|
|
110658
111567
|
const agentDir = resolveAgentDirFromEnv();
|
|
110659
111568
|
if (agentDir) {
|
|
110660
|
-
const local = await fetchQuota2({ claudeConfigDir:
|
|
111569
|
+
const local = await fetchQuota2({ claudeConfigDir: join70(agentDir, ".claude") });
|
|
110661
111570
|
if (local.ok)
|
|
110662
111571
|
return formatQuotaLine2(local.data);
|
|
110663
111572
|
}
|
|
@@ -110904,9 +111813,9 @@ function effortMenuReplyMarkup(reply) {
|
|
|
110904
111813
|
function flushAgentHandoff(agentDir) {
|
|
110905
111814
|
let removed = 0;
|
|
110906
111815
|
for (const fname of [".handoff.md", ".handoff-topic"]) {
|
|
110907
|
-
const p =
|
|
111816
|
+
const p = join70(agentDir, fname);
|
|
110908
111817
|
try {
|
|
110909
|
-
if (
|
|
111818
|
+
if (existsSync60(p)) {
|
|
110910
111819
|
unlinkSync31(p);
|
|
110911
111820
|
removed++;
|
|
110912
111821
|
}
|
|
@@ -110962,7 +111871,7 @@ async function handleNewCommand(ctx) {
|
|
|
110962
111871
|
writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
|
|
110963
111872
|
if (agentDir != null) {
|
|
110964
111873
|
try {
|
|
110965
|
-
|
|
111874
|
+
writeFileSync52(join70(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
|
|
110966
111875
|
`, "utf8");
|
|
110967
111876
|
} catch (err) {
|
|
110968
111877
|
process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
|
|
@@ -111075,16 +111984,16 @@ function buildFolderPickerDeps() {
|
|
|
111075
111984
|
};
|
|
111076
111985
|
}
|
|
111077
111986
|
var lockoutOps = {
|
|
111078
|
-
readFileSync: (p, enc) =>
|
|
111079
|
-
writeFileSync: (p, data, opts) =>
|
|
111080
|
-
existsSync: (p) =>
|
|
111987
|
+
readFileSync: (p, enc) => readFileSync62(p, enc),
|
|
111988
|
+
writeFileSync: (p, data, opts) => writeFileSync52(p, data, opts),
|
|
111989
|
+
existsSync: (p) => existsSync60(p),
|
|
111081
111990
|
mkdirSync: (p, opts) => mkdirSync51(p, opts),
|
|
111082
|
-
joinPath: (...parts) =>
|
|
111991
|
+
joinPath: (...parts) => join70(...parts)
|
|
111083
111992
|
};
|
|
111084
111993
|
var FLEET_FALLBACK_DEDUP_MS = 30000;
|
|
111085
111994
|
function isAuthBrokerSocketReachable() {
|
|
111086
111995
|
try {
|
|
111087
|
-
return
|
|
111996
|
+
return existsSync60(resolveAuthBrokerSocketPath2());
|
|
111088
111997
|
} catch {
|
|
111089
111998
|
return false;
|
|
111090
111999
|
}
|
|
@@ -111339,7 +112248,7 @@ async function runCreditWatch() {
|
|
|
111339
112248
|
if (!agentDir)
|
|
111340
112249
|
return;
|
|
111341
112250
|
const agentName3 = getMyAgentName();
|
|
111342
|
-
const claudeConfigDir =
|
|
112251
|
+
const claudeConfigDir = join70(agentDir, ".claude");
|
|
111343
112252
|
const stateDir = STATE_DIR;
|
|
111344
112253
|
const reason = readClaudeJsonOverage(claudeConfigDir);
|
|
111345
112254
|
const prev = loadCreditState(stateDir);
|
|
@@ -113095,7 +114004,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
|
|
|
113095
114004
|
await switchroomReply(ctx, "**/usage:** cannot resolve agent dir.", { html: true });
|
|
113096
114005
|
return;
|
|
113097
114006
|
}
|
|
113098
|
-
const result = await fetchQuota2({ claudeConfigDir:
|
|
114007
|
+
const result = await fetchQuota2({ claudeConfigDir: join70(agentDir, ".claude") });
|
|
113099
114008
|
if (!result.ok) {
|
|
113100
114009
|
await switchroomReply(ctx, `**/usage:** ${escapeHtmlForTg2(result.reason)}`, { html: true });
|
|
113101
114010
|
return;
|
|
@@ -113482,7 +114391,7 @@ ${interimLabel}` : interimLabel
|
|
|
113482
114391
|
const unifiedDiff = (() => {
|
|
113483
114392
|
try {
|
|
113484
114393
|
const cfgPath = process.env.SWITCHROOM_CONFIG ?? SWITCHROOM_CONFIG ?? findConfigFile2();
|
|
113485
|
-
const raw =
|
|
114394
|
+
const raw = readFileSync62(cfgPath, "utf8");
|
|
113486
114395
|
return synthesizeAllowRuleDiff({ agentName: agentName3, rule: chosen.rule, configText: raw });
|
|
113487
114396
|
} catch (err) {
|
|
113488
114397
|
process.stderr.write(`telegram gateway: always-allow diff synth failed: ${err.message}
|
|
@@ -114351,7 +115260,7 @@ async function startGateway() {
|
|
|
114351
115260
|
return;
|
|
114352
115261
|
}
|
|
114353
115262
|
})();
|
|
114354
|
-
const resolvedAgentDirForBootCard = agentDir ??
|
|
115263
|
+
const resolvedAgentDirForBootCard = agentDir ?? join70(homedir20(), ".switchroom", "agents", agentSlug);
|
|
114355
115264
|
const handle = await startBootCard(chatId, threadId, botApiForCard, {
|
|
114356
115265
|
agentName: agentDisplayName,
|
|
114357
115266
|
agentSlug,
|
|
@@ -114365,8 +115274,8 @@ async function startGateway() {
|
|
|
114365
115274
|
probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
|
|
114366
115275
|
tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
|
|
114367
115276
|
dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
|
|
114368
|
-
configSnapshotPath:
|
|
114369
|
-
bootCardStatePath:
|
|
115277
|
+
configSnapshotPath: join70(resolvedAgentDirForBootCard, ".config-snapshot.json"),
|
|
115278
|
+
bootCardStatePath: join70(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
|
|
114370
115279
|
floodStatePath: FLOOD_STATE_PATH,
|
|
114371
115280
|
...updateOutcomeLine ? { updateOutcomeLine } : {}
|
|
114372
115281
|
}, ackMsgId);
|
|
@@ -114399,7 +115308,7 @@ async function startGateway() {
|
|
|
114399
115308
|
if (smAgentDir) {
|
|
114400
115309
|
const resolutionTimeoutMs = resolveSessionModelResolutionTimeoutMs(process.env.SWITCHROOM_SESSION_MODEL_RESOLUTION_TIMEOUT_MS);
|
|
114401
115310
|
const resolved = await waitForSessionModelResolution({
|
|
114402
|
-
barrierExists: () =>
|
|
115311
|
+
barrierExists: () => existsSync60(join70(smAgentDir, ".session-model-resolved")),
|
|
114403
115312
|
timeoutMs: resolutionTimeoutMs
|
|
114404
115313
|
});
|
|
114405
115314
|
if (!resolved) {
|
|
@@ -114407,10 +115316,10 @@ async function startGateway() {
|
|
|
114407
115316
|
process.stderr.write(`telegram gateway: gw /model relaunch UNRESOLVED agent=${getMyAgentName()} target=${target} (barrier timeout after ${resolutionTimeoutMs}ms)
|
|
114408
115317
|
`);
|
|
114409
115318
|
} else {
|
|
114410
|
-
const activePath =
|
|
114411
|
-
if (
|
|
115319
|
+
const activePath = join70(smAgentDir, ".active-session-model");
|
|
115320
|
+
if (existsSync60(activePath)) {
|
|
114412
115321
|
try {
|
|
114413
|
-
const launched =
|
|
115322
|
+
const launched = readFileSync62(activePath, "utf8").trim();
|
|
114414
115323
|
const configured = (() => {
|
|
114415
115324
|
const d = switchroomExecJson(["agent", "list"]);
|
|
114416
115325
|
const raw = d?.agents?.find((a) => a.name === getMyAgentName())?.model ?? null;
|
|
@@ -114443,24 +115352,24 @@ async function startGateway() {
|
|
|
114443
115352
|
deliverModelSwitchBootNotice({
|
|
114444
115353
|
...modelBootCardDeps,
|
|
114445
115354
|
confirmation,
|
|
114446
|
-
hasSessionModelAlert:
|
|
115355
|
+
hasSessionModelAlert: existsSync60(join70(smAgentDir, ".session-model-alert"))
|
|
114447
115356
|
});
|
|
114448
115357
|
}
|
|
114449
115358
|
} catch {}
|
|
114450
115359
|
}
|
|
114451
|
-
const activeEffortPath =
|
|
114452
|
-
if (
|
|
115360
|
+
const activeEffortPath = join70(smAgentDir, ".active-session-effort");
|
|
115361
|
+
if (existsSync60(activeEffortPath)) {
|
|
114453
115362
|
try {
|
|
114454
|
-
const launchedEffort =
|
|
115363
|
+
const launchedEffort = readFileSync62(activeEffortPath, "utf8").trim();
|
|
114455
115364
|
const configuredEffort = getConfiguredEffortForPersist();
|
|
114456
115365
|
sessionEffortOverride = launchedEffort.length > 0 && launchedEffort !== configuredEffort ? launchedEffort : null;
|
|
114457
115366
|
} catch {}
|
|
114458
115367
|
}
|
|
114459
|
-
const alertPath =
|
|
114460
|
-
if (
|
|
115368
|
+
const alertPath = join70(smAgentDir, ".session-model-alert");
|
|
115369
|
+
if (existsSync60(alertPath)) {
|
|
114461
115370
|
let alertText = null;
|
|
114462
115371
|
try {
|
|
114463
|
-
alertText =
|
|
115372
|
+
alertText = readFileSync62(alertPath, "utf8").trim();
|
|
114464
115373
|
} catch {
|
|
114465
115374
|
alertText = null;
|
|
114466
115375
|
}
|