switchroom 0.13.33 → 0.13.36

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 (34) hide show
  1. package/bin/timezone-hook.sh +1 -1
  2. package/dist/agent-scheduler/index.js +8 -1
  3. package/dist/auth-broker/index.js +8 -1
  4. package/dist/cli/switchroom.js +176 -26
  5. package/dist/host-control/main.js +5222 -203
  6. package/dist/vault/approvals/kernel-server.js +9 -2
  7. package/dist/vault/broker/server.js +9 -2
  8. package/package.json +1 -1
  9. package/profiles/default/CLAUDE.md.hbs +1 -1
  10. package/telegram-plugin/dist/gateway/gateway.js +234 -31
  11. package/telegram-plugin/docs/waiting-ux-spec.md +40 -0
  12. package/telegram-plugin/gateway/config-approval-handler.test.ts +188 -1
  13. package/telegram-plugin/gateway/config-approval-handler.ts +170 -15
  14. package/telegram-plugin/gateway/diff-preview-card.test.ts +2 -2
  15. package/telegram-plugin/gateway/diff-preview-card.ts +2 -2
  16. package/telegram-plugin/gateway/drive-write-approval.test.ts +70 -0
  17. package/telegram-plugin/gateway/drive-write-approval.ts +51 -2
  18. package/telegram-plugin/gateway/error-envelope-card.ts +64 -0
  19. package/telegram-plugin/gateway/gateway.ts +112 -15
  20. package/telegram-plugin/gateway/ipc-protocol.ts +10 -1
  21. package/telegram-plugin/gateway/oversize-card-body.test.ts +108 -0
  22. package/telegram-plugin/gateway/oversize-card-body.ts +114 -0
  23. package/telegram-plugin/gateway/unhandled-rejection-policy.ts +46 -1
  24. package/telegram-plugin/hooks/silent-end-interrupt-stop.mjs +118 -41
  25. package/telegram-plugin/hooks/silent-end-scan.mjs +190 -0
  26. package/telegram-plugin/pending-work-progress.ts +37 -1
  27. package/telegram-plugin/tests/boot-clears-clean-shutdown-marker.test.ts +75 -0
  28. package/telegram-plugin/tests/error-envelope-unlock-card.test.ts +79 -0
  29. package/telegram-plugin/tests/pending-work-progress.test.ts +134 -0
  30. package/telegram-plugin/tests/silent-end-integration.test.ts +268 -0
  31. package/telegram-plugin/tests/silent-end-interrupt-stop-integration.test.ts +242 -0
  32. package/telegram-plugin/tests/silent-end-interrupt-stop-scan.test.ts +314 -0
  33. package/telegram-plugin/tests/silent-end.test.ts +227 -38
  34. package/telegram-plugin/tests/unhandled-rejection-policy.test.ts +51 -6
@@ -103,11 +103,6 @@ describe('classifyRejection — genuine errors still crash', () => {
103
103
  expect(classifyRejection(err)).toBe('shutdown')
104
104
  })
105
105
 
106
- it('returns "shutdown" for GrammyError 429 (rate limit) — should be retried not masked', () => {
107
- const err = grammyError(429, 'Too Many Requests: retry after 5')
108
- expect(classifyRejection(err)).toBe('shutdown')
109
- })
110
-
111
106
  it('returns "shutdown" for GrammyError 400 with NEW unknown description', () => {
112
107
  // Use a description that's not in the benign list — the policy is
113
108
  // intentionally narrow so genuinely new bug categories still crash
@@ -115,9 +110,59 @@ describe('classifyRejection — genuine errors still crash', () => {
115
110
  const err = grammyError(400, 'Bad Request: PHOTO_INVALID_DIMENSIONS')
116
111
  expect(classifyRejection(err)).toBe('shutdown')
117
112
  })
113
+ })
114
+
115
+ describe('classifyRejection — transient API errors (2026-05-25 follow-up)', () => {
116
+ // The pre-fix policy crashed on 429 + 5xx + network errors. That's
117
+ // wrong shape: those errors are already handled by retry-api-call.ts
118
+ // (3 attempts with backoff). When one leaks past the retry policy —
119
+ // either because the caller didn't wrap, or because 3 sustained
120
+ // attempts all failed — crashing the gateway is the worst outcome
121
+ // (one bad packet = visible "agent-crashed" banner + restart loop
122
+ // risk). log_only matches the daemon-stays-up posture.
123
+ //
124
+ // Surfaced 2026-05-25 on clerk: a sendMessage hit 429 after exhausting
125
+ // retries, the rejection bubbled to the unhandledRejection handler,
126
+ // shutdown fired, operator-event banner posted, "clerk seems to be
127
+ // crashing" user-visible.
128
+
129
+ it('returns "log_only" for GrammyError 429 (rate limit) — already handled by retry-api-call', () => {
130
+ const err = grammyError(429, 'Too Many Requests: retry after 5')
131
+ expect(classifyRejection(err)).toBe('log_only')
132
+ })
118
133
 
119
- it('returns "shutdown" for GrammyError 500 server error', () => {
134
+ it('returns "log_only" for GrammyError 500 (server error)', () => {
120
135
  const err = grammyError(500, 'Internal Server Error')
136
+ expect(classifyRejection(err)).toBe('log_only')
137
+ })
138
+
139
+ it('returns "log_only" for GrammyError 502 (Bad Gateway)', () => {
140
+ const err = grammyError(502, 'Bad Gateway')
141
+ expect(classifyRejection(err)).toBe('log_only')
142
+ })
143
+
144
+ it('returns "log_only" for GrammyError 503 (Service Unavailable)', () => {
145
+ const err = grammyError(503, 'Service Unavailable')
146
+ expect(classifyRejection(err)).toBe('log_only')
147
+ })
148
+
149
+ it('returns "log_only" for HttpError (network failure) via injected detector', () => {
150
+ // Real grammy HttpError surfaces as e.g. "Network request for
151
+ // 'sendMessage' failed!" wrapping ECONNRESET / ETIMEDOUT / fetch
152
+ // failed. We inject the detector so this test doesn't depend on
153
+ // grammy internals; the production path uses `err instanceof
154
+ // HttpError`.
155
+ const fakeHttp = new Error("Network request for 'sendMessage' failed!")
156
+ expect(
157
+ classifyRejection(fakeHttp, { isHttpError: () => true }),
158
+ ).toBe('log_only')
159
+ })
160
+
161
+ it('still returns "shutdown" for GrammyError 403 (forbidden) — must not be masked', () => {
162
+ // 401 (covered above at line 101) and 403 are NOT transient — they
163
+ // indicate a genuine configuration bug (revoked bot token, removed
164
+ // from chat) and must crash so systemd surfaces the failure.
165
+ const err = grammyError(403, 'Forbidden: bot was blocked by the user')
121
166
  expect(classifyRejection(err)).toBe('shutdown')
122
167
  })
123
168
  })