switchroom 0.11.1 → 0.12.1

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 (77) hide show
  1. package/README.md +32 -16
  2. package/dist/agent-scheduler/index.js +216 -97
  3. package/dist/auth-broker/index.js +176 -97
  4. package/dist/cli/drive-write-pretool.mjs +26 -11
  5. package/dist/cli/skill-validate-pretool.mjs +7209 -0
  6. package/dist/cli/switchroom.js +45571 -42642
  7. package/dist/cli/ui/index.html +1281 -0
  8. package/dist/host-control/main.js +3628 -309
  9. package/dist/vault/approvals/kernel-server.js +207 -98
  10. package/dist/vault/broker/server.js +249 -119
  11. package/examples/personal-google-workspace-mcp/README.md +8 -3
  12. package/examples/switchroom.yaml +91 -42
  13. package/package.json +4 -3
  14. package/profiles/_base/start.sh.hbs +76 -36
  15. package/profiles/_shared/agent-self-service.md.hbs +1 -1
  16. package/profiles/default/CLAUDE.md.hbs +4 -2
  17. package/skills/file-bug/SKILL.md +6 -4
  18. package/skills/skill-creator/SKILL.md +52 -0
  19. package/skills/switchroom-cli/SKILL.md +20 -4
  20. package/skills/switchroom-install/SKILL.md +3 -3
  21. package/telegram-plugin/auth-snapshot-format.ts +9 -9
  22. package/telegram-plugin/card-format.ts +3 -3
  23. package/telegram-plugin/dist/bridge/bridge.js +112 -112
  24. package/telegram-plugin/dist/gateway/gateway.js +853 -414
  25. package/telegram-plugin/dist/server.js +162 -161
  26. package/telegram-plugin/format.ts +71 -0
  27. package/telegram-plugin/gateway/access-validator.test.ts +8 -8
  28. package/telegram-plugin/gateway/access-validator.ts +1 -1
  29. package/telegram-plugin/gateway/approval-card.test.ts +18 -18
  30. package/telegram-plugin/gateway/approval-card.ts +1 -1
  31. package/telegram-plugin/gateway/auth-command.ts +2 -2
  32. package/telegram-plugin/gateway/boot-card.ts +40 -3
  33. package/telegram-plugin/gateway/boot-probes.ts +114 -30
  34. package/telegram-plugin/gateway/diff-preview-card.test.ts +15 -15
  35. package/telegram-plugin/gateway/diff-preview-card.ts +1 -1
  36. package/telegram-plugin/gateway/drive-write-approval.test.ts +2 -2
  37. package/telegram-plugin/gateway/gateway.ts +265 -22
  38. package/telegram-plugin/gateway/update-announce.ts +167 -0
  39. package/telegram-plugin/quota-check.ts +0 -195
  40. package/telegram-plugin/recent-outbound-dedup.ts +1 -1
  41. package/telegram-plugin/registry/turns-schema.ts +1 -1
  42. package/telegram-plugin/retry-api-call.ts +24 -0
  43. package/telegram-plugin/server.ts +8 -5
  44. package/telegram-plugin/tests/auth-add-flow.test.ts +32 -3
  45. package/telegram-plugin/tests/auth-command-format2.test.ts +4 -4
  46. package/telegram-plugin/tests/auth-snapshot-format.test.ts +17 -17
  47. package/telegram-plugin/tests/auto-fallback-fleet.test.ts +10 -10
  48. package/telegram-plugin/tests/boot-probes.test.ts +90 -2
  49. package/telegram-plugin/tests/bot-runtime.test.ts +23 -1
  50. package/telegram-plugin/tests/fixtures/service-log-current-claude-code.bin +1 -1
  51. package/telegram-plugin/tests/fleet-state.test.ts +3 -2
  52. package/telegram-plugin/tests/quota-check.test.ts +0 -409
  53. package/telegram-plugin/tests/retry-api-call.test.ts +76 -0
  54. package/telegram-plugin/tests/secret-detect-audit.test.ts +1 -1
  55. package/telegram-plugin/tests/secret-detect-pipeline.test.ts +7 -6
  56. package/telegram-plugin/tests/secret-detect-suppressor-no-silent-allow.test.ts +6 -5
  57. package/telegram-plugin/tests/secret-detect.test.ts +8 -8
  58. package/telegram-plugin/tests/telegram-format.test.ts +84 -1
  59. package/telegram-plugin/tests/update-announce.test.ts +154 -0
  60. package/telegram-plugin/tests/vault-grant-inbound-builders.test.ts +8 -8
  61. package/telegram-plugin/tests/vault-request-access-tool.test.ts +51 -0
  62. package/telegram-plugin/welcome-text.ts +1 -8
  63. package/profiles/default/CLAUDE.md +0 -192
  64. package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-313.pyc +0 -0
  65. package/skills/docx/scripts/office/validators/__pycache__/base.cpython-313.pyc +0 -0
  66. package/skills/skill-creator/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
  67. package/skills/skill-creator/scripts/__pycache__/generate_report.cpython-313.pyc +0 -0
  68. package/skills/skill-creator/scripts/__pycache__/improve_description.cpython-313.pyc +0 -0
  69. package/skills/skill-creator/scripts/__pycache__/run_eval.cpython-313.pyc +0 -0
  70. package/skills/skill-creator/scripts/__pycache__/run_loop.cpython-313.pyc +0 -0
  71. package/skills/skill-creator/scripts/__pycache__/utils.cpython-313.pyc +0 -0
  72. package/telegram-plugin/first-paint.ts +0 -225
  73. package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
  74. package/telegram-plugin/server.js +0 -41795
  75. package/telegram-plugin/tests/html-balanced.ts +0 -63
  76. package/telegram-plugin/tests/snapshot-serializer.ts +0 -79
  77. package/telegram-plugin/tool-error-filter.ts +0 -89
