switchroom 0.20.11 → 0.20.13
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 +5 -2
- package/dist/auth-broker/index.js +32 -25
- package/dist/cli/notion-write-pretool.mjs +5 -2
- package/dist/cli/self-improve-stop.mjs +13 -1
- package/dist/cli/switchroom.js +3069 -1146
- package/dist/host-control/main.js +33 -26
- package/dist/vault/approvals/kernel-server.js +32 -25
- package/dist/vault/broker/server.js +32 -25
- package/examples/personal-google-workspace-mcp/compose.yaml +1 -1
- package/package.json +7 -4
- package/skills/switchroom-architecture/telegram.md +0 -1
- package/skills/switchroom-cli/SKILL.md +0 -1
- package/skills/switchroom-release/SKILL.md +3 -2
- package/telegram-plugin/README.md +2 -11
- package/telegram-plugin/bridge/bridge.ts +0 -12
- package/telegram-plugin/bunfig.toml +9 -5
- package/telegram-plugin/chat-lock.ts +1 -1
- package/telegram-plugin/dist/bridge/bridge.js +0 -12
- package/telegram-plugin/dist/gateway/gateway.js +219 -127
- package/telegram-plugin/dist/server.js +0 -12
- package/telegram-plugin/gateway/captured-answer-resume.ts +23 -1
- package/telegram-plugin/gateway/gateway.ts +25 -59
- package/telegram-plugin/gateway/liveness-wiring.ts +6 -1
- package/telegram-plugin/gateway/outbound-send-path.ts +111 -6
- package/telegram-plugin/gateway/outbox-sweep.ts +69 -0
- package/telegram-plugin/gateway/stale-pin-sweep.ts +4 -3
- package/telegram-plugin/gateway/status-pin-store.ts +10 -9
- package/telegram-plugin/gateway/stream-render.ts +8 -4
- package/telegram-plugin/gateway/turn-record-status.ts +32 -1
- package/telegram-plugin/hooks/audience-classify.d.mts +26 -0
- package/telegram-plugin/hooks/audience-classify.mjs +193 -0
- package/telegram-plugin/hooks/hooks.json +13 -12
- package/telegram-plugin/hooks/narration-classify.mjs +1 -2
- package/telegram-plugin/hooks/silent-end-interrupt-stop.mjs +31 -1
- package/telegram-plugin/hooks/silent-end-scan.mjs +9 -2
- package/telegram-plugin/outbox.ts +69 -3
- package/telegram-plugin/silent-end.ts +48 -5
- package/telegram-plugin/status-pin.ts +2 -5
- package/telegram-plugin/tests/backstop-exactly-once.test.ts +8 -2
- package/telegram-plugin/tests/captured-answer-resume.test.ts +26 -11
- package/telegram-plugin/tests/framework-fallback-duration-guard.test.ts +125 -0
- package/telegram-plugin/tests/hindsight-bank-preload.test.ts +50 -0
- package/telegram-plugin/tests/outbox-live-path-review-4490.test.ts +613 -0
- package/telegram-plugin/tests/outbox-self-improve-review.test.ts +401 -0
- package/telegram-plugin/tests/pin-message-tool-retired.test.ts +64 -0
- package/telegram-plugin/tests/status-pin-boot-recovery.test.ts +38 -0
- package/telegram-plugin/tests/worker-activity-feed.test.ts +40 -1
- package/telegram-plugin/worker-activity-feed.ts +1 -1
- package/vendor/hindsight-memory/scripts/recall.py +140 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_latency_instrumentation.py +277 -0
|
@@ -22025,6 +22025,7 @@ var init_schema = __esm(() => {
|
|
|
22025
22025
|
}).describe("Per-operation LLM override. Every field optional; an unset field (or " + "an omitted op block) inherits the global `hindsight.llm.*`, which is " + "already the engine's fallback \u2014 switchroom emits only the vars set.");
|
|
22026
22026
|
HindsightConfigSchema = exports_external.object({
|
|
22027
22027
|
gpu: exports_external.boolean().optional().describe("Force GPU passthrough for the hindsight container on (`true`) or off " + "(`false`), overriding host autodetection in BOTH directions. Absent " + "(the default) \u2192 autodetect from the persisted host-capabilities " + "verdict (`~/.switchroom/host-capabilities.json`), which enables " + "`--gpus all` only when that file proves BOTH a GPU and the nvidia " + "container toolkit. Set `true` when that verdict is wrong or unreadable " + "and you know the host has a working toolkit \u2014 switchroom cannot verify " + "it for you, and `docker run --gpus all` hard-fails container create on " + "a host without one. Set `false` to pin the container to CPU on a GPU " + "host. This is also the declarative opt-out for the recreate-time GPU " + "drop guard (`switchroom memory setup --recreate` refuses to silently " + "turn a GPU container into a CPU one). `--gpu`/`--no-gpu` on `memory " + "setup` override this for a single run."),
|
|
22028
|
+
mem_limit: exports_external.string().regex(/^\d+(\.\d+)?\s*[bkmgt]?b?$/i, "hindsight.mem_limit must be a docker memory size, e.g. `24g` or `16384m`").optional().describe("Hard memory cap for the hindsight container \u2014 docker `--memory` on the " + "`memory setup` path and `mem_limit:` in the emitted compose snippet. A " + "docker size string (`24g`, `16384m`). Absent \u2192 switchroom's default " + "(`HINDSIGHT_DEFAULT_MEM_LIMIT`, 16g). This is a docker HostConfig flag, " + "not a container env var, which is why it is a first-class field here " + "rather than an `env:` key. **Move it together with " + "`env.SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS`**: Postgres pins the " + "buffer pool as unreclaimable shared memory inside this cap, so a cap " + "that does not clear `shared_buffers` by the container's app working " + "set plus its page-cache floor puts Postgres in a near-OOM " + "configuration. switchroom warns, naming both numbers, when it does " + "not. A value below the container's memory RESERVATION (4g) is " + "rejected outright \u2014 docker will not create such a container."),
|
|
22028
22029
|
cp_access_key: exports_external.string().min(1).optional().describe("Access key for the hindsight control-plane DASHBOARD (upstream " + "`HINDSIGHT_CP_ACCESS_KEY`, port 9999). Literal or \u2014 strongly preferred " + "\u2014 a `vault:` reference such as `vault:hindsight_cp_access_key`, read " + "through the broker at container-launch time. This is the ONLY thing " + "that arms the dashboard's login: upstream's middleware short-circuits " + "to `next()` when the var is unset, so an unset key means the dashboard " + "has no authentication at all, not weak authentication. Because of that, " + "leaving it unset is FAIL-CLOSED: switchroom pins " + "`HINDSIGHT_CP_HOSTNAME=127.0.0.1` so the loginless dashboard is " + "reachable only from the host, and warns. Set this to serve the " + "dashboard on the LAN/tailnet."),
|
|
22029
22030
|
llm: exports_external.object({
|
|
22030
22031
|
provider: exports_external.string().min(1).optional().describe("Hindsight LLM provider (upstream `HINDSIGHT_API_LLM_PROVIDER`). " + "Defaults to `claude-code` (subscription-honest, broker-fed OAuth). " + "Any litellm-routable provider the upstream image supports is valid. " + "Serves as the GLOBAL default for every op absent a per-op override."),
|
|
@@ -22036,7 +22037,7 @@ var init_schema = __esm(() => {
|
|
|
22036
22037
|
reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent \u2192 uses global."),
|
|
22037
22038
|
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent \u2192 global.")
|
|
22038
22039
|
}).optional().describe("LLM knob for the hindsight container. The flat `provider`/`model` set " + "the global default (backward-compatible); optional `retain`/`reflect`/" + "`consolidation` blocks override individual ops. All fields optional; " + "unset fields fall back to the hard-coded defaults."),
|
|
22039
|
-
env: exports_external.record(exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean()])).optional().describe("Operator overrides for switchroom's capability-gated Hindsight " + "performance defaults. Only the keys switchroom actually manages are " + "honoured (`HINDSIGHT_PERF_ENV_KEYS` in " + "src/setup/hindsight-perf-defaults.ts: RERANKER_LOCAL_FP16, " + "RERANKER_LOCAL_BATCH_SIZE, LLM_MAX_CONCURRENT, " + "RETAIN/CONSOLIDATION_LLM_MAX_CONCURRENT, LLM_STRICT_SCHEMA, " + "LLM_MAX_RETRIES, CONSOLIDATION_LLM_PARALLELISM, " + "MAX_OBSERVATIONS_PER_SCOPE, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT, " + "RERANKER_LOCAL_BUCKET_BATCHING, RERANKER_MAX_CANDIDATES, " + "RERANKER_LOCAL_MAX_CONCURRENT, RECALL_MAX_CONCURRENT, " + "REFLECT_WALL_TIMEOUT, WORKER_CONSOLIDATION_RESERVED_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND, GRAPH_SEED_MIN_SIMILARITY, " + "LLM_SUPPORTS_MAX_ITEMS, RECENCY_DECAY_FUNCTION, " + "RECENCY_DECAY_HALFLIFE_DAYS \u2014 switchroom defaults recall's recency " + "curve to `exponential` with a 30-day half-life so a fact retained " + "today outranks a stale one, instead of upstream's near-flat " + "linear/365-day window), the override-only keys " + "switchroom manages but ships NO default for " + "(`HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS`: " + "HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY \u2014 a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP \u2014 the rollback knob for " + "switchroom's CE-saturation damping patch, a float; >= ~0.65 backs the " + "damping out entirely, unset means the patch's own derived gap; and " + "HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS \u2014 only read when the " + "decay function is `linear`, so switchroom ships no default for it but " + "still honours an operator who flips the function back; and " + "HINDSIGHT_API_WORKER_MAX_SLOTS \u2014 the worker poller's TOTAL in-flight " + "task budget, the pool WORKER_CONSOLIDATION_RESERVED_SLOTS reserves out of; " + "unset means upstream's own default; and " + "HINDSIGHT_API_WORKER_RETAIN_RESERVED_SLOTS \u2014 the reserved slot FLOOR for the " + "retain (memory write) lane, carved from that same total; unset means " + "upstream's own 0, i.e. no floor and retain competes for the shared " + "pool; and " + "the pre-0.8.6 name HINDSIGHT_API_WORKER_<TYPE>_MAX_SLOTS is still " + "accepted for every operation type and normalised to " + "..._RESERVED_SLOTS on the way in, because setting both names for " + "one type is a hard boot failure in the engine and switchroom " + "therefore emits only the canonical name; and " + "HINDSIGHT_API_SEMANTIC_MIN_SIMILARITY \u2014 the cosine-similarity floor a " + "candidate must reach to be returned by the semantic retrieval arm at " + "all (0..1); unset means upstream's own 0.3, and where it should sit " + "depends on the bank's embedding model and phrasing diversity, so " + "switchroom ships no opinion; and " + "HINDSIGHT_MCP_RECALL_BUDGET_MODE \u2014 the rollback knob for switchroom's " + "mcp-recall-token-budget image patch; `legacy` restores upstream's " + "exact pre-patch recall returns, anything else or unset is the " + "honest-envelope mode; and " + "HINDSIGHT_API_LLM_TEMPERATURE_REFLECT \u2014 upstream's own per-op reflect " + "temperature knob, made live by switchroom's reflect-temperature image " + "patch; a float, or `none` to omit the kwarg (provider default, " + "upstream's accidental pre-patch behaviour); unset means the image's " + "baked default 0.1; and " + "HINDSIGHT_API_CONSOLIDATION_RECALL_MAX_CONCURRENT \u2014 the background " + "half of switchroom's recall-admission split (#3660): of the " + "RECALL_MAX_CONCURRENT admission slots, at most this many may be held " + "by background consolidation recalls at once, so foreground per-turn " + "recall always keeps the remainder; must be >= 1 and strictly less " + "than RECALL_MAX_CONCURRENT or the engine refuses to boot; unset " + "means the image's derived default min(2, RECALL_MAX_CONCURRENT - 1); " + "1 biases hard toward the interactive lane while a consolidation " + "backlog drains; and " + "HINDSIGHT_MM_REFRESH_MIN_INTERVAL_S \u2014 the rollback/tuning knob for " + "switchroom's MM-refresh-debounce image patch: the minimum seconds " + "between consolidation-triggered refreshes of one mental model; unset " + "means the image's baked 3600, 0 restores upstream's " + "refresh-every-round behaviour; explicit and cron-scheduled refreshes " + "are never debounced; and " + "HINDSIGHT_API_TEMPORAL_LANGUAGES \u2014 the language set dateparser is " + "restricted to during temporal query analysis, made live by switchroom's " + "temporal-language image patch (which ended a 200+-locale auto-detection " + "pass that blocked the shared asyncio loop on every recall); " + "comma-separated, unset means the image's baked `en`, set e.g. `en,es` to " + "restore i18n parsing; and " + "HINDSIGHT_API_TEMPORAL_MAX_QUERY_CHARS \u2014 the max chars of query text " + "dateparser.search_dates() is handed during temporal analysis (0 = " + "unlimited), made live by switchroom's temporal-offload image patch " + "(which moved the synchronous extraction off the shared asyncio loop onto " + "a single-worker thread and bounds its input, ending multi-second loop " + "stalls on multi-KB consolidation queries); unset means the image's baked " + "2000, set 0 to restore an unbounded full scan), plus the " + "embedded-PostgreSQL (pg0) sizing keys switchroom manages in " + "src/setup/hindsight-pg-defaults.ts (`HINDSIGHT_PG_ENV_KEYS`: " + "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE, " + "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS \u2014 a postgres size string such " + "as `4GB`, or the sentinel `off` to leave pg0's own default for that " + "one knob). A value set here " + "REPLACES switchroom's default and is emitted even when the gating " + "capability is absent, so an operator can always force a knob. Other " + "`HINDSIGHT_API_*` keys are deliberately IGNORED \u2014 a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
|
|
22040
|
+
env: exports_external.record(exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean()])).optional().describe("Operator overrides for switchroom's capability-gated Hindsight " + "performance defaults. Only the keys switchroom actually manages are " + "honoured (`HINDSIGHT_PERF_ENV_KEYS` in " + "src/setup/hindsight-perf-defaults.ts: RERANKER_LOCAL_FP16, " + "RERANKER_LOCAL_BATCH_SIZE, LLM_MAX_CONCURRENT, " + "RETAIN/CONSOLIDATION_LLM_MAX_CONCURRENT, LLM_STRICT_SCHEMA, " + "LLM_MAX_RETRIES, CONSOLIDATION_LLM_PARALLELISM, " + "MAX_OBSERVATIONS_PER_SCOPE, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT, " + "RERANKER_LOCAL_BUCKET_BATCHING, RERANKER_MAX_CANDIDATES, " + "RERANKER_LOCAL_MAX_CONCURRENT, RECALL_MAX_CONCURRENT, " + "REFLECT_WALL_TIMEOUT, WORKER_CONSOLIDATION_RESERVED_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND, GRAPH_SEED_MIN_SIMILARITY, " + "LLM_SUPPORTS_MAX_ITEMS, RECENCY_DECAY_FUNCTION, " + "RECENCY_DECAY_HALFLIFE_DAYS \u2014 switchroom defaults recall's recency " + "curve to `exponential` with a 30-day half-life so a fact retained " + "today outranks a stale one, instead of upstream's near-flat " + "linear/365-day window), the override-only keys " + "switchroom manages but ships NO default for " + "(`HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS`: " + "HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY \u2014 a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP \u2014 the rollback knob for " + "switchroom's CE-saturation damping patch, a float; >= ~0.65 backs the " + "damping out entirely, unset means the patch's own derived gap; and " + "HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS \u2014 only read when the " + "decay function is `linear`, so switchroom ships no default for it but " + "still honours an operator who flips the function back; and " + "HINDSIGHT_API_WORKER_MAX_SLOTS \u2014 the worker poller's TOTAL in-flight " + "task budget, the pool WORKER_CONSOLIDATION_RESERVED_SLOTS reserves out of; " + "unset means upstream's own default; and " + "HINDSIGHT_API_WORKER_RETAIN_RESERVED_SLOTS \u2014 the reserved slot FLOOR for the " + "retain (memory write) lane, carved from that same total; unset means " + "upstream's own 0, i.e. no floor and retain competes for the shared " + "pool; and " + "the pre-0.8.6 name HINDSIGHT_API_WORKER_<TYPE>_MAX_SLOTS is still " + "accepted for every operation type and normalised to " + "..._RESERVED_SLOTS on the way in, because setting both names for " + "one type is a hard boot failure in the engine and switchroom " + "therefore emits only the canonical name; and " + "HINDSIGHT_API_SEMANTIC_MIN_SIMILARITY \u2014 the cosine-similarity floor a " + "candidate must reach to be returned by the semantic retrieval arm at " + "all (0..1); unset means upstream's own 0.3, and where it should sit " + "depends on the bank's embedding model and phrasing diversity, so " + "switchroom ships no opinion; and " + "HINDSIGHT_MCP_RECALL_BUDGET_MODE \u2014 the rollback knob for switchroom's " + "mcp-recall-token-budget image patch; `legacy` restores upstream's " + "exact pre-patch recall returns, anything else or unset is the " + "honest-envelope mode; and " + "HINDSIGHT_API_LLM_TEMPERATURE_REFLECT \u2014 upstream's own per-op reflect " + "temperature knob, made live by switchroom's reflect-temperature image " + "patch; a float, or `none` to omit the kwarg (provider default, " + "upstream's accidental pre-patch behaviour); unset means the image's " + "baked default 0.1; and " + "HINDSIGHT_API_CONSOLIDATION_RECALL_MAX_CONCURRENT \u2014 the background " + "half of switchroom's recall-admission split (#3660): of the " + "RECALL_MAX_CONCURRENT admission slots, at most this many may be held " + "by background consolidation recalls at once, so foreground per-turn " + "recall always keeps the remainder; must be >= 1 and strictly less " + "than RECALL_MAX_CONCURRENT or the engine refuses to boot; unset " + "means the image's derived default min(2, RECALL_MAX_CONCURRENT - 1); " + "1 biases hard toward the interactive lane while a consolidation " + "backlog drains; and " + "HINDSIGHT_MM_REFRESH_MIN_INTERVAL_S \u2014 the rollback/tuning knob for " + "switchroom's MM-refresh-debounce image patch: the minimum seconds " + "between consolidation-triggered refreshes of one mental model; unset " + "means the image's baked 3600, 0 restores upstream's " + "refresh-every-round behaviour; explicit and cron-scheduled refreshes " + "are never debounced; and " + "HINDSIGHT_API_TEMPORAL_LANGUAGES \u2014 the language set dateparser is " + "restricted to during temporal query analysis, made live by switchroom's " + "temporal-language image patch (which ended a 200+-locale auto-detection " + "pass that blocked the shared asyncio loop on every recall); " + "comma-separated, unset means the image's baked `en`, set e.g. `en,es` to " + "restore i18n parsing; and " + "HINDSIGHT_API_TEMPORAL_MAX_QUERY_CHARS \u2014 the max chars of query text " + "dateparser.search_dates() is handed during temporal analysis (0 = " + "unlimited), made live by switchroom's temporal-offload image patch " + "(which moved the synchronous extraction off the shared asyncio loop onto " + "a single-worker thread and bounds its input, ending multi-second loop " + "stalls on multi-KB consolidation queries); unset means the image's baked " + "2000, set 0 to restore an unbounded full scan); and " + "HINDSIGHT_API_TEXT_SEARCH_EXTENSION_NATIVE_LANGUAGE \u2014 the Postgres " + "text-search regconfig the native BM25 arms and the search_vector index " + "write run to_tsvector/to_tsquery against; unset means upstream's " + "always-safe `english`, set `hindsight_english` to use switchroom's " + "junk-stopping config (dict + stopword file provisioned durably by the " + "image entrypoint, recall degrading to semantic-only if it is ever " + "missing); plus the " + "embedded-PostgreSQL (pg0) sizing keys switchroom manages in " + "src/setup/hindsight-pg-defaults.ts (`HINDSIGHT_PG_ENV_KEYS`: " + "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE, " + "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS \u2014 a postgres size string such " + "as `4GB`, or the sentinel `off` to leave pg0's own default for that " + "one knob). A value set here " + "REPLACES switchroom's default and is emitted even when the gating " + "capability is absent, so an operator can always force a knob. Other " + "`HINDSIGHT_API_*` keys are deliberately IGNORED \u2014 a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
|
|
22040
22041
|
});
|
|
22041
22042
|
MicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
22042
22043
|
microsoft_client_id: exports_external.string().min(1).optional().describe("Microsoft OAuth application (client) ID from Entra portal " + "(literal string or vault reference e.g. " + "'vault:microsoft-oauth-client-id'). OPTIONAL \u2014 omit it to use " + "switchroom's shipped default Microsoft app (zero-config). " + "Set it only to bring your own Entra app (BYO)."),
|
|
@@ -22489,7 +22490,9 @@ var init_schema = __esm(() => {
|
|
|
22489
22490
|
path: exports_external.string().regex(/^[a-zA-Z0-9~._\-/]+$/, "config_repo.path must not contain shell-special characters ($, `, \", ', \\, etc.)").default("~/.switchroom-config").describe("Filesystem path to the operator's private config repo. Must be a git " + "repo (else `switchroom doctor` FAILs). Tilde-expanded at read time."),
|
|
22490
22491
|
push: exports_external.boolean().default(true).describe("When true, `config-repo sync` pushes after committing (subject to the " + "`require_private` gate). When false, it commits locally only \u2014 useful " + "for offline hosts or a review-before-push workflow."),
|
|
22491
22492
|
remote: exports_external.string().min(1).default("origin").describe("Git remote name to push to. Default `origin`."),
|
|
22492
|
-
require_private: exports_external.boolean().default(true).describe("Refuse to PUSH unless the GitHub API confirms the remote repo is " + "private. On a public remote \u2014 or when the API is unreachable \u2014 the " + "push is skipped (commits still land locally) and a WARN is emitted. " + "Fail-safe against exfiltrating memory files / workspace state to a " + "repo that has been flipped public. Default true.")
|
|
22493
|
+
require_private: exports_external.boolean().default(true).describe("Refuse to PUSH unless the GitHub API confirms the remote repo is " + "private. On a public remote \u2014 or when the API is unreachable \u2014 the " + "push is skipped (commits still land locally) and a WARN is emitted. " + "Fail-safe against exfiltrating memory files / workspace state to a " + "repo that has been flipped public. Default true."),
|
|
22494
|
+
interval_minutes: exports_external.number().int().min(5).max(59).default(30).describe("Cadence (minutes) of the scheduled `config-repo sync` tick installed " + "by `switchroom config-repo --install-cron`. Rendered as a cron " + "`*/N * * * *` minute field, so it must be 5..59 (the 5-min floor " + "matches the rest of the fleet's cron floor). Default 30 (Ken's " + "approved cadence). Changing it takes effect on the next " + "`--install-cron` / `switchroom update` reconcile."),
|
|
22495
|
+
include_vault_backup: exports_external.enum(["off", "daily", "every_tick"]).default("daily").describe("Whether the scheduled tick also runs `switchroom vault backup` " + "(an encrypted vault/memory snapshot into <path>/vault-backups/) " + "before the sync. `off`: sync only. `daily`: an extra once-per-day " + "cron leg runs `vault backup && config-repo sync` (the 30-min legs " + "stay sync-only). `every_tick`: every tick runs the backup first. " + "Default `daily` \u2014 the first automated vault backup on this host.")
|
|
22493
22496
|
});
|
|
22494
22497
|
SwitchroomConfigSchema = exports_external.object({
|
|
22495
22498
|
switchroom: exports_external.object({
|
|
@@ -49357,7 +49360,7 @@ function createWorkerActivityFeed(opts) {
|
|
|
49357
49360
|
const floodWaitRemainingMs = opts.floodWaitRemainingMs ?? (() => 0);
|
|
49358
49361
|
const minEditInterval = opts.minEditIntervalMs ?? 2500;
|
|
49359
49362
|
const elapsedRefreshMs = Math.max(minEditInterval, Math.floor(opts.elapsedRefreshMs ?? 15000));
|
|
49360
|
-
const firstPaintMin = opts.firstPaintMinMs ??
|
|
49363
|
+
const firstPaintMin = opts.firstPaintMinMs ?? 4000;
|
|
49361
49364
|
const heartbeatTickMs = opts.heartbeatTickMs ?? 6000;
|
|
49362
49365
|
const maxRows = Math.max(1, Math.floor(opts.maxRows ?? 8));
|
|
49363
49366
|
const staleWorkerTtlMs = Math.max(1, Math.floor(opts.staleWorkerTtlMs ?? 50 * 60000));
|
|
@@ -79040,6 +79043,17 @@ import { homedir as homedir9 } from "node:os";
|
|
|
79040
79043
|
// hooks/audience-classify.mjs
|
|
79041
79044
|
var AUDIENCE_USER = "user";
|
|
79042
79045
|
var AUDIENCE_INTERNAL = "internal";
|
|
79046
|
+
function decideCaptureAudience(signals) {
|
|
79047
|
+
if (signals?.reviewOriginated === true) {
|
|
79048
|
+
return signals?.reviewTextIsCard === true ? AUDIENCE_USER : AUDIENCE_INTERNAL;
|
|
79049
|
+
}
|
|
79050
|
+
const threw = signals?.replyToolThrewThisTurn === true;
|
|
79051
|
+
if (!threw)
|
|
79052
|
+
return AUDIENCE_USER;
|
|
79053
|
+
if (signals?.openInboundObligation !== false)
|
|
79054
|
+
return AUDIENCE_USER;
|
|
79055
|
+
return AUDIENCE_INTERNAL;
|
|
79056
|
+
}
|
|
79043
79057
|
function resolveRecordAudience(value) {
|
|
79044
79058
|
return value === AUDIENCE_INTERNAL ? AUDIENCE_INTERNAL : AUDIENCE_USER;
|
|
79045
79059
|
}
|
|
@@ -79066,6 +79080,31 @@ function formatReplyThrowFraming(s) {
|
|
|
79066
79080
|
return `telegram gateway: outbox provenance framing nonce=${s.turnNonce} ` + `turnId=${s.turnId ?? "unknown"} chatId=${s.chatId ?? "unresolved"} ` + `sha=${(s.textSha256 ?? "").slice(0, 12)} source=${s.source ?? "unknown"} ` + `audience=user replyToolThrew=true \u2014 captured prose delivered with its ` + `provenance stated, not suppressed
|
|
79067
79081
|
`;
|
|
79068
79082
|
}
|
|
79083
|
+
var SELF_IMPROVEMENT_TITLE = "\uD83D\uDD27 **Self-improvement**";
|
|
79084
|
+
function isSelfImprovementCard(text4) {
|
|
79085
|
+
if (typeof text4 !== "string")
|
|
79086
|
+
return false;
|
|
79087
|
+
return text4.trimStart().startsWith(SELF_IMPROVEMENT_TITLE);
|
|
79088
|
+
}
|
|
79089
|
+
function shouldFrameSelfImprovement(record, opts) {
|
|
79090
|
+
if (opts?.frameEnabled === false)
|
|
79091
|
+
return false;
|
|
79092
|
+
return record?.reviewOriginated === true;
|
|
79093
|
+
}
|
|
79094
|
+
function applySelfImprovementFraming(text4) {
|
|
79095
|
+
const body = typeof text4 === "string" ? text4 : "";
|
|
79096
|
+
if (body.trim().length === 0)
|
|
79097
|
+
return body;
|
|
79098
|
+
if (isSelfImprovementCard(body))
|
|
79099
|
+
return body;
|
|
79100
|
+
return `${SELF_IMPROVEMENT_TITLE}
|
|
79101
|
+
|
|
79102
|
+
${body}`;
|
|
79103
|
+
}
|
|
79104
|
+
function formatSelfImprovementFraming(s) {
|
|
79105
|
+
return `telegram gateway: outbox self-improvement framing nonce=${s.turnNonce} ` + `turnId=${s.turnId ?? "unknown"} chatId=${s.chatId ?? "unresolved"} ` + `sha=${(s.textSha256 ?? "").slice(0, 12)} source=${s.source ?? "unknown"} ` + `reviewOriginated=true \u2014 review-turn prose delivered with its self-improvement ` + `title, never as raw unlabelled reasoning
|
|
79106
|
+
`;
|
|
79107
|
+
}
|
|
79069
79108
|
function formatInternalSuppression(s) {
|
|
79070
79109
|
return `telegram gateway: outbox audience suppression nonce=${s.turnNonce} ` + `turnId=${s.turnId ?? "unknown"} chatId=${s.chatId ?? "unresolved"} ` + `sha=${(s.textSha256 ?? "").slice(0, 12)} ageMs=${s.ageMs ?? 0} ` + `source=${s.source ?? "unknown"} pastWindow=${s.pastWindow === true} ` + `audience=internal \u2014 internal prose withheld from chat, journaled terminally
|
|
79071
79110
|
`;
|
|
@@ -79252,7 +79291,8 @@ function decideOutboxSweep(input) {
|
|
|
79252
79291
|
quietMs = OUTBOX_QUIET_MS,
|
|
79253
79292
|
maxAgeMs = OUTBOX_MAX_AGE_MS,
|
|
79254
79293
|
shownLedgerHit = false,
|
|
79255
|
-
provenanceFraming = true
|
|
79294
|
+
provenanceFraming = true,
|
|
79295
|
+
selfImprovementFraming = true
|
|
79256
79296
|
} = input;
|
|
79257
79297
|
if (deliveredNonces.has(record.turnNonce))
|
|
79258
79298
|
return { action: "skip-journaled" };
|
|
@@ -79268,11 +79308,14 @@ function decideOutboxSweep(input) {
|
|
|
79268
79308
|
const delayed = age > maxAgeMs;
|
|
79269
79309
|
const prefix = (delayed ? "(delayed) " : "") + routePrefix;
|
|
79270
79310
|
const framed = shouldFrameReplyThrow(record, { frameEnabled: provenanceFraming });
|
|
79271
|
-
const
|
|
79311
|
+
const provenanceBody = framed ? applyReplyThrowFraming(record.text) : record.text;
|
|
79312
|
+
const selfImproveFramed = shouldFrameSelfImprovement(record, { frameEnabled: selfImprovementFraming }) && !isSelfImprovementCard(record.text);
|
|
79313
|
+
const body = selfImproveFramed ? applySelfImprovementFraming(provenanceBody) : provenanceBody;
|
|
79272
79314
|
return {
|
|
79273
79315
|
action: delayed ? "send-delayed" : "send",
|
|
79274
79316
|
text: prefix + body,
|
|
79275
|
-
...framed &&
|
|
79317
|
+
...framed && provenanceBody !== record.text ? { framedProvenance: "reply-throw" } : {},
|
|
79318
|
+
...selfImproveFramed && body !== provenanceBody ? { framedSelfImprovement: "self-improve" } : {}
|
|
79276
79319
|
};
|
|
79277
79320
|
}
|
|
79278
79321
|
function resolveOutboxChat(record, deps) {
|
|
@@ -79336,7 +79379,6 @@ function isStructuralNarration(text4, followedByToolUse) {
|
|
|
79336
79379
|
var EPHEMERAL_TOOLS = new Set([
|
|
79337
79380
|
"react",
|
|
79338
79381
|
"send_typing",
|
|
79339
|
-
"pin_message",
|
|
79340
79382
|
"delete_message",
|
|
79341
79383
|
"edit_message"
|
|
79342
79384
|
]);
|
|
@@ -79604,7 +79646,10 @@ function journalExternalDelivery(args, stateDir, now = Date.now()) {
|
|
|
79604
79646
|
ts: now,
|
|
79605
79647
|
deliverySource: args.deliverySource ?? "reply-tool",
|
|
79606
79648
|
...args.replyAlreadyDeliveredThisTurn == null ? {} : { replyAlreadyDeliveredThisTurn: args.replyAlreadyDeliveredThisTurn },
|
|
79607
|
-
...args.framedProvenance == null ? {} : { framedProvenance: args.framedProvenance }
|
|
79649
|
+
...args.framedProvenance == null ? {} : { framedProvenance: args.framedProvenance },
|
|
79650
|
+
...args.audience == null ? {} : { audience: args.audience },
|
|
79651
|
+
...args.suppressedAudience == null ? {} : { suppressedAudience: args.suppressedAudience },
|
|
79652
|
+
...args.framedSelfImprovement == null ? {} : { framedSelfImprovement: args.framedSelfImprovement }
|
|
79608
79653
|
}, stateDir);
|
|
79609
79654
|
clearOutboxRecord(nonce, stateDir);
|
|
79610
79655
|
}
|
|
@@ -80284,7 +80329,8 @@ function decideCapturedProseDelivery(args, deps) {
|
|
|
80284
80329
|
deliver: true,
|
|
80285
80330
|
text: text4,
|
|
80286
80331
|
reason: "captured-prose",
|
|
80287
|
-
...state3.replyToolThrewThisTurn === true ? { replyToolThrewThisTurn: true } : {}
|
|
80332
|
+
...state3.replyToolThrewThisTurn === true ? { replyToolThrewThisTurn: true } : {},
|
|
80333
|
+
...state3.reviewOriginated === true ? { reviewOriginated: true } : {}
|
|
80288
80334
|
};
|
|
80289
80335
|
}
|
|
80290
80336
|
function settleCapturedProseDelivery(outcome, effects) {
|
|
@@ -81365,67 +81411,104 @@ async function deliverCapturedProse(deps, args) {
|
|
|
81365
81411
|
const { chatId, threadId, statusKeyStr, registryKey, originTurnId, text: text4, turnDurationMs } = args;
|
|
81366
81412
|
const now = Date.now();
|
|
81367
81413
|
const framed = shouldFrameReplyThrow({ replyToolThrewThisTurn: args.replyToolThrewThisTurn }, { frameEnabled: process.env.SWITCHROOM_TG_OUTBOX_PROVENANCE_FRAMING !== "0" });
|
|
81414
|
+
const audienceGateEnabled = process.env.SWITCHROOM_TG_OUTBOX_AUDIENCE_GATE !== "0";
|
|
81415
|
+
const audience = decideCaptureAudience({
|
|
81416
|
+
reviewOriginated: args.reviewOriginated === true,
|
|
81417
|
+
reviewTextIsCard: isSelfImprovementCard(text4)
|
|
81418
|
+
});
|
|
81419
|
+
const suppressed = audienceGateEnabled && audience === AUDIENCE_INTERNAL;
|
|
81420
|
+
const selfImproveFramed = !suppressed && shouldFrameSelfImprovement({ reviewOriginated: args.reviewOriginated }, { frameEnabled: process.env.SWITCHROOM_TG_OUTBOX_SELF_IMPROVE_FRAMING !== "0" }) && !isSelfImprovementCard(text4);
|
|
81368
81421
|
let outcome;
|
|
81369
|
-
|
|
81370
|
-
|
|
81371
|
-
|
|
81372
|
-
|
|
81373
|
-
|
|
81374
|
-
|
|
81375
|
-
|
|
81376
|
-
|
|
81377
|
-
|
|
81378
|
-
|
|
81379
|
-
|
|
81380
|
-
|
|
81381
|
-
|
|
81382
|
-
|
|
81383
|
-
|
|
81384
|
-
|
|
81385
|
-
|
|
81386
|
-
|
|
81422
|
+
if (suppressed) {
|
|
81423
|
+
journalExternalDelivery({
|
|
81424
|
+
turnNonce: originTurnId,
|
|
81425
|
+
text: text4,
|
|
81426
|
+
replyAlreadyDeliveredThisTurn: false,
|
|
81427
|
+
audience,
|
|
81428
|
+
suppressedAudience: AUDIENCE_INTERNAL
|
|
81429
|
+
});
|
|
81430
|
+
process.stderr.write(formatInternalSuppression({
|
|
81431
|
+
turnNonce: originTurnId,
|
|
81432
|
+
turnId: originTurnId,
|
|
81433
|
+
chatId,
|
|
81434
|
+
textSha256: sha256Hex(text4),
|
|
81435
|
+
source: "captured-prose-bridge"
|
|
81436
|
+
}));
|
|
81437
|
+
outcome = "suppressed-internal";
|
|
81438
|
+
} else {
|
|
81439
|
+
const already = outboundDedup.check(chatId, threadId, text4, now, registryKey);
|
|
81440
|
+
if (already == null) {
|
|
81441
|
+
let out = normalizeParagraphBreaks(repairEscapedWhitespace(framed ? applyReplyThrowFraming(text4) : text4));
|
|
81442
|
+
out = selfImproveFramed ? applySelfImprovementFraming(out) : out;
|
|
81443
|
+
out = redactOutboundText(out, "captured_prose");
|
|
81444
|
+
const chunks = splitMarkdownChunks(out, RICH_MESSAGE_MAX_CHARS);
|
|
81445
|
+
const sentIds = [];
|
|
81446
|
+
try {
|
|
81447
|
+
let liveThreadId = threadId;
|
|
81448
|
+
for (const c of chunks) {
|
|
81449
|
+
const sent = await retryWithThreadFallback(robustApiCall, (tid) => {
|
|
81450
|
+
const opts = {
|
|
81451
|
+
link_preview_options: { is_disabled: true },
|
|
81452
|
+
...tid != null ? { message_thread_id: tid } : {}
|
|
81453
|
+
};
|
|
81454
|
+
return bot.api.sendRichMessage(chatId, richMessage(c), opts);
|
|
81455
|
+
}, { threadId: liveThreadId, chat_id: chatId, verb: "captured-prose.sendMessage" });
|
|
81456
|
+
if (liveThreadId != null && sent.message_thread_id == null) {
|
|
81457
|
+
liveThreadId = undefined;
|
|
81458
|
+
}
|
|
81459
|
+
sentIds.push(sent.message_id);
|
|
81387
81460
|
}
|
|
81388
|
-
sentIds.
|
|
81389
|
-
|
|
81390
|
-
|
|
81391
|
-
|
|
81392
|
-
|
|
81393
|
-
|
|
81394
|
-
|
|
81395
|
-
|
|
81396
|
-
|
|
81397
|
-
|
|
81398
|
-
|
|
81399
|
-
|
|
81400
|
-
outboundDedup.record(chatId, threadId, text4, now, registryKey);
|
|
81401
|
-
journalExternalDelivery({
|
|
81402
|
-
turnNonce: originTurnId,
|
|
81403
|
-
text: text4,
|
|
81404
|
-
tgMessageId: sentIds[sentIds.length - 1],
|
|
81405
|
-
replyAlreadyDeliveredThisTurn: false,
|
|
81406
|
-
...framed ? { framedProvenance: "reply-throw" } : {}
|
|
81407
|
-
});
|
|
81408
|
-
process.stderr.write(`telegram gateway: captured-prose delivery \u2014 sent ${out.length} chars recovered from ` + `transcript scan (chat=${chatId} origin=${originTurnId})
|
|
81409
|
-
`);
|
|
81410
|
-
if (framed) {
|
|
81411
|
-
process.stderr.write(formatReplyThrowFraming({
|
|
81461
|
+
if (HISTORY_ENABLED && sentIds.length > 0) {
|
|
81462
|
+
try {
|
|
81463
|
+
recordOutbound2({
|
|
81464
|
+
chat_id: chatId,
|
|
81465
|
+
thread_id: threadId ?? null,
|
|
81466
|
+
message_ids: sentIds,
|
|
81467
|
+
texts: chunks
|
|
81468
|
+
});
|
|
81469
|
+
} catch {}
|
|
81470
|
+
}
|
|
81471
|
+
outboundDedup.record(chatId, threadId, text4, now, registryKey);
|
|
81472
|
+
journalExternalDelivery({
|
|
81412
81473
|
turnNonce: originTurnId,
|
|
81413
|
-
|
|
81414
|
-
|
|
81415
|
-
|
|
81416
|
-
|
|
81417
|
-
|
|
81474
|
+
text: text4,
|
|
81475
|
+
tgMessageId: sentIds[sentIds.length - 1],
|
|
81476
|
+
replyAlreadyDeliveredThisTurn: false,
|
|
81477
|
+
...framed ? { framedProvenance: "reply-throw" } : {},
|
|
81478
|
+
...selfImproveFramed ? { framedSelfImprovement: "self-improve" } : {},
|
|
81479
|
+
audience
|
|
81480
|
+
});
|
|
81481
|
+
process.stderr.write(`telegram gateway: captured-prose delivery \u2014 sent ${out.length} chars recovered from ` + `transcript scan (chat=${chatId} origin=${originTurnId})
|
|
81482
|
+
`);
|
|
81483
|
+
if (framed) {
|
|
81484
|
+
process.stderr.write(formatReplyThrowFraming({
|
|
81485
|
+
turnNonce: originTurnId,
|
|
81486
|
+
turnId: originTurnId,
|
|
81487
|
+
chatId,
|
|
81488
|
+
textSha256: sha256Hex(text4),
|
|
81489
|
+
source: "captured-prose-bridge"
|
|
81490
|
+
}));
|
|
81491
|
+
}
|
|
81492
|
+
if (selfImproveFramed) {
|
|
81493
|
+
process.stderr.write(formatSelfImprovementFraming({
|
|
81494
|
+
turnNonce: originTurnId,
|
|
81495
|
+
turnId: originTurnId,
|
|
81496
|
+
chatId,
|
|
81497
|
+
textSha256: sha256Hex(text4),
|
|
81498
|
+
source: "captured-prose-bridge"
|
|
81499
|
+
}));
|
|
81500
|
+
}
|
|
81501
|
+
outcome = "sent";
|
|
81502
|
+
} catch (err) {
|
|
81503
|
+
process.stderr.write(`telegram gateway: captured-prose delivery failed: ${err.message} \u2014 ` + `arming the silent-end re-prompt net (recordUndeliveredTurnEnd) so the ` + `answer is recoverable (chat=${chatId} origin=${originTurnId})
|
|
81504
|
+
`);
|
|
81505
|
+
outcome = "failed";
|
|
81418
81506
|
}
|
|
81419
|
-
|
|
81420
|
-
|
|
81421
|
-
process.stderr.write(`telegram gateway: captured-prose delivery failed: ${err.message} \u2014 ` + `arming the silent-end re-prompt net (recordUndeliveredTurnEnd) so the ` + `answer is recoverable (chat=${chatId} origin=${originTurnId})
|
|
81507
|
+
} else {
|
|
81508
|
+
process.stderr.write(`telegram gateway: captured-prose delivery skipped \u2014 this answer already went out ` + `(dedup age=${already.ageMs}ms chat=${chatId} origin=${originTurnId}); settling bookkeeping
|
|
81422
81509
|
`);
|
|
81423
|
-
outcome = "
|
|
81510
|
+
outcome = "skipped-dedup";
|
|
81424
81511
|
}
|
|
81425
|
-
} else {
|
|
81426
|
-
process.stderr.write(`telegram gateway: captured-prose delivery skipped \u2014 this answer already went out ` + `(dedup age=${already.ageMs}ms chat=${chatId} origin=${originTurnId}); settling bookkeeping
|
|
81427
|
-
`);
|
|
81428
|
-
outcome = "skipped-dedup";
|
|
81429
81512
|
}
|
|
81430
81513
|
const settlement = settleCapturedProseDelivery(outcome, {
|
|
81431
81514
|
closeObligation: () => {
|
|
@@ -82650,6 +82733,12 @@ function decideTurnEndGate(snapshot) {
|
|
|
82650
82733
|
}
|
|
82651
82734
|
|
|
82652
82735
|
// gateway/turn-record-status.ts
|
|
82736
|
+
function computeTurnDurationMs(startedAt, now) {
|
|
82737
|
+
if (typeof startedAt !== "number" || !Number.isFinite(startedAt) || startedAt <= 0) {
|
|
82738
|
+
return 0;
|
|
82739
|
+
}
|
|
82740
|
+
return Math.max(0, now - startedAt);
|
|
82741
|
+
}
|
|
82653
82742
|
function backstopSendOutcomeGated(args) {
|
|
82654
82743
|
if (args.threw)
|
|
82655
82744
|
return "failed";
|
|
@@ -83605,7 +83694,7 @@ function handleSessionEvent(deps, ev) {
|
|
|
83605
83694
|
chatId,
|
|
83606
83695
|
threadId,
|
|
83607
83696
|
turnId: turn.turnId,
|
|
83608
|
-
turnDurationMs: turn.startedAt
|
|
83697
|
+
turnDurationMs: computeTurnDurationMs(turn.startedAt, Date.now()),
|
|
83609
83698
|
reactionCount: reactionTransitionCounts.get(tKey) ?? 0
|
|
83610
83699
|
});
|
|
83611
83700
|
}
|
|
@@ -83623,7 +83712,7 @@ function handleSessionEvent(deps, ev) {
|
|
|
83623
83712
|
finalizeStatusReaction(chatId, threadId, "done");
|
|
83624
83713
|
{
|
|
83625
83714
|
const sKey = streamKey2(chatId, threadId);
|
|
83626
|
-
const turnDurationMs = turn.startedAt
|
|
83715
|
+
const turnDurationMs = computeTurnDurationMs(turn.startedAt, Date.now());
|
|
83627
83716
|
logStreamingEvent2({
|
|
83628
83717
|
kind: "turn_end",
|
|
83629
83718
|
chatId,
|
|
@@ -83857,7 +83946,7 @@ function handleSessionEvent(deps, ev) {
|
|
|
83857
83946
|
finalizeStatusReaction(chatId, threadId, terminalReason);
|
|
83858
83947
|
{
|
|
83859
83948
|
const sKey = streamKey2(chatId, threadId);
|
|
83860
|
-
const turnDurationMs = turn.startedAt
|
|
83949
|
+
const turnDurationMs = computeTurnDurationMs(turn.startedAt, Date.now());
|
|
83861
83950
|
logStreamingEvent2({
|
|
83862
83951
|
kind: "turn_end",
|
|
83863
83952
|
chatId,
|
|
@@ -83902,6 +83991,7 @@ function handleSessionEvent(deps, ev) {
|
|
|
83902
83991
|
originTurnId: turn.turnId,
|
|
83903
83992
|
text: proseDecision.text,
|
|
83904
83993
|
replyToolThrewThisTurn: proseDecision.replyToolThrewThisTurn === true,
|
|
83994
|
+
reviewOriginated: proseDecision.reviewOriginated === true,
|
|
83905
83995
|
turnDurationMs
|
|
83906
83996
|
});
|
|
83907
83997
|
} else {
|
|
@@ -87743,6 +87833,27 @@ init_peercred();
|
|
|
87743
87833
|
// ../src/agents/scaffold.ts
|
|
87744
87834
|
init_generation_stamp();
|
|
87745
87835
|
|
|
87836
|
+
// ../src/setup/hindsight-recall-passthrough.ts
|
|
87837
|
+
var HINDSIGHT_RECALL_TAG_WEIGHT_SEED = Object.freeze({ sidechain: 0.8 });
|
|
87838
|
+
var HINDSIGHT_RECALL_PROMPT_PREAMBLE_DEFAULT = "Relevant memories from past conversations (prioritize recent when " + "conflicting). Only use memories that are directly useful to continue " + "this conversation; ignore the rest:";
|
|
87839
|
+
var RECALL_PASSTHROUGH_DEFAULTS = Object.freeze({
|
|
87840
|
+
budget: "low",
|
|
87841
|
+
maxTokens: 1024,
|
|
87842
|
+
preferObservations: true,
|
|
87843
|
+
contextTurns: 2,
|
|
87844
|
+
roles: ["user", "assistant"],
|
|
87845
|
+
promptPreamble: HINDSIGHT_RECALL_PROMPT_PREAMBLE_DEFAULT,
|
|
87846
|
+
tags: [],
|
|
87847
|
+
tagsMatch: "any",
|
|
87848
|
+
tagGroups: {},
|
|
87849
|
+
tagWeights: HINDSIGHT_RECALL_TAG_WEIGHT_SEED,
|
|
87850
|
+
additionalBankFilters: {},
|
|
87851
|
+
transcriptFallback: true,
|
|
87852
|
+
transcriptTailBytes: 262144,
|
|
87853
|
+
maxQueryChars: 800,
|
|
87854
|
+
parallel: true
|
|
87855
|
+
});
|
|
87856
|
+
|
|
87746
87857
|
// ../src/setup/hindsight-recall-tunables.ts
|
|
87747
87858
|
var RECALL_DEADLINE_HEADROOM_SECONDS = 2;
|
|
87748
87859
|
var MIN_RECALL_HOOK_TIMEOUT_SECONDS = RECALL_DEADLINE_HEADROOM_SECONDS + 1;
|
|
@@ -87870,6 +87981,7 @@ var HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB = 12288;
|
|
|
87870
87981
|
function pgMib(mib) {
|
|
87871
87982
|
return `${mib}MB`;
|
|
87872
87983
|
}
|
|
87984
|
+
var HINDSIGHT_PG_MIN_NON_BUFFER_MIB = HINDSIGHT_PG_APP_ANON_MIB + HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB;
|
|
87873
87985
|
var HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE_ENV = "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE";
|
|
87874
87986
|
var HINDSIGHT_PG_SHARED_BUFFERS_ENV = "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS";
|
|
87875
87987
|
var HINDSIGHT_PG_FSYNC_ENV = "SWITCHROOM_HINDSIGHT_PG_FSYNC";
|
|
@@ -87917,6 +88029,7 @@ var HINDSIGHT_DEFAULT_GRAPH_SEED_MIN_SIMILARITY = 0.3;
|
|
|
87917
88029
|
var HINDSIGHT_DEFAULT_LLM_SUPPORTS_MAX_ITEMS = "false";
|
|
87918
88030
|
var HINDSIGHT_DEFAULT_RECENCY_DECAY_FUNCTION = "exponential";
|
|
87919
88031
|
var HINDSIGHT_DEFAULT_RECENCY_DECAY_HALFLIFE_DAYS = 30;
|
|
88032
|
+
var HINDSIGHT_DEFAULT_TEXT_SEARCH_EXTENSION = "pg_search";
|
|
87920
88033
|
var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
87921
88034
|
[
|
|
87922
88035
|
"HINDSIGHT_API_RECALL_MAX_CANDIDATES_PER_SOURCE",
|
|
@@ -87982,7 +88095,8 @@ var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
|
87982
88095
|
[
|
|
87983
88096
|
"HINDSIGHT_API_GRAPH_SEED_MIN_SIMILARITY",
|
|
87984
88097
|
String(HINDSIGHT_DEFAULT_GRAPH_SEED_MIN_SIMILARITY)
|
|
87985
|
-
]
|
|
88098
|
+
],
|
|
88099
|
+
["HINDSIGHT_API_TEXT_SEARCH_EXTENSION", HINDSIGHT_DEFAULT_TEXT_SEARCH_EXTENSION]
|
|
87986
88100
|
];
|
|
87987
88101
|
var HINDSIGHT_PERF_DEFAULTS_GPU = [
|
|
87988
88102
|
["HINDSIGHT_API_RERANKER_LOCAL_FP16", HINDSIGHT_DEFAULT_RERANKER_LOCAL_FP16],
|
|
@@ -88018,7 +88132,8 @@ var HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS = new Set([
|
|
|
88018
88132
|
"HINDSIGHT_API_CONSOLIDATION_RECALL_MAX_CONCURRENT",
|
|
88019
88133
|
"HINDSIGHT_MM_REFRESH_MIN_INTERVAL_S",
|
|
88020
88134
|
"HINDSIGHT_API_TEMPORAL_LANGUAGES",
|
|
88021
|
-
"HINDSIGHT_API_TEMPORAL_MAX_QUERY_CHARS"
|
|
88135
|
+
"HINDSIGHT_API_TEMPORAL_MAX_QUERY_CHARS",
|
|
88136
|
+
"HINDSIGHT_API_TEXT_SEARCH_EXTENSION_NATIVE_LANGUAGE"
|
|
88022
88137
|
]);
|
|
88023
88138
|
var HINDSIGHT_WORKER_SLOT_TYPES = [
|
|
88024
88139
|
"consolidation",
|
|
@@ -88440,27 +88555,6 @@ init_vault();
|
|
|
88440
88555
|
// ../src/setup/onboarding.ts
|
|
88441
88556
|
init_paths();
|
|
88442
88557
|
|
|
88443
|
-
// ../src/setup/hindsight-recall-passthrough.ts
|
|
88444
|
-
var HINDSIGHT_RECALL_TAG_WEIGHT_SEED = Object.freeze({ sidechain: 0.8 });
|
|
88445
|
-
var HINDSIGHT_RECALL_PROMPT_PREAMBLE_DEFAULT = "Relevant memories from past conversations (prioritize recent when " + "conflicting). Only use memories that are directly useful to continue " + "this conversation; ignore the rest:";
|
|
88446
|
-
var RECALL_PASSTHROUGH_DEFAULTS = Object.freeze({
|
|
88447
|
-
budget: "low",
|
|
88448
|
-
maxTokens: 1024,
|
|
88449
|
-
preferObservations: true,
|
|
88450
|
-
contextTurns: 2,
|
|
88451
|
-
roles: ["user", "assistant"],
|
|
88452
|
-
promptPreamble: HINDSIGHT_RECALL_PROMPT_PREAMBLE_DEFAULT,
|
|
88453
|
-
tags: [],
|
|
88454
|
-
tagsMatch: "any",
|
|
88455
|
-
tagGroups: {},
|
|
88456
|
-
tagWeights: HINDSIGHT_RECALL_TAG_WEIGHT_SEED,
|
|
88457
|
-
additionalBankFilters: {},
|
|
88458
|
-
transcriptFallback: true,
|
|
88459
|
-
transcriptTailBytes: 262144,
|
|
88460
|
-
maxQueryChars: 800,
|
|
88461
|
-
parallel: true
|
|
88462
|
-
});
|
|
88463
|
-
|
|
88464
88558
|
// ../src/repos/bare-clone.ts
|
|
88465
88559
|
init_paths();
|
|
88466
88560
|
|
|
@@ -94204,6 +94298,7 @@ function hydrateResumeLedger(ledger, turnId, snapshot, hasLanded) {
|
|
|
94204
94298
|
}
|
|
94205
94299
|
function createCapturedResumeDispatcher(ports) {
|
|
94206
94300
|
const stderr = ports.stderr ?? (() => {});
|
|
94301
|
+
const hasOutboundWithText3 = ports.hasOutboundWithText ?? hasOutboundWithText2;
|
|
94207
94302
|
const inFlight = new Set;
|
|
94208
94303
|
function deliver(o, snapshot) {
|
|
94209
94304
|
return ports.deliverAnswer({
|
|
@@ -94215,7 +94310,7 @@ function createCapturedResumeDispatcher(ports) {
|
|
|
94215
94310
|
replyToMessageId: o.messageId,
|
|
94216
94311
|
resume: {
|
|
94217
94312
|
snapshot,
|
|
94218
|
-
hydrate: (ledger, turnId) => hydrateResumeLedger(ledger, turnId, snapshot, (txt) => ports.historyEnabled &&
|
|
94313
|
+
hydrate: (ledger, turnId) => hydrateResumeLedger(ledger, turnId, snapshot, (txt) => ports.historyEnabled && hasOutboundWithText3(o.chatId, txt, o.threadId ?? null, o.openedAt))
|
|
94219
94314
|
}
|
|
94220
94315
|
});
|
|
94221
94316
|
}
|
|
@@ -95142,6 +95237,12 @@ function maybeRotate(path3, fs3, maxBytes = TURNS_JSONL_MAX_BYTES) {
|
|
|
95142
95237
|
}
|
|
95143
95238
|
|
|
95144
95239
|
// gateway/turn-record-status.ts
|
|
95240
|
+
function computeTurnDurationMs2(startedAt, now) {
|
|
95241
|
+
if (typeof startedAt !== "number" || !Number.isFinite(startedAt) || startedAt <= 0) {
|
|
95242
|
+
return 0;
|
|
95243
|
+
}
|
|
95244
|
+
return Math.max(0, now - startedAt);
|
|
95245
|
+
}
|
|
95145
95246
|
function computeTurnStatus(turn) {
|
|
95146
95247
|
switch (turn.deliveryOutcome) {
|
|
95147
95248
|
case "failed":
|
|
@@ -95170,7 +95271,7 @@ function buildTurnRecord(turn, endedAt) {
|
|
|
95170
95271
|
return {
|
|
95171
95272
|
ts: Math.floor(endedAt / 1000),
|
|
95172
95273
|
agent: turn.agent,
|
|
95173
|
-
duration_ms: turn.startedAt
|
|
95274
|
+
duration_ms: computeTurnDurationMs2(turn.startedAt, endedAt),
|
|
95174
95275
|
tools: turn.toolCallCount ?? 0,
|
|
95175
95276
|
status: computeTurnStatus(turn),
|
|
95176
95277
|
turn_id: turn.turnId,
|
|
@@ -96933,7 +97034,7 @@ function buildSilencePokeOptions(deps) {
|
|
|
96933
97034
|
const tearsDownLiveTurn = turnMatchesFallback && wedgedTurn != null && turnLiveForItsTopic(wedgedTurn);
|
|
96934
97035
|
const turnStartedAt = activeTurnStartedAt.get(fbKey);
|
|
96935
97036
|
if (turnStartedAt != null) {
|
|
96936
|
-
const turnDurationMs = Date.now()
|
|
97037
|
+
const turnDurationMs = computeTurnDurationMs(turnStartedAt, Date.now());
|
|
96937
97038
|
const outboundMetrics = getOutboundMetrics2(fbKey);
|
|
96938
97039
|
emitRuntimeMetric2({
|
|
96939
97040
|
kind: "turn_ended",
|
|
@@ -103016,6 +103117,8 @@ async function sweepOutbox(deps) {
|
|
|
103016
103117
|
const escalateInternalSuppression = deps.escalateInternalSuppression ?? log;
|
|
103017
103118
|
const provenanceFramingEnabled = deps.provenanceFramingEnabled?.() ?? process.env.SWITCHROOM_TG_OUTBOX_PROVENANCE_FRAMING !== "0";
|
|
103018
103119
|
const logProvenanceFraming = deps.logProvenanceFraming ?? log;
|
|
103120
|
+
const selfImprovementFramingEnabled = deps.selfImprovementFramingEnabled?.() ?? process.env.SWITCHROOM_TG_OUTBOX_SELF_IMPROVE_FRAMING !== "0";
|
|
103121
|
+
const logSelfImprovementFraming = deps.logSelfImprovementFraming ?? log;
|
|
103019
103122
|
for (const record2 of records) {
|
|
103020
103123
|
summary.scanned++;
|
|
103021
103124
|
if (shouldSuppressForAudience(record2, { gateEnabled: audienceGateEnabled })) {
|
|
@@ -103061,7 +103164,8 @@ async function sweepOutbox(deps) {
|
|
|
103061
103164
|
routePrefix,
|
|
103062
103165
|
quietMs: deps.quietMs ?? OUTBOX_QUIET_MS,
|
|
103063
103166
|
shownLedgerHit: isShownBlock(record2.turnNonce, record2.text, deps.stateDir),
|
|
103064
|
-
provenanceFraming: provenanceFramingEnabled
|
|
103167
|
+
provenanceFraming: provenanceFramingEnabled,
|
|
103168
|
+
selfImprovementFraming: selfImprovementFramingEnabled
|
|
103065
103169
|
});
|
|
103066
103170
|
if (decision.action !== "send" && decision.action !== "send-delayed") {
|
|
103067
103171
|
summary.skipped++;
|
|
@@ -103105,7 +103209,8 @@ async function sweepOutbox(deps) {
|
|
|
103105
103209
|
ts: now,
|
|
103106
103210
|
deliverySource: "sweep",
|
|
103107
103211
|
replyAlreadyDeliveredThisTurn: record2.replyAlreadyDeliveredThisTurn === true,
|
|
103108
|
-
...decision.framedProvenance != null ? { framedProvenance: decision.framedProvenance } : {}
|
|
103212
|
+
...decision.framedProvenance != null ? { framedProvenance: decision.framedProvenance } : {},
|
|
103213
|
+
...decision.framedSelfImprovement != null ? { framedSelfImprovement: decision.framedSelfImprovement } : {}
|
|
103109
103214
|
}, deps.stateDir);
|
|
103110
103215
|
if (decision.framedProvenance != null) {
|
|
103111
103216
|
summary.provenanceFramed = (summary.provenanceFramed ?? 0) + 1;
|
|
@@ -103117,6 +103222,16 @@ async function sweepOutbox(deps) {
|
|
|
103117
103222
|
source: record2.source
|
|
103118
103223
|
}));
|
|
103119
103224
|
}
|
|
103225
|
+
if (decision.framedSelfImprovement != null) {
|
|
103226
|
+
summary.selfImprovementFramed = (summary.selfImprovementFramed ?? 0) + 1;
|
|
103227
|
+
logSelfImprovementFraming(formatSelfImprovementFraming({
|
|
103228
|
+
turnNonce: record2.turnNonce,
|
|
103229
|
+
turnId: turnIdFromNonce(record2.turnNonce),
|
|
103230
|
+
chatId: resolvedChat.chatId,
|
|
103231
|
+
textSha256: record2.textSha256,
|
|
103232
|
+
source: record2.source
|
|
103233
|
+
}));
|
|
103234
|
+
}
|
|
103120
103235
|
if (deps.recordOutbound != null && sendResult.chunks.length > 0) {
|
|
103121
103236
|
try {
|
|
103122
103237
|
deps.recordOutbound(resolvedChat.chatId, resolvedChat.threadId, sendResult.chunks.map((c) => c.messageId), sendResult.chunks.map((c) => c.text));
|
|
@@ -103323,10 +103438,10 @@ function startOutboxSweep(deps) {
|
|
|
103323
103438
|
}
|
|
103324
103439
|
|
|
103325
103440
|
// ../src/build-info.ts
|
|
103326
|
-
var VERSION2 = "0.20.
|
|
103327
|
-
var COMMIT_SHA = "
|
|
103328
|
-
var COMMIT_DATE = "2026-08-
|
|
103329
|
-
var LATEST_PR =
|
|
103441
|
+
var VERSION2 = "0.20.13";
|
|
103442
|
+
var COMMIT_SHA = "a73b0fd2";
|
|
103443
|
+
var COMMIT_DATE = "2026-08-07T03:01:37Z";
|
|
103444
|
+
var LATEST_PR = 4502;
|
|
103330
103445
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
103331
103446
|
|
|
103332
103447
|
// gateway/boot-version.ts
|
|
@@ -108773,10 +108888,6 @@ function clearQueuedCard(key, messageId) {
|
|
|
108773
108888
|
}
|
|
108774
108889
|
var bannerPinPersistEnabled = !STATIC;
|
|
108775
108890
|
var toolPinPersistEnabled = !STATIC;
|
|
108776
|
-
var TOOL_PIN_TTL_MS = (() => {
|
|
108777
|
-
const v = Number(process.env.SWITCHROOM_TOOL_PIN_TTL_MS);
|
|
108778
|
-
return Number.isFinite(v) && v > 0 ? v : 604800000;
|
|
108779
|
-
})();
|
|
108780
108891
|
function statusPinApi() {
|
|
108781
108892
|
return createStatusPinApi(() => lockedBot, robustApiCall);
|
|
108782
108893
|
}
|
|
@@ -110377,7 +110488,6 @@ var ALLOWED_TOOLS = new Set([
|
|
|
110377
110488
|
"download_attachment",
|
|
110378
110489
|
"edit_message",
|
|
110379
110490
|
"send_typing",
|
|
110380
|
-
"pin_message",
|
|
110381
110491
|
"delete_message",
|
|
110382
110492
|
"forward_message",
|
|
110383
110493
|
"get_recent_messages",
|
|
@@ -110411,8 +110521,6 @@ async function executeToolCall(tool, args, callerAgentName = null) {
|
|
|
110411
110521
|
return executeEditMessage(args);
|
|
110412
110522
|
case "send_typing":
|
|
110413
110523
|
return executeSendTyping(args);
|
|
110414
|
-
case "pin_message":
|
|
110415
|
-
return executePinMessage(args);
|
|
110416
110524
|
case "delete_message":
|
|
110417
110525
|
return executeDeleteMessage(args);
|
|
110418
110526
|
case "forward_message":
|
|
@@ -111218,27 +111326,6 @@ async function executeSendTyping(args) {
|
|
|
111218
111326
|
}
|
|
111219
111327
|
return { content: [{ type: "text", text: `${action} indicator sent (auto-refreshes every 4s, stops after 30s or next reply)` }] };
|
|
111220
111328
|
}
|
|
111221
|
-
async function executePinMessage(args) {
|
|
111222
|
-
if (!args.chat_id)
|
|
111223
|
-
throw new Error("pin_message: chat_id is required");
|
|
111224
|
-
if (!args.message_id)
|
|
111225
|
-
throw new Error("pin_message: message_id is required");
|
|
111226
|
-
const pinChatId = String(args.chat_id ?? "");
|
|
111227
|
-
assertAllowedChat(pinChatId);
|
|
111228
|
-
const pinMsgId = Number(args.message_id);
|
|
111229
|
-
await robustApiCall(() => lockedBot.api.pinChatMessage(pinChatId, pinMsgId), { chat_id: pinChatId, verb: "pin_message" });
|
|
111230
|
-
statusPinRightsCache.clear(pinChatId);
|
|
111231
|
-
if (toolPinPersistEnabled) {
|
|
111232
|
-
const toolPinKey = `tool:${pinChatId}:${pinMsgId}`;
|
|
111233
|
-
mutateStatusPinRow(STATUS_PIN_STORE_PATH, statusPinStoreFs, toolPinKey, {
|
|
111234
|
-
pinKey: toolPinKey,
|
|
111235
|
-
chatId: pinChatId,
|
|
111236
|
-
messageId: pinMsgId,
|
|
111237
|
-
expiresAt: Date.now() + TOOL_PIN_TTL_MS
|
|
111238
|
-
});
|
|
111239
|
-
}
|
|
111240
|
-
return { content: [{ type: "text", text: `pinned message ${args.message_id}` }] };
|
|
111241
|
-
}
|
|
111242
111329
|
async function executeDeleteMessage(args) {
|
|
111243
111330
|
if (!args.chat_id)
|
|
111244
111331
|
throw new Error("delete_message: chat_id is required");
|
|
@@ -117137,6 +117224,10 @@ async function startGateway() {
|
|
|
117137
117224
|
const raw = Number(process.env.SWITCHROOM_TG_WORKER_FEED_MAX_ROWS);
|
|
117138
117225
|
return Number.isInteger(raw) && raw > 0 ? raw : undefined;
|
|
117139
117226
|
})();
|
|
117227
|
+
const workerFeedFirstPaintMs = (() => {
|
|
117228
|
+
const raw = Number(process.env.SWITCHROOM_TG_WORKER_FEED_FIRST_PAINT_MS);
|
|
117229
|
+
return Number.isInteger(raw) && raw > 0 ? raw : undefined;
|
|
117230
|
+
})();
|
|
117140
117231
|
const foregroundNestingEnabled = process.env.SWITCHROOM_FOREGROUND_SUBAGENT_NESTING !== "0";
|
|
117141
117232
|
const orphanStatusEnabled = isOrphanSubagentStatusEnabled(process.env.SWITCHROOM_ORPHAN_SUBAGENT_STATUS);
|
|
117142
117233
|
if (workerActivityFeed != null) {
|
|
@@ -117159,6 +117250,7 @@ async function startGateway() {
|
|
|
117159
117250
|
},
|
|
117160
117251
|
floodWaitRemainingMs: probeFloodWaitRemainingMs,
|
|
117161
117252
|
maxRows: workerFeedMaxRows,
|
|
117253
|
+
firstPaintMinMs: workerFeedFirstPaintMs,
|
|
117162
117254
|
staleWorkerTtlMs: resolveInflightTerminalCapMs() + WORKER_FEED_STALE_TTL_MARGIN_MS,
|
|
117163
117255
|
absoluteRowLifetimeCapMs: resolveInflightTerminalCapMs() * WORKER_FEED_ABSOLUTE_ROW_LIFETIME_CAP_MULTIPLE,
|
|
117164
117256
|
groupMessageLifetimeCapMs: WORKER_FEED_GROUP_MESSAGE_LIFETIME_CAP_MS,
|