switchroom 0.18.3 → 0.18.7
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 +3 -1
- package/dist/auth-broker/index.js +3 -1
- package/dist/cli/autoaccept-poll.js +140 -33
- package/dist/cli/notion-write-pretool.mjs +3 -1
- package/dist/cli/switchroom.js +386 -128
- package/dist/host-control/main.js +4 -2
- package/dist/vault/approvals/kernel-server.js +3 -1
- package/dist/vault/broker/server.js +38 -8
- package/package.json +3 -3
- package/profiles/_base/cron-session.sh.hbs +55 -16
- package/profiles/_base/start.sh.hbs +35 -16
- package/profiles/default/CLAUDE.md.hbs +1 -1
- package/skills/switchroom-release/SKILL.md +78 -0
- package/telegram-plugin/auth-snapshot-format.ts +15 -1
- package/telegram-plugin/dist/bridge/bridge.js +22 -0
- package/telegram-plugin/dist/gateway/gateway.js +2852 -1032
- package/telegram-plugin/dist/server.js +24 -0
- package/telegram-plugin/gateway/always-allow-persist-queue.ts +438 -0
- package/telegram-plugin/gateway/approval-timeout-inbound-builders.ts +150 -0
- package/telegram-plugin/gateway/clean-shutdown-marker.ts +68 -20
- package/telegram-plugin/gateway/gateway.ts +1331 -151
- package/telegram-plugin/gateway/inbound-spool.ts +2 -1
- package/telegram-plugin/gateway/inject-handler.test.ts +19 -0
- package/telegram-plugin/gateway/inject-handler.ts +17 -0
- package/telegram-plugin/gateway/ipc-protocol.ts +44 -2
- package/telegram-plugin/gateway/ipc-server.ts +40 -0
- package/telegram-plugin/gateway/model-command.ts +212 -51
- package/telegram-plugin/gateway/pending-card-expiry.ts +98 -0
- package/telegram-plugin/gateway/pending-card-store.ts +173 -0
- package/telegram-plugin/gateway/pending-inbound-buffer.ts +12 -2
- package/telegram-plugin/gateway/resolve-person.ts +304 -0
- package/telegram-plugin/gateway/resume-inbound-builder.ts +240 -2
- package/telegram-plugin/gateway/session-model-source.ts +73 -0
- package/telegram-plugin/gateway/unhandled-rejection-policy.ts +21 -1
- package/telegram-plugin/gateway/worker-feed-dispatch.ts +24 -1
- package/telegram-plugin/hooks/silent-end-scan.mjs +164 -40
- package/telegram-plugin/hooks/subagent-tracker-pretool.mjs +30 -7
- package/telegram-plugin/model-label.ts +69 -0
- package/telegram-plugin/operator-events.ts +45 -0
- package/telegram-plugin/pending-work-progress.ts +42 -7
- package/telegram-plugin/permission-diff.ts +128 -0
- package/telegram-plugin/quota-bar-format.ts +360 -0
- package/telegram-plugin/registry/subagents-schema.ts +80 -1
- package/telegram-plugin/registry/subagents.test.ts +90 -0
- package/telegram-plugin/session-tail.ts +28 -0
- package/telegram-plugin/silent-end.ts +49 -4
- package/telegram-plugin/subagent-watcher.ts +249 -46
- package/telegram-plugin/tests/always-allow-persist-queue.test.ts +529 -0
- package/telegram-plugin/tests/approval-timeout-inbound-builders.test.ts +94 -0
- package/telegram-plugin/tests/auth-snapshot-format.test.ts +21 -0
- package/telegram-plugin/tests/button-tap-turn-gated.test.ts +263 -0
- package/telegram-plugin/tests/gateway-boot-marker-clear.test.ts +3 -3
- package/telegram-plugin/tests/gateway-clean-shutdown-marker.test.ts +85 -27
- package/telegram-plugin/tests/gateway-session-model-relaunch.test.ts +4 -2
- package/telegram-plugin/tests/ipc-server-query-pending-permission.test.ts +157 -0
- package/telegram-plugin/tests/mental-model-propose-callback-gate.test.ts +8 -5
- package/telegram-plugin/tests/model-command.test.ts +202 -42
- package/telegram-plugin/tests/model-label.test.ts +64 -0
- package/telegram-plugin/tests/operator-events.test.ts +17 -0
- package/telegram-plugin/tests/pending-card-durability-wiring.test.ts +202 -0
- package/telegram-plugin/tests/pending-card-expiry.test.ts +190 -0
- package/telegram-plugin/tests/pending-card-store.test.ts +173 -0
- package/telegram-plugin/tests/pending-work-progress.test.ts +116 -3
- package/telegram-plugin/tests/permission-diff.test.ts +111 -0
- package/telegram-plugin/tests/quota-bar-format.test.ts +444 -0
- package/telegram-plugin/tests/resolve-person.test.ts +290 -0
- package/telegram-plugin/tests/resume-inbound-builder.test.ts +286 -0
- package/telegram-plugin/tests/session-model-source.test.ts +67 -0
- package/telegram-plugin/tests/session-tail.test.ts +64 -0
- package/telegram-plugin/tests/silent-end-interrupt-stop-integration.test.ts +53 -0
- package/telegram-plugin/tests/silent-end-interrupt-stop-scan.test.ts +138 -0
- package/telegram-plugin/tests/silent-end.test.ts +46 -1
- package/telegram-plugin/tests/subagent-tracker-hooks.test.ts +39 -0
- package/telegram-plugin/tests/subagent-watcher-boot-promotion-replay.test.ts +107 -4
- package/telegram-plugin/tests/subagent-watcher-handback-gaps.test.ts +42 -4
- package/telegram-plugin/tests/subagent-watcher-parent-turn-key.test.ts +47 -0
- package/telegram-plugin/tests/subagent-watcher-terminated-ids-cap.test.ts +150 -0
- package/telegram-plugin/tests/subagent-watcher.test.ts +115 -0
- package/telegram-plugin/tests/tool-activity-summary.test.ts +37 -0
- package/telegram-plugin/tests/typing-wrap.test.ts +23 -0
- package/telegram-plugin/tests/unhandled-rejection-policy.test.ts +19 -0
- package/telegram-plugin/tests/worker-activity-feed.test.ts +108 -0
- package/telegram-plugin/tests/worker-feed-dispatch.test.ts +126 -0
- package/telegram-plugin/tool-activity-summary.ts +22 -2
- package/telegram-plugin/typing-wrap.ts +72 -25
- package/telegram-plugin/worker-activity-feed.ts +229 -15
- package/profiles/default/CLAUDE.md +0 -116
- package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/vendor/hindsight-memory/scripts/__pycache__/directive_verify.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/drain_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/recall.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/retain.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/__pycache__/session_end.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/bank.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/client.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/config.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/content.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/daemon.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/directives.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/gateway_ipc.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/llm.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/state.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/lib/__pycache__/switchroom_envelope.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/__init__.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_config_client_casts.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_config_client_casts.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_capture_nudge.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_capture_nudge.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_verify.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directive_verify.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directives.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_directives.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_gateway_ipc.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_gateway_ipc.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_context_slice.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_context_slice.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_integration.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_integration.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_tag_filters.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_tag_filters.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_topic_filter.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_topic_filter.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_trivial_skip.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_recall_trivial_skip.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_retain_window.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_retain_window.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_sender_routing.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_sender_routing.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/scripts/tests/__pycache__/test_switchroom_envelope.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/conftest.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_bank.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_bank.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_client.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_client.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313-pytest-9.0.3.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_config.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_content.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_content.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_drain_pending.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_drain_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_hooks.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_hooks.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_manifest.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_manifest.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_pending.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_recall_exit_codes.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_recall_exit_codes.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_session_end_pending.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_session_end_pending.cpython-313.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_state.cpython-313-pytest-9.1.1.pyc +0 -0
- package/vendor/hindsight-memory/tests/__pycache__/test_state.cpython-313.pyc +0 -0
|
@@ -182,44 +182,92 @@ export function resolveShutdownMarker(
|
|
|
182
182
|
return { ts: now, signal, reason: EXTERNAL_RESTART_FALLBACK_REASON };
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
/**
|
|
186
|
+
* Boot-resume policy, from `session_continuity.boot_resume`
|
|
187
|
+
* (SWITCHROOM_BOOT_RESUME env). Governs whether a clean/deliberate
|
|
188
|
+
* shutdown suppresses auto-resume of a genuinely in-flight turn:
|
|
189
|
+
*
|
|
190
|
+
* - 'always' — always resume interrupted work, even after a clean
|
|
191
|
+
* shutdown (equivalent to SWITCHROOM_BOOT_RESUME_ALWAYS=1).
|
|
192
|
+
* - 'in-flight' — (DEFAULT) resume work that was genuinely in flight at
|
|
193
|
+
* shutdown, even after a deliberate/operator restart. This
|
|
194
|
+
* is the whole point: a sanctioned restart that lands mid-
|
|
195
|
+
* turn must not silently drop the work. The boot-resume
|
|
196
|
+
* block only runs when a pending interrupted turn exists —
|
|
197
|
+
* i.e. when there IS in-flight work — so in this mode the
|
|
198
|
+
* clean-shutdown gate never swallows real work.
|
|
199
|
+
* - 'never' — the #2585 quota-saving posture: suppress AUTO-resume on a
|
|
200
|
+
* fresh clean shutdown. NOTE the caller still delivers a
|
|
201
|
+
* passive REPORT inbound in this case (silence is never
|
|
202
|
+
* acceptable when work was in flight); this flag only
|
|
203
|
+
* downgrades an active resume to a passive notice.
|
|
204
|
+
*/
|
|
205
|
+
export type BootResumeMode = 'always' | 'in-flight' | 'never';
|
|
206
|
+
|
|
207
|
+
/** The product default (2026-07): resume genuinely in-flight work even
|
|
208
|
+
* after a deliberate restart. Restores the pre-#2585 recovery of real
|
|
209
|
+
* work while keeping #2585's "don't replay abandoned work" intent for the
|
|
210
|
+
* no-in-flight-turn case (which is a no-op — the boot block never runs
|
|
211
|
+
* without a pending turn). */
|
|
212
|
+
export const DEFAULT_BOOT_RESUME_MODE: BootResumeMode = 'in-flight';
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Parse the SWITCHROOM_BOOT_RESUME env value into a BootResumeMode. Unknown
|
|
216
|
+
* / empty / undefined falls back to the default ('in-flight'). Case- and
|
|
217
|
+
* separator-insensitive ("in_flight", "inflight", "IN-FLIGHT" all map to
|
|
218
|
+
* 'in-flight') so a config typo degrades to the safe default rather than a
|
|
219
|
+
* silent misparse.
|
|
220
|
+
*/
|
|
221
|
+
export function parseBootResumeMode(raw: string | undefined | null): BootResumeMode {
|
|
222
|
+
if (typeof raw !== 'string') return DEFAULT_BOOT_RESUME_MODE;
|
|
223
|
+
const norm = raw.trim().toLowerCase().replace(/[_\s]+/g, '-');
|
|
224
|
+
if (norm === 'always') return 'always';
|
|
225
|
+
if (norm === 'never') return 'never';
|
|
226
|
+
if (norm === 'in-flight' || norm === 'inflight') return 'in-flight';
|
|
227
|
+
return DEFAULT_BOOT_RESUME_MODE;
|
|
228
|
+
}
|
|
229
|
+
|
|
185
230
|
/**
|
|
186
231
|
* Pure decision: should the boot-resume path SUPPRESS the active
|
|
187
232
|
* resume_interrupted inbound because the prior shutdown was clean?
|
|
188
233
|
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
234
|
+
* This block is only ever consulted when a pending INTERRUPTED turn
|
|
235
|
+
* exists — i.e. there IS genuinely in-flight work to recover. The product
|
|
236
|
+
* decision (2026-07, superseding #2585) is that a deliberate restart must
|
|
237
|
+
* NOT silently drop that work, so the DEFAULT mode ('in-flight') never
|
|
238
|
+
* suppresses. #2585's quota-saving posture survives only as the opt-in
|
|
239
|
+
* 'never' mode — and even then the caller downgrades to a passive REPORT
|
|
240
|
+
* rather than going silent.
|
|
195
241
|
*
|
|
196
|
-
* Returns true ONLY when:
|
|
242
|
+
* Returns true (suppress the ACTIVE resume) ONLY when:
|
|
243
|
+
* - mode === 'never', AND
|
|
197
244
|
* - a clean marker is present, AND
|
|
198
245
|
* - the marker is younger than maxAgeMs (default 60s), AND
|
|
199
|
-
* -
|
|
200
|
-
*
|
|
201
|
-
* Returns false when:
|
|
202
|
-
* - no marker (crash / OOM / unexpected kill — resume normally), OR
|
|
203
|
-
* - marker is stale (>= maxAgeMs — something stalled; treat as crash), OR
|
|
204
|
-
* - forceAlways is true (the escape hatch is active).
|
|
246
|
+
* - neither forceAlways nor mode 'always' is set.
|
|
205
247
|
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
248
|
+
* Returns false (resume normally) when:
|
|
249
|
+
* - mode is 'always' or 'in-flight' (the default), OR
|
|
250
|
+
* - forceAlways is true (SWITCHROOM_BOOT_RESUME_ALWAYS=1 escape hatch), OR
|
|
251
|
+
* - no marker (crash / OOM / unexpected kill), OR
|
|
252
|
+
* - marker is stale (>= maxAgeMs — clean shutdown that stalled mid-drain;
|
|
253
|
+
* preserves the ">60s slow-rollout already resumes" behaviour).
|
|
210
254
|
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
255
|
+
* forceAlways maps to SWITCHROOM_BOOT_RESUME_ALWAYS=1 and is preserved for
|
|
256
|
+
* back-compat: it forces resume regardless of mode. Passed in as a parsed
|
|
257
|
+
* boolean + mode so this function stays pure and testable.
|
|
213
258
|
*/
|
|
214
259
|
export function shouldSuppressBootResume(
|
|
215
260
|
marker: CleanShutdownMarker | null,
|
|
216
261
|
now: number,
|
|
217
|
-
{ maxAgeMs = DEFAULT_MAX_AGE_MS, forceAlways = false }: {
|
|
262
|
+
{ maxAgeMs = DEFAULT_MAX_AGE_MS, forceAlways = false, mode = DEFAULT_BOOT_RESUME_MODE }: {
|
|
218
263
|
maxAgeMs?: number;
|
|
219
264
|
forceAlways?: boolean;
|
|
265
|
+
mode?: BootResumeMode;
|
|
220
266
|
} = {},
|
|
221
267
|
): boolean {
|
|
222
268
|
if (forceAlways) return false;
|
|
269
|
+
if (mode === 'always' || mode === 'in-flight') return false;
|
|
270
|
+
// mode === 'never': the legacy #2585 clean-shutdown gate.
|
|
223
271
|
if (marker === null) return false;
|
|
224
272
|
const age = now - marker.ts;
|
|
225
273
|
if (age < 0) return false; // clock skew defence — treat as stale
|