switchroom 0.19.27 → 0.19.28
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 +129 -8
- package/dist/cli/autoaccept-poll.js +225 -17
- package/dist/cli/notion-write-pretool.mjs +5 -2
- package/dist/cli/switchroom.js +796 -35
- package/dist/host-control/main.js +130 -9
- package/dist/vault/approvals/kernel-server.js +129 -8
- package/dist/vault/broker/server.js +129 -8
- package/package.json +3 -2
- package/profiles/_base/start.sh.hbs +70 -15
- package/telegram-plugin/dist/bridge/bridge.js +1 -0
- package/telegram-plugin/dist/gateway/gateway.js +568 -49
- package/telegram-plugin/dist/server.js +1 -0
- package/telegram-plugin/edit-flood-fuse.ts +230 -27
- package/telegram-plugin/gateway/callback-query-handlers.ts +6 -0
- package/telegram-plugin/gateway/gateway.ts +9 -2
- package/telegram-plugin/gateway/mcp-failure-hook.ts +74 -0
- package/telegram-plugin/inline-keyboard-callbacks.ts +202 -21
- package/telegram-plugin/mcp-credential-failure.ts +459 -0
- package/telegram-plugin/operator-events.ts +38 -0
- package/telegram-plugin/tests/edit-flood-fuse-ban-awareness.test.ts +58 -1
- package/telegram-plugin/tests/edit-flood-fuse-reply-reserve.test.ts +340 -0
- package/telegram-plugin/tests/finalize-callback-flood-policy.test.ts +298 -0
- package/telegram-plugin/tests/finalize-callback.test.ts +41 -8
- package/telegram-plugin/tests/mcp-credential-failure.test.ts +310 -0
- package/vendor/hindsight-memory/scripts/drain_pending.py +433 -11
- package/vendor/hindsight-memory/scripts/lib/pending.py +193 -28
- package/vendor/hindsight-memory/scripts/tests/test_drain_circuit_breaker.py +401 -0
- package/vendor/hindsight-memory/scripts/tests/test_drain_serialisation.py +286 -0
- package/vendor/hindsight-memory/scripts/tests/test_pending_drops.py +817 -8
- package/vendor/hindsight-memory/settings.json +1 -1
- package/vendor/hindsight-memory/tests/test_hooks.py +11 -2
|
@@ -11347,7 +11347,10 @@ var LiteLLMConfigSchema = exports_external.object({
|
|
|
11347
11347
|
admin_key: exports_external.string().optional().describe("LiteLLM master/admin key used at apply time to provision the team + " + "virtual key. Supports a vault reference (e.g. " + "'vault:litellm/master-key') — resolution happens at apply time via " + "the vault-broker. Never injected into the agent container."),
|
|
11348
11348
|
team: exports_external.string().optional().describe("LiteLLM team alias the per-agent key is created under. Defaults to " + "'switchroom' (applied in code, not as a schema default)."),
|
|
11349
11349
|
small_fast_model: exports_external.string().optional().describe("Model id exported as ANTHROPIC_SMALL_FAST_MODEL for the claude CLI's " + "background/fast lane, e.g. 'claude-haiku-4-5-20251001'."),
|
|
11350
|
-
tags: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Extra key/value metadata tags attached to the provisioned LiteLLM " + "virtual key. Merged per-key across cascade layers (agent wins).")
|
|
11350
|
+
tags: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Extra key/value metadata tags attached to the provisioned LiteLLM " + "virtual key. Merged per-key across cascade layers (agent wins)."),
|
|
11351
|
+
max_budget: exports_external.number().positive().optional().describe("HARD spend cap in USD for this agent's virtual key over one " + "`budget_duration` window. LiteLLM refuses the request once the key's " + "tracked spend exceeds it, so a runaway loop costs at most this much " + "before it is stopped. Defaults to " + "DEFAULT_KEY_MAX_BUDGET_USD (see src/litellm/budget.ts) — deliberately " + "conservative; raise it per-agent rather than removing it. Set 0 or " + "omit `budget_duration` at your own risk: an uncapped key is only as " + "bounded as the upstream account balance."),
|
|
11352
|
+
soft_budget: exports_external.number().positive().optional().describe("ADVISORY spend threshold in USD. LiteLLM keeps serving past it and " + "raises a budget alert instead. Must be < max_budget. NOTE: LiteLLM " + "accepts soft_budget only on POST /key/generate (GenerateKeyRequest); " + "UpdateKeyRequest does NOT carry it, so changing this value only takes " + "effect on a key that is (re)generated, not on an existing one."),
|
|
11353
|
+
budget_duration: exports_external.string().regex(/^\d+(s|m|h|d|mo)$/, "budget_duration must be a LiteLLM duration like '30d', '24h', '1mo'").optional().describe("Rolling window the budget resets on, in LiteLLM duration syntax " + "('30d', '24h', '1mo'). Defaults to DEFAULT_KEY_BUDGET_DURATION. " + "WITHOUT a duration LiteLLM treats max_budget as a LIFETIME cap that " + "never resets — the key silently dies for good once it is hit.")
|
|
11351
11354
|
}).optional().describe("LiteLLM routing config — opt-in per-agent virtual-key auto-provisioning " + "+ routing env. Default OFF. See LiteLLMConfigSchema doc for the full flow.");
|
|
11352
11355
|
var HindsightPerOpLlmSchema = exports_external.object({
|
|
11353
11356
|
model: exports_external.string().min(1).optional().describe("Per-op model (upstream `HINDSIGHT_API_<OP>_LLM_MODEL`). Absent → " + "inherit the global `hindsight.llm.model`."),
|
|
@@ -11366,7 +11369,7 @@ var HindsightConfigSchema = exports_external.object({
|
|
|
11366
11369
|
reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent → uses global."),
|
|
11367
11370
|
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent → global.")
|
|
11368
11371
|
}).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."),
|
|
11369
|
-
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_MAX_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND), the override-only keys " + "switchroom manages but ships NO default for " + "(`HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS`: " + "HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY — a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP — 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), 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 — 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 — a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
|
|
11372
|
+
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_MAX_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND, RECENCY_DECAY_FUNCTION, " + "RECENCY_DECAY_HALFLIFE_DAYS — 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 — a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP — 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 — 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 — the worker poller's TOTAL in-flight " + "task budget, the pool WORKER_CONSOLIDATION_MAX_SLOTS reserves out of; " + "unset means upstream's own default), 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 — 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 — a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
|
|
11370
11373
|
});
|
|
11371
11374
|
var MicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
11372
11375
|
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 — omit it to use " + "switchroom's shipped default Microsoft app (zero-config). " + "Set it only to bring your own Entra app (BYO)."),
|
|
@@ -16989,7 +16989,10 @@ var LiteLLMConfigSchema = exports_external.object({
|
|
|
16989
16989
|
admin_key: exports_external.string().optional().describe("LiteLLM master/admin key used at apply time to provision the team + " + "virtual key. Supports a vault reference (e.g. " + "'vault:litellm/master-key') — resolution happens at apply time via " + "the vault-broker. Never injected into the agent container."),
|
|
16990
16990
|
team: exports_external.string().optional().describe("LiteLLM team alias the per-agent key is created under. Defaults to " + "'switchroom' (applied in code, not as a schema default)."),
|
|
16991
16991
|
small_fast_model: exports_external.string().optional().describe("Model id exported as ANTHROPIC_SMALL_FAST_MODEL for the claude CLI's " + "background/fast lane, e.g. 'claude-haiku-4-5-20251001'."),
|
|
16992
|
-
tags: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Extra key/value metadata tags attached to the provisioned LiteLLM " + "virtual key. Merged per-key across cascade layers (agent wins).")
|
|
16992
|
+
tags: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Extra key/value metadata tags attached to the provisioned LiteLLM " + "virtual key. Merged per-key across cascade layers (agent wins)."),
|
|
16993
|
+
max_budget: exports_external.number().positive().optional().describe("HARD spend cap in USD for this agent's virtual key over one " + "`budget_duration` window. LiteLLM refuses the request once the key's " + "tracked spend exceeds it, so a runaway loop costs at most this much " + "before it is stopped. Defaults to " + "DEFAULT_KEY_MAX_BUDGET_USD (see src/litellm/budget.ts) — deliberately " + "conservative; raise it per-agent rather than removing it. Set 0 or " + "omit `budget_duration` at your own risk: an uncapped key is only as " + "bounded as the upstream account balance."),
|
|
16994
|
+
soft_budget: exports_external.number().positive().optional().describe("ADVISORY spend threshold in USD. LiteLLM keeps serving past it and " + "raises a budget alert instead. Must be < max_budget. NOTE: LiteLLM " + "accepts soft_budget only on POST /key/generate (GenerateKeyRequest); " + "UpdateKeyRequest does NOT carry it, so changing this value only takes " + "effect on a key that is (re)generated, not on an existing one."),
|
|
16995
|
+
budget_duration: exports_external.string().regex(/^\d+(s|m|h|d|mo)$/, "budget_duration must be a LiteLLM duration like '30d', '24h', '1mo'").optional().describe("Rolling window the budget resets on, in LiteLLM duration syntax " + "('30d', '24h', '1mo'). Defaults to DEFAULT_KEY_BUDGET_DURATION. " + "WITHOUT a duration LiteLLM treats max_budget as a LIFETIME cap that " + "never resets — the key silently dies for good once it is hit.")
|
|
16993
16996
|
}).optional().describe("LiteLLM routing config — opt-in per-agent virtual-key auto-provisioning " + "+ routing env. Default OFF. See LiteLLMConfigSchema doc for the full flow.");
|
|
16994
16997
|
var HindsightPerOpLlmSchema = exports_external.object({
|
|
16995
16998
|
model: exports_external.string().min(1).optional().describe("Per-op model (upstream `HINDSIGHT_API_<OP>_LLM_MODEL`). Absent → " + "inherit the global `hindsight.llm.model`."),
|
|
@@ -17008,7 +17011,7 @@ var HindsightConfigSchema = exports_external.object({
|
|
|
17008
17011
|
reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent → uses global."),
|
|
17009
17012
|
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent → global.")
|
|
17010
17013
|
}).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."),
|
|
17011
|
-
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_MAX_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND), the override-only keys " + "switchroom manages but ships NO default for " + "(`HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS`: " + "HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY — a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP — 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), 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 — 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 — a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
|
|
17014
|
+
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_MAX_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND, RECENCY_DECAY_FUNCTION, " + "RECENCY_DECAY_HALFLIFE_DAYS — 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 — a per-deployment " + "`bank-pattern:priority,...` map; unset means upstream's flat " + "created_at FIFO across banks; and " + "HINDSIGHT_CE_DECISIVE_RELATIVE_GAP — 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 — 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 — the worker poller's TOTAL in-flight " + "task budget, the pool WORKER_CONSOLIDATION_MAX_SLOTS reserves out of; " + "unset means upstream's own default), 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 — 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 — a blanket " + "passthrough would collide with the vars startHindsight() derives " + "itself (HINDSIGHT_API_PORT, the retain token/deadline budget).")
|
|
17012
17015
|
});
|
|
17013
17016
|
var MicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
17014
17017
|
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 — omit it to use " + "switchroom's shipped default Microsoft app (zero-config). " + "Set it only to bring your own Entra app (BYO)."),
|
|
@@ -18500,7 +18503,13 @@ var HINDSIGHT_DEFAULT_LINK_EXPANSION_TIMEOUT_S = 2;
|
|
|
18500
18503
|
var HINDSIGHT_DEFAULT_LLM_REASONING_EFFORT = "low";
|
|
18501
18504
|
var HINDSIGHT_DEFAULT_LLM_MAX_CONCURRENT = 4;
|
|
18502
18505
|
var HINDSIGHT_DEFAULT_RETAIN_LLM_MAX_CONCURRENT = 1;
|
|
18503
|
-
|
|
18506
|
+
function hindsightConsolidationLlmMaxConcurrentDefault(globalMaxConcurrent = HINDSIGHT_DEFAULT_LLM_MAX_CONCURRENT, retainMaxConcurrent = HINDSIGHT_DEFAULT_RETAIN_LLM_MAX_CONCURRENT) {
|
|
18507
|
+
const globalCap = Number.isFinite(globalMaxConcurrent) && globalMaxConcurrent >= 1 ? Math.floor(globalMaxConcurrent) : HINDSIGHT_DEFAULT_LLM_MAX_CONCURRENT;
|
|
18508
|
+
const retainCap = Number.isFinite(retainMaxConcurrent) && retainMaxConcurrent >= 0 ? Math.floor(retainMaxConcurrent) : HINDSIGHT_DEFAULT_RETAIN_LLM_MAX_CONCURRENT;
|
|
18509
|
+
const headroomBound = Math.max(1, globalCap - 1);
|
|
18510
|
+
return Math.min(headroomBound, Math.max(1, globalCap - retainCap - 1));
|
|
18511
|
+
}
|
|
18512
|
+
var HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_MAX_CONCURRENT = hindsightConsolidationLlmMaxConcurrentDefault();
|
|
18504
18513
|
var HINDSIGHT_DEFAULT_RERANKER_LOCAL_FP16 = "true";
|
|
18505
18514
|
var HINDSIGHT_DEFAULT_RERANKER_LOCAL_BATCH_SIZE = 128;
|
|
18506
18515
|
var HINDSIGHT_DEFAULT_LLM_STRICT_SCHEMA = "true";
|
|
@@ -18515,6 +18524,8 @@ var HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S = 600;
|
|
|
18515
18524
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND = 500;
|
|
18516
18525
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_SLOT_LIMIT = 6;
|
|
18517
18526
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS = 1;
|
|
18527
|
+
var HINDSIGHT_DEFAULT_RECENCY_DECAY_FUNCTION = "exponential";
|
|
18528
|
+
var HINDSIGHT_DEFAULT_RECENCY_DECAY_HALFLIFE_DAYS = 30;
|
|
18518
18529
|
var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
18519
18530
|
[
|
|
18520
18531
|
"HINDSIGHT_API_RECALL_MAX_CANDIDATES_PER_SOURCE",
|
|
@@ -18568,6 +18579,14 @@ var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
|
18568
18579
|
[
|
|
18569
18580
|
"HINDSIGHT_API_CONSOLIDATION_MAX_MEMORIES_PER_ROUND",
|
|
18570
18581
|
String(HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND)
|
|
18582
|
+
],
|
|
18583
|
+
[
|
|
18584
|
+
"HINDSIGHT_API_RECENCY_DECAY_FUNCTION",
|
|
18585
|
+
HINDSIGHT_DEFAULT_RECENCY_DECAY_FUNCTION
|
|
18586
|
+
],
|
|
18587
|
+
[
|
|
18588
|
+
"HINDSIGHT_API_RECENCY_DECAY_HALFLIFE_DAYS",
|
|
18589
|
+
String(HINDSIGHT_DEFAULT_RECENCY_DECAY_HALFLIFE_DAYS)
|
|
18571
18590
|
]
|
|
18572
18591
|
];
|
|
18573
18592
|
var HINDSIGHT_PERF_DEFAULTS_GPU = [
|
|
@@ -18592,7 +18611,9 @@ var HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM = [
|
|
|
18592
18611
|
];
|
|
18593
18612
|
var HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS = new Set([
|
|
18594
18613
|
"HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY",
|
|
18595
|
-
"HINDSIGHT_CE_DECISIVE_RELATIVE_GAP"
|
|
18614
|
+
"HINDSIGHT_CE_DECISIVE_RELATIVE_GAP",
|
|
18615
|
+
"HINDSIGHT_API_RECENCY_DECAY_LINEAR_WINDOW_DAYS",
|
|
18616
|
+
"HINDSIGHT_API_WORKER_MAX_SLOTS"
|
|
18596
18617
|
]);
|
|
18597
18618
|
var HINDSIGHT_PERF_ENV_KEYS = new Set([
|
|
18598
18619
|
...[
|
|
@@ -18658,6 +18679,16 @@ var DEFAULT_RETAIN_MISSION = `Extract durable facts that will still be true and
|
|
|
18658
18679
|
` + `- Hindsight's own errors, retries, backlogs, or internal state — the memory
|
|
18659
18680
|
` + ` system's self-reports are not memories.
|
|
18660
18681
|
` + `- Restatements of the user's current request or the task in progress.
|
|
18682
|
+
` + `- Volatile state written as a timeless assertion. A version, count, size,
|
|
18683
|
+
` + ` backlog, status, or any "X is running Y" / "X is at Y" / "X is currently Y"
|
|
18684
|
+
` + ` claim is true only at the instant it was said. Concretely, never produce a
|
|
18685
|
+
` + ` fact whose text resembles any of these: "Switchroom fleet is running image
|
|
18686
|
+
` + ` version v0.18.19", "The switchroom repo is at /path/to/fleet, version
|
|
18687
|
+
` + ` v0.19.5", "Bank overlord has 43155 pending consolidations", "The build is
|
|
18688
|
+
` + ` currently green". If the claim is worth keeping, put the date INSIDE the
|
|
18689
|
+
` + ` fact text ("As of 2026-07-19 the fleet was running v0.18.19"); if you
|
|
18690
|
+
` + ` cannot date it, drop it. An undated one is recalled forever as though it
|
|
18691
|
+
` + ` were still true, which is worse than not remembering it at all.
|
|
18661
18692
|
` + `- Transient state (unread counts, build status, what is running right now) unless
|
|
18662
18693
|
` + ` the fact is explicitly dated, in which case record it as a dated observation.
|
|
18663
18694
|
` + `- Greetings, acknowledgements, and routine operational chatter.
|
|
@@ -18679,18 +18710,108 @@ var SUPERSEDED_RETAIN_MISSIONS = [
|
|
|
18679
18710
|
` + "- Transient state (unread counts, build status, what is running right now) " + "unless the fact is explicitly dated, in which case record it as a dated " + `observation.
|
|
18680
18711
|
` + `- Greetings, acknowledgements, and routine operational chatter.
|
|
18681
18712
|
|
|
18682
|
-
` + "If a candidate fact matches an exclusion, drop it rather than rewording " + "it. If nothing durable remains, return an empty facts list."
|
|
18713
|
+
` + "If a candidate fact matches an exclusion, drop it rather than rewording " + "it. If nothing durable remains, return an empty facts list.",
|
|
18714
|
+
`Extract durable facts that will still be true and useful weeks from now: user preferences and standing rules, ongoing projects and recurring commitments, technical and architectural decisions with their rationale, and people/tool relationships. A preference revealed by a request is durable — record the preference (what the user likes, wants, or always does), not the request itself.
|
|
18715
|
+
` + `
|
|
18716
|
+
` + `A TOOL RESULT IS NOT A FACT. Before extracting, ask: is the subject of this
|
|
18717
|
+
` + `candidate a file path, a command/process/agent/session id, a temp directory, or
|
|
18718
|
+
` + `the location where some output was written? If yes, drop it — it is transcript
|
|
18719
|
+
` + `exhaust, not memory.
|
|
18720
|
+
` + `
|
|
18721
|
+
` + `NEVER extract:
|
|
18722
|
+
` + `- Tool results verbatim or paraphrased. Concretely, never produce a fact whose
|
|
18723
|
+
` + ` text resembles any of these: "File created successfully at /path/to/file",
|
|
18724
|
+
` + ` "A background command with ID bctz4yskm is running, and its output will be
|
|
18725
|
+
` + ` written to /tmp/...", "Async agent a745598ba84e71df1 was launched successfully
|
|
18726
|
+
` + ` and is running in the background", "User executed a Bash command to sleep for
|
|
18727
|
+
` + ` 200 seconds", "The assistant used grep to locate 'truncateSync' in src/foo.ts".
|
|
18728
|
+
` + `- Anything mentioning a path under /tmp, a scratchpad directory, or a .tmp file.
|
|
18729
|
+
` + `- Agent tool-use traces or narration of what the assistant did (e.g. "the
|
|
18730
|
+
` + ` assistant used X to query Y", "ran a search", "sent the message").
|
|
18731
|
+
` + `- In-flight workflow/process narration (a sub-task started, paused, or is still
|
|
18732
|
+
` + ` running) — retain the outcome only once the task completes or a decision is made.
|
|
18733
|
+
` + `- Operation, request, batch, agent, command or session IDs, UUIDs, hashes, or error codes.
|
|
18734
|
+
` + `- Slash commands the user typed and their effects (e.g. "User issued /clear to
|
|
18735
|
+
` + ` reset assistant state").
|
|
18736
|
+
` + `- Hindsight's own errors, retries, backlogs, or internal state — the memory
|
|
18737
|
+
` + ` system's self-reports are not memories.
|
|
18738
|
+
` + `- Restatements of the user's current request or the task in progress.
|
|
18739
|
+
` + `- Transient state (unread counts, build status, what is running right now) unless
|
|
18740
|
+
` + ` the fact is explicitly dated, in which case record it as a dated observation.
|
|
18741
|
+
` + `- Greetings, acknowledgements, and routine operational chatter.
|
|
18742
|
+
` + `
|
|
18743
|
+
` + `If a candidate fact matches an exclusion, drop it rather than rewording it. If
|
|
18744
|
+
` + "nothing durable remains, return an empty facts list."
|
|
18745
|
+
];
|
|
18746
|
+
var DEFAULT_OBSERVATIONS_MISSION = `Synthesise durable, standing knowledge about the people, projects, and systems this agent works with: preferences and standing rules, roles and relationships, skills and recurring patterns, technical and operational decisions with their rationale, and the state of long-running work once it lands.
|
|
18747
|
+
` + `
|
|
18748
|
+
` + `The test is durability, not notability: an observation must still be worth reading weeks from now. A single dated event belongs in an observation only when it establishes or changes a standing fact.
|
|
18749
|
+
` + `
|
|
18750
|
+
` + `Do NOT synthesise observations from:
|
|
18751
|
+
` + `- Transcript exhaust — tool calls and their results, file paths, temp or scratchpad directories, where some output was written, or narration of what an assistant did.
|
|
18752
|
+
` + `- Identifiers with no standing meaning: session, agent, request, batch or command IDs, UUIDs, hashes, error codes.
|
|
18753
|
+
` + `- In-flight process narration — a task started, paused, or still running. Record the outcome once it lands, not the running state.
|
|
18754
|
+
` + `- The memory system's own errors, retries, backlogs, or internal state.
|
|
18755
|
+
` + `- Transient state (what is running right now, unread counts, build status) unless the fact is explicitly dated, in which case record it as dated.
|
|
18756
|
+
` + `
|
|
18757
|
+
` + "If the new facts contain nothing durable, record nothing rather than synthesising a weak observation.";
|
|
18758
|
+
var SUPERSEDED_OBSERVATIONS_MISSIONS = [
|
|
18759
|
+
"Synthesise the person's wellbeing patterns, motivations, and emotional " + "context — how habits, setbacks, and encouragement connect over time."
|
|
18683
18760
|
];
|
|
18684
18761
|
var PROFILE_MEMORY_DEFAULTS = {
|
|
18685
18762
|
"health-coach": {
|
|
18686
18763
|
disposition: { skepticism: 2, literalism: 2, empathy: 5 },
|
|
18687
|
-
observations_mission:
|
|
18764
|
+
observations_mission: `You consolidate the memory of a health and fitness coach working with one person. This bank records how that person actually lives and trains.
|
|
18765
|
+
` + `
|
|
18766
|
+
` + `Synthesise into durable observations:
|
|
18767
|
+
` + `- Goals, targets, and the plan currently in force, with the reasoning behind each.
|
|
18768
|
+
` + `- Training, nutrition, sleep, and alcohol patterns as they hold over weeks — what the person reliably does, not what they did once.
|
|
18769
|
+
` + `- Constraints that shape the plan: injuries, medical guidance, schedule, equipment, foods and sessions they refuse.
|
|
18770
|
+
` + `- Motivations, and what actually helps or backfires when they slip.
|
|
18771
|
+
` + `- Trends in the numbers: direction and range over time, not any single reading.
|
|
18772
|
+
` + `- Corrections to earlier beliefs, recorded explicitly as corrections.
|
|
18773
|
+
` + `
|
|
18774
|
+
` + `A single day's log, weight reading, or session is evidence, not an observation. Record one only when it establishes or changes a standing pattern, target, or constraint — then fold it into the observation for that pattern and say what changed and roughly when.
|
|
18775
|
+
` + `
|
|
18776
|
+
` + `Granularity: one observation per habit, target, constraint, or trend. Aggregate repeated daily evidence into the observation for that pattern rather than creating one per day, and keep training, nutrition, and sleep as separate observations.
|
|
18777
|
+
` + `
|
|
18778
|
+
` + "Word observations as the person's own pattern and framing, never as a verdict on them."
|
|
18688
18779
|
},
|
|
18689
18780
|
"executive-assistant": {
|
|
18690
|
-
disposition: { skepticism: 4, literalism: 4, empathy: 3 }
|
|
18781
|
+
disposition: { skepticism: 4, literalism: 4, empathy: 3 },
|
|
18782
|
+
observations_mission: `You consolidate the memory of an executive assistant working for one person. This bank records that person's commitments, people, and standing arrangements.
|
|
18783
|
+
` + `
|
|
18784
|
+
` + `Synthesise into durable observations:
|
|
18785
|
+
` + `- Standing rules and preferences: how they want things scheduled, written, and filed, and when they want to be interrupted.
|
|
18786
|
+
` + `- People and organisations, and the relationship: who they are, what they are involved in, how to reach them.
|
|
18787
|
+
` + `- Recurring commitments and routines, and the constraints around them.
|
|
18788
|
+
` + `- Obligations and their state: what was promised to whom, the deadline, and what is still outstanding.
|
|
18789
|
+
` + `- Decisions made and decisions deferred, with the reasoning and the trade accepted.
|
|
18790
|
+
` + `- Corrections to earlier beliefs, recorded explicitly as corrections.
|
|
18791
|
+
` + `
|
|
18792
|
+
` + `Live commitment state IS durable knowledge here, not ephemeral chatter. An outstanding obligation, a travel window, an unanswered request, a "currently X" arrangement — these are precisely what this agent must recall later. Keep them, and embed the date inside the observation text so a later reader can judge staleness. Do not drop them as ephemeral.
|
|
18793
|
+
` + `
|
|
18794
|
+
` + `Granularity: one observation per person, arrangement, or obligation. Aggregate repeated mentions into that observation rather than creating siblings; never merge two different people or two different commitments into one.
|
|
18795
|
+
` + `
|
|
18796
|
+
` + "Do not synthesise from transcript exhaust: tool calls and their results, message or event identifiers, or narration of what the assistant did."
|
|
18691
18797
|
},
|
|
18692
18798
|
coding: {
|
|
18693
|
-
disposition: { skepticism: 4, literalism: 5, empathy: 2 }
|
|
18799
|
+
disposition: { skepticism: 4, literalism: 5, empathy: 2 },
|
|
18800
|
+
observations_mission: `You consolidate the memory of a software-engineering agent. This bank records real work on real codebases.
|
|
18801
|
+
` + `
|
|
18802
|
+
` + `Synthesise into durable observations:
|
|
18803
|
+
` + `- Architecture and design decisions, each with its rationale and the trade accepted.
|
|
18804
|
+
` + `- Root causes, with the evidence chain, and negative results — what was ruled out matters as much as what was found.
|
|
18805
|
+
` + `- How the repository works: build, test, and lint commands, conventions, CI gates, and where things live.
|
|
18806
|
+
` + `- Outcomes of code work: issue and PR numbers, what changed, whether it merged, what review found.
|
|
18807
|
+
` + `- The user's standing rules, preferences, and corrections to this agent's behaviour.
|
|
18808
|
+
` + `- Corrections to earlier beliefs, recorded explicitly as corrections.
|
|
18809
|
+
` + `
|
|
18810
|
+
` + `Repository and service state IS durable knowledge here, not ephemeral chatter. Versions, a failing gate, an open PR, a measured number, a "currently X" claim — these are precisely what this agent must recall later. Keep them, and embed the date inside the observation text so a later reader can judge staleness. Do not drop them as ephemeral.
|
|
18811
|
+
` + `
|
|
18812
|
+
` + `Granularity: one observation per distinct decision, cause, convention, or work item. Aggregate repeated evidence about the same one into that observation rather than creating siblings; never merge two separate decisions into a single summary.
|
|
18813
|
+
` + `
|
|
18814
|
+
` + "Do not synthesise from transcript exhaust: tool calls and their results, scratch paths, session or request identifiers, or narration of what the agent did. Prefer specific and falsifiable — naming the file, the number, the commit, or the decision beats summarising the topic."
|
|
18694
18815
|
}
|
|
18695
18816
|
};
|
|
18696
18817
|
|
|
@@ -295,6 +295,182 @@ function queryPendingPermission(agentName, opts = {}) {
|
|
|
295
295
|
});
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
+
// telegram-plugin/flood-circuit-breaker.ts
|
|
299
|
+
import {
|
|
300
|
+
existsSync,
|
|
301
|
+
readFileSync as readFileSync2,
|
|
302
|
+
writeFileSync as writeFileSync2,
|
|
303
|
+
mkdirSync as mkdirSync2,
|
|
304
|
+
chmodSync as chmodSync2,
|
|
305
|
+
unlinkSync as unlinkSync2,
|
|
306
|
+
renameSync
|
|
307
|
+
} from "node:fs";
|
|
308
|
+
import { dirname as dirname2, join as join3 } from "node:path";
|
|
309
|
+
|
|
310
|
+
// telegram-plugin/flood-429-ledger.ts
|
|
311
|
+
import { readFileSync, writeFileSync, mkdirSync, chmodSync, unlinkSync } from "node:fs";
|
|
312
|
+
import { dirname, join as join2 } from "node:path";
|
|
313
|
+
var FLOOD_429_LEDGER_FILE = "429-ledger.json";
|
|
314
|
+
var FLOOD_429_LEDGER_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
|
|
315
|
+
var PENALTY_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
|
|
316
|
+
var TRIVIAL_PRESSURE_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
317
|
+
function flood429LedgerPath(stateDir) {
|
|
318
|
+
return join2(stateDir, FLOOD_429_LEDGER_FILE);
|
|
319
|
+
}
|
|
320
|
+
function parseEpisode(raw) {
|
|
321
|
+
if (typeof raw !== "object" || raw === null)
|
|
322
|
+
return null;
|
|
323
|
+
const r = raw;
|
|
324
|
+
const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
325
|
+
const firstTs = num(r.firstTs);
|
|
326
|
+
const untilTs = num(r.untilTs);
|
|
327
|
+
const peak = num(r.peakRetryAfterSec);
|
|
328
|
+
if (firstTs === null || untilTs === null || peak === null)
|
|
329
|
+
return null;
|
|
330
|
+
return {
|
|
331
|
+
firstTs,
|
|
332
|
+
lastTs: num(r.lastTs) ?? firstTs,
|
|
333
|
+
peakRetryAfterSec: peak,
|
|
334
|
+
untilTs,
|
|
335
|
+
count: num(r.count) ?? 1
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
function readFlood429LedgerResult(path, readFile = (p) => readFileSync(p, "utf-8")) {
|
|
339
|
+
let text;
|
|
340
|
+
try {
|
|
341
|
+
text = readFile(path);
|
|
342
|
+
} catch (err) {
|
|
343
|
+
const code = err?.code;
|
|
344
|
+
if (code === "ENOENT" || code === "ENOTDIR")
|
|
345
|
+
return { status: "absent", episodes: [] };
|
|
346
|
+
return {
|
|
347
|
+
status: "unreadable",
|
|
348
|
+
episodes: [],
|
|
349
|
+
error: `${code ?? "EUNKNOWN"}: ${err?.message ?? String(err)}`
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
let parsed;
|
|
353
|
+
try {
|
|
354
|
+
parsed = JSON.parse(text);
|
|
355
|
+
} catch (err) {
|
|
356
|
+
return { status: "corrupt", episodes: [], error: err?.message ?? String(err) };
|
|
357
|
+
}
|
|
358
|
+
if (!Array.isArray(parsed)) {
|
|
359
|
+
return { status: "corrupt", episodes: [], error: "ledger is not a JSON array" };
|
|
360
|
+
}
|
|
361
|
+
const out = [];
|
|
362
|
+
let dropped = 0;
|
|
363
|
+
for (const raw of parsed) {
|
|
364
|
+
const ep = parseEpisode(raw);
|
|
365
|
+
if (ep)
|
|
366
|
+
out.push(ep);
|
|
367
|
+
else
|
|
368
|
+
dropped += 1;
|
|
369
|
+
}
|
|
370
|
+
if (dropped > 0 && out.length === 0) {
|
|
371
|
+
return { status: "corrupt", episodes: [], error: `${dropped} unparseable entr(ies)` };
|
|
372
|
+
}
|
|
373
|
+
return { status: "ok", episodes: out };
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// telegram-plugin/flood-circuit-breaker.ts
|
|
377
|
+
var blindLoggedAt = new Map;
|
|
378
|
+
var FLOOD_WINDOWS_FILE = "flood-windows.json";
|
|
379
|
+
function floodWindowsPath(stateDir) {
|
|
380
|
+
return join3(stateDir, FLOOD_WINDOWS_FILE);
|
|
381
|
+
}
|
|
382
|
+
var FLOOD_WINDOWS_CORRUPT_SUPPRESS_MS = 5 * 60000;
|
|
383
|
+
function readFloodWindows(path, now, log = (l) => process.stderr.write(l)) {
|
|
384
|
+
if (!existsSync(path))
|
|
385
|
+
return [];
|
|
386
|
+
const failSafe = (why) => {
|
|
387
|
+
log(`telegram gateway: flood-breaker: scoped-windows file ${path} is ${why} \u2014 ` + `failing SAFE: opening a conservative ${FLOOD_WINDOWS_CORRUPT_SUPPRESS_MS}ms global ` + `window rather than booting into a possible open ban with no windows (issue #3106)
|
|
388
|
+
`);
|
|
389
|
+
return [
|
|
390
|
+
{
|
|
391
|
+
scopeKey: "global",
|
|
392
|
+
untilTs: now + FLOOD_WINDOWS_CORRUPT_SUPPRESS_MS,
|
|
393
|
+
retryAfterSrc: `failsafe:${why}`,
|
|
394
|
+
observedAt: now
|
|
395
|
+
}
|
|
396
|
+
];
|
|
397
|
+
};
|
|
398
|
+
let text;
|
|
399
|
+
try {
|
|
400
|
+
text = readFileSync2(path, "utf-8");
|
|
401
|
+
} catch (err) {
|
|
402
|
+
const code = err?.code;
|
|
403
|
+
if (code === "ENOENT" || code === "ENOTDIR")
|
|
404
|
+
return [];
|
|
405
|
+
return failSafe(`unreadable (${code ?? "error"})`);
|
|
406
|
+
}
|
|
407
|
+
let raw;
|
|
408
|
+
try {
|
|
409
|
+
raw = JSON.parse(text);
|
|
410
|
+
} catch {
|
|
411
|
+
return failSafe("corrupt (invalid JSON)");
|
|
412
|
+
}
|
|
413
|
+
if (!Array.isArray(raw))
|
|
414
|
+
return failSafe("corrupt (not an array)");
|
|
415
|
+
const out = [];
|
|
416
|
+
for (const r of raw) {
|
|
417
|
+
const rec = r;
|
|
418
|
+
if (typeof rec.scopeKey !== "string" || typeof rec.untilTs !== "number")
|
|
419
|
+
continue;
|
|
420
|
+
if (rec.untilTs <= now)
|
|
421
|
+
continue;
|
|
422
|
+
out.push({
|
|
423
|
+
scopeKey: rec.scopeKey,
|
|
424
|
+
untilTs: rec.untilTs,
|
|
425
|
+
retryAfterSrc: typeof rec.retryAfterSrc === "string" ? rec.retryAfterSrc : "unknown",
|
|
426
|
+
observedAt: typeof rec.observedAt === "number" ? rec.observedAt : now,
|
|
427
|
+
...typeof rec.alertedAt === "number" ? { alertedAt: rec.alertedAt } : {}
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
return out;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// src/agents/flood-pressure-probe.ts
|
|
434
|
+
var FLOOD_PRESSURE_GRACE_MS = 120000;
|
|
435
|
+
function scopeBlocksCardDelivery(scopeKey) {
|
|
436
|
+
return !scopeKey.startsWith("msg-edit:");
|
|
437
|
+
}
|
|
438
|
+
var NO_PRESSURE = { active: false, reason: "" };
|
|
439
|
+
function resolveTelegramStateDir() {
|
|
440
|
+
return process.env.TELEGRAM_STATE_DIR ?? "/state/agent/telegram";
|
|
441
|
+
}
|
|
442
|
+
function readFloodPressure(now, opts = {}) {
|
|
443
|
+
const stateDir = opts.stateDir ?? resolveTelegramStateDir();
|
|
444
|
+
const graceMs = opts.graceMs ?? FLOOD_PRESSURE_GRACE_MS;
|
|
445
|
+
try {
|
|
446
|
+
const windows = readFloodWindows(floodWindowsPath(stateDir), now - graceMs, () => {});
|
|
447
|
+
for (const w of windows) {
|
|
448
|
+
if (!scopeBlocksCardDelivery(w.scopeKey))
|
|
449
|
+
continue;
|
|
450
|
+
const msLeft = w.untilTs - now;
|
|
451
|
+
return {
|
|
452
|
+
active: true,
|
|
453
|
+
reason: msLeft > 0 ? `open flood window ${w.scopeKey} (${Math.ceil(msLeft / 1000)}s left, src=${w.retryAfterSrc})` : `flood window ${w.scopeKey} closed ${Math.ceil(-msLeft / 1000)}s ago (within ${Math.round(graceMs / 1000)}s grace)`
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
const ledger = readFlood429LedgerResult(flood429LedgerPath(stateDir));
|
|
457
|
+
if (ledger.status === "ok") {
|
|
458
|
+
for (const ep of ledger.episodes) {
|
|
459
|
+
const lastEvidenceTs = Math.max(ep.lastTs, ep.untilTs);
|
|
460
|
+
if (lastEvidenceTs >= now - graceMs) {
|
|
461
|
+
return {
|
|
462
|
+
active: true,
|
|
463
|
+
reason: `429 episode ${ep.count}x peak=${ep.peakRetryAfterSec}s last seen ` + `${Math.max(0, Math.round((now - ep.lastTs) / 1000))}s ago ` + `(within ${Math.round(graceMs / 1000)}s grace)`
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return NO_PRESSURE;
|
|
469
|
+
} catch {
|
|
470
|
+
return NO_PRESSURE;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
298
474
|
// src/agents/wedge-watchdog.ts
|
|
299
475
|
var WEDGE_FOOTER_SIGNATURE = /(?=[\s\S]*[Ee]sc(?:ape)?[^\n]*cancel)(?=[\s\S]*(?:to select|to navigate|\u2191\/\u2193))/;
|
|
300
476
|
var RATE_LIMIT_MENU_SIGNATURE = /(?=[\s\S]*Stop and wait for)(?=[\s\S]*(?:usage credits|Upgrade your plan|\/rate-limit-options))/;
|
|
@@ -438,6 +614,8 @@ var DEFAULT_COOLDOWN_MS = 60000;
|
|
|
438
614
|
var DEFAULT_CONFIRM_MODAL_POLLS = 3;
|
|
439
615
|
var DEFAULT_MANIFEST_STALL_POLLS = 60;
|
|
440
616
|
var DEFAULT_PERMISSION_PROMPT_POLLS = 3;
|
|
617
|
+
var DEFAULT_PERMISSION_CARDLESS_POLLS = 24;
|
|
618
|
+
var DEFAULT_PERMISSION_FLOOD_MAX_POLLS = 120;
|
|
441
619
|
function envInt(name, fallback) {
|
|
442
620
|
const raw = process.env[name];
|
|
443
621
|
if (raw === undefined || raw.trim() === "")
|
|
@@ -467,6 +645,9 @@ async function runWedgeWatchdog(opts) {
|
|
|
467
645
|
const confirmModalPolls = opts.confirmModalPolls ?? envInt("SWITCHROOM_WEDGE_CONFIRM_POLLS", DEFAULT_CONFIRM_MODAL_POLLS);
|
|
468
646
|
const permissionPromptSignature = opts.permissionPromptSignature === null ? null : opts.permissionPromptSignature ?? PERMISSION_PROMPT_SIGNATURE;
|
|
469
647
|
const permissionPromptPolls = opts.permissionPromptPolls ?? envInt("SWITCHROOM_WEDGE_PERMISSION_POLLS", DEFAULT_PERMISSION_PROMPT_POLLS);
|
|
648
|
+
const permissionCardlessPolls = Math.max(permissionPromptPolls, opts.permissionCardlessPolls ?? envInt("SWITCHROOM_WEDGE_PERMISSION_CARDLESS_POLLS", DEFAULT_PERMISSION_CARDLESS_POLLS));
|
|
649
|
+
const floodPressure = opts.floodPressure === null ? null : opts.floodPressure ?? ((n) => readFloodPressure(n));
|
|
650
|
+
const permissionFloodMaxPolls = Math.max(permissionCardlessPolls, opts.permissionFloodMaxPolls ?? envInt("SWITCHROOM_WEDGE_PERMISSION_FLOOD_MAX_POLLS", DEFAULT_PERMISSION_FLOOD_MAX_POLLS));
|
|
470
651
|
const manifestStallSignature = opts.manifestStallSignature === null ? null : opts.manifestStallSignature ?? MANIFESTING_SIGNATURE;
|
|
471
652
|
const manifestStallPolls = opts.manifestStallPolls ?? envInt("SWITCHROOM_WEDGE_MANIFEST_POLLS", DEFAULT_MANIFEST_STALL_POLLS);
|
|
472
653
|
const requestRestart = opts.requestRestart;
|
|
@@ -485,6 +666,8 @@ async function runWedgeWatchdog(opts) {
|
|
|
485
666
|
let confirmModalFires = 0;
|
|
486
667
|
let permissionPromptFires = 0;
|
|
487
668
|
let permissionPromptDeferrals = 0;
|
|
669
|
+
let permissionPromptFloodHolds = 0;
|
|
670
|
+
let permissionPromptCardlessHolds = 0;
|
|
488
671
|
let restartEscalations = 0;
|
|
489
672
|
let polls = 0;
|
|
490
673
|
let confirmModalPresent = 0;
|
|
@@ -493,6 +676,7 @@ async function runWedgeWatchdog(opts) {
|
|
|
493
676
|
let lastManifestKey = null;
|
|
494
677
|
let confirmCooldownUntil = 0;
|
|
495
678
|
let permissionCooldownUntil = 0;
|
|
679
|
+
let lastPermissionHoldLogAt = Number.NEGATIVE_INFINITY;
|
|
496
680
|
while (polls < maxPolls) {
|
|
497
681
|
polls++;
|
|
498
682
|
let text = "";
|
|
@@ -602,17 +786,37 @@ async function runWedgeWatchdog(opts) {
|
|
|
602
786
|
permissionPromptDeferrals++;
|
|
603
787
|
permissionCooldownUntil = now() + cooldownMs;
|
|
604
788
|
} else {
|
|
605
|
-
const
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
789
|
+
const held = now();
|
|
790
|
+
const pressure = floodPressure ? floodPressure(held) : null;
|
|
791
|
+
const logHold = (line) => {
|
|
792
|
+
if (held - lastPermissionHoldLogAt < cooldownMs)
|
|
793
|
+
return;
|
|
794
|
+
lastPermissionHoldLogAt = held;
|
|
795
|
+
console.error(line);
|
|
796
|
+
};
|
|
797
|
+
const floodHold = pressure?.active === true && permissionPromptPresent < permissionFloodMaxPolls;
|
|
798
|
+
const cardless = status != null && status.ok && !status.pending;
|
|
799
|
+
const cardlessHold = !floodHold && cardless && permissionPromptPresent < permissionCardlessPolls;
|
|
800
|
+
if (floodHold) {
|
|
801
|
+
permissionPromptFloodHolds++;
|
|
802
|
+
logHold(`[wedge-watchdog] ${opts.agentName}: permission prompt present ` + `${permissionPromptPresent} polls ` + `(~${Math.round(permissionPromptPresent * pollIntervalMs / 1000)}s) but ` + `Telegram is flood-throttling this agent (${pressure.reason}) \u2014 ` + `an approval card may be deferred, NOT sending Esc ` + `(ceiling ${permissionFloodMaxPolls} polls)`);
|
|
803
|
+
} else if (cardlessHold) {
|
|
804
|
+
permissionPromptCardlessHolds++;
|
|
805
|
+
logHold(`[wedge-watchdog] ${opts.agentName}: gateway reports no pending ` + `permission after ${permissionPromptPresent} polls ` + `(~${Math.round(permissionPromptPresent * pollIntervalMs / 1000)}s) \u2014 ` + `holding Esc until the card-less streak of ${permissionCardlessPolls} polls ` + `(~${Math.round(permissionCardlessPolls * pollIntervalMs / 1000)}s), ` + `in case the card is merely late`);
|
|
806
|
+
} else {
|
|
807
|
+
const reason = queryPendingPermission2 == null ? "card-aware check disabled" : status && status.ok ? `gateway reports no pending permission (card-less prompt) for ${permissionCardlessPolls}+ polls` : `gateway unreachable/no answer within budget${status && !status.ok ? ` (${status.reason})` : ""}`;
|
|
808
|
+
const floodNote = pressure?.active ? ` [flood ceiling ${permissionFloodMaxPolls} polls reached while ${pressure.reason}]` : "";
|
|
809
|
+
console.error(`[wedge-watchdog] ${opts.agentName}: dismissing stuck per-tool ` + `permission prompt (Esc == decline == safe DENY) after ` + `${permissionPromptPresent} polls present ` + `(~${Math.round(permissionPromptPresent * pollIntervalMs / 1000)}s) \u2014 ${reason}${floodNote}`);
|
|
810
|
+
try {
|
|
811
|
+
send(opts.agentName, ["Escape"]);
|
|
812
|
+
} catch (err) {
|
|
813
|
+
console.error(`[wedge-watchdog] ${opts.agentName}: send threw: ${err.message}`);
|
|
814
|
+
}
|
|
815
|
+
fires++;
|
|
816
|
+
permissionPromptFires++;
|
|
817
|
+
permissionCooldownUntil = now() + cooldownMs;
|
|
818
|
+
permissionPromptPresent = 0;
|
|
611
819
|
}
|
|
612
|
-
fires++;
|
|
613
|
-
permissionPromptFires++;
|
|
614
|
-
permissionCooldownUntil = now() + cooldownMs;
|
|
615
|
-
permissionPromptPresent = 0;
|
|
616
820
|
}
|
|
617
821
|
}
|
|
618
822
|
} else if (isConfirmModal) {
|
|
@@ -667,6 +871,8 @@ async function runWedgeWatchdog(opts) {
|
|
|
667
871
|
confirmModalFires,
|
|
668
872
|
permissionPromptFires,
|
|
669
873
|
permissionPromptDeferrals,
|
|
874
|
+
permissionPromptFloodHolds,
|
|
875
|
+
permissionPromptCardlessHolds,
|
|
670
876
|
restartEscalations,
|
|
671
877
|
polls,
|
|
672
878
|
reason: "max-polls"
|
|
@@ -675,10 +881,10 @@ async function runWedgeWatchdog(opts) {
|
|
|
675
881
|
|
|
676
882
|
// src/agents/rate-limit-signal.ts
|
|
677
883
|
import { createConnection as createConnection2 } from "node:net";
|
|
678
|
-
import { join as
|
|
884
|
+
import { join as join4 } from "node:path";
|
|
679
885
|
function resolveGatewaySocketPath2() {
|
|
680
886
|
const stateDir = process.env.TELEGRAM_STATE_DIR ?? "/state/agent/telegram";
|
|
681
|
-
return process.env.SWITCHROOM_GATEWAY_SOCKET ??
|
|
887
|
+
return process.env.SWITCHROOM_GATEWAY_SOCKET ?? join4(stateDir, "gateway.sock");
|
|
682
888
|
}
|
|
683
889
|
function signalQuotaWall(agentName, resetAt, opts = {}) {
|
|
684
890
|
const socketPath = opts.socketPath ?? resolveGatewaySocketPath2();
|
|
@@ -744,7 +950,7 @@ function signalQuotaWall(agentName, resetAt, opts = {}) {
|
|
|
744
950
|
import * as net from "node:net";
|
|
745
951
|
import { homedir } from "node:os";
|
|
746
952
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
747
|
-
import { join as
|
|
953
|
+
import { join as join5 } from "node:path";
|
|
748
954
|
|
|
749
955
|
// node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
|
|
750
956
|
var exports_external = {};
|
|
@@ -5031,7 +5237,7 @@ function reviveDate(v) {
|
|
|
5031
5237
|
return Number.isNaN(d.getTime()) ? null : d;
|
|
5032
5238
|
}
|
|
5033
5239
|
function operatorSocketPath(home = homedir()) {
|
|
5034
|
-
return
|
|
5240
|
+
return join5(home, ".switchroom", "state", "auth-broker-operator", "sock");
|
|
5035
5241
|
}
|
|
5036
5242
|
function resolveAuthBrokerSocketPath(opts) {
|
|
5037
5243
|
if (opts?.socket)
|
|
@@ -5406,8 +5612,9 @@ async function main() {
|
|
|
5406
5612
|
const rateLimitDetect = process.env.SWITCHROOM_RATE_LIMIT_DETECT !== "0";
|
|
5407
5613
|
const overageSelect = rateLimitDetect && process.env.SWITCHROOM_RATE_LIMIT_OVERAGE !== "0";
|
|
5408
5614
|
const permissionCardAware = process.env.SWITCHROOM_PERMISSION_CARD_AWARE !== "0";
|
|
5615
|
+
const permissionFloodAware = process.env.SWITCHROOM_WEDGE_FLOOD_AWARE !== "0";
|
|
5409
5616
|
try {
|
|
5410
|
-
console.error(`[autoaccept-poll] ${agentName}: entering wedge-watchdog (continuous)` + (rateLimitDetect ? " +rate-limit-detect" : " (rate-limit-detect OFF)") + (overageSelect ? " +overage-carveout" : "") + (permissionCardAware ? " +permission-card-aware" : " (permission-card-aware OFF)"));
|
|
5617
|
+
console.error(`[autoaccept-poll] ${agentName}: entering wedge-watchdog (continuous)` + (rateLimitDetect ? " +rate-limit-detect" : " (rate-limit-detect OFF)") + (overageSelect ? " +overage-carveout" : "") + (permissionCardAware ? " +permission-card-aware" : " (permission-card-aware OFF)") + (permissionFloodAware ? " +permission-flood-aware" : " (permission-flood-aware OFF)"));
|
|
5411
5618
|
const res = await runWedgeWatchdog({
|
|
5412
5619
|
agentName,
|
|
5413
5620
|
rateLimitSignature: rateLimitDetect ? undefined : null,
|
|
@@ -5416,9 +5623,10 @@ async function main() {
|
|
|
5416
5623
|
} : undefined,
|
|
5417
5624
|
overageDecision: overageSelect ? () => queryActiveOverageServing() : undefined,
|
|
5418
5625
|
requestRestart: requestWedgeRestart,
|
|
5419
|
-
queryPendingPermission: permissionCardAware ? undefined : null
|
|
5626
|
+
queryPendingPermission: permissionCardAware ? undefined : null,
|
|
5627
|
+
floodPressure: permissionFloodAware ? undefined : null
|
|
5420
5628
|
});
|
|
5421
|
-
console.error(`[autoaccept-poll] ${agentName}: wedge-watchdog returned reason=${res.reason} fires=${res.fires} rateLimitFires=${res.rateLimitFires} overageCreditSelections=${res.overageCreditSelections} confirmModalFires=${res.confirmModalFires} permissionPromptFires=${res.permissionPromptFires} permissionPromptDeferrals=${res.permissionPromptDeferrals} restartEscalations=${res.restartEscalations}`);
|
|
5629
|
+
console.error(`[autoaccept-poll] ${agentName}: wedge-watchdog returned reason=${res.reason} fires=${res.fires} rateLimitFires=${res.rateLimitFires} overageCreditSelections=${res.overageCreditSelections} confirmModalFires=${res.confirmModalFires} permissionPromptFires=${res.permissionPromptFires} permissionPromptDeferrals=${res.permissionPromptDeferrals} permissionPromptFloodHolds=${res.permissionPromptFloodHolds} permissionPromptCardlessHolds=${res.permissionPromptCardlessHolds} restartEscalations=${res.restartEscalations}`);
|
|
5422
5630
|
} catch (err) {
|
|
5423
5631
|
console.error(`[autoaccept-poll] ${agentName}: wedge-watchdog unexpected throw: ${err.message}`);
|
|
5424
5632
|
}
|
|
@@ -12106,7 +12106,10 @@ var LiteLLMConfigSchema = exports_external.object({
|
|
|
12106
12106
|
admin_key: exports_external.string().optional().describe("LiteLLM master/admin key used at apply time to provision the team + " + "virtual key. Supports a vault reference (e.g. " + "'vault:litellm/master-key') \u2014 resolution happens at apply time via " + "the vault-broker. Never injected into the agent container."),
|
|
12107
12107
|
team: exports_external.string().optional().describe("LiteLLM team alias the per-agent key is created under. Defaults to " + "'switchroom' (applied in code, not as a schema default)."),
|
|
12108
12108
|
small_fast_model: exports_external.string().optional().describe("Model id exported as ANTHROPIC_SMALL_FAST_MODEL for the claude CLI's " + "background/fast lane, e.g. 'claude-haiku-4-5-20251001'."),
|
|
12109
|
-
tags: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Extra key/value metadata tags attached to the provisioned LiteLLM " + "virtual key. Merged per-key across cascade layers (agent wins).")
|
|
12109
|
+
tags: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Extra key/value metadata tags attached to the provisioned LiteLLM " + "virtual key. Merged per-key across cascade layers (agent wins)."),
|
|
12110
|
+
max_budget: exports_external.number().positive().optional().describe("HARD spend cap in USD for this agent's virtual key over one " + "`budget_duration` window. LiteLLM refuses the request once the key's " + "tracked spend exceeds it, so a runaway loop costs at most this much " + "before it is stopped. Defaults to " + "DEFAULT_KEY_MAX_BUDGET_USD (see src/litellm/budget.ts) \u2014 deliberately " + "conservative; raise it per-agent rather than removing it. Set 0 or " + "omit `budget_duration` at your own risk: an uncapped key is only as " + "bounded as the upstream account balance."),
|
|
12111
|
+
soft_budget: exports_external.number().positive().optional().describe("ADVISORY spend threshold in USD. LiteLLM keeps serving past it and " + "raises a budget alert instead. Must be < max_budget. NOTE: LiteLLM " + "accepts soft_budget only on POST /key/generate (GenerateKeyRequest); " + "UpdateKeyRequest does NOT carry it, so changing this value only takes " + "effect on a key that is (re)generated, not on an existing one."),
|
|
12112
|
+
budget_duration: exports_external.string().regex(/^\d+(s|m|h|d|mo)$/, "budget_duration must be a LiteLLM duration like '30d', '24h', '1mo'").optional().describe("Rolling window the budget resets on, in LiteLLM duration syntax " + "('30d', '24h', '1mo'). Defaults to DEFAULT_KEY_BUDGET_DURATION. " + "WITHOUT a duration LiteLLM treats max_budget as a LIFETIME cap that " + "never resets \u2014 the key silently dies for good once it is hit.")
|
|
12110
12113
|
}).optional().describe("LiteLLM routing config \u2014 opt-in per-agent virtual-key auto-provisioning " + "+ routing env. Default OFF. See LiteLLMConfigSchema doc for the full flow.");
|
|
12111
12114
|
var HindsightPerOpLlmSchema = exports_external.object({
|
|
12112
12115
|
model: exports_external.string().min(1).optional().describe("Per-op model (upstream `HINDSIGHT_API_<OP>_LLM_MODEL`). Absent \u2192 " + "inherit the global `hindsight.llm.model`."),
|
|
@@ -12125,7 +12128,7 @@ var HindsightConfigSchema = exports_external.object({
|
|
|
12125
12128
|
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."),
|
|
12126
12129
|
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent \u2192 global.")
|
|
12127
12130
|
}).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."),
|
|
12128
|
-
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_MAX_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND), 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), 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).")
|
|
12131
|
+
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_MAX_SLOTS, " + "WORKER_CONSOLIDATION_SLOT_LIMIT, " + "CONSOLIDATION_MAX_MEMORIES_PER_ROUND, 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_MAX_SLOTS reserves out of; " + "unset means upstream's own default), 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).")
|
|
12129
12132
|
});
|
|
12130
12133
|
var MicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
12131
12134
|
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)."),
|