switchroom 0.18.9 → 0.18.11

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 (65) hide show
  1. package/dist/agent-scheduler/index.js +1 -0
  2. package/dist/auth-broker/index.js +198 -13
  3. package/dist/cli/notion-write-pretool.mjs +1 -0
  4. package/dist/cli/switchroom.js +214 -83
  5. package/dist/cli/ui/apple-touch-icon.png +0 -0
  6. package/dist/cli/ui/favicon-32.png +0 -0
  7. package/dist/cli/ui/favicon.ico +0 -0
  8. package/dist/host-control/main.js +760 -257
  9. package/dist/vault/approvals/kernel-server.js +3 -1
  10. package/dist/vault/broker/server.js +3 -1
  11. package/package.json +1 -1
  12. package/profiles/_base/start.sh.hbs +119 -37
  13. package/profiles/_shared/dev-protocol.md.hbs +42 -0
  14. package/skills/dev-protocol/SKILL.md +131 -0
  15. package/telegram-plugin/README.md +2 -1
  16. package/telegram-plugin/admin-commands/dispatch.test.ts +40 -2
  17. package/telegram-plugin/admin-commands/index.ts +6 -1
  18. package/telegram-plugin/bridge/bridge.ts +23 -1
  19. package/telegram-plugin/bridge/crash-breadcrumb.ts +42 -0
  20. package/telegram-plugin/chat-lock.ts +13 -0
  21. package/telegram-plugin/dist/bridge/bridge.js +24 -1
  22. package/telegram-plugin/dist/gateway/gateway.js +1832 -263
  23. package/telegram-plugin/dist/server.js +29 -2
  24. package/telegram-plugin/fallback-card-collapse.ts +131 -0
  25. package/telegram-plugin/gateway/bridge-dead-watchdog.ts +546 -0
  26. package/telegram-plugin/gateway/effort-command.ts +47 -3
  27. package/telegram-plugin/gateway/gateway.ts +1435 -211
  28. package/telegram-plugin/gateway/model-command.ts +94 -8
  29. package/telegram-plugin/gateway/pending-session-command.ts +365 -0
  30. package/telegram-plugin/gateway/permission-timeout.ts +25 -0
  31. package/telegram-plugin/gateway/resume-inbound-builder.ts +23 -3
  32. package/telegram-plugin/gateway/session-model-file.ts +166 -23
  33. package/telegram-plugin/gateway/stop-command.ts +56 -0
  34. package/telegram-plugin/photo-precheck.ts +201 -0
  35. package/telegram-plugin/quota-watch.ts +141 -2
  36. package/telegram-plugin/registry/subagents-schema.ts +26 -3
  37. package/telegram-plugin/registry/subagents.test.ts +67 -0
  38. package/telegram-plugin/retry-api-call.ts +31 -0
  39. package/telegram-plugin/subagent-watcher.ts +392 -1
  40. package/telegram-plugin/tests/bridge-dead-watchdog.test.ts +576 -0
  41. package/telegram-plugin/tests/buffer-gate-broadened.test.ts +11 -5
  42. package/telegram-plugin/tests/chat-lock-unhandled-rejection.test.ts +101 -0
  43. package/telegram-plugin/tests/crash-breadcrumb.test.ts +57 -0
  44. package/telegram-plugin/tests/effort-command.test.ts +59 -2
  45. package/telegram-plugin/tests/fallback-card-collapse.test.ts +104 -0
  46. package/telegram-plugin/tests/gateway-pending-command-wiring.test.ts +124 -0
  47. package/telegram-plugin/tests/gateway-secret-detect.test.ts +7 -1
  48. package/telegram-plugin/tests/gateway-session-model-relaunch.test.ts +19 -11
  49. package/telegram-plugin/tests/model-command.test.ts +46 -3
  50. package/telegram-plugin/tests/pending-session-command.test.ts +322 -0
  51. package/telegram-plugin/tests/permission-timeout.test.ts +26 -0
  52. package/telegram-plugin/tests/permission-verdict-resume-guard.test.ts +16 -0
  53. package/telegram-plugin/tests/photo-dimension-fallback.test.ts +129 -0
  54. package/telegram-plugin/tests/photo-precheck.test.ts +240 -0
  55. package/telegram-plugin/tests/photo-reroute-wiring.test.ts +85 -0
  56. package/telegram-plugin/tests/quota-watch.test.ts +225 -0
  57. package/telegram-plugin/tests/session-model-file.test.ts +101 -2
  58. package/telegram-plugin/tests/stop-command.test.ts +234 -0
  59. package/telegram-plugin/tests/subagent-watcher-env-thresholds.test.ts +27 -9
  60. package/telegram-plugin/tests/subagent-watcher-resurrection.test.ts +398 -0
  61. package/telegram-plugin/tests/subagent-watcher-stall-terminal.test.ts +172 -0
  62. package/telegram-plugin/tests/worker-activity-feed.test.ts +37 -0
  63. package/telegram-plugin/tests/worker-visibility-prose-silent-harness.test.ts +18 -4
  64. package/telegram-plugin/welcome-text.ts +4 -3
  65. package/telegram-plugin/worker-activity-feed.ts +27 -0
