switchroom 0.17.5 → 0.17.10

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.
Files changed (158) hide show
  1. package/dist/agent-scheduler/index.js +39 -5
  2. package/dist/auth-broker/index.js +386 -208
  3. package/dist/cli/notion-write-pretool.mjs +36 -3
  4. package/dist/cli/switchroom.js +1185 -585
  5. package/dist/host-control/main.js +149 -15
  6. package/dist/vault/approvals/kernel-server.js +141 -56
  7. package/dist/vault/broker/server.js +143 -58
  8. package/package.json +1 -1
  9. package/profiles/_base/start.sh.hbs +50 -6
  10. package/profiles/default/CLAUDE.md +116 -0
  11. package/skills/mental-model-curator/SKILL.md +162 -0
  12. package/telegram-plugin/auth-snapshot-format.ts +50 -2
  13. package/telegram-plugin/bridge/bridge.ts +80 -1
  14. package/telegram-plugin/bridge/ipc-client.ts +19 -0
  15. package/telegram-plugin/bridge/permission-ledger.ts +61 -0
  16. package/telegram-plugin/consolidation-legibility.ts +279 -0
  17. package/telegram-plugin/dist/bridge/bridge.js +85 -1
  18. package/telegram-plugin/dist/gateway/gateway.js +2597 -645
  19. package/telegram-plugin/dist/server.js +86 -2
  20. package/telegram-plugin/feed-heartbeat-climb.ts +206 -0
  21. package/telegram-plugin/gateway/activity-card-store.ts +293 -0
  22. package/telegram-plugin/gateway/auth-command.ts +1 -1
  23. package/telegram-plugin/gateway/gateway.ts +1414 -119
  24. package/telegram-plugin/gateway/inbound-spool.ts +22 -0
  25. package/telegram-plugin/gateway/mental-model-propose-card.ts +69 -0
  26. package/telegram-plugin/gateway/mental-model-propose-diff.ts +171 -0
  27. package/telegram-plugin/gateway/mental-model-propose-inbound-builders.ts +147 -0
  28. package/telegram-plugin/gateway/mental-model-propose-resolve.ts +201 -0
  29. package/telegram-plugin/gateway/missed-approvals-card.ts +161 -0
  30. package/telegram-plugin/gateway/missed-approvals-store.ts +167 -0
  31. package/telegram-plugin/gateway/permission-rearm.ts +115 -0
  32. package/telegram-plugin/gateway/scoped-grant-store.ts +89 -0
  33. package/telegram-plugin/memory-legibility.ts +217 -0
  34. package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  35. package/telegram-plugin/scoped-approval.ts +59 -0
  36. package/telegram-plugin/silent-end.ts +78 -0
  37. package/telegram-plugin/subagent-watcher.ts +60 -6
  38. package/telegram-plugin/tests/activity-card-store.test.ts +436 -0
  39. package/telegram-plugin/tests/activity-card-wiring.test.ts +88 -0
  40. package/telegram-plugin/tests/auth-snapshot-format.test.ts +75 -0
  41. package/telegram-plugin/tests/consolidation-legibility.test.ts +224 -0
  42. package/telegram-plugin/tests/emission-authority-facade.test.ts +25 -10
  43. package/telegram-plugin/tests/feed-heartbeat-liveness-open.test.ts +33 -9
  44. package/telegram-plugin/tests/gateway-boot-marker-clear.test.ts +3 -3
  45. package/telegram-plugin/tests/inbound-spool.test.ts +105 -0
  46. package/telegram-plugin/tests/memory-legibility.test.ts +216 -0
  47. package/telegram-plugin/tests/mental-model-propose-callback-gate.test.ts +67 -0
  48. package/telegram-plugin/tests/mental-model-propose-card.test.ts +56 -0
  49. package/telegram-plugin/tests/mental-model-propose-diff.test.ts +201 -0
  50. package/telegram-plugin/tests/mental-model-propose-inbound-builders.test.ts +68 -0
  51. package/telegram-plugin/tests/mental-model-propose-resolve.test.ts +157 -0
  52. package/telegram-plugin/tests/missed-approvals-card.test.ts +145 -0
  53. package/telegram-plugin/tests/missed-approvals-store.test.ts +147 -0
  54. package/telegram-plugin/tests/missed-approvals-wiring.test.ts +89 -0
  55. package/telegram-plugin/tests/permission-ledger.test.ts +166 -0
  56. package/telegram-plugin/tests/permission-no-repeat-wiring.test.ts +1 -1
  57. package/telegram-plugin/tests/permission-rearm-wiring.test.ts +175 -0
  58. package/telegram-plugin/tests/permission-rearm.test.ts +126 -0
  59. package/telegram-plugin/tests/scoped-grant-persist.test.ts +223 -0
  60. package/telegram-plugin/tests/silent-end-transport.test.ts +290 -0
  61. package/telegram-plugin/tests/silent-turn-climb-transport.test.ts +337 -0
  62. package/telegram-plugin/tests/subagent-watcher.test.ts +139 -0
  63. package/telegram-plugin/tests/worktree-watch-cwds.test.ts +103 -0
  64. package/telegram-plugin/uat/assertions.ts +88 -4
  65. package/telegram-plugin/uat/feed-matcher.test.ts +69 -0
  66. package/telegram-plugin/uat/scenarios/fuzz-liveness-climb-dm.test.ts +155 -0
  67. package/telegram-plugin/uat/scenarios/jtbd-directive-capture-nudge-dm.test.ts +185 -0
  68. package/telegram-plugin/uat/scenarios/jtbd-liveness-climb-channel.test.ts +192 -0
  69. package/telegram-plugin/uat/scenarios/jtbd-liveness-climb-dm.test.ts +220 -0
  70. package/telegram-plugin/uat/scenarios/jtbd-liveness-narration-channel.test.ts +137 -0
  71. package/telegram-plugin/uat/scenarios/jtbd-liveness-narration-dm.test.ts +148 -0
  72. package/telegram-plugin/uat/scenarios/jtbd-memory-legibility-channel.test.ts +66 -0
  73. package/telegram-plugin/uat/scenarios/jtbd-memory-legibility-dm.test.ts +61 -0
  74. package/telegram-plugin/uat/scenarios/silent-end-recovery-channel.test.ts +136 -0
  75. package/telegram-plugin/uat/scenarios/silent-end-recovery-dm.test.ts +24 -2
  76. package/telegram-plugin/worktree-watch-cwds.ts +60 -0
  77. package/vendor/hindsight-memory/hooks/hooks.json +9 -0
  78. package/vendor/hindsight-memory/scripts/__pycache__/directive_verify.cpython-313.pyc +0 -0
  79. package/vendor/hindsight-memory/scripts/__pycache__/drain_pending.cpython-313.pyc +0 -0
  80. package/vendor/hindsight-memory/scripts/__pycache__/recall.cpython-313.pyc +0 -0
  81. package/vendor/hindsight-memory/scripts/__pycache__/retain.cpython-313.pyc +0 -0
  82. package/vendor/hindsight-memory/scripts/__pycache__/session_end.cpython-313.pyc +0 -0
  83. package/vendor/hindsight-memory/scripts/directive_verify.py +445 -0
  84. package/vendor/hindsight-memory/scripts/lib/__pycache__/__init__.cpython-313.pyc +0 -0
  85. package/vendor/hindsight-memory/scripts/lib/__pycache__/bank.cpython-313.pyc +0 -0
  86. package/vendor/hindsight-memory/scripts/lib/__pycache__/client.cpython-313.pyc +0 -0
  87. package/vendor/hindsight-memory/scripts/lib/__pycache__/config.cpython-313.pyc +0 -0
  88. package/vendor/hindsight-memory/scripts/lib/__pycache__/content.cpython-313.pyc +0 -0
  89. package/vendor/hindsight-memory/scripts/lib/__pycache__/daemon.cpython-313.pyc +0 -0
  90. package/vendor/hindsight-memory/scripts/lib/__pycache__/directives.cpython-313.pyc +0 -0
  91. package/vendor/hindsight-memory/scripts/lib/__pycache__/gateway_ipc.cpython-313.pyc +0 -0
  92. package/vendor/hindsight-memory/scripts/lib/__pycache__/llm.cpython-313.pyc +0 -0
  93. package/vendor/hindsight-memory/scripts/lib/__pycache__/pending.cpython-313.pyc +0 -0
  94. package/vendor/hindsight-memory/scripts/lib/__pycache__/state.cpython-313.pyc +0 -0
  95. package/vendor/hindsight-memory/scripts/lib/__pycache__/switchroom_envelope.cpython-313.pyc +0 -0
  96. package/vendor/hindsight-memory/scripts/lib/config.py +37 -0
  97. package/vendor/hindsight-memory/scripts/lib/directives.py +88 -0
  98. package/vendor/hindsight-memory/scripts/lib/switchroom_envelope.py +77 -0
  99. package/vendor/hindsight-memory/scripts/recall.py +153 -4
  100. package/vendor/hindsight-memory/scripts/retain.py +17 -0
  101. package/vendor/hindsight-memory/scripts/setup_hooks.py +9 -0
  102. package/vendor/hindsight-memory/scripts/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  103. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_config_client_casts.cpython-313-pytest-9.1.1.pyc +0 -0
  104. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_config_client_casts.cpython-313.pyc +0 -0
  105. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_capture_nudge.cpython-313-pytest-9.1.1.pyc +0 -0
  106. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_capture_nudge.cpython-313.pyc +0 -0
  107. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_verify.cpython-313-pytest-9.1.1.pyc +0 -0
  108. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_verify.cpython-313.pyc +0 -0
  109. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directives.cpython-313-pytest-9.1.1.pyc +0 -0
  110. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directives.cpython-313.pyc +0 -0
  111. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_gateway_ipc.cpython-313-pytest-9.1.1.pyc +0 -0
  112. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_gateway_ipc.cpython-313.pyc +0 -0
  113. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_context_slice.cpython-313-pytest-9.1.1.pyc +0 -0
  114. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_context_slice.cpython-313.pyc +0 -0
  115. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_integration.cpython-313-pytest-9.1.1.pyc +0 -0
  116. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_integration.cpython-313.pyc +0 -0
  117. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_tag_filters.cpython-313-pytest-9.1.1.pyc +0 -0
  118. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_tag_filters.cpython-313.pyc +0 -0
  119. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_topic_filter.cpython-313-pytest-9.1.1.pyc +0 -0
  120. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_topic_filter.cpython-313.pyc +0 -0
  121. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_trivial_skip.cpython-313-pytest-9.1.1.pyc +0 -0
  122. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_trivial_skip.cpython-313.pyc +0 -0
  123. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_retain_window.cpython-313-pytest-9.1.1.pyc +0 -0
  124. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_retain_window.cpython-313.pyc +0 -0
  125. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_sender_routing.cpython-313-pytest-9.1.1.pyc +0 -0
  126. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_sender_routing.cpython-313.pyc +0 -0
  127. package/vendor/hindsight-memory/scripts/tests/__pycache__/test_switchroom_envelope.cpython-313-pytest-9.1.1.pyc +0 -0
  128. package/vendor/hindsight-memory/scripts/tests/test_directive_capture_nudge.py +185 -0
  129. package/vendor/hindsight-memory/scripts/tests/test_directive_verify.py +516 -0
  130. package/vendor/hindsight-memory/scripts/tests/test_directives.py +49 -0
  131. package/vendor/hindsight-memory/scripts/tests/test_retain_window.py +66 -1
  132. package/vendor/hindsight-memory/scripts/tests/test_switchroom_envelope.py +69 -0
  133. package/vendor/hindsight-memory/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  134. package/vendor/hindsight-memory/tests/__pycache__/conftest.cpython-313-pytest-9.1.1.pyc +0 -0
  135. package/vendor/hindsight-memory/tests/__pycache__/test_bank.cpython-313-pytest-9.1.1.pyc +0 -0
  136. package/vendor/hindsight-memory/tests/__pycache__/test_bank.cpython-313.pyc +0 -0
  137. package/vendor/hindsight-memory/tests/__pycache__/test_client.cpython-313-pytest-9.1.1.pyc +0 -0
  138. package/vendor/hindsight-memory/tests/__pycache__/test_client.cpython-313.pyc +0 -0
  139. package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313-pytest-9.0.3.pyc +0 -0
  140. package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313-pytest-9.1.1.pyc +0 -0
  141. package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313.pyc +0 -0
  142. package/vendor/hindsight-memory/tests/__pycache__/test_content.cpython-313-pytest-9.1.1.pyc +0 -0
  143. package/vendor/hindsight-memory/tests/__pycache__/test_content.cpython-313.pyc +0 -0
  144. package/vendor/hindsight-memory/tests/__pycache__/test_drain_pending.cpython-313-pytest-9.1.1.pyc +0 -0
  145. package/vendor/hindsight-memory/tests/__pycache__/test_drain_pending.cpython-313.pyc +0 -0
  146. package/vendor/hindsight-memory/tests/__pycache__/test_hooks.cpython-313-pytest-9.1.1.pyc +0 -0
  147. package/vendor/hindsight-memory/tests/__pycache__/test_hooks.cpython-313.pyc +0 -0
  148. package/vendor/hindsight-memory/tests/__pycache__/test_manifest.cpython-313-pytest-9.1.1.pyc +0 -0
  149. package/vendor/hindsight-memory/tests/__pycache__/test_manifest.cpython-313.pyc +0 -0
  150. package/vendor/hindsight-memory/tests/__pycache__/test_pending.cpython-313-pytest-9.1.1.pyc +0 -0
  151. package/vendor/hindsight-memory/tests/__pycache__/test_pending.cpython-313.pyc +0 -0
  152. package/vendor/hindsight-memory/tests/__pycache__/test_recall_exit_codes.cpython-313-pytest-9.1.1.pyc +0 -0
  153. package/vendor/hindsight-memory/tests/__pycache__/test_recall_exit_codes.cpython-313.pyc +0 -0
  154. package/vendor/hindsight-memory/tests/__pycache__/test_session_end_pending.cpython-313-pytest-9.1.1.pyc +0 -0
  155. package/vendor/hindsight-memory/tests/__pycache__/test_session_end_pending.cpython-313.pyc +0 -0
  156. package/vendor/hindsight-memory/tests/__pycache__/test_state.cpython-313-pytest-9.1.1.pyc +0 -0
  157. package/vendor/hindsight-memory/tests/__pycache__/test_state.cpython-313.pyc +0 -0
  158. package/vendor/hindsight-memory/tests/test_recall_exit_codes.py +49 -2
