switchroom 0.19.17 → 0.19.19
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/bin/run-hook.sh +148 -0
- package/bin/workspace-dynamic-hook.sh +147 -38
- package/dist/agent-scheduler/index.js +13 -4
- package/dist/auth-broker/index.js +32 -5
- package/dist/cli/drive-write-pretool.mjs +48 -5
- package/dist/cli/ms-365-write-pretool.mjs +40 -2
- package/dist/cli/notion-write-pretool.mjs +13 -4
- package/dist/cli/switchroom.js +10614 -8104
- package/dist/host-control/main.js +12849 -11446
- package/dist/vault/approvals/kernel-server.js +90 -12
- package/dist/vault/broker/server.js +277 -94
- package/package.json +5 -3
- package/profiles/_base/start.sh.hbs +69 -5
- package/profiles/coding/CLAUDE.md.hbs +1 -1
- package/profiles/default/CLAUDE.md.hbs +3 -3
- package/profiles/executive-assistant/CLAUDE.md.hbs +1 -1
- package/profiles/health-coach/CLAUDE.md.hbs +1 -1
- package/skills/mental-model-curator/SKILL.md +8 -6
- package/telegram-plugin/bridge/bridge.ts +25 -19
- package/telegram-plugin/bridge/mcp-instructions.ts +87 -0
- package/telegram-plugin/dist/bridge/bridge.js +28 -20
- package/telegram-plugin/dist/gateway/gateway.js +2077 -1087
- package/telegram-plugin/dist/server.js +32 -20
- package/telegram-plugin/gateway/always-allow-persist-queue.ts +97 -11
- package/telegram-plugin/gateway/boot-card.ts +5 -1
- package/telegram-plugin/gateway/boot-probes.ts +113 -0
- package/telegram-plugin/gateway/config-approval-handler.test.ts +54 -0
- package/telegram-plugin/gateway/config-approval-handler.ts +16 -1
- package/telegram-plugin/gateway/disconnect-flush.ts +17 -0
- package/telegram-plugin/gateway/gateway.ts +43 -1
- package/telegram-plugin/gateway/handback-preturn-signal.ts +61 -7
- package/telegram-plugin/gateway/ipc-protocol.ts +5 -0
- package/telegram-plugin/gateway/ipc-server.ts +13 -0
- package/telegram-plugin/gateway/liveness-wiring.ts +125 -5
- package/telegram-plugin/gateway/missed-approvals-store.ts +66 -17
- package/telegram-plugin/gateway/obligation-ledger.ts +84 -4
- package/telegram-plugin/gateway/pending-card-store.ts +46 -16
- package/telegram-plugin/gateway/resume-inbound-builder.ts +13 -4
- package/telegram-plugin/gateway/scoped-grant-store.ts +39 -14
- package/telegram-plugin/gateway/store-file.ts +244 -0
- package/telegram-plugin/gateway/stream-render.ts +24 -5
- package/telegram-plugin/hooks/secret-guard-pretool.mjs +249 -76
- package/telegram-plugin/hooks/tool-label-pretool.mjs +88 -2
- package/telegram-plugin/registry/turns-schema.test.ts +8 -3
- package/telegram-plugin/registry/turns-schema.ts +40 -12
- package/telegram-plugin/runtime-metrics.ts +14 -0
- package/telegram-plugin/silence-poke.ts +138 -0
- package/telegram-plugin/tests/boot-probe-drift.test.ts +152 -0
- package/telegram-plugin/tests/bridge-tool-parity.test.ts +95 -0
- package/telegram-plugin/tests/gateway-disconnect-flush.test.ts +32 -0
- package/telegram-plugin/tests/handback-preturn-signal.test.ts +62 -0
- package/telegram-plugin/tests/helpers/liveness-wiring-fixture.ts +178 -0
- package/telegram-plugin/tests/ipc-server-validate-config-approval.test.ts +95 -0
- package/telegram-plugin/tests/mcp-instructions-budget.test.ts +184 -0
- package/telegram-plugin/tests/multitopic-routing-wiring.test.ts +22 -2
- package/telegram-plugin/tests/obligation-determinism.test.ts +114 -3
- package/telegram-plugin/tests/obligation-ledger.test.ts +310 -0
- package/telegram-plugin/tests/registry-turns.test.ts +13 -0
- package/telegram-plugin/tests/resume-inbound-builder.test.ts +15 -0
- package/telegram-plugin/tests/secret-guard-pretool.test.ts +347 -16
- package/telegram-plugin/tests/silence-poke-orphan-reap.test.ts +392 -0
- package/telegram-plugin/tests/silence-poke-teardown-notice.test.ts +301 -0
- package/telegram-plugin/tests/store-atomic-write.test.ts +411 -0
- package/telegram-plugin/tests/stream-render-golden.test.ts +103 -1
- package/telegram-plugin/tests/tool-activity-summary.test.ts +9 -2
- package/telegram-plugin/tests/tool-label-pretool.test.ts +94 -0
- package/telegram-plugin/tests/tts-normalize.test.ts +43 -0
- package/telegram-plugin/tests/voice-normalize-text.test.ts +212 -3
- package/telegram-plugin/tests/worker-feed-repeat-steps.test.ts +147 -0
- package/telegram-plugin/tts-normalize.ts +6 -4
- package/telegram-plugin/voice-normalize-text.ts +168 -11
- package/telegram-plugin/worker-activity-feed.ts +51 -1
- package/vendor/hindsight-memory/CHANGELOG.md +73 -0
- package/vendor/hindsight-memory/scripts/drain_pending.py +668 -56
- package/vendor/hindsight-memory/scripts/lib/client.py +124 -0
- package/vendor/hindsight-memory/scripts/lib/config.py +8 -3
- package/vendor/hindsight-memory/scripts/lib/directives.py +62 -4
- package/vendor/hindsight-memory/scripts/lib/pending.py +865 -33
- package/vendor/hindsight-memory/scripts/lib/retain_split.py +449 -0
- package/vendor/hindsight-memory/scripts/recall.py +257 -12
- package/vendor/hindsight-memory/scripts/retain.py +12 -6
- package/vendor/hindsight-memory/scripts/session_start.py +48 -0
- package/vendor/hindsight-memory/scripts/tests/test_client_document_exists.py +470 -0
- package/vendor/hindsight-memory/scripts/tests/test_directives.py +80 -9
- package/vendor/hindsight-memory/scripts/tests/test_pending_drops.py +2121 -0
- package/vendor/hindsight-memory/scripts/tests/test_recall_integration.py +362 -18
- package/vendor/hindsight-memory/scripts/tests/test_retain_split.py +430 -0
- package/vendor/hindsight-memory/scripts/tests/test_session_start_version_skew.py +204 -0
- package/vendor/hindsight-memory/settings.json +1 -1
- package/vendor/hindsight-memory/tests/test_drain_pending.py +102 -6
- package/vendor/hindsight-memory/tests/test_pending.py +32 -7
|
@@ -5300,6 +5300,34 @@ function truncateLine2(s) {
|
|
|
5300
5300
|
return cleaned.length <= 60 ? cleaned : `${cleaned.slice(0, 59)}\u2026`;
|
|
5301
5301
|
}
|
|
5302
5302
|
|
|
5303
|
+
// src/vault/approvals/gated-write-policy.ts
|
|
5304
|
+
var REQUIRE_OPERATOR_WRITE_ENV = "SWITCHROOM_REQUIRE_OPERATOR_APPROVAL_WRITE";
|
|
5305
|
+
function requireOperatorApprovalForWrites(env = process.env) {
|
|
5306
|
+
return env[REQUIRE_OPERATOR_WRITE_ENV] === "1";
|
|
5307
|
+
}
|
|
5308
|
+
function isGatedWriteApproved(lookup, requireOperator) {
|
|
5309
|
+
if (lookup.state !== "granted") {
|
|
5310
|
+
return { allow: false, reason: `approval state is '${lookup.state ?? "unknown"}', not 'granted'` };
|
|
5311
|
+
}
|
|
5312
|
+
if (!requireOperator)
|
|
5313
|
+
return { allow: true };
|
|
5314
|
+
if (lookup.origin !== "operator") {
|
|
5315
|
+
return {
|
|
5316
|
+
allow: false,
|
|
5317
|
+
reason: `approval was recorded with origin='${lookup.origin ?? "unknown"}', not 'operator' \u2014 ` + `an agent-origin decision is not proof an operator tapped ` + `(${REQUIRE_OPERATOR_WRITE_ENV}=1)`
|
|
5318
|
+
};
|
|
5319
|
+
}
|
|
5320
|
+
return { allow: true };
|
|
5321
|
+
}
|
|
5322
|
+
function evaluateGatedWrite(lookup, requireOperator) {
|
|
5323
|
+
const verdict = isGatedWriteApproved(lookup, requireOperator);
|
|
5324
|
+
if (verdict.allow)
|
|
5325
|
+
return { kind: "allow" };
|
|
5326
|
+
if (lookup.state === "granted")
|
|
5327
|
+
return { kind: "block", reason: verdict.reason };
|
|
5328
|
+
return { kind: "wait" };
|
|
5329
|
+
}
|
|
5330
|
+
|
|
5303
5331
|
// src/cli/drive-write-pretool.ts
|
|
5304
5332
|
var HOOK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
5305
5333
|
var KERNEL_POLL_INTERVAL_MS = 2000;
|
|
@@ -5383,9 +5411,15 @@ async function approvalLookup(scope, approverSet) {
|
|
|
5383
5411
|
current_approver_set: approverSet
|
|
5384
5412
|
});
|
|
5385
5413
|
if (r === null || r.ok !== true)
|
|
5386
|
-
return null;
|
|
5387
|
-
|
|
5414
|
+
return { state: null };
|
|
5415
|
+
const dec = r.decision;
|
|
5416
|
+
const origin = dec != null && (dec.origin === "operator" || dec.origin === "agent") ? dec.origin : undefined;
|
|
5417
|
+
return {
|
|
5418
|
+
state: typeof r.state === "string" ? r.state : null,
|
|
5419
|
+
origin
|
|
5420
|
+
};
|
|
5388
5421
|
}
|
|
5422
|
+
var REQUIRE_OPERATOR_ORIGIN = requireOperatorApprovalForWrites();
|
|
5389
5423
|
function loadAllowFrom() {
|
|
5390
5424
|
const stateDir = process.env.TELEGRAM_STATE_DIR ?? join2(homedir2(), ".claude", "channels", "telegram");
|
|
5391
5425
|
const accessPath = join2(stateDir, "access.json");
|
|
@@ -5500,9 +5534,13 @@ async function main() {
|
|
|
5500
5534
|
block("no operator paired \u2014 cannot post approval card");
|
|
5501
5535
|
}
|
|
5502
5536
|
const existing = await approvalLookup(scope, allowFrom);
|
|
5503
|
-
|
|
5537
|
+
const existingAction = evaluateGatedWrite(existing, REQUIRE_OPERATOR_ORIGIN);
|
|
5538
|
+
if (existingAction.kind === "allow") {
|
|
5504
5539
|
allow();
|
|
5505
5540
|
}
|
|
5541
|
+
if (existingAction.kind === "block") {
|
|
5542
|
+
block(`Drive write refused: ${existingAction.reason}`);
|
|
5543
|
+
}
|
|
5506
5544
|
let handle;
|
|
5507
5545
|
try {
|
|
5508
5546
|
handle = await loadFromAuthBroker();
|
|
@@ -5546,10 +5584,15 @@ async function main() {
|
|
|
5546
5584
|
if (!first) {
|
|
5547
5585
|
await new Promise((r) => setTimeout(r, KERNEL_POLL_INTERVAL_MS));
|
|
5548
5586
|
}
|
|
5549
|
-
const
|
|
5550
|
-
|
|
5587
|
+
const lookup = await approvalLookup(scope, allowFrom);
|
|
5588
|
+
const state = lookup.state;
|
|
5589
|
+
const action = evaluateGatedWrite(lookup, REQUIRE_OPERATOR_ORIGIN);
|
|
5590
|
+
if (action.kind === "allow") {
|
|
5551
5591
|
allow();
|
|
5552
5592
|
}
|
|
5593
|
+
if (action.kind === "block") {
|
|
5594
|
+
block(`Drive write refused: ${action.reason}`);
|
|
5595
|
+
}
|
|
5553
5596
|
if (state === "denied" || state === "drift_revoked") {
|
|
5554
5597
|
block(`user denied Drive write (kernel verdict: ${state})`);
|
|
5555
5598
|
}
|
|
@@ -4944,6 +4944,34 @@ function buildBatchAbortReason(appliedInBatch) {
|
|
|
4944
4944
|
return `grant lapsed mid-batch \u2014 ${n} MS-365 write${n === 1 ? "" : "s"} already ` + `applied in this batch; remaining ops aborted. STOP: do not retry blindly \u2014 ` + `reconcile what landed before re-issuing.`;
|
|
4945
4945
|
}
|
|
4946
4946
|
|
|
4947
|
+
// src/vault/approvals/gated-write-policy.ts
|
|
4948
|
+
var REQUIRE_OPERATOR_WRITE_ENV = "SWITCHROOM_REQUIRE_OPERATOR_APPROVAL_WRITE";
|
|
4949
|
+
function requireOperatorApprovalForWrites(env = process.env) {
|
|
4950
|
+
return env[REQUIRE_OPERATOR_WRITE_ENV] === "1";
|
|
4951
|
+
}
|
|
4952
|
+
function isGatedWriteApproved(lookup, requireOperator) {
|
|
4953
|
+
if (lookup.state !== "granted") {
|
|
4954
|
+
return { allow: false, reason: `approval state is '${lookup.state ?? "unknown"}', not 'granted'` };
|
|
4955
|
+
}
|
|
4956
|
+
if (!requireOperator)
|
|
4957
|
+
return { allow: true };
|
|
4958
|
+
if (lookup.origin !== "operator") {
|
|
4959
|
+
return {
|
|
4960
|
+
allow: false,
|
|
4961
|
+
reason: `approval was recorded with origin='${lookup.origin ?? "unknown"}', not 'operator' \u2014 ` + `an agent-origin decision is not proof an operator tapped ` + `(${REQUIRE_OPERATOR_WRITE_ENV}=1)`
|
|
4962
|
+
};
|
|
4963
|
+
}
|
|
4964
|
+
return { allow: true };
|
|
4965
|
+
}
|
|
4966
|
+
function evaluateGatedWrite(lookup, requireOperator) {
|
|
4967
|
+
const verdict = isGatedWriteApproved(lookup, requireOperator);
|
|
4968
|
+
if (verdict.allow)
|
|
4969
|
+
return { kind: "allow" };
|
|
4970
|
+
if (lookup.state === "granted")
|
|
4971
|
+
return { kind: "block", reason: verdict.reason };
|
|
4972
|
+
return { kind: "wait" };
|
|
4973
|
+
}
|
|
4974
|
+
|
|
4947
4975
|
// src/cli/ms-365-write-pretool.ts
|
|
4948
4976
|
var HOOK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
4949
4977
|
var KERNEL_POLL_INTERVAL_MS = 2000;
|
|
@@ -5350,8 +5378,14 @@ async function approvalLookupByRequest(agentUnit, requestId, approverSet) {
|
|
|
5350
5378
|
});
|
|
5351
5379
|
if (!res.ok)
|
|
5352
5380
|
return null;
|
|
5353
|
-
|
|
5381
|
+
const value = res.value;
|
|
5382
|
+
const o = value.decision?.origin;
|
|
5383
|
+
return {
|
|
5384
|
+
state: value.state,
|
|
5385
|
+
origin: o === "operator" || o === "agent" ? o : undefined
|
|
5386
|
+
};
|
|
5354
5387
|
}
|
|
5388
|
+
var REQUIRE_OPERATOR_ORIGIN = requireOperatorApprovalForWrites();
|
|
5355
5389
|
function fail(reason) {
|
|
5356
5390
|
process.stdout.write(JSON.stringify({
|
|
5357
5391
|
decision: "block",
|
|
@@ -5426,10 +5460,14 @@ async function main() {
|
|
|
5426
5460
|
if (!lookup)
|
|
5427
5461
|
continue;
|
|
5428
5462
|
const state = lookup.state;
|
|
5429
|
-
|
|
5463
|
+
const action = evaluateGatedWrite({ state: state ?? null, origin: lookup.origin }, REQUIRE_OPERATOR_ORIGIN);
|
|
5464
|
+
if (action.kind === "allow") {
|
|
5430
5465
|
recordOutcome(Date.now(), { ...ledgerEntry, outcome: "applied" });
|
|
5431
5466
|
allow();
|
|
5432
5467
|
}
|
|
5468
|
+
if (action.kind === "block") {
|
|
5469
|
+
fail(action.reason);
|
|
5470
|
+
}
|
|
5433
5471
|
if (state === "expired" || state === "drift_revoked") {
|
|
5434
5472
|
const t = Date.now();
|
|
5435
5473
|
const applied = countCurrentBatchApplied(readLedger(t).entries, t);
|
|
@@ -11190,7 +11190,8 @@ var init_protocol = __esm(() => {
|
|
|
11190
11190
|
ttl_expires_at: exports_external.number().nullable(),
|
|
11191
11191
|
last_used_at: exports_external.number().nullable(),
|
|
11192
11192
|
revoked_at: exports_external.number().nullable(),
|
|
11193
|
-
revoke_reason: exports_external.string().nullable()
|
|
11193
|
+
revoke_reason: exports_external.string().nullable(),
|
|
11194
|
+
origin: exports_external.enum(["agent", "operator"]).optional()
|
|
11194
11195
|
});
|
|
11195
11196
|
OkApprovalLookupResponseSchema = exports_external.object({
|
|
11196
11197
|
ok: exports_external.literal(true),
|
|
@@ -11865,7 +11866,7 @@ var AgentMemorySchema = exports_external.object({
|
|
|
11865
11866
|
recall: exports_external.object({
|
|
11866
11867
|
max_memories: exports_external.number().int().min(0).optional().describe("Cap on the number of memories injected into the prompt by " + "auto-recall, regardless of token budget. Plugin default is 12. " + "0 disables the cap (all memories Hindsight returns are injected)."),
|
|
11867
11868
|
cache_ttl_secs: exports_external.number().int().min(0).optional().describe("Per-session recall cache TTL in seconds. When > 0, identical " + "(prompt, bank) within the same session reuse the cached recall " + "result instead of round-tripping to Hindsight. 0 disables. " + "Default is 600 (10 min) for switchroom-managed agents."),
|
|
11868
|
-
min_overlap: exports_external.number().min(0).max(1).optional().describe("Minimum
|
|
11869
|
+
min_overlap: exports_external.number().min(0).max(1).optional().describe("Minimum containment token overlap [0.0\u20131.0] between the user " + "prompt and a memory's text for the memory to be injected. " + "A cheap FLOOR that removes candidates with (near-)zero lexical " + "relationship to the prompt \u2014 NOT a precision control: at the " + "recommended 0.10 the gate is near-passthrough, and the " + "effective precision control is the engine rerank plus the " + "max_memories head-slice. 0.0 disables (default \u2014 current " + "behaviour). Use 0.10; values at or above 0.20 measurably " + "starve recall \u2014 on production replay 0.20 leaves ~41.9% of " + "turns with NO memories at all, re-creating the bug #3541 " + "fixed. Observe the `overlap_dropped` field via " + "`switchroom memory recall-log`."),
|
|
11869
11870
|
types: exports_external.array(exports_external.string()).optional().describe("Hindsight fact types to recall. Switchroom default is " + '["world", "experience", "observation"] \u2014 the synthesized ' + "`observation` tier is on by default. Set to " + '["world", "experience"] to opt out of observation-backed ' + "recall for this agent (or fleet-wide under defaults)."),
|
|
11870
11871
|
additional_banks: exports_external.array(exports_external.string()).optional().describe("Extra Hindsight banks to recall from on every turn, merged into " + "the agent's own bank results \u2014 e.g. a shared operator/household " + "profile bank authored via `switchroom memory profile`. Each is " + "recalled with an 8s timeout and is non-fatal on failure. Stays " + "within the single tenant: all banks are the operator's data, in " + "the operator's Hindsight instance (see the `single-tenant` " + "invariant). Defaults to [] (no extra banks)."),
|
|
11871
11872
|
sender_banks: exports_external.record(exports_external.string(), exports_external.string()).optional().describe("Per-speaker recall routing: a map of Telegram sender \u2192 extra " + "recall bank. When a message arrives, the agent also recalls the " + "speaker's bank (matched by Telegram username \u2014 a leading @ is " + "optional \u2014 or numeric user_id), merged " + "into its own results \u2014 so each trusted user gets their own " + "profile context. Additive recall scoping within the single " + "tenant: never an access boundary (who may drive an agent stays " + "the per-agent user assignment in `access.allowFrom`). Author the " + "banks via `switchroom memory profile`."),
|
|
@@ -12177,9 +12178,16 @@ var ReactionDispatchSchema = exports_external.object({
|
|
|
12177
12178
|
enabled: exports_external.boolean().optional().describe("Master switch for the reaction-dispatch path. Default false \u2014 " + "with no reaction_dispatch block, reactions are persisted (and may " + "feed the `reactions` feedback path) but are NEVER dispatched as " + "event-driven inbound turns."),
|
|
12178
12179
|
emojis: exports_external.array(exports_external.string()).optional().describe('Emoji allowlist that triggers a `<channel event="reaction">` ' + "inbound turn when reacted to any message. Default [] (nothing " + "fires). Cascade mode: REPLACE (not union) \u2014 a layer's list " + "replaces lower layers entirely so an operator can narrow per-agent.")
|
|
12179
12180
|
}).optional();
|
|
12180
|
-
var
|
|
12181
|
+
var releaseBlockFields = {
|
|
12181
12182
|
channel: exports_external.enum(["dev", "rc", "latest"]).optional(),
|
|
12182
12183
|
pin: exports_external.string().regex(/^(sha-[0-9a-f]{7,40}|v\d+\.\d+\.\d+)$/).optional()
|
|
12184
|
+
};
|
|
12185
|
+
var ReleaseBlock = exports_external.object(releaseBlockFields).strict().refine((r) => !(r.channel && r.pin), {
|
|
12186
|
+
message: "release.channel and release.pin are mutually exclusive"
|
|
12187
|
+
});
|
|
12188
|
+
var RootReleaseBlock = exports_external.object({
|
|
12189
|
+
...releaseBlockFields,
|
|
12190
|
+
auto_update: exports_external.boolean().optional().describe("Opt-in unattended fleet auto-update (KEN-131). When true, hostd's " + "release watcher polls the published release version and, on a new " + "release, drives the EXISTING staggered canary rollout " + "(`switchroom rollout --pin vX.Y.Z`) unattended: canary-first, " + "per-agent version assert, durable pin persisted only after the " + "canary is green, abort + operator alert card on canary failure, " + "and compose rollback on a failed apply. Default false \u2014 with it " + "unset/false, behaviour is unchanged.")
|
|
12183
12191
|
}).strict().refine((r) => !(r.channel && r.pin), {
|
|
12184
12192
|
message: "release.channel and release.pin are mutually exclusive"
|
|
12185
12193
|
});
|
|
@@ -12423,6 +12431,7 @@ var AutoReleaseCheckSchema = exports_external.object({
|
|
|
12423
12431
|
enabled: exports_external.boolean().default(false).describe("When true, hostd polls the remote release tag every " + "`interval_minutes` and applies + restarts the fleet when a new " + "release is detected. Default false \u2014 opt-in."),
|
|
12424
12432
|
interval_minutes: exports_external.number().int().min(5).max(1440).default(5).describe("Poll interval in minutes. Floor of 5m matches the agent-config " + "cron rate limit; ceiling of 1440m (24h) is a sanity cap."),
|
|
12425
12433
|
apply_on_detect: exports_external.boolean().default(true).describe("When false, hostd logs `release_detected` but does NOT call " + "update_apply / restart all. Useful for dogfooding the detector " + "without rolling the fleet."),
|
|
12434
|
+
notify_on_detect: exports_external.boolean().default(false).describe("KEN-129 \u2014 operator-in-the-loop update prompt. Only consulted " + "when apply_on_detect is false (auto-apply supersedes notify): " + "a newly detected release posts ONE operator approval card " + "('fleet is behind \u2014 tap to apply') via an admin agent's " + "gateway; Approve runs hostd's update_apply path (fleet-" + "mutation-locked, durable status rows, get_status-pollable). " + "Dedup on release id: the last-notified id persists in " + "~/.switchroom/release-notify-state.json, so a card that " + "reached the operator is never re-posted for the same release."),
|
|
12426
12435
|
image_ref: exports_external.string().default("ghcr.io/switchroom/switchroom-agent:latest").describe("Image reference whose remote digest is compared to the local " + "image digest. Defaults to the agent image's :latest tag, which " + "is the canonical signal that a release has been promoted.")
|
|
12427
12436
|
});
|
|
12428
12437
|
var HostControlConfigSchema = exports_external.object({
|
|
@@ -12465,7 +12474,7 @@ var SwitchroomConfigSchema = exports_external.object({
|
|
|
12465
12474
|
timezone: exports_external.string().regex(TIMEZONE_REGEX, "timezone must be an IANA zone name like 'Australia/Melbourne' or 'UTC'").optional().describe("Global default IANA timezone applied to every agent unless the " + "agent (or its profile) declares its own. See the per-agent " + "timezone field for the full cascade and auto-detection fallback.")
|
|
12466
12475
|
}),
|
|
12467
12476
|
telegram: TelegramConfigSchema,
|
|
12468
|
-
release:
|
|
12477
|
+
release: RootReleaseBlock.optional().describe("Fleet-wide default release-channel pin / pointer for the update " + "flow. Either `channel` (dev|rc|latest) or `pin` (sha-<hex>|v<semver>) " + "\u2014 mutually exclusive. Per-agent `release` REPLACES this entirely " + "(except `auto_update`, which is root-only \u2014 a fleet property read by " + "hostd's release watcher, never per-agent)."),
|
|
12469
12478
|
memory: MemoryBackendConfigSchema.optional(),
|
|
12470
12479
|
hindsight: HindsightConfigSchema.optional().describe("Fleet-singleton Hindsight (memory backend) configuration. Currently " + "just the LLM knob (provider + model) used for retain/reflect/" + "consolidation. Both fields optional; when unset the container falls " + "back to provider=claude-code + the hard-coded HINDSIGHT_DEFAULT_MODEL " + "so nothing changes for operators who don't set it. Read at container " + "launch by startHindsight() (src/setup/hindsight.ts) \u2014 takes effect on " + "the next `switchroom apply` / `memory setup --recreate`."),
|
|
12471
12480
|
vault: VaultConfigSchema.optional(),
|