@@ -1,63 +0,0 @@
1
- /**
2
- * Tiny HTML tag-balance validator for the Telegram allowlist.
3
- *
4
- * Replaces fast-check (not a current dep, see #662 P1) — paired with
5
- * vitest `it.each` it gives us property-style coverage across many
6
- * randomised renderer inputs without a new dependency.
7
- *
8
- * Telegram's HTML parser only accepts a small allowlist of tags
9
- * (https://core.telegram.org/bots/api#html-style). Anything outside
10
- * the allowlist is treated as raw text — we don't try to validate it.
11
- *
12
- * Self-closing tags inside Telegram's allowlist: only `<br/>` (and even
13
- * that is normalised to `\n`). We treat any `<x/>` as self-closing for
14
- * robustness, but emit nothing into the stack.
15
- */
16
-
17
- const ALLOWED = new Set([
18
- 'b', 'strong',
19
- 'i', 'em',
20
- 'u', 'ins',
21
- 's', 'strike', 'del',
22
- 'a',
23
- 'code',
24
- 'pre',
25
- 'tg-spoiler',
26
- 'span',
27
- 'tg-emoji',
28
- 'blockquote',
29
- 'br',
30
- ])
31
-
32
- export interface BalanceResult {
33
- balanced: boolean
34
- openTags: string[]
35
- extraCloses: string[]
36
- }
37
-
38
- export function isBalancedHtml(html: string): BalanceResult {
39
- const stack: string[] = []
40
- const extraCloses: string[] = []
41
- // Match tags but skip HTML entities (`&lt;` etc) — those are NOT tags.
42
- const re = /<\/?\s*([A-Za-z][A-Za-z0-9-]*)([^>]*)>/g
43
- let m: RegExpExecArray | null
44
- while ((m = re.exec(html)) !== null) {
45
- const raw = m[0]
46
- const name = m[1].toLowerCase()
47
- if (!ALLOWED.has(name)) continue
48
- const isClose = raw.startsWith('</')
49
- const selfClose = raw.endsWith('/>') || name === 'br'
50
- if (selfClose && !isClose) continue
51
- if (isClose) {
52
- const top = stack[stack.length - 1]
53
- if (top === name) {
54
- stack.pop()
55
- } else {
56
- extraCloses.push(name)
57
- }
58
- } else {
59
- stack.push(name)
60
- }
61
- }
62
- return { balanced: stack.length === 0 && extraCloses.length === 0, openTags: stack, extraCloses }
63
- }
@@ -1,79 +0,0 @@
1
- /**
2
- * Normalizing serializer for HTML / progress-card snapshot tests.
3
- *
4
- * Problem: the raw progress-card render contains
5
- * - elapsed-time strings like "12s elapsed" / "1m34s"
6
- * - ISO timestamps like "2026-04-18T12:34:56.789Z"
7
- * - message-ids, toolUseIds, runtime-generated UUIDs
8
- *
9
- * Any of these flip on every test run, so a naive snapshot rots the
10
- * moment it lands. This serializer canonicalizes those fields to stable
11
- * tokens (`<ELAPSED>`, `<TIME>`, `<ID>`, `<TOOL_USE_ID>`) so the
12
- * snapshot diff reflects *semantic* changes only.
13
- *
14
- * Use by passing `snapshotSerializer` to vitest's `expect.addSnapshotSerializer`
15
- * in a test's `beforeAll`, or globally via `vitest.config.ts`
16
- * (`test.snapshotSerializers`).
17
- */
18
-
19
- const PATTERNS: Array<{ re: RegExp; replacement: string }> = [
20
- // ISO8601 timestamps
21
- { re: /\b\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z\b/g, replacement: '<TIME>' },
22
- // Unix seconds (10 digits) — only within JSON-like contexts, prefixed by `date":` or `"date":`
23
- { re: /"date"\s*:\s*\d{10}\b/g, replacement: '"date":<UNIX_SEC>' },
24
- // Elapsed-time strings rendered by progress-card (hhh? mm ss).
25
- // Match "NNs elapsed", "NNmNNs", "NNhNNm".
26
- { re: /\b\d+m\d+s\b/g, replacement: '<ELAPSED>' },
27
- { re: /\b\d+h\d+m\b/g, replacement: '<ELAPSED>' },
28
- { re: /\b\d+s\s+elapsed\b/g, replacement: '<ELAPSED> elapsed' },
29
- // Claude Code tool_use ids: `toolu_01ABC...` (base62, 20+ chars).
30
- { re: /\btoolu_[A-Za-z0-9_]{10,}\b/g, replacement: '<TOOL_USE_ID>' },
31
- // grammy/telegram file_ids: long alphanumerics starting with AgACAgI / BQACAgI etc.
32
- { re: /\b(?:AgAC|BQAC|CQAC|DQAC)[A-Za-z0-9_-]{10,}\b/g, replacement: '<FILE_ID>' },
33
- // UUIDs
34
- { re: /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/g, replacement: '<UUID>' },
35
- ]
36
-
37
- /**
38
- * Apply all normalizing replacements. Safe to call on any string —
39
- * non-matching content passes through unchanged.
40
- */
41
- export function normalizeSnapshot(input: string): string {
42
- let out = input
43
- for (const { re, replacement } of PATTERNS) {
44
- out = out.replace(re, replacement)
45
- }
46
- return out
47
- }
48
-
49
- /**
50
- * Vitest snapshot serializer compatible with `expect.addSnapshotSerializer`.
51
- *
52
- * Only applies to string values that clearly look like HTML / Telegram
53
- * rendered content. Everything else passes through without interference
54
- * so we don't accidentally mangle unrelated assertions.
55
- */
56
- export const snapshotSerializer = {
57
- test(val: unknown): boolean {
58
- if (typeof val !== 'string') return false
59
- // Cheap pre-filter: only normalize when at least one pattern would hit.
60
- for (const { re } of PATTERNS) {
61
- re.lastIndex = 0
62
- if (re.test(val)) {
63
- re.lastIndex = 0
64
- return true
65
- }
66
- re.lastIndex = 0
67
- }
68
- return false
69
- },
70
- serialize(val: unknown): string {
71
- return JSON.stringify(normalizeSnapshot(val as string))
72
- },
73
- }
74
-
75
- /** Convenience: normalize + stringify with stable indent. Good for
76
- * inline-snapshot-style assertions without invoking the serializer. */
77
- export function stableHtmlSnapshot(html: string): string {
78
- return normalizeSnapshot(html)
79
- }
@@ -1,89 +0,0 @@
1
- /**
2
- * tool-error-filter.ts — severity classifier for tool_result isError events.
3
- *
4
- * Acceptance item 5: benign tool failures (no-match, file-not-found,
5
- * recoverable Telegram errors) don't surface as raw debug in the progress
6
- * card checklist. Real failures (auth, crash, network) still escalate.
7
- *
8
- * Design: pure function, no side effects, no dependencies. The gateway
9
- * calls isBenignToolError() on every tool_result with isError=true; when
10
- * it returns true, the progress-card item is marked 'done' (✅) rather
11
- * than 'failed' (❌) and no operator notification is raised.
12
- *
13
- * Important: this classification applies to the DISPLAY only. The agent
14
- * session transcript is never mutated — the raw error text still reaches
15
- * the model so it can reason about what happened.
16
- */
17
-
18
- /**
19
- * Pattern groups for benign tool errors.
20
- *
21
- * These are errors that represent "no results" or "resource absent" — the
22
- * tool ran correctly but found nothing. Surfacing them as ❌ in the checklist
23
- * is noise: the user can't act on "grep found nothing" and the agent will
24
- * handle it in context.
25
- */
26
-
27
- // File-not-found patterns (common across Bash, Read, Edit tools)
28
- const FILE_NOT_FOUND_RE =
29
- /no such file or directory|file not found|path does not exist|enoent/i
30
-
31
- // No-match patterns (grep, find, search tools)
32
- const NO_MATCH_RE =
33
- /no match(es)? found|returned no results?|not found in|0 result/i
34
-
35
- // Recoverable Telegram API patterns (message deleted, not modified, etc.)
36
- const TELEGRAM_RECOVERABLE_RE =
37
- /message (is not modified|to edit not found|can't be deleted|was deleted)|MESSAGE_ID_INVALID|message not found/i
38
-
39
- // "Not a git repository" — narrow tool-setup pattern. Earlier drafts also
40
- // matched `command not found` and `permission denied` but those were too
41
- // broad: a real EACCES on /etc/passwd, a real "kubectl not found" during
42
- // a deploy, are genuine failures the user must see. Kept tight to the one
43
- // truly-benign case (running git outside a repo).
44
- const TOOL_SETUP_RE =
45
- /not a git repository/i
46
-
47
- // Timeout / cancellation that the agent will retry. The bare `aborted`
48
- // substring was previously included but matched DB transaction aborts,
49
- // git merge aborts, and policy-rejection messages — all real failures.
50
- // Dropped in favor of explicit timeout and operation-cancelled phrasing.
51
- const TIMEOUT_RE =
52
- /timed? ?out|operation cancelled/i
53
-
54
- /**
55
- * Returns true when a tool error text matches a known benign pattern (no
56
- * results / resource absent / recoverable Telegram error / explicit timeout).
57
- * Returns false for empty input, unknown text, or any text that doesn't
58
- * match a pattern.
59
- *
60
- * The function is fail-closed: empty / undefined input → false. Callers
61
- * that want to suppress only on positive evidence should call this directly;
62
- * callers that need an extra short-circuit on missing input should guard at
63
- * the call site with `text && isBenignToolError(text)`.
64
- *
65
- * The text parameter is the raw tool result content; the first ~500 chars
66
- * are sufficient for pattern matching, and callers should truncate before
67
- * calling for performance / event-size reasons.
68
- */
69
- export function isBenignToolError(text: string): boolean {
70
- if (!text) return false
71
- return (
72
- FILE_NOT_FOUND_RE.test(text) ||
73
- NO_MATCH_RE.test(text) ||
74
- TELEGRAM_RECOVERABLE_RE.test(text) ||
75
- TOOL_SETUP_RE.test(text) ||
76
- TIMEOUT_RE.test(text)
77
- )
78
- }
79
-
80
- /**
81
- * Severity of a tool error for routing purposes.
82
- * - `benign`: no-match / not-found / recoverable — suppress from UI
83
- * - `real`: auth failure, crash, unexpected error — surface in UI
84
- */
85
- export type ToolErrorSeverity = 'benign' | 'real'
86
-
87
- export function classifyToolError(text: string): ToolErrorSeverity {
88
- return isBenignToolError(text) ? 'benign' : 'real'
89
- }