switchroom 0.19.23 → 0.19.24
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 +4 -2
- package/dist/auth-broker/index.js +35 -9
- package/dist/cli/notion-write-pretool.mjs +4 -2
- package/dist/cli/switchroom.js +242 -82
- package/dist/host-control/main.js +36 -10
- package/dist/vault/approvals/kernel-server.js +35 -9
- package/dist/vault/broker/server.js +35 -9
- package/package.json +1 -1
- package/profiles/_shared/dev-protocol.md.hbs +3 -4
- package/skills/dev-protocol/SKILL.md +22 -15
- package/skills/switchroom-release/SKILL.md +2 -1
- package/telegram-plugin/dist/gateway/gateway.js +171 -48
- package/telegram-plugin/gateway/gateway.ts +34 -35
- package/telegram-plugin/gateway/latest-turn-lookup.ts +60 -0
- package/telegram-plugin/gateway/outbound-send-path.ts +53 -21
- package/telegram-plugin/gateway/subagent-handback-marker.ts +1 -1
- package/telegram-plugin/gateway/turn-end.ts +1 -1
- package/telegram-plugin/reply-owner-resolve.ts +110 -9
- package/telegram-plugin/send-gate-degraded.test.ts +45 -16
- package/telegram-plugin/send-gate.ts +185 -24
- package/telegram-plugin/tests/activity-card-send-gate.test.ts +9 -9
- package/telegram-plugin/tests/latest-turn-lookup.test.ts +77 -0
- package/telegram-plugin/tests/narrative-lane-golden.test.ts +23 -1
- package/telegram-plugin/tests/reply-owner-resolve.test.ts +531 -0
- package/telegram-plugin/tests/send-reply-golden.test.ts +296 -28
- package/telegram-plugin/tests/stream-controller-send-gate.test.ts +134 -28
- package/telegram-plugin/tests/stream-render-golden.test.ts +25 -3
|
@@ -4377,17 +4377,19 @@ var init_schema = __esm(() => {
|
|
|
4377
4377
|
model: exports_external.string().min(1).optional().describe("Per-op model (upstream `HINDSIGHT_API_<OP>_LLM_MODEL`). Absent → " + "inherit the global `hindsight.llm.model`."),
|
|
4378
4378
|
provider: exports_external.string().min(1).optional().describe("Per-op provider (upstream `HINDSIGHT_API_<OP>_LLM_PROVIDER`). " + "Absent → inherit the global `hindsight.llm.provider`."),
|
|
4379
4379
|
base_url: exports_external.string().min(1).optional().describe("Per-op base URL (upstream `HINDSIGHT_API_<OP>_LLM_BASE_URL`). " + "Optional passthrough; absent → inherit the global."),
|
|
4380
|
-
api_key: exports_external.string().min(1).optional().describe("Per-op API key (upstream `HINDSIGHT_API_<OP>_LLM_API_KEY`). Literal " + "or `vault:` reference. Optional passthrough; absent → inherit global.")
|
|
4380
|
+
api_key: exports_external.string().min(1).optional().describe("Per-op API key (upstream `HINDSIGHT_API_<OP>_LLM_API_KEY`). Literal " + "or `vault:` reference. Optional passthrough; absent → inherit global."),
|
|
4381
|
+
context_window: exports_external.number().int().positive().optional().describe("Context window (tokens) of the backend serving THIS op. NOT an " + "upstream env var — switchroom derives the op's token budget " + "(consolidation batch size / max-completion caps / reflect " + "max-context cap) from it so a single call can never overflow the " + "window. Absent → inherit " + "`hindsight.llm.context_window`, else a per-provider default " + "(conservative for non-`claude-code` providers, which usually mean " + "a local llama.cpp/Ollama slot; a self-hosted `base_url` — loopback, " + "RFC1918, `.local`/`.internal` — forces the conservative default too, " + "regardless of the provider NAME, since the endpoint is where the " + "traffic actually terminates). All three lanes (`retain`, " + "`reflect`, `consolidation`) are budgeted independently.")
|
|
4381
4382
|
}).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 — switchroom emits only the vars set.");
|
|
4382
4383
|
HindsightConfigSchema = exports_external.object({
|
|
4383
4384
|
llm: exports_external.object({
|
|
4384
4385
|
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."),
|
|
4385
4386
|
model: exports_external.string().min(1).optional().describe("Hindsight LLM model (upstream `HINDSIGHT_API_LLM_MODEL`). Defaults " + "to HINDSIGHT_DEFAULT_MODEL. Any model your LiteLLM proxy can route " + "is valid, e.g. `openrouter/z-ai/glm-5.2` when routing through the " + "fleet proxy. With provider=claude-code this value is ALSO exported " + "as `ANTHROPIC_MODEL` to the claude subprocess. Serves as the GLOBAL " + "default for every op absent a per-op override."),
|
|
4387
|
+
context_window: exports_external.number().int().positive().optional().describe("GLOBAL context window (tokens) of the backend serving hindsight's " + "LLM ops — the declared size switchroom derives every token budget " + "from. Set this to the real window of whatever you point " + "`hindsight.llm` at (e.g. 32768 for a llama.cpp slot launched with " + "`-c 65536 -np 2`, 131072 for a large-window OpenRouter model). " + "Absent → a per-provider default: 200000 for `claude-code`, a " + "conservative 32768 for everything else. Overflowing a local " + "backend's window does NOT error — llama.cpp context-shift silently " + "drops the system prompt and the model answers conversationally " + "with HTTP 200 — so this value is what makes the failure " + "detectable at setup time instead of never."),
|
|
4386
4388
|
retain: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `retain` LLM op (memory ingestion). Emits " + "`HINDSIGHT_API_RETAIN_LLM_*`. Absent → uses the global model/provider."),
|
|
4387
4389
|
reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent → uses global."),
|
|
4388
4390
|
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent → global.")
|
|
4389
4391
|
}).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."),
|
|
4390
|
-
env: exports_external.record(exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean()])).optional().describe("Operator overrides for switchroom's capability-gated Hindsight " + "performance defaults. Only the keys switchroom actually manages are " + "honoured (`HINDSIGHT_PERF_ENV_KEYS` in " + "src/setup/hindsight-perf-defaults.ts: RERANKER_LOCAL_FP16, " + "LLM_MAX_CONCURRENT, RETAIN/CONSOLIDATION_LLM_MAX_CONCURRENT, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT), plus the " + "embedded-PostgreSQL (pg0) sizing keys switchroom manages in " + "src/setup/hindsight-pg-defaults.ts (`HINDSIGHT_PG_ENV_KEYS`: " + "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE, " + "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS — 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).")
|
|
4392
|
+
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, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT), 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), 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).")
|
|
4391
4393
|
});
|
|
4392
4394
|
MicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
4393
4395
|
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)."),
|
|
@@ -19202,6 +19204,10 @@ var HINDSIGHT_DEFAULT_LLM_MAX_CONCURRENT = 4;
|
|
|
19202
19204
|
var HINDSIGHT_DEFAULT_RETAIN_LLM_MAX_CONCURRENT = 1;
|
|
19203
19205
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_MAX_CONCURRENT = 1;
|
|
19204
19206
|
var HINDSIGHT_DEFAULT_RERANKER_LOCAL_FP16 = "true";
|
|
19207
|
+
var HINDSIGHT_DEFAULT_RERANKER_LOCAL_BATCH_SIZE = 128;
|
|
19208
|
+
var HINDSIGHT_DEFAULT_LLM_STRICT_SCHEMA = "true";
|
|
19209
|
+
var HINDSIGHT_DEFAULT_LLM_MAX_RETRIES = 2;
|
|
19210
|
+
var HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM = 2;
|
|
19205
19211
|
var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
19206
19212
|
[
|
|
19207
19213
|
"HINDSIGHT_API_RECALL_MAX_CANDIDATES_PER_SOURCE",
|
|
@@ -19215,10 +19221,18 @@ var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
|
19215
19221
|
"HINDSIGHT_API_LINK_EXPANSION_TIMEOUT",
|
|
19216
19222
|
String(HINDSIGHT_DEFAULT_LINK_EXPANSION_TIMEOUT_S)
|
|
19217
19223
|
],
|
|
19218
|
-
["HINDSIGHT_API_LLM_REASONING_EFFORT", HINDSIGHT_DEFAULT_LLM_REASONING_EFFORT]
|
|
19224
|
+
["HINDSIGHT_API_LLM_REASONING_EFFORT", HINDSIGHT_DEFAULT_LLM_REASONING_EFFORT],
|
|
19225
|
+
[
|
|
19226
|
+
"HINDSIGHT_API_CONSOLIDATION_LLM_PARALLELISM",
|
|
19227
|
+
String(HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM)
|
|
19228
|
+
]
|
|
19219
19229
|
];
|
|
19220
19230
|
var HINDSIGHT_PERF_DEFAULTS_GPU = [
|
|
19221
|
-
["HINDSIGHT_API_RERANKER_LOCAL_FP16", HINDSIGHT_DEFAULT_RERANKER_LOCAL_FP16]
|
|
19231
|
+
["HINDSIGHT_API_RERANKER_LOCAL_FP16", HINDSIGHT_DEFAULT_RERANKER_LOCAL_FP16],
|
|
19232
|
+
[
|
|
19233
|
+
"HINDSIGHT_API_RERANKER_LOCAL_BATCH_SIZE",
|
|
19234
|
+
String(HINDSIGHT_DEFAULT_RERANKER_LOCAL_BATCH_SIZE)
|
|
19235
|
+
]
|
|
19222
19236
|
];
|
|
19223
19237
|
var HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM = [
|
|
19224
19238
|
["HINDSIGHT_API_LLM_MAX_CONCURRENT", String(HINDSIGHT_DEFAULT_LLM_MAX_CONCURRENT)],
|
|
@@ -19229,13 +19243,21 @@ var HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM = [
|
|
|
19229
19243
|
[
|
|
19230
19244
|
"HINDSIGHT_API_CONSOLIDATION_LLM_MAX_CONCURRENT",
|
|
19231
19245
|
String(HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_MAX_CONCURRENT)
|
|
19232
|
-
]
|
|
19246
|
+
],
|
|
19247
|
+
["HINDSIGHT_API_LLM_STRICT_SCHEMA", HINDSIGHT_DEFAULT_LLM_STRICT_SCHEMA],
|
|
19248
|
+
["HINDSIGHT_API_LLM_MAX_RETRIES", String(HINDSIGHT_DEFAULT_LLM_MAX_RETRIES)]
|
|
19233
19249
|
];
|
|
19250
|
+
var HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS = new Set([
|
|
19251
|
+
"HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY"
|
|
19252
|
+
]);
|
|
19234
19253
|
var HINDSIGHT_PERF_ENV_KEYS = new Set([
|
|
19235
|
-
...
|
|
19236
|
-
|
|
19237
|
-
|
|
19238
|
-
|
|
19254
|
+
...[
|
|
19255
|
+
...HINDSIGHT_PERF_DEFAULTS_UNGATED,
|
|
19256
|
+
...HINDSIGHT_PERF_DEFAULTS_GPU,
|
|
19257
|
+
...HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM
|
|
19258
|
+
].map(([k]) => k),
|
|
19259
|
+
...HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS
|
|
19260
|
+
]);
|
|
19239
19261
|
|
|
19240
19262
|
// src/setup/hindsight-pg-defaults.ts
|
|
19241
19263
|
var HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION = 8 * 1024;
|
|
@@ -19258,6 +19280,10 @@ var HINDSIGHT_PG_DEFAULTS = [
|
|
|
19258
19280
|
];
|
|
19259
19281
|
var HINDSIGHT_PG_ENV_KEYS = new Set(HINDSIGHT_PG_DEFAULTS.map(([k]) => k));
|
|
19260
19282
|
|
|
19283
|
+
// src/setup/hindsight-context-budget.ts
|
|
19284
|
+
var HINDSIGHT_UPSTREAM_RETAIN_CHUNK_SIZE = 3000;
|
|
19285
|
+
var HINDSIGHT_RETAIN_MAX_COMPLETION_FLOOR = HINDSIGHT_UPSTREAM_RETAIN_CHUNK_SIZE + 72;
|
|
19286
|
+
|
|
19261
19287
|
// src/setup/hindsight.ts
|
|
19262
19288
|
var HINDSIGHT_DEFAULT_API_PORT = 18888;
|
|
19263
19289
|
var HINDSIGHT_DEFAULT_MCP_URL = `http://127.0.0.1:${HINDSIGHT_DEFAULT_API_PORT}/mcp/`;
|
|
@@ -4377,17 +4377,19 @@ var init_schema = __esm(() => {
|
|
|
4377
4377
|
model: exports_external.string().min(1).optional().describe("Per-op model (upstream `HINDSIGHT_API_<OP>_LLM_MODEL`). Absent → " + "inherit the global `hindsight.llm.model`."),
|
|
4378
4378
|
provider: exports_external.string().min(1).optional().describe("Per-op provider (upstream `HINDSIGHT_API_<OP>_LLM_PROVIDER`). " + "Absent → inherit the global `hindsight.llm.provider`."),
|
|
4379
4379
|
base_url: exports_external.string().min(1).optional().describe("Per-op base URL (upstream `HINDSIGHT_API_<OP>_LLM_BASE_URL`). " + "Optional passthrough; absent → inherit the global."),
|
|
4380
|
-
api_key: exports_external.string().min(1).optional().describe("Per-op API key (upstream `HINDSIGHT_API_<OP>_LLM_API_KEY`). Literal " + "or `vault:` reference. Optional passthrough; absent → inherit global.")
|
|
4380
|
+
api_key: exports_external.string().min(1).optional().describe("Per-op API key (upstream `HINDSIGHT_API_<OP>_LLM_API_KEY`). Literal " + "or `vault:` reference. Optional passthrough; absent → inherit global."),
|
|
4381
|
+
context_window: exports_external.number().int().positive().optional().describe("Context window (tokens) of the backend serving THIS op. NOT an " + "upstream env var — switchroom derives the op's token budget " + "(consolidation batch size / max-completion caps / reflect " + "max-context cap) from it so a single call can never overflow the " + "window. Absent → inherit " + "`hindsight.llm.context_window`, else a per-provider default " + "(conservative for non-`claude-code` providers, which usually mean " + "a local llama.cpp/Ollama slot; a self-hosted `base_url` — loopback, " + "RFC1918, `.local`/`.internal` — forces the conservative default too, " + "regardless of the provider NAME, since the endpoint is where the " + "traffic actually terminates). All three lanes (`retain`, " + "`reflect`, `consolidation`) are budgeted independently.")
|
|
4381
4382
|
}).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 — switchroom emits only the vars set.");
|
|
4382
4383
|
HindsightConfigSchema = exports_external.object({
|
|
4383
4384
|
llm: exports_external.object({
|
|
4384
4385
|
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."),
|
|
4385
4386
|
model: exports_external.string().min(1).optional().describe("Hindsight LLM model (upstream `HINDSIGHT_API_LLM_MODEL`). Defaults " + "to HINDSIGHT_DEFAULT_MODEL. Any model your LiteLLM proxy can route " + "is valid, e.g. `openrouter/z-ai/glm-5.2` when routing through the " + "fleet proxy. With provider=claude-code this value is ALSO exported " + "as `ANTHROPIC_MODEL` to the claude subprocess. Serves as the GLOBAL " + "default for every op absent a per-op override."),
|
|
4387
|
+
context_window: exports_external.number().int().positive().optional().describe("GLOBAL context window (tokens) of the backend serving hindsight's " + "LLM ops — the declared size switchroom derives every token budget " + "from. Set this to the real window of whatever you point " + "`hindsight.llm` at (e.g. 32768 for a llama.cpp slot launched with " + "`-c 65536 -np 2`, 131072 for a large-window OpenRouter model). " + "Absent → a per-provider default: 200000 for `claude-code`, a " + "conservative 32768 for everything else. Overflowing a local " + "backend's window does NOT error — llama.cpp context-shift silently " + "drops the system prompt and the model answers conversationally " + "with HTTP 200 — so this value is what makes the failure " + "detectable at setup time instead of never."),
|
|
4386
4388
|
retain: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `retain` LLM op (memory ingestion). Emits " + "`HINDSIGHT_API_RETAIN_LLM_*`. Absent → uses the global model/provider."),
|
|
4387
4389
|
reflect: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `reflect` LLM op (synthesis / mental-model " + "refresh). Emits `HINDSIGHT_API_REFLECT_LLM_*`. Absent → uses global."),
|
|
4388
4390
|
consolidation: HindsightPerOpLlmSchema.optional().describe("Per-op override for the `consolidation` LLM op (background memory " + "merge). Emits `HINDSIGHT_API_CONSOLIDATION_LLM_*`. Absent → global.")
|
|
4389
4391
|
}).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."),
|
|
4390
|
-
env: exports_external.record(exports_external.union([exports_external.string(), exports_external.number(), exports_external.boolean()])).optional().describe("Operator overrides for switchroom's capability-gated Hindsight " + "performance defaults. Only the keys switchroom actually manages are " + "honoured (`HINDSIGHT_PERF_ENV_KEYS` in " + "src/setup/hindsight-perf-defaults.ts: RERANKER_LOCAL_FP16, " + "LLM_MAX_CONCURRENT, RETAIN/CONSOLIDATION_LLM_MAX_CONCURRENT, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT), plus the " + "embedded-PostgreSQL (pg0) sizing keys switchroom manages in " + "src/setup/hindsight-pg-defaults.ts (`HINDSIGHT_PG_ENV_KEYS`: " + "SWITCHROOM_HINDSIGHT_PG_EFFECTIVE_CACHE_SIZE, " + "SWITCHROOM_HINDSIGHT_PG_SHARED_BUFFERS — 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).")
|
|
4392
|
+
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, " + "RECALL_MAX_CANDIDATES_PER_SOURCE, LINK_EXPANSION_PER_ENTITY_LIMIT, " + "LINK_EXPANSION_TIMEOUT, LLM_REASONING_EFFORT), 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), 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).")
|
|
4391
4393
|
});
|
|
4392
4394
|
MicrosoftWorkspaceConfigSchema = exports_external.object({
|
|
4393
4395
|
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)."),
|
|
@@ -18875,6 +18877,10 @@ var HINDSIGHT_DEFAULT_LLM_MAX_CONCURRENT = 4;
|
|
|
18875
18877
|
var HINDSIGHT_DEFAULT_RETAIN_LLM_MAX_CONCURRENT = 1;
|
|
18876
18878
|
var HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_MAX_CONCURRENT = 1;
|
|
18877
18879
|
var HINDSIGHT_DEFAULT_RERANKER_LOCAL_FP16 = "true";
|
|
18880
|
+
var HINDSIGHT_DEFAULT_RERANKER_LOCAL_BATCH_SIZE = 128;
|
|
18881
|
+
var HINDSIGHT_DEFAULT_LLM_STRICT_SCHEMA = "true";
|
|
18882
|
+
var HINDSIGHT_DEFAULT_LLM_MAX_RETRIES = 2;
|
|
18883
|
+
var HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM = 2;
|
|
18878
18884
|
var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
18879
18885
|
[
|
|
18880
18886
|
"HINDSIGHT_API_RECALL_MAX_CANDIDATES_PER_SOURCE",
|
|
@@ -18888,10 +18894,18 @@ var HINDSIGHT_PERF_DEFAULTS_UNGATED = [
|
|
|
18888
18894
|
"HINDSIGHT_API_LINK_EXPANSION_TIMEOUT",
|
|
18889
18895
|
String(HINDSIGHT_DEFAULT_LINK_EXPANSION_TIMEOUT_S)
|
|
18890
18896
|
],
|
|
18891
|
-
["HINDSIGHT_API_LLM_REASONING_EFFORT", HINDSIGHT_DEFAULT_LLM_REASONING_EFFORT]
|
|
18897
|
+
["HINDSIGHT_API_LLM_REASONING_EFFORT", HINDSIGHT_DEFAULT_LLM_REASONING_EFFORT],
|
|
18898
|
+
[
|
|
18899
|
+
"HINDSIGHT_API_CONSOLIDATION_LLM_PARALLELISM",
|
|
18900
|
+
String(HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_PARALLELISM)
|
|
18901
|
+
]
|
|
18892
18902
|
];
|
|
18893
18903
|
var HINDSIGHT_PERF_DEFAULTS_GPU = [
|
|
18894
|
-
["HINDSIGHT_API_RERANKER_LOCAL_FP16", HINDSIGHT_DEFAULT_RERANKER_LOCAL_FP16]
|
|
18904
|
+
["HINDSIGHT_API_RERANKER_LOCAL_FP16", HINDSIGHT_DEFAULT_RERANKER_LOCAL_FP16],
|
|
18905
|
+
[
|
|
18906
|
+
"HINDSIGHT_API_RERANKER_LOCAL_BATCH_SIZE",
|
|
18907
|
+
String(HINDSIGHT_DEFAULT_RERANKER_LOCAL_BATCH_SIZE)
|
|
18908
|
+
]
|
|
18895
18909
|
];
|
|
18896
18910
|
var HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM = [
|
|
18897
18911
|
["HINDSIGHT_API_LLM_MAX_CONCURRENT", String(HINDSIGHT_DEFAULT_LLM_MAX_CONCURRENT)],
|
|
@@ -18902,13 +18916,21 @@ var HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM = [
|
|
|
18902
18916
|
[
|
|
18903
18917
|
"HINDSIGHT_API_CONSOLIDATION_LLM_MAX_CONCURRENT",
|
|
18904
18918
|
String(HINDSIGHT_DEFAULT_CONSOLIDATION_LLM_MAX_CONCURRENT)
|
|
18905
|
-
]
|
|
18919
|
+
],
|
|
18920
|
+
["HINDSIGHT_API_LLM_STRICT_SCHEMA", HINDSIGHT_DEFAULT_LLM_STRICT_SCHEMA],
|
|
18921
|
+
["HINDSIGHT_API_LLM_MAX_RETRIES", String(HINDSIGHT_DEFAULT_LLM_MAX_RETRIES)]
|
|
18906
18922
|
];
|
|
18923
|
+
var HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS = new Set([
|
|
18924
|
+
"HINDSIGHT_API_WORKER_CONSOLIDATION_BANK_PRIORITY"
|
|
18925
|
+
]);
|
|
18907
18926
|
var HINDSIGHT_PERF_ENV_KEYS = new Set([
|
|
18908
|
-
...
|
|
18909
|
-
|
|
18910
|
-
|
|
18911
|
-
|
|
18927
|
+
...[
|
|
18928
|
+
...HINDSIGHT_PERF_DEFAULTS_UNGATED,
|
|
18929
|
+
...HINDSIGHT_PERF_DEFAULTS_GPU,
|
|
18930
|
+
...HINDSIGHT_PERF_DEFAULTS_LOCAL_LLM
|
|
18931
|
+
].map(([k]) => k),
|
|
18932
|
+
...HINDSIGHT_PERF_OVERRIDE_ONLY_KEYS
|
|
18933
|
+
]);
|
|
18912
18934
|
|
|
18913
18935
|
// src/setup/hindsight-pg-defaults.ts
|
|
18914
18936
|
var HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION = 8 * 1024;
|
|
@@ -18931,6 +18953,10 @@ var HINDSIGHT_PG_DEFAULTS = [
|
|
|
18931
18953
|
];
|
|
18932
18954
|
var HINDSIGHT_PG_ENV_KEYS = new Set(HINDSIGHT_PG_DEFAULTS.map(([k]) => k));
|
|
18933
18955
|
|
|
18956
|
+
// src/setup/hindsight-context-budget.ts
|
|
18957
|
+
var HINDSIGHT_UPSTREAM_RETAIN_CHUNK_SIZE = 3000;
|
|
18958
|
+
var HINDSIGHT_RETAIN_MAX_COMPLETION_FLOOR = HINDSIGHT_UPSTREAM_RETAIN_CHUNK_SIZE + 72;
|
|
18959
|
+
|
|
18934
18960
|
// src/setup/hindsight.ts
|
|
18935
18961
|
var HINDSIGHT_DEFAULT_API_PORT = 18888;
|
|
18936
18962
|
var HINDSIGHT_DEFAULT_MCP_URL = `http://127.0.0.1:${HINDSIGHT_DEFAULT_API_PORT}/mcp/`;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "switchroom",
|
|
3
3
|
"//version": "NOT the release version — source of truth is the git tag, resolved by scripts/build.mjs:resolveVersion() (see CLAUDE.md > Standard release process). This field is stale by design and only the Layer-4 dev/non-tag fallback for build.mjs + src/cli/resolve-version.ts; do NOT bump it expecting a release to pick it up. npm-pack tarball naming needs a real version — do that as an UNCOMMITTED pack-time bump (see release step 6), never a committed one.",
|
|
4
|
-
"version": "0.19.
|
|
4
|
+
"version": "0.19.24",
|
|
5
5
|
"description": "Run Claude Code 24/7 on your Claude Pro/Max subscription over Telegram. Open-source alternative to OpenClaw and NanoClaw — no API keys.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
## Development Protocol
|
|
2
2
|
|
|
3
|
-
Judgement criteria for substantive coding, infra, or debugging work.
|
|
3
|
+
Judgement criteria for substantive coding, infra, or debugging work.
|
|
4
4
|
|
|
5
5
|
This governs HOW delegated work is done, and is **not a license to do it inline**: dispatch first (see Delegation), then these bind the worker.
|
|
6
6
|
|
|
7
7
|
- **Orient before you build — validate, don't assume.** Root-cause in real source — the repo's files at HEAD, not `dist/`, caches, or generated output. If the evidence contradicts your theory, the plan, or the task description, report the contradiction rather than force-fitting it. Cite `file:line`, commits, PRs.
|
|
8
8
|
- **Infer before you ask.** Most questions are answerable from the code and history. If genuinely unsure, ask ONE question at a time, during planning; act autonomously during execution — make the reasonable call and note the assumption.
|
|
9
9
|
- **Design report before implementation, when the change is large.** Architecturally significant, cross-cutting, or ambiguous-approach work earns an evidence-grounded design report and a red-team pass before code, and ships as focused single-concern PRs.
|
|
10
|
-
- **
|
|
10
|
+
- **Run `git fetch origin` first, then branch off `origin/main`.** Never branch off the working copy as-is: you do not start in the repo you are coding in, and the checkout on disk may be many commits stale. Scoped tests + lint locally; CI is the full-suite authority.
|
|
11
11
|
- **Adversarial review of the diff.** Read every change as an adversary would: what breaks, what's untested, what's inconsistent.
|
|
12
|
-
- **Blockers and
|
|
13
|
-
- **Re-review only a behavioural fix** — a docs/comment/log/test-only commit doesn't earn one. Prefix a fix commit `review-fix:`; two rounds is the cap, and CI enforces it.
|
|
12
|
+
- **Blockers and majors block the merge; lows don't** — file a low as a follow-up issue rather than fixing it now. Filing is mandatory. Fix what blocks, then merge on CI green. Do not count review rounds and do not run a mandatory re-review pass: verifying your own fix is part of making it, not a separate step.
|
|
14
13
|
- **Durable fixes over hack patches. Deterministic mechanisms over model-dependent behaviour** — if code can enforce a guarantee, don't leave it to prompt discipline. **Tests assert outcomes, not just code paths:** one that wouldn't fail on the bug it guards is not a test.
|
|
15
14
|
- **Communicate as you go.** Consolidated messages — one substantive update beats five fragments; never go dark; no foreground watch over 30 seconds (background it); max 15 parallel sub-agents.
|
|
@@ -80,29 +80,36 @@ fixing lows produces a new diff, and a new diff earned another re-review. It
|
|
|
80
80
|
produced PRs going four rounds where the last round's only finding was an
|
|
81
81
|
inaccurate doc comment. So:
|
|
82
82
|
|
|
83
|
-
- **Blockers and
|
|
84
|
-
- **Lows do NOT block.**
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
- **
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
is on the PR.
|
|
83
|
+
- **Blockers and majors block the merge.** Fix them.
|
|
84
|
+
- **Lows do NOT block.** File a low as a follow-up issue rather than fixing it
|
|
85
|
+
now. Filing is mandatory — an unfiled low is a dropped bug, and there is no
|
|
86
|
+
human team to catch it later.
|
|
87
|
+
- **Fix what blocks, then merge on CI green.**
|
|
88
|
+
- **Do not count review rounds, and do not run a mandatory re-review pass.**
|
|
89
|
+
Verifying your own fix is part of making it, not a separate step. Counting
|
|
90
|
+
rounds was itself a loop driver: it made the review process the subject of
|
|
91
|
+
the work instead of the change.
|
|
93
92
|
|
|
94
93
|
If a finding is genuinely invalid, rebut it with evidence in writing; silence
|
|
95
94
|
is not a rebuttal.
|
|
96
95
|
|
|
97
|
-
When you do re-review, the verdict states per original finding: the finding
|
|
98
|
-
ID, what changed (`file:line` of the fix), whether it fully resolves the
|
|
99
|
-
finding (`RESOLVED` / `PARTIAL` / `REBUTTED` with evidence), and whether the
|
|
100
|
-
fix introduced anything new. A bare "fixed" is not a verdict.
|
|
101
|
-
|
|
102
96
|
## 5. Non-obvious pipeline rules
|
|
103
97
|
|
|
104
98
|
- **CI is the full-suite authority.** Local scoped tests are a fast filter,
|
|
105
99
|
never the merge evidence. Never claim done off a local run alone.
|
|
100
|
+
- **`main` is behind a merge queue: `gh pr merge` ENQUEUES, it does not
|
|
101
|
+
merge.** The command exits 0 and the PR stays `OPEN`. The queue then re-runs
|
|
102
|
+
all seven required contexts on its own `gh-readonly-queue/main/pr-<n>-<sha>`
|
|
103
|
+
ref before landing anything, so "green on the PR" is necessary but not
|
|
104
|
+
sufficient. Never report a PR merged off that exit code — poll until its
|
|
105
|
+
state is `MERGED` and the commit is an ancestor of `origin/main`. Two flags
|
|
106
|
+
to know: `--delete-branch` is **rejected outright** while the queue is on
|
|
107
|
+
(delete the branch after it lands), and `--subject` is ignored, since the
|
|
108
|
+
queue composes the merge commit from the PR title plus `(#<pr>)` — so the PR
|
|
109
|
+
title is the commit title, write it accordingly. An entry stuck in
|
|
110
|
+
`AWAITING_CHECKS` means a required workflow is not listening for
|
|
111
|
+
`merge_group`; `.github/MERGE-QUEUE.md` owns that failure mode and the
|
|
112
|
+
invariants that prevent it.
|
|
106
113
|
- **A test that wouldn't fail on the bug it guards is not a test.** Assert the
|
|
107
114
|
observable outcome, not that the code path executed.
|
|
108
115
|
- **Prefer a deterministic mechanism over prompt discipline.** If a check, a
|
|
@@ -100,7 +100,8 @@ The tag push triggers `docker-images` and `release`. `release` internally waits
|
|
|
100
100
|
|
|
101
101
|
### Gate B — the release pipeline (`release.yml`)
|
|
102
102
|
- `gh run list --workflow=release.yml --limit 1` — wait for `completed` / `success`. Expect ~25-30 minutes: four native build legs plus the wait on `docker-images`.
|
|
103
|
-
- Its jobs, in order: `guard` (release exists + held out of `latest`) → `build` ×4 → `bundle` → `publish` (attach) → `images-gate` (wait on docker-images) → `npm` → `finalize` (un-draft). A red job anywhere leaves the release a **draft** and npm **unpublished** — which is the correct, recoverable state.
|
|
103
|
+
- Its jobs, in order: `guard` (release exists + held out of `latest`) → `build` ×4 → `bundle` → `publish` (attach) → `images-gate` (wait on docker-images) → `npm` → `finalize` (un-draft) → `images-latest` (promote `:vX.Y.Z` → `:latest`). A red job anywhere leaves the release a **draft** and npm **unpublished** — which is the correct, recoverable state.
|
|
104
|
+
- `images-latest` is why a tag push no longer moves the `:latest` image tag by itself (#3685). If that last job is the one that failed, every other leg shipped and only the image tag lags: re-run it with `gh workflow run promote.yml -f from=vX.Y.Z -f to=latest`. Don't roll the fleet off `:latest` until it is green — `docker manifest inspect ghcr.io/switchroom/switchroom-agent:latest` should report the same digest as `:vX.Y.Z`.
|
|
104
105
|
- Verify the release page actually has assets **and is no longer a draft**:
|
|
105
106
|
```bash
|
|
106
107
|
gh release view vX.Y.Z -R switchroom/switchroom --json isDraft,assets \
|