@@ -11070,8 +11070,35 @@ var AgentMemorySchema = exports_external.object({
11070
11070
  auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
11071
11071
  file: exports_external.boolean().default(true).describe("Maintain a curated workspace MEMORY.md file (seeded once, " + "auto-loaded every turn). Set false for hindsight-only memory: " + "the file is not seeded or re-created, so once migrated into " + "Hindsight and deleted it stays gone. Recall + directives carry " + "the memory instead. Cascade: override (per-agent wins over default)."),
11072
11072
  isolation: exports_external.enum(["default", "strict"]).default("default").describe("strict = never shared cross-agent, default = eligible for reflect"),
11073
- bank_mission: exports_external.string().optional().describe("Bank-level mission statement used during recall to contextualize results"),
11074
- retain_mission: exports_external.string().optional().describe("Instructions for the fact extraction LLM during retain"),
11073
+ bank_mission: exports_external.string().optional().describe("Bank-level mission statement used during recall to contextualize " + "results. NOTE: this is an alias for the Hindsight engine's " + "`reflect_mission` field (verified live: switchroom's bank_mission " + "lands in `config.reflect_mission`). Prefer `reflect_mission` going " + "forward; `bank_mission` is retained for back-compat. If both are " + "set, `reflect_mission` wins. Cascade: override."),
11074
+ reflect_mission: exports_external.string().optional().describe("Mission/context steering Hindsight Reflect operations (the bank's " + "'who am I / what matters' framing applied during recall). The " + "engine-accurate name for what `bank_mission` sets. Cascade: override."),
11075
+ retain_mission: exports_external.string().optional().describe("Instructions for the fact extraction LLM during retain. Cascade: override."),
11076
+ mental_models: exports_external.array(exports_external.object({
11077
+ name: exports_external.string().min(1).describe("Stable model name (identity key for idempotent ensure). Two " + "declarations with the same name in one agent are rejected."),
11078
+ source_query: exports_external.string().min(1).max(2000).describe("The reflection query the model answers, semantically " + "refreshed from the bank's content. Capped at 2000 chars — a " + "standing reflection query, not a document; the ceiling also " + "bounds what an agent-proposed model can smuggle past the " + "operator approval card."),
11079
+ refresh_after_consolidation: exports_external.boolean().optional().describe("Refresh this model after each consolidation. Defaults OFF — " + "refresh adds bounded background model-spend + timeout risk " + "(RFC Phase 5), so it is opt-in per model."),
11080
+ max_tokens: exports_external.number().int().positive().max(8192).optional().describe("Cap on the synthesized model's token size. Upper-bounded at " + "8192 — a mental model is a standing summary, not a corpus; the " + "ceiling also caps what an agent-proposed model can request.")
11081
+ })).superRefine((models, ctx) => {
11082
+ const seen = new Set;
11083
+ for (let i = 0;i < models.length; i++) {
11084
+ const key = models[i].name;
11085
+ if (seen.has(key)) {
11086
+ ctx.addIssue({
11087
+ code: exports_external.ZodIssueCode.custom,
11088
+ path: [i, "name"],
11089
+ message: `duplicate mental_models name "${key}" — model names must be ` + `unique within an agent (they are the idempotent-ensure key)`
11090
+ });
11091
+ }
11092
+ seen.add(key);
11093
+ }
11094
+ }).optional().describe("Operator-declared, per-specialist Hindsight mental models (RFC " + "Phase 5). Named, opt-in curated reflections this agent's bank should " + "carry — e.g. a coach's 'training-plan-state' or a lawyer's " + "'open-matters'. Ensured idempotently at scaffold/reconcile: NOTHING " + "is created unless declared here (zero declarations = zero models, " + "matching post-#2447 behaviour), and no fixed identity model is " + "reintroduced — 'who the user is' stays owned by dedicated profile " + "banks (users.*.profile_bank), never a per-agent model. Per-agent " + "ONLY: intentionally not accepted at the defaults/profile tier, so a " + "model can never be fleet-seeded — each specialist opts in on its own " + "(the invariant-clean inverse of the retired blind auto-seeding)."),
11095
+ observations_mission: exports_external.string().optional().describe("Steers what the observation-consolidation LLM synthesises from raw " + "facts (the higher-order 'what patterns matter' lens). Cascade: override."),
11096
+ disposition: exports_external.object({
11097
+ skepticism: exports_external.number().int().min(1).max(5).optional().describe("How much the bank doubts unverified claims (1-5; engine default 3)."),
11098
+ literalism: exports_external.number().int().min(1).max(5).optional().describe("How literally the bank reads statements vs inferring intent (1-5; engine default 3)."),
11099
+ empathy: exports_external.number().int().min(1).max(5).optional().describe("How much the bank weights emotional/relational context (1-5; engine default 3).")
11100
+ }).optional().describe("Personality traits (1-5 each) steering how this bank frames recall, " + "reflect, and observation synthesis — a coach leans empathy-high, a " + "lawyer/analyst leans skepticism/literalism-high. Maps to the engine's " + "flat `disposition_skepticism`/`_literalism`/`_empathy` fields. " + "Cascade: per-key merge (an agent overrides individual traits and " + "inherits the rest, matching `recall`)."),
11101
+ directive_capture_nudge: exports_external.boolean().optional().describe("Deterministic directive-capture nudge (issue #2848 Stage B). When " + "on (switchroom default true — Stage A measured a ~55% miss rate on " + "durable corrections), the auto-recall hook regex-detects correction " + '/ standing-rule-shaped inbound ("always/never …", "from now on …", ' + `"stop doing …", a stated preference, "that's wrong, it's …") and ` + "appends a terse advisory to the turn's context telling the model to " + "persist the rule with mcp__hindsight__create_directive if it IS " + "durable. Detection is pure regex — the model does the judgment " + "in-session and calls create_directive itself (no model callsite, no " + "silent hook-side write). Set false to disable per-agent. " + "Cascade: override (per-agent wins over default)."),
11075
11102
  recall: exports_external.object({
11076
11103
  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)."),
11077
11104
  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."),
@@ -11336,6 +11363,7 @@ var profileFields = {
11336
11363
  auto_recall: exports_external.boolean().optional(),
11337
11364
  file: exports_external.boolean().optional(),
11338
11365
  isolation: exports_external.enum(["default", "strict"]).optional(),
11366
+ directive_capture_nudge: exports_external.boolean().optional(),
11339
11367
  recall: exports_external.object({
11340
11368
  max_memories: exports_external.number().int().min(0).optional(),
11341
11369
  cache_ttl_secs: exports_external.number().int().min(0).optional(),
@@ -11591,7 +11619,7 @@ var SwitchroomConfigSchema = exports_external.object({
11591
11619
  name: exports_external.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9_-]*$/, {
11592
11620
  message: "Consumer name must be a path-safe slug (letters, digits, underscore, hyphen)"
11593
11621
  }).describe("Socket-path identity; binds at /run/switchroom/auth-broker/<name>/sock"),
11594
- account: exports_external.string().min(1).describe("Pinned account label for this consumer. `get-credentials` returns " + "this account's credentials; `mark-exhausted` from this consumer " + "only affects this account."),
11622
+ account: exports_external.string().min(1).optional().describe("Optional pinned account label for this consumer. When set, " + "`get-credentials` serves this account (with automatic failover " + "while it is quota-exhausted) and `mark-exhausted` from this " + "consumer attributes to it — use a pin for quota isolation. " + "When omitted, the consumer follows the fleet `auth.active` " + "exactly like an agent: same account swaps, same failover."),
11595
11623
  uid: exports_external.number().int().nonnegative().optional().describe("Optional UID to chown the consumer socket to (defaults to 0 = root, " + "suitable for sibling containers running as root)."),
11596
11624
  mirror_dir: exports_external.string().optional().describe("Optional host-side directory path. When set, the broker actively " + "writes the consumer's effective-account `.credentials.json` mirror " + "here — in addition to serving creds on demand via `get-credentials`. " + "Use this to eliminate the pull-latency gap: without a mirror the " + "consumer only gets failover creds at its next scheduled re-fetch " + "(up to 30 min). With a mirror the broker pushes failover creds " + "immediately when it detects exhaustion (consumer-quota-sensor tick, " + "or a mark-exhausted RPC on the pinned account). The directory must " + "be accessible to the broker container (bind-mounted from the host) " + "and to the consumer container; the broker writes " + "`<mirror_dir>/.credentials.json` atomically. Chown is attempted to " + "`uid` (default 0) — swallowed when CAP_CHOWN is absent.")
11597
11625
  })).optional().describe("Non-agent peers that hold a broker socket (RFC H §4.8). Each gets " + "its own `/run/switchroom/auth-broker/<name>/sock` chowned to its UID. " + "Consumers cannot be admins; a consumer name that collides with an " + "agent (whether that agent has `admin: true` or not) is a config " + "error caught at schema validation."),
@@ -11979,6 +12007,11 @@ function mergeAgentConfig(defaultsIn, agentIn) {
11979
12007
  continue;
11980
12008
  if (k === "recall" && base.recall && typeof v === "object" && v !== null && !Array.isArray(v)) {
11981
12009
  combined[k] = { ...base.recall, ...v };
12010
+ } else if (k === "disposition" && base.disposition && typeof v === "object" && v !== null && !Array.isArray(v)) {
12011
+ combined[k] = {
12012
+ ...base.disposition,
12013
+ ...v
12014
+ };
11982
12015
  } else {
11983
12016
  combined[k] = v;
11984
12017
  }
@@ -12426,7 +12459,8 @@ function dispatchAsInbound(entry, options, dispatcher) {
12426
12459
  schedule_index: String(entry.scheduleIndex),
12427
12460
  prompt_key: entry.promptKey,
12428
12461
  ...options.session === "cron" ? { session: "cron" } : {},
12429
- ...options.session === "cron" && options.model ? { model: options.model } : {}
12462
+ ...options.session === "cron" && options.model ? { model: options.model } : {},
12463
+ ...options.replayFireMs !== undefined ? { replay_fire_ms: String(options.replayFireMs) } : {}
12430
12464
  }
12431
12465
  };
12432
12466
  const delivered = dispatcher.sendToAgent(entry.agent, message);
@@ -14962,7 +14996,7 @@ async function main() {
14962
14996
  continue;
14963
14997
  }
14964
14998
  const threadId = resolveEntryThreadId(m.entry, channel);
14965
- const result = dispatchAsInbound(m.entry, { chatId: channel.chatId, threadId }, dispatcher);
14999
+ const result = dispatchAsInbound(m.entry, { chatId: channel.chatId, threadId, replayFireMs: m.expectedFireMs }, dispatcher);
14966
15000
  sink.recordFire({
14967
15001
  agent: m.entry.agent,
14968
15002
  scheduleIndex: m.entry.scheduleIndex,