switchroom 0.7.15 → 0.10.0
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/README.md +51 -59
- package/bin/run-hook.sh +27 -11
- package/bin/timezone-hook.sh +9 -7
- package/dist/agent-scheduler/index.js +410 -133
- package/dist/auth-broker/index.js +13932 -0
- package/dist/cli/switchroom.js +26937 -5601
- package/dist/host-control/main.js +12702 -0
- package/dist/vault/approvals/kernel-server.js +467 -184
- package/dist/vault/broker/server.js +1430 -724
- package/examples/minimal.yaml +63 -0
- package/examples/personal-google-workspace-mcp/.env.example +34 -0
- package/examples/personal-google-workspace-mcp/README.md +194 -0
- package/examples/personal-google-workspace-mcp/compose.yaml +66 -0
- package/examples/switchroom.yaml +220 -0
- package/package.json +7 -4
- package/profiles/_base/settings.json.hbs +20 -5
- package/profiles/_base/start.sh.hbs +16 -3
- package/profiles/_shared/agent-self-service.md.hbs +126 -0
- package/profiles/_shared/telegram-style.md.hbs +20 -90
- package/profiles/_shared/vault-protocol.md.hbs +68 -0
- package/profiles/default/CLAUDE.md +50 -96
- package/profiles/default/CLAUDE.md.hbs +36 -6
- package/profiles/default/workspace/SOUL.md.hbs +12 -5
- package/skills/buildkite-agent-infrastructure/SKILL.md +30 -11
- package/skills/buildkite-agent-runtime/SKILL.md +44 -11
- package/skills/buildkite-api/SKILL.md +31 -8
- package/skills/buildkite-cli/SKILL.md +27 -9
- package/skills/buildkite-migration/SKILL.md +22 -9
- package/skills/buildkite-pipelines/SKILL.md +26 -9
- package/skills/buildkite-secure-delivery/SKILL.md +23 -9
- package/skills/buildkite-test-engine/SKILL.md +25 -8
- package/skills/docx/SKILL.md +1 -1
- package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-313.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/base.cpython-313.pyc +0 -0
- package/skills/file-bug/SKILL.md +34 -6
- package/skills/humanizer/SKILL.md +15 -0
- package/skills/humanizer-calibrate/SKILL.md +7 -1
- package/skills/mcp-builder/SKILL.md +1 -1
- package/skills/pdf/SKILL.md +1 -1
- package/skills/pptx/SKILL.md +1 -1
- package/skills/skill-creator/SKILL.md +21 -1
- package/skills/skill-creator/scripts/__pycache__/__init__.cpython-313.pyc +0 -0
- package/skills/skill-creator/scripts/__pycache__/generate_report.cpython-313.pyc +0 -0
- package/skills/skill-creator/scripts/__pycache__/improve_description.cpython-313.pyc +0 -0
- package/skills/skill-creator/scripts/__pycache__/run_eval.cpython-313.pyc +0 -0
- package/skills/skill-creator/scripts/__pycache__/run_loop.cpython-313.pyc +0 -0
- package/skills/skill-creator/scripts/__pycache__/utils.cpython-313.pyc +0 -0
- package/skills/switchroom-cli/SKILL.md +63 -64
- package/skills/switchroom-health/SKILL.md +23 -10
- package/skills/switchroom-install/SKILL.md +3 -3
- package/skills/switchroom-manage/SKILL.md +26 -19
- package/skills/switchroom-runtime/SKILL.md +191 -0
- package/skills/switchroom-status/SKILL.md +27 -2
- package/skills/telegram-test-harness/SKILL.md +3 -0
- package/skills/token-helpers/SKILL.md +24 -1
- package/skills/webapp-testing/SKILL.md +31 -1
- package/skills/xlsx/SKILL.md +1 -1
- package/telegram-plugin/admin-commands/index.ts +7 -5
- package/telegram-plugin/analytics-posthog.ts +191 -0
- package/telegram-plugin/bridge/bridge.ts +69 -0
- package/telegram-plugin/bridge/ipc-client.ts +4 -1
- package/telegram-plugin/dist/bridge/bridge.js +194 -119
- package/telegram-plugin/dist/gateway/gateway.js +23611 -19671
- package/telegram-plugin/dist/server.js +245 -189
- package/telegram-plugin/first-paint.ts +3 -24
- package/telegram-plugin/gateway/auth-add-flow.ts +326 -0
- package/telegram-plugin/gateway/auth-broker-client.ts +75 -0
- package/telegram-plugin/gateway/auth-command.ts +794 -0
- package/telegram-plugin/gateway/auth-line.ts +123 -0
- package/telegram-plugin/gateway/boot-card.ts +169 -40
- package/telegram-plugin/gateway/boot-issue-cache.ts +308 -0
- package/telegram-plugin/gateway/boot-probes.ts +166 -123
- package/telegram-plugin/gateway/boot-reason.ts +41 -7
- package/telegram-plugin/gateway/boot-version.ts +66 -0
- package/telegram-plugin/gateway/gateway.ts +3499 -1885
- package/telegram-plugin/gateway/hostd-dispatch.ts +117 -0
- package/telegram-plugin/gateway/ipc-protocol.ts +18 -0
- package/telegram-plugin/gateway/pending-inbound-buffer.ts +106 -0
- package/telegram-plugin/gateway/quarantine.ts +69 -0
- package/telegram-plugin/gateway/quota-cache.ts +9 -4
- package/telegram-plugin/gateway/reaction-trigger.ts +401 -0
- package/telegram-plugin/gateway/recent-denials.test.ts +103 -0
- package/telegram-plugin/gateway/recent-denials.ts +77 -0
- package/telegram-plugin/gateway/startup-network-retry.ts +109 -31
- package/telegram-plugin/gateway/vault-grant-inbound-builders.ts +125 -0
- package/telegram-plugin/history.ts +91 -0
- package/telegram-plugin/hooks/hooks.json +10 -0
- package/telegram-plugin/hooks/sandbox-hint-posttool.mjs +130 -0
- package/telegram-plugin/hooks/subagent-tracker-posttool.mjs +19 -2
- package/telegram-plugin/hooks/subagent-tracker-pretool.mjs +22 -2
- package/telegram-plugin/hooks/tool-label-pretool.mjs +11 -0
- package/telegram-plugin/hooks/wedge-detect-posttool.mjs +303 -0
- package/telegram-plugin/inbound-classifier.ts +50 -0
- package/telegram-plugin/inline-keyboard-callbacks.ts +136 -0
- package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/telegram-plugin/package.json +4 -2
- package/telegram-plugin/permission-rule.ts +51 -0
- package/telegram-plugin/permission-title.ts +56 -0
- package/telegram-plugin/quota-check.ts +19 -41
- package/telegram-plugin/registry/reaper.ts +223 -0
- package/telegram-plugin/retry-api-call.ts +80 -0
- package/telegram-plugin/runtime-metrics.ts +177 -0
- package/telegram-plugin/scripts/build.mjs +0 -1
- package/telegram-plugin/secret-detect/index.ts +24 -0
- package/telegram-plugin/secret-detect/vault-error.test.ts +64 -12
- package/telegram-plugin/secret-detect/vault-error.ts +78 -11
- package/telegram-plugin/secret-detect/vault-write.ts +14 -2
- package/telegram-plugin/server.js +41795 -0
- package/telegram-plugin/session-tail.ts +6 -1
- package/telegram-plugin/shared/bot-runtime.ts +5 -4
- package/telegram-plugin/silence-poke.ts +420 -0
- package/telegram-plugin/silent-end.ts +174 -0
- package/telegram-plugin/stream-controller.ts +13 -0
- package/telegram-plugin/stream-reply-handler.ts +7 -0
- package/telegram-plugin/subagent-watcher.ts +213 -4
- package/telegram-plugin/tests/auth-add-flow.test.ts +559 -0
- package/telegram-plugin/tests/auth-code-redact.test.ts +8 -4
- package/telegram-plugin/tests/auth-command-vernacular.test.ts +531 -0
- package/telegram-plugin/tests/boot-card-issue-dedup.test.ts +247 -0
- package/telegram-plugin/tests/boot-card-reason-to-render.test.ts +182 -0
- package/telegram-plugin/tests/boot-card-reason.test.ts +65 -2
- package/telegram-plugin/tests/boot-card-render.test.ts +146 -0
- package/telegram-plugin/tests/boot-card-silent-on-operator.test.ts +103 -0
- package/telegram-plugin/tests/boot-probes.test.ts +216 -10
- package/telegram-plugin/tests/boot-version-string.test.ts +0 -0
- package/telegram-plugin/tests/finalize-callback.test.ts +190 -0
- package/telegram-plugin/tests/gateway-message-validator.test.ts +26 -0
- package/telegram-plugin/tests/gateway-secret-detect.test.ts +12 -3
- package/telegram-plugin/tests/gateway-startup-network-retry.test.ts +104 -0
- package/telegram-plugin/tests/history-reaper.test.ts +378 -0
- package/telegram-plugin/tests/hostd-dispatch.test.ts +129 -0
- package/telegram-plugin/tests/inbound-classifier.test.ts +76 -0
- package/telegram-plugin/tests/inbound-message-types.test.ts +267 -0
- package/telegram-plugin/tests/issues-card.test.ts +49 -0
- package/telegram-plugin/tests/pending-inbound-buffer.test.ts +132 -0
- package/telegram-plugin/tests/permission-rule.test.ts +80 -1
- package/telegram-plugin/tests/permission-title.test.ts +31 -0
- package/telegram-plugin/tests/quota-check.test.ts +5 -35
- package/telegram-plugin/tests/races.test.ts +179 -0
- package/telegram-plugin/tests/reaction-trigger-flow.test.ts +353 -0
- package/telegram-plugin/tests/reaction-trigger.test.ts +397 -0
- package/telegram-plugin/tests/retry-api-call.test.ts +152 -1
- package/telegram-plugin/tests/runtime-metrics.test.ts +145 -0
- package/telegram-plugin/tests/sandbox-hint-posttool.test.ts +155 -0
- package/telegram-plugin/tests/secret-detect-delete-must-surface-failures.test.ts +133 -0
- package/telegram-plugin/tests/secret-detect-false-positives.test.ts +137 -0
- package/telegram-plugin/tests/silence-poke.test.ts +493 -0
- package/telegram-plugin/tests/silent-end.test.ts +206 -0
- package/telegram-plugin/tests/subagent-tracker-hooks.test.ts +107 -0
- package/telegram-plugin/tests/subagent-watcher-env-thresholds.test.ts +224 -0
- package/telegram-plugin/tests/subagent-watcher-stall-terminal.test.ts +316 -0
- package/telegram-plugin/tests/subagent-watcher.test.ts +263 -0
- package/telegram-plugin/tests/turn-signal-tracker.test.ts +81 -0
- package/telegram-plugin/tests/vault-approval-posture.test.ts +256 -0
- package/telegram-plugin/tests/vault-grant-auto-resume.test.ts +73 -0
- package/telegram-plugin/tests/vault-grant-inbound-builders.test.ts +226 -0
- package/telegram-plugin/tests/vault-grant-union.test.ts +130 -0
- package/telegram-plugin/tests/vault-key-regex-allows-slash.test.ts +140 -0
- package/telegram-plugin/tests/vault-posture-quarantine.test.ts +104 -0
- package/telegram-plugin/tests/vault-request-access-tool.test.ts +114 -0
- package/telegram-plugin/tests/vault-request-access-unlock-resume.test.ts +106 -0
- package/telegram-plugin/turn-signal-tracker.ts +100 -24
- package/telegram-plugin/uat/SETUP.md +210 -35
- package/telegram-plugin/uat/assertions.ts +264 -37
- package/telegram-plugin/uat/driver-info.ts +57 -0
- package/telegram-plugin/uat/driver.ts +590 -51
- package/telegram-plugin/uat/harness.ts +140 -94
- package/telegram-plugin/uat/load-env.test.ts +72 -0
- package/telegram-plugin/uat/load-env.ts +48 -0
- package/telegram-plugin/uat/login.ts +96 -53
- package/telegram-plugin/uat/runners/agent-self-sufficiency.ts +457 -0
- package/telegram-plugin/uat/runners/paraphrases.ts +231 -0
- package/telegram-plugin/uat/runners/report.ts +150 -0
- package/telegram-plugin/uat/runners/run-agent-self-sufficiency.sh +50 -0
- package/telegram-plugin/uat/runners/scorer.test.ts +196 -0
- package/telegram-plugin/uat/runners/scorer.ts +106 -0
- package/telegram-plugin/uat/runners/skill-coverage.test.ts +100 -0
- package/telegram-plugin/uat/runners/skill-coverage.ts +620 -0
- package/telegram-plugin/uat/scenarios/ask-user-button-tap-dm.test.ts +141 -0
- package/telegram-plugin/uat/scenarios/bg-sub-agent-dispatch-dm.test.ts +191 -0
- package/telegram-plugin/uat/scenarios/fuzz-extended-dm.test.ts +255 -0
- package/telegram-plugin/uat/scenarios/fuzz-human-style-dm.test.ts +275 -0
- package/telegram-plugin/uat/scenarios/fuzz-random-prompts-dm.test.ts +146 -0
- package/telegram-plugin/uat/scenarios/fuzz-status-ask-dm.test.ts +486 -0
- package/telegram-plugin/uat/scenarios/jtbd-interrupt-marker-dm.test.ts +67 -0
- package/telegram-plugin/uat/scenarios/jtbd-rapid-followup-dm.test.ts +100 -0
- package/telegram-plugin/uat/scenarios/jtbd-soft-commit-dm.test.ts +67 -0
- package/telegram-plugin/uat/scenarios/jtbd-status-query-dm.test.ts +49 -0
- package/telegram-plugin/uat/scenarios/location-inbound-dm.test.ts +65 -0
- package/telegram-plugin/uat/scenarios/midturn-silent-dm.test.ts +175 -0
- package/telegram-plugin/uat/scenarios/reactions-dm.test.ts +142 -0
- package/telegram-plugin/uat/scenarios/reactions-trigger-turn-dm.test.ts +96 -0
- package/telegram-plugin/uat/scenarios/secret-redaction-deletes-original-dm.test.ts +123 -0
- package/telegram-plugin/uat/scenarios/secret-redaction-no-false-positive-dm.test.ts +87 -0
- package/telegram-plugin/uat/scenarios/silence-poke-soft-dm.test.ts +155 -0
- package/telegram-plugin/uat/scenarios/silent-end-recovery-dm.test.ts +95 -0
- package/telegram-plugin/uat/scenarios/smoke-dm-reply.test.ts +57 -0
- package/telegram-plugin/uat/scenarios/subagent-watcher-no-rerun-dm.test.ts +135 -0
- package/telegram-plugin/uat/scenarios/vault-approval-posture-telegram-id-dm.test.ts +191 -0
- package/telegram-plugin/uat/scenarios/vault-audit-allow-dm.test.ts +108 -0
- package/telegram-plugin/uat/scenarios/vault-grant-auto-resume-dm.test.ts +121 -0
- package/telegram-plugin/uat/scenarios/vault-request-access-concurrent-dm.test.ts +161 -0
- package/telegram-plugin/uat/scenarios/vault-request-access-end-to-end-dm.test.ts +158 -0
- package/telegram-plugin/uat/scenarios/voice-inbound-dm.test.ts +65 -0
- package/telegram-plugin/vault-approval-posture.ts +42 -0
- package/telegram-plugin/welcome-text.ts +1 -0
- package/telegram-plugin/active-pins-sweep.ts +0 -204
- package/telegram-plugin/active-pins.ts +0 -146
- package/telegram-plugin/auth-dashboard.ts +0 -1104
- package/telegram-plugin/auth-slot-parser.ts +0 -497
- package/telegram-plugin/card-event-log.ts +0 -138
- package/telegram-plugin/dist/foreman/foreman.js +0 -31106
- package/telegram-plugin/docs/multi-agent-card-design.md +0 -847
- package/telegram-plugin/docs/pinned-progress-card-reliability.md +0 -144
- package/telegram-plugin/foreman/foreman-create-flow.ts +0 -202
- package/telegram-plugin/foreman/foreman-handlers.ts +0 -493
- package/telegram-plugin/foreman/foreman.ts +0 -1165
- package/telegram-plugin/foreman/setup-flow.ts +0 -345
- package/telegram-plugin/foreman/setup-state.ts +0 -239
- package/telegram-plugin/foreman/state.ts +0 -203
- package/telegram-plugin/pin-event-log.ts +0 -76
- package/telegram-plugin/progress-card-driver.ts +0 -2886
- package/telegram-plugin/progress-card-pin-manager.ts +0 -589
- package/telegram-plugin/progress-card-pin-watchdog.ts +0 -98
- package/telegram-plugin/progress-card.ts +0 -1409
- package/telegram-plugin/tests/HARNESS.md +0 -340
- package/telegram-plugin/tests/_progress-card-harness.ts +0 -109
- package/telegram-plugin/tests/active-pins-boot-reaper.test.ts +0 -211
- package/telegram-plugin/tests/active-pins-sweep.test.ts +0 -309
- package/telegram-plugin/tests/active-pins.test.ts +0 -187
- package/telegram-plugin/tests/auth-account-identity-surface.test.ts +0 -118
- package/telegram-plugin/tests/auth-dashboard-edge-cases.test.ts +0 -260
- package/telegram-plugin/tests/auth-dashboard-restart-flow.test.ts +0 -140
- package/telegram-plugin/tests/auth-dashboard-v3b.test.ts +0 -559
- package/telegram-plugin/tests/auth-dashboard.test.ts +0 -1045
- package/telegram-plugin/tests/auth-slot-commands.test.ts +0 -640
- package/telegram-plugin/tests/bg-agent-progress-card-757.test.ts +0 -201
- package/telegram-plugin/tests/boot-card-account-quota.test.ts +0 -137
- package/telegram-plugin/tests/card-event-log.test.ts +0 -145
- package/telegram-plugin/tests/first-paint.test.ts +0 -257
- package/telegram-plugin/tests/foreman-create-flow.test.ts +0 -359
- package/telegram-plugin/tests/foreman-handlers.test.ts +0 -347
- package/telegram-plugin/tests/foreman-state.test.ts +0 -164
- package/telegram-plugin/tests/foreman-write-ops.test.ts +0 -214
- package/telegram-plugin/tests/harness-ordering-invariants.test.ts +0 -243
- package/telegram-plugin/tests/pin-event-log.test.ts +0 -124
- package/telegram-plugin/tests/progress-card-api-failure-during-deferred.test.ts +0 -73
- package/telegram-plugin/tests/progress-card-close-paths-converge.test.ts +0 -272
- package/telegram-plugin/tests/progress-card-cross-turn.test.ts +0 -258
- package/telegram-plugin/tests/progress-card-delay-842.test.ts +0 -160
- package/telegram-plugin/tests/progress-card-dispose-preservepending.test.ts +0 -81
- package/telegram-plugin/tests/progress-card-draft-flag.test.ts +0 -80
- package/telegram-plugin/tests/progress-card-driver-eviction.test.ts +0 -215
- package/telegram-plugin/tests/progress-card-driver-fleet-shadow.test.ts +0 -123
- package/telegram-plugin/tests/progress-card-driver-force-complete-parent-done.test.ts +0 -76
- package/telegram-plugin/tests/progress-card-edit-timestamps-budget.test.ts +0 -62
- package/telegram-plugin/tests/progress-card-memory-bounds.test.ts +0 -84
- package/telegram-plugin/tests/progress-card-pin-failure-paths.test.ts +0 -139
- package/telegram-plugin/tests/progress-card-pin-manager.test.ts +0 -773
- package/telegram-plugin/tests/progress-card-pin-race-fast-turn.test.ts +0 -66
- package/telegram-plugin/tests/progress-card-pin-sidecar-partial-write.test.ts +0 -64
- package/telegram-plugin/tests/progress-card-pin-watchdog.test.ts +0 -190
- package/telegram-plugin/tests/progress-card-sigterm-pin-flush.test.ts +0 -146
- package/telegram-plugin/tests/real-gateway-f1-ladder-integrity.test.ts +0 -123
- package/telegram-plugin/tests/real-gateway-f2-instant-draft.test.ts +0 -82
- package/telegram-plugin/tests/real-gateway-f3-late-card.test.ts +0 -114
- package/telegram-plugin/tests/real-gateway-harness.ts +0 -699
- package/telegram-plugin/tests/real-gateway-i6-turn-flush-replay-dedup.test.ts +0 -313
- package/telegram-plugin/tests/real-gateway-ipc-lifecycle.test.ts +0 -299
- package/telegram-plugin/tests/real-gateway-spec.test.ts +0 -487
- package/telegram-plugin/tests/real-gateway.smoke.test.ts +0 -101
- package/telegram-plugin/tests/setup-flow.test.ts +0 -510
- package/telegram-plugin/tests/setup-state.test.ts +0 -146
- package/telegram-plugin/tests/sync-chat-running-subagents.test.ts +0 -116
- package/telegram-plugin/tests/turn-end-regressions.test.ts +0 -489
- package/telegram-plugin/tests/turn-flush-card-takeover.test.ts +0 -218
- package/telegram-plugin/tests/turn-flush-prose-recovery.test.ts +0 -78
- package/telegram-plugin/tests/two-zone-bg-carry-full-lifecycle.test.ts +0 -131
- package/telegram-plugin/tests/two-zone-bg-detection.test.ts +0 -120
- package/telegram-plugin/tests/two-zone-bg-done-when-all-terminal.test.ts +0 -116
- package/telegram-plugin/tests/two-zone-bg-early-turn-end.test.ts +0 -87
- package/telegram-plugin/tests/two-zone-bg-survives-next-turn.test.ts +0 -211
- package/telegram-plugin/tests/two-zone-card-cap.test.ts +0 -62
- package/telegram-plugin/tests/two-zone-card-fleet-row.test.ts +0 -101
- package/telegram-plugin/tests/two-zone-card-header-phases.test.ts +0 -78
- package/telegram-plugin/tests/two-zone-card-html-balance.test.ts +0 -110
- package/telegram-plugin/tests/two-zone-card-lifecycle.test.ts +0 -128
- package/telegram-plugin/tests/two-zone-card-sanitise.test.ts +0 -58
- package/telegram-plugin/tests/two-zone-card-snapshot.test.ts +0 -133
- package/telegram-plugin/tests/two-zone-concurrent-turns-isolation.test.ts +0 -155
- package/telegram-plugin/tests/two-zone-phasefor-precedence.test.ts +0 -117
- package/telegram-plugin/tests/two-zone-snapshot-extras.test.ts +0 -187
- package/telegram-plugin/tests/two-zone-stuck-edit-throttle.test.ts +0 -149
- package/telegram-plugin/tests/two-zone-stuck-header-escalation.test.ts +0 -101
- package/telegram-plugin/tests/two-zone-stuck-per-member.test.ts +0 -114
- package/telegram-plugin/tests/two-zone-stuck-recovery.test.ts +0 -105
- package/telegram-plugin/tests/waiting-ux-harness.ts +0 -381
- package/telegram-plugin/tests/waiting-ux.e2e.test.ts +0 -233
- package/telegram-plugin/turn-flush-prose-recovery.ts +0 -40
- package/telegram-plugin/two-zone-card.ts +0 -269
- package/telegram-plugin/uat/scenarios/smoke-clerk-reply.test.ts +0 -61
|
@@ -68,71 +68,298 @@ export async function expectEventually(
|
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
// ---------- Phase
|
|
71
|
+
// ---------- Phase 2a (DM smoke) ----------
|
|
72
|
+
|
|
73
|
+
export interface ExpectMessageOptions extends PollOptions {
|
|
74
|
+
threadId?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Filter the observed stream by sender. `userId` matches exact
|
|
77
|
+
* senders; `notUserId` excludes a specific sender (used by the
|
|
78
|
+
* harness to translate `from: "bot"` into "anyone but the driver").
|
|
79
|
+
*/
|
|
80
|
+
senderFilter?: { userId: number } | { notUserId: number };
|
|
81
|
+
}
|
|
72
82
|
|
|
73
83
|
/**
|
|
74
|
-
*
|
|
75
|
-
* matching `match`
|
|
76
|
-
* `ObservedMessage
|
|
84
|
+
* Wait for the next message in `chatId` (optionally a forum topic)
|
|
85
|
+
* matching `match` — a substring, regex, or predicate over the raw
|
|
86
|
+
* `ObservedMessage`. Returns the matched message.
|
|
87
|
+
*
|
|
88
|
+
* The implementation iterates the live `driver.observeMessages`
|
|
89
|
+
* stream, so messages sent *before* the call started are not
|
|
90
|
+
* considered; backfill is a Phase 2b helper.
|
|
77
91
|
*/
|
|
78
92
|
export async function expectMessage(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
93
|
+
driver: Driver,
|
|
94
|
+
chatId: number,
|
|
95
|
+
match: string | RegExp | ((m: ObservedMessage) => boolean),
|
|
96
|
+
opts: ExpectMessageOptions,
|
|
83
97
|
): Promise<ObservedMessage> {
|
|
84
|
-
|
|
98
|
+
const predicate = compileMatcher(match);
|
|
99
|
+
const senderOk = compileSenderFilter(opts.senderFilter);
|
|
100
|
+
const iter = driver.observeMessages(chatId, opts.threadId !== undefined ? { threadId: opts.threadId } : undefined)[Symbol.asyncIterator]();
|
|
101
|
+
const deadline = Date.now() + opts.timeout;
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
while (Date.now() < deadline) {
|
|
105
|
+
// Race the next observation against the remaining timeout so
|
|
106
|
+
// we don't hang forever if no messages arrive.
|
|
107
|
+
const remaining = deadline - Date.now();
|
|
108
|
+
const next = await raceTimeout(iter.next(), remaining);
|
|
109
|
+
if (next === "timeout") break;
|
|
110
|
+
if (next.done === true) break;
|
|
111
|
+
const msg = next.value;
|
|
112
|
+
if (!senderOk(msg)) continue;
|
|
113
|
+
if (predicate(msg)) return msg;
|
|
114
|
+
}
|
|
115
|
+
} finally {
|
|
116
|
+
await iter.return?.();
|
|
117
|
+
}
|
|
118
|
+
throw new Error(
|
|
119
|
+
`expectMessage: no matching message in chat=${chatId} within ${opts.timeout}ms`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function compileMatcher(
|
|
124
|
+
match: string | RegExp | ((m: ObservedMessage) => boolean),
|
|
125
|
+
): (m: ObservedMessage) => boolean {
|
|
126
|
+
if (typeof match === "string") return (m) => m.text.includes(match);
|
|
127
|
+
if (match instanceof RegExp) return (m) => match.test(m.text);
|
|
128
|
+
return match;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function compileSenderFilter(
|
|
132
|
+
f: ExpectMessageOptions["senderFilter"],
|
|
133
|
+
): (m: ObservedMessage) => boolean {
|
|
134
|
+
if (!f) return () => true;
|
|
135
|
+
if ("userId" in f) return (m) => m.senderUserId === f.userId;
|
|
136
|
+
return (m) => m.senderUserId !== f.notUserId;
|
|
85
137
|
}
|
|
86
138
|
|
|
139
|
+
function raceTimeout<T>(p: Promise<T>, ms: number): Promise<T | "timeout"> {
|
|
140
|
+
if (ms <= 0) return Promise.resolve("timeout");
|
|
141
|
+
return new Promise<T | "timeout">((resolve) => {
|
|
142
|
+
const t = setTimeout(() => resolve("timeout"), ms);
|
|
143
|
+
p.then((v) => {
|
|
144
|
+
clearTimeout(t);
|
|
145
|
+
resolve(v);
|
|
146
|
+
}).catch(() => {
|
|
147
|
+
clearTimeout(t);
|
|
148
|
+
resolve("timeout");
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ---------- Phase 2b stubs (deferred to follow-up PR) ----------
|
|
154
|
+
|
|
87
155
|
/**
|
|
88
|
-
*
|
|
89
|
-
* emoji
|
|
90
|
-
* other
|
|
91
|
-
*
|
|
156
|
+
* Wait for a reaction `sequence` on `messageId` in `chatId`. Each
|
|
157
|
+
* emoji must appear (as an add `+` op) in the order given;
|
|
158
|
+
* intermediate add/remove ops for other emojis are tolerated.
|
|
159
|
+
*
|
|
160
|
+
* Returns the full observed trail (every add/remove seen up to and
|
|
161
|
+
* including the final match) so scenarios can do follow-up
|
|
162
|
+
* assertions on the order or count.
|
|
163
|
+
*
|
|
164
|
+
* Production note: the gateway calls `setMessageReaction` which
|
|
165
|
+
* REPLACES the prior emoji (not adds-in-addition-to). So an
|
|
166
|
+
* 👀 → 🤔 transition emits `-👀` and `+🤔` in the same observation
|
|
167
|
+
* window. This helper only watches the `+` ops, so the
|
|
168
|
+
* gateway-replace pattern reads as a clean sequence.
|
|
169
|
+
*
|
|
170
|
+
* Fast-turn note: turns shorter than the gateway's
|
|
171
|
+
* `progress-card initialDelayMs` may collapse intermediate
|
|
172
|
+
* reactions — you might only see 👀 and 👍. The sequence-match
|
|
173
|
+
* tolerates this: every emoji in `sequence` must appear in order,
|
|
174
|
+
* but we don't require it to be the ONLY emojis added.
|
|
92
175
|
*/
|
|
93
176
|
export async function expectReaction(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
177
|
+
driver: Driver,
|
|
178
|
+
chatId: number,
|
|
179
|
+
messageId: number,
|
|
180
|
+
sequence: string[],
|
|
181
|
+
opts: PollOptions,
|
|
99
182
|
): Promise<ObservedReaction[]> {
|
|
100
|
-
|
|
183
|
+
if (sequence.length === 0) {
|
|
184
|
+
throw new Error("expectReaction: sequence must be non-empty");
|
|
185
|
+
}
|
|
186
|
+
const trail: ObservedReaction[] = [];
|
|
187
|
+
const iter = driver.observeReactions(chatId, { messageId })[Symbol.asyncIterator]();
|
|
188
|
+
const deadline = Date.now() + opts.timeout;
|
|
189
|
+
let cursor = 0;
|
|
190
|
+
try {
|
|
191
|
+
while (Date.now() < deadline && cursor < sequence.length) {
|
|
192
|
+
const remaining = deadline - Date.now();
|
|
193
|
+
const next = await raceTimeout(iter.next(), remaining);
|
|
194
|
+
if (next === "timeout") break;
|
|
195
|
+
if (next.done === true) break;
|
|
196
|
+
const r = next.value;
|
|
197
|
+
trail.push(r);
|
|
198
|
+
if (r.op === "+" && r.emoji === sequence[cursor]) {
|
|
199
|
+
cursor++;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
} finally {
|
|
203
|
+
await iter.return?.();
|
|
204
|
+
}
|
|
205
|
+
if (cursor < sequence.length) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
`expectReaction: saw ${cursor}/${sequence.length} expected emoji ` +
|
|
208
|
+
`(missing ${sequence.slice(cursor).map((e) => JSON.stringify(e)).join(", ")}) ` +
|
|
209
|
+
`on chat=${chatId} msg=${messageId} within ${opts.timeout}ms ` +
|
|
210
|
+
`(observed ops: ${trail.map((t) => `${t.op}${t.emoji}`).join(" ")})`,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
return trail;
|
|
101
214
|
}
|
|
102
215
|
|
|
216
|
+
export type CardPhase = "boot" | "working" | "background" | "done" | "error";
|
|
217
|
+
|
|
103
218
|
export interface PinnedCardSnapshot {
|
|
219
|
+
chatId: number;
|
|
104
220
|
messageId: number;
|
|
105
221
|
text: string;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
phase: string;
|
|
222
|
+
/** Detected phase, or `"unknown"` when the text doesn't match any marker. */
|
|
223
|
+
phase: CardPhase | "unknown";
|
|
109
224
|
}
|
|
110
225
|
|
|
111
226
|
/**
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
227
|
+
* Wait for a pinned message to appear in `chatId` (the progress
|
|
228
|
+
* card). Resolves with a snapshot of its current text + phase.
|
|
229
|
+
*
|
|
230
|
+
* Implementation:
|
|
231
|
+
* 1. Subscribe to `driver.observePins(chatId)`.
|
|
232
|
+
* 2. On the first pin event, fetch the message text via
|
|
233
|
+
* `driver.getMessage` (the pin update carries only ids).
|
|
234
|
+
* 3. Return a snapshot with the parsed phase.
|
|
235
|
+
*
|
|
236
|
+
* Fast-turn note: the gateway's `progress_card.delay_ms` (default
|
|
237
|
+
* 45s) suppresses the card entirely for short turns. UAT runs
|
|
238
|
+
* against the standard-runtime test-harness agent will time out
|
|
239
|
+
* here unless the operator sets `delay_ms` to something small in
|
|
240
|
+
* `switchroom.yaml` under the agent's `channels.telegram` block.
|
|
115
241
|
*/
|
|
116
242
|
export async function expectPinnedCard(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
243
|
+
driver: Driver,
|
|
244
|
+
chatId: number,
|
|
245
|
+
opts: PollOptions & { threadId?: number },
|
|
120
246
|
): Promise<PinnedCardSnapshot> {
|
|
121
|
-
|
|
247
|
+
void opts.threadId; // forum/topic routing rolls in with Phase 2d
|
|
248
|
+
const iter = driver.observePins(chatId)[Symbol.asyncIterator]();
|
|
249
|
+
const deadline = Date.now() + opts.timeout;
|
|
250
|
+
try {
|
|
251
|
+
while (Date.now() < deadline) {
|
|
252
|
+
const remaining = deadline - Date.now();
|
|
253
|
+
const next = await raceTimeout(iter.next(), remaining);
|
|
254
|
+
if (next === "timeout") break;
|
|
255
|
+
if (next.done === true) break;
|
|
256
|
+
const pin = next.value;
|
|
257
|
+
if (!pin.pinned) continue; // skip unpin events; we want pins
|
|
258
|
+
// Fetch the message body to compose the snapshot. If the
|
|
259
|
+
// message has been deleted in the gap between pin event and
|
|
260
|
+
// lookup, treat as a miss and keep waiting.
|
|
261
|
+
const msg = await driver.getMessage(chatId, pin.messageId).catch(() => null);
|
|
262
|
+
if (!msg) continue;
|
|
263
|
+
return {
|
|
264
|
+
chatId,
|
|
265
|
+
messageId: pin.messageId,
|
|
266
|
+
text: msg.text,
|
|
267
|
+
phase: detectPhase(msg.text),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
} finally {
|
|
271
|
+
await iter.return?.();
|
|
272
|
+
}
|
|
273
|
+
throw new Error(
|
|
274
|
+
`expectPinnedCard: no pinned message in chat=${chatId} within ${opts.timeout}ms`,
|
|
275
|
+
);
|
|
122
276
|
}
|
|
123
277
|
|
|
124
278
|
/**
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
279
|
+
* Wait for the pinned progress card to transition to `phase`.
|
|
280
|
+
*
|
|
281
|
+
* The gateway updates the card via `editMessage`, NOT by sending a
|
|
282
|
+
* new message. We observe edits via `driver.observeMessages` on
|
|
283
|
+
* the card's chat, filter to `msg.messageId === card.messageId`,
|
|
284
|
+
* and detect the target phase via the same regex set as
|
|
285
|
+
* `detectPhase`. If the snapshot we were handed is already at the
|
|
286
|
+
* target phase (e.g. a fast-turn scenario where the card was
|
|
287
|
+
* first-rendered straight at `done`), resolve immediately.
|
|
288
|
+
*
|
|
289
|
+
* The iterator yields a fresh `PinnedCardSnapshot` for each
|
|
290
|
+
* matching edit, so callers can chain phase transitions
|
|
291
|
+
* (`boot → working → done`) without re-subscribing.
|
|
128
292
|
*/
|
|
129
293
|
export async function waitForCardPhase(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
294
|
+
driver: Driver,
|
|
295
|
+
card: PinnedCardSnapshot,
|
|
296
|
+
phase: CardPhase,
|
|
297
|
+
opts: PollOptions,
|
|
134
298
|
): Promise<PinnedCardSnapshot> {
|
|
135
|
-
|
|
299
|
+
if (detectPhase(card.text) === phase) {
|
|
300
|
+
// Refresh the phase field — the snapshot we were handed may
|
|
301
|
+
// have a stale `phase` from when the snapshot was captured
|
|
302
|
+
// (e.g. pin-time at "boot") even though the text has since
|
|
303
|
+
// been edited to a later phase. Returning the input as-is
|
|
304
|
+
// would surface that stale phase to the caller.
|
|
305
|
+
return { ...card, phase };
|
|
306
|
+
}
|
|
307
|
+
const iter = driver
|
|
308
|
+
.observeMessages(card.chatId)
|
|
309
|
+
[Symbol.asyncIterator]();
|
|
310
|
+
const deadline = Date.now() + opts.timeout;
|
|
311
|
+
try {
|
|
312
|
+
while (Date.now() < deadline) {
|
|
313
|
+
const remaining = deadline - Date.now();
|
|
314
|
+
const next = await raceTimeout(iter.next(), remaining);
|
|
315
|
+
if (next === "timeout") break;
|
|
316
|
+
if (next.done === true) break;
|
|
317
|
+
const msg = next.value;
|
|
318
|
+
if (msg.messageId !== card.messageId) continue;
|
|
319
|
+
const detected = detectPhase(msg.text);
|
|
320
|
+
if (detected === phase) {
|
|
321
|
+
return {
|
|
322
|
+
chatId: card.chatId,
|
|
323
|
+
messageId: card.messageId,
|
|
324
|
+
text: msg.text,
|
|
325
|
+
phase,
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
} finally {
|
|
330
|
+
await iter.return?.();
|
|
331
|
+
}
|
|
332
|
+
throw new Error(
|
|
333
|
+
`waitForCardPhase: card ${card.messageId} did not reach phase="${phase}" within ${opts.timeout}ms`,
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Detect the progress card's phase from its rendered text.
|
|
339
|
+
*
|
|
340
|
+
* The actual card render (telegram-plugin/progress-card.ts) uses
|
|
341
|
+
* emoji markers in the header: `✅` for done, `❌` for errors, `⚙️`
|
|
342
|
+
* while working (foreground), `🌀` for Background (parent done but
|
|
343
|
+
* fleet still running, see #862 / status-card-design.md §Header),
|
|
344
|
+
* and `⏳` during the boot-card window. These markers are stable
|
|
345
|
+
* enough to key on for UAT — finer parsing (checklist items,
|
|
346
|
+
* sub-agent row content) is out of scope.
|
|
347
|
+
*
|
|
348
|
+
* Phase precedence (highest first): error → done → background →
|
|
349
|
+
* working → boot. "background" sits above "working" because the
|
|
350
|
+
* Background phase implies parent reached terminal state — the
|
|
351
|
+
* card is no longer in foreground "working" mode even though the
|
|
352
|
+
* fleet is still alive. Tests asserting "is still working" should
|
|
353
|
+
* use either "working" or "background" depending on whether the
|
|
354
|
+
* parent has replied.
|
|
355
|
+
*/
|
|
356
|
+
function detectPhase(text: string): CardPhase | "unknown" {
|
|
357
|
+
if (/❌|\bFailed\b|\bError\b/i.test(text)) return "error";
|
|
358
|
+
if (/✅|\bDone\b/i.test(text)) return "done";
|
|
359
|
+
if (/🌀|\bBackground\b/i.test(text)) return "background";
|
|
360
|
+
if (/⚙️|🤖|\bWorking\b/i.test(text)) return "working";
|
|
361
|
+
if (/⏳|\bStarting\b/i.test(text)) return "boot";
|
|
362
|
+
return "unknown";
|
|
136
363
|
}
|
|
137
364
|
|
|
138
365
|
function sleep(ms: number): Promise<void> {
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bun uat/driver-info.ts` — prints the driver user account's
|
|
3
|
+
* numeric `user_id` to stdout. Used during one-shot `test-harness`
|
|
4
|
+
* agent creation (see uat/SETUP.md §5) to populate `--allow-from`.
|
|
5
|
+
*
|
|
6
|
+
* Issue: https://github.com/switchroom/switchroom/issues/866
|
|
7
|
+
*
|
|
8
|
+
* Reads vault credentials via `switchroom vault get --no-broker`
|
|
9
|
+
* (the operator already has `SWITCHROOM_VAULT_PASSPHRASE` set per
|
|
10
|
+
* the runbook). The session string lives in-process only; never
|
|
11
|
+
* printed.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { execFileSync } from "node:child_process";
|
|
15
|
+
import { Driver } from "./driver.js";
|
|
16
|
+
|
|
17
|
+
async function main(): Promise<void> {
|
|
18
|
+
const apiIdRaw = vaultGet("telegram-uat-api-id");
|
|
19
|
+
const apiHash = vaultGet("telegram-uat-api-hash");
|
|
20
|
+
const session = vaultGet("telegram-uat-driver-session");
|
|
21
|
+
|
|
22
|
+
const apiId = Number.parseInt(apiIdRaw, 10);
|
|
23
|
+
if (!Number.isFinite(apiId)) {
|
|
24
|
+
process.stderr.write(
|
|
25
|
+
`uat/driver-info: invalid TELEGRAM_API_ID in vault (got '${apiIdRaw}')\n`,
|
|
26
|
+
);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const driver = new Driver({ apiId, apiHash, session });
|
|
31
|
+
try {
|
|
32
|
+
await driver.connect();
|
|
33
|
+
const uid = await driver.getMyUserId();
|
|
34
|
+
process.stdout.write(`${uid}\n`);
|
|
35
|
+
} finally {
|
|
36
|
+
await driver.disconnect().catch(() => undefined);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function vaultGet(key: string): string {
|
|
41
|
+
const out = execFileSync(
|
|
42
|
+
"switchroom",
|
|
43
|
+
["vault", "get", "--no-broker", key],
|
|
44
|
+
{ encoding: "utf8", stdio: ["inherit", "pipe", "inherit"] },
|
|
45
|
+
);
|
|
46
|
+
return out.trim();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
main().catch((err) => {
|
|
50
|
+
// Strip any long base64 blob defensively (same posture as login.ts).
|
|
51
|
+
const sanitized = String(err?.message ?? err).replace(
|
|
52
|
+
/[A-Za-z0-9+/=_-]{64,}/g,
|
|
53
|
+
"<redacted>",
|
|
54
|
+
);
|
|
55
|
+
process.stderr.write(`uat/driver-info failed: ${sanitized}\n`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
});
|