@@ -158,6 +158,7 @@ function makeHarness() {
158
158
  stallThresholdMs: 60_000, // tight active-loop threshold
159
159
  silentSynthesisStallThresholdMs: 300_000, // widened window (also long-runner window)
160
160
  silentStallTerminalMs: 300_000, // post-stall terminal-synthesis window (unchanged)
161
+ inflightTerminalCapMs: 900_000, // died-mid-tool cap (15 min, compressed from the 45-min default)
161
162
  rescanMs: 500,
162
163
  now: () => currentTime,
163
164
  setInterval: sharedSetInterval,
@@ -278,10 +279,23 @@ describe('prose-silent background worker — end-to-end visibility harness (PR 2
278
279
  expect(h.stallCalls[0].agentId).toBe(h.agentId)
279
280
  expect(h.stallTerminalCalls).toHaveLength(0)
280
281
 
281
- // (d) TERMINAL GATE RELEASE: 300s past the stall, the silent-stall terminal
282
- // synthesis fires — onStallTerminal + onFinish — and the feed finalizes.
283
- // This is the untouched, load-bearing completion path.
284
- h.advance(310_000)
282
+ // (d) IN-FLIGHT DEFERRAL: 300s past the stall the OLD contract
283
+ // (#2777/#2782) synthesised terminal here. But this worker's transcript
284
+ // ends in a Bash tool_use with NO tool_result — it may still be inside
285
+ // the tool call (incident 2026-07-10: exactly this shape finalised a
286
+ // LIVE worker's card mid-Bash). Synthesis is now deferred while the
287
+ // tool call is in flight...
288
+ h.advance(310_000) // total idle ~672s — past silentStallTerminalMs, under the 900s cap
289
+ await h.flush()
290
+ expect(h.stallTerminalCalls).toHaveLength(0)
291
+ expect(h.finishCalls).toHaveLength(0)
292
+
293
+ // (e) TERMINAL GATE RELEASE AT THE CAP: ...but not forever. A worker
294
+ // that died mid-tool (JSONL frozen, tool_result never coming) must not
295
+ // wedge — past inflightTerminalCapMs of total idle the synthesis
296
+ // proceeds and the completion gate releases. The load-bearing release
297
+ // from #2777/#2782 survives, bounded instead of at the raw synth window.
298
+ h.advance(300_000) // total idle ~972s ≥ 900s cap
285
299
  await h.flush()
286
300
  expect(h.stallTerminalCalls).toHaveLength(1)
287
301
  expect(h.finishCalls).toHaveLength(1)
@@ -279,9 +279,9 @@ export function statusUnpairedText(): string {
279
279
  */
280
280
  export const switchroomHelpCommandNames = [
281
281
  // Session & approvals
282
- "new", "compact", "clear", "approve", "deny", "pending", "interrupt",
282
+ "new", "compact", "clear", "approve", "deny", "pending", "interrupt", "stop",
283
283
  // Agents
284
- "agents", "agentstart", "stop", "restart", "logs", "memory",
284
+ "agents", "agentstart", "agentstop", "restart", "logs", "memory",
285
285
  // Auth & config — consolidated onto the `/auth` dashboard.
286
286
  "auth", "model",
287
287
  "topics", "update", "version", "whoami",
@@ -380,11 +380,12 @@ export function switchroomHelpText(agentName: string): string {
380
380
  `\`/deny [id]\` — deny pending tool permission`,
381
381
  `\`/pending\` — list pending permission prompts`,
382
382
  `\`/interrupt [name]\` — interrupt an agent turn`,
383
+ `\`/stop\` — cancel my in-flight turn (bare "stop" works too)`,
383
384
  ``,
384
385
  `**Agents**`,
385
386
  `\`/agents\` — list all agents`,
386
387
  `\`/agentstart [name]\` — start an agent`,
387
- `\`/stop [name]\` — stop an agent`,
388
+ `\`/agentstop [name]\` — stop an agent's container`,
388
389
  `\`/logs [name] [lines]\` — show agent logs`,
389
390
  `\`/memory <query>\` — search agent memory`,
390
391
  ``,
@@ -342,6 +342,16 @@ export interface WorkerActivityFeed {
342
342
  finish(agentId: string, view: WorkerActivityView): Promise<void>
343
343
  /** Forget a worker's state without editing (e.g. error path). */
344
344
  drop(agentId: string): void
345
+ /**
346
+ * Issue #3023 (card resurrection). Undo a finalization: clear the durable
347
+ * `finalized` gate (and any lingering per-handle `finished` latch) so a
348
+ * worker whose card was FALSELY finalized can be painted/edited again. The
349
+ * watcher calls this (via the gateway's `onResurrect` wiring) when a
350
+ * falsely-finalized worker's JSONL resumes growing. A fresh `running` cue
351
+ * after this repaints a live card — the operator invariant that active work
352
+ * stays visible. Idempotent; a no-op if the worker was never finalized.
353
+ */
354
+ resurrect(agentId: string): void
345
355
  /** Clear the heartbeat interval (gateway shutdown). Idempotent. */
346
356
  stop(): void
347
357
  /** Manually fire one heartbeat tick (test hook). */
@@ -777,6 +787,23 @@ export function createWorkerActivityFeed(opts: WorkerActivityFeedOpts): WorkerAc
777
787
  markFinalized(agentId)
778
788
  handles.delete(agentId)
779
789
  },
790
+ resurrect(agentId) {
791
+ // Issue #3023: the worker's card was falsely finalized and its JSONL has
792
+ // resumed. Re-open the paint path: drop the durable finalized gate so a
793
+ // fresh `running` cue creates a new handle and first-paints a live card
794
+ // again, and un-latch any surviving handle (finish deleted it in the
795
+ // common case, but a staged pendingFinish could keep it alive). The next
796
+ // `update` tick from the watcher's replayed progress does the repaint.
797
+ const wasFinalized = finalized.delete(agentId)
798
+ const h = handles.get(agentId)
799
+ if (h != null) {
800
+ h.finished = false
801
+ h.pendingFinish = null
802
+ }
803
+ if (wasFinalized || h != null) {
804
+ log(`worker-feed: resurrect agent=${agentId} — cleared finalized gate; card will repaint on next running cue`)
805
+ }
806
+ },
780
807
  heartbeatTick,
781
808
  stop() {
782
809
  if (heartbeatTimer != null) {