switchroom 0.15.36 → 0.15.38
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 +10 -9
- package/dist/auth-broker/index.js +9 -9
- package/dist/cli/autoaccept-poll.js +13 -7
- package/dist/cli/notion-write-pretool.mjs +9 -9
- package/dist/cli/switchroom.js +480 -217
- package/dist/cli/ui/index.html +87 -17
- package/dist/host-control/main.js +10 -10
- package/dist/vault/approvals/kernel-server.js +9 -9
- package/dist/vault/broker/server.js +9 -9
- package/package.json +1 -1
- package/profiles/_base/cron-session.sh.hbs +1 -1
- package/profiles/_base/start.sh.hbs +1 -1
- package/profiles/_shared/agent-self-service.md.hbs +25 -0
- package/skills/switchroom-manage/SKILL.md +1 -1
- package/skills/switchroom-runtime/SKILL.md +1 -1
- package/telegram-plugin/answer-stream.ts +1 -1
- package/telegram-plugin/bridge/bridge.ts +50 -1
- package/telegram-plugin/bridge/ipc-client.ts +4 -1
- package/telegram-plugin/bridge/tool-filter.ts +77 -0
- package/telegram-plugin/chat-lock.ts +1 -1
- package/telegram-plugin/credits-watch.ts +1 -1
- package/telegram-plugin/dist/bridge/bridge.js +60 -3
- package/telegram-plugin/dist/gateway/gateway.js +753 -207
- package/telegram-plugin/dist/server.js +64 -4
- package/telegram-plugin/gateway/auto-classify-mid-turn.ts +1 -1
- package/telegram-plugin/gateway/boot-card.ts +5 -1
- package/telegram-plugin/gateway/boot-probes.ts +62 -0
- package/telegram-plugin/gateway/cron-session.ts +1 -1
- package/telegram-plugin/gateway/gateway.ts +254 -15
- package/telegram-plugin/gateway/grant-restart.ts +1 -1
- package/telegram-plugin/gateway/inbound-delivery-machine-dispatch.ts +1 -1
- package/telegram-plugin/gateway/inbound-delivery-machine-shadow.ts +1 -1
- package/telegram-plugin/gateway/inbound-delivery-machine.ts +1 -1
- package/telegram-plugin/gateway/interrupt-defer.ts +1 -1
- package/telegram-plugin/gateway/ipc-protocol.ts +12 -0
- package/telegram-plugin/gateway/linear-activity.ts +56 -0
- package/telegram-plugin/gateway/linear-auth-watch.ts +102 -0
- package/telegram-plugin/gateway/linear-setup.ts +196 -0
- package/telegram-plugin/gateway/permission-card-origin.ts +62 -0
- package/telegram-plugin/gateway/permission-timeout.ts +70 -0
- package/telegram-plugin/gateway/prefix-warmup.ts +1 -1
- package/telegram-plugin/gateway/webhook-ingest-server.test.ts +1 -1
- package/telegram-plugin/gateway/webhook-ingest-server.ts +1 -1
- package/telegram-plugin/hooks/subagent-tracker-pretool.mjs +1 -1
- package/telegram-plugin/interrupt-marker.ts +1 -1
- package/telegram-plugin/over-ping-safety-net.ts +1 -1
- package/telegram-plugin/scoped-approval.ts +1 -1
- package/telegram-plugin/secret-detect/vault-error.ts +1 -1
- package/telegram-plugin/silence-poke.ts +2 -2
- package/telegram-plugin/silent-reply-anchor.ts +1 -1
- package/telegram-plugin/slot-banner-driver.ts +1 -1
- package/telegram-plugin/startup-reset.ts +1 -1
- package/telegram-plugin/tests/boot-probes-connections.test.ts +66 -0
- package/telegram-plugin/tests/gateway-startup-reset.test.ts +1 -1
- package/telegram-plugin/tests/inbound-delivery-machine.test.ts +1 -1
- package/telegram-plugin/tests/linear-agent-activity.test.ts +77 -0
- package/telegram-plugin/tests/linear-agent-setup.test.ts +132 -0
- package/telegram-plugin/tests/linear-auth-watch.test.ts +79 -0
- package/telegram-plugin/tests/linear-create-issue.test.ts +3 -1
- package/telegram-plugin/tests/permission-card-origin.test.ts +97 -0
- package/telegram-plugin/tests/permission-card-routing.test.ts +23 -0
- package/telegram-plugin/tests/permission-no-repeat-wiring.test.ts +76 -0
- package/telegram-plugin/tests/permission-timeout.test.ts +87 -0
- package/telegram-plugin/tests/scoped-approval.test.ts +1 -1
- package/telegram-plugin/tests/silence-poke.test.ts +1 -1
- package/telegram-plugin/tests/tool-filter.test.ts +87 -0
- package/telegram-plugin/tests/turn-flush-safety.test.ts +1 -1
- package/telegram-plugin/turn-flush-safety.ts +1 -1
- package/telegram-plugin/uat/assertions.ts +1 -1
- package/telegram-plugin/uat/scenarios/bg-sub-agent-dispatch-dm.test.ts +1 -1
- package/telegram-plugin/uat/scenarios/fuzz-extended-dm.test.ts +1 -1
- package/telegram-plugin/uat/scenarios/jtbd-fast-ack-dm.test.ts +1 -1
- package/telegram-plugin/uat/scenarios/jtbd-fast-trivial-dm.test.ts +2 -2
- package/telegram-plugin/uat/scenarios/jtbd-forwarded-burst-dm.test.ts +1 -1
- package/telegram-plugin/uat/scenarios/jtbd-memory-survives-restart-dm.test.ts +1 -1
- package/telegram-plugin/uat/scenarios/jtbd-rapid-followup-dm.test.ts +1 -1
- package/telegram-plugin/uat/scenarios/jtbd-reflective-status-reaction-dm.test.ts +1 -1
- package/telegram-plugin/uat/scenarios/jtbd-wake-audit-content-dm.test.ts +1 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the switchroom-telegram tool-surface right-sizing (P4):
|
|
3
|
+
* connection gating of linear_* (A) + per-tool alwaysLoad pins for the hot
|
|
4
|
+
* path (B). Pure function — no bridge.ts import (which has side effects).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect } from 'bun:test'
|
|
8
|
+
import {
|
|
9
|
+
buildEffectiveToolSchemas,
|
|
10
|
+
ALWAYS_LOAD_TOOLS,
|
|
11
|
+
LINEAR_TOOLS,
|
|
12
|
+
type NamedTool,
|
|
13
|
+
} from '../bridge/tool-filter.js'
|
|
14
|
+
|
|
15
|
+
// A representative slice mirroring the real TOOL_SCHEMAS names.
|
|
16
|
+
const SAMPLE: NamedTool[] = [
|
|
17
|
+
{ name: 'reply', description: 'r' },
|
|
18
|
+
{ name: 'stream_reply', description: 's' },
|
|
19
|
+
{ name: 'get_recent_messages', description: 'g' },
|
|
20
|
+
{ name: 'react', description: 'k' },
|
|
21
|
+
{ name: 'edit_message', description: 'e' },
|
|
22
|
+
{ name: 'send_typing', description: 't' },
|
|
23
|
+
{ name: 'download_attachment', description: 'd' },
|
|
24
|
+
{ name: 'ask_user', description: 'a' }, // cold
|
|
25
|
+
{ name: 'send_gif', description: 'gif' }, // cold
|
|
26
|
+
{ name: 'vault_request_access', description: 'v' }, // cold
|
|
27
|
+
{ name: 'linear_agent_activity', description: 'la' },
|
|
28
|
+
{ name: 'linear_create_issue', description: 'lc' },
|
|
29
|
+
{ name: 'linear_agent_setup', description: 'ls' },
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
const names = (tools: NamedTool[]) => tools.map((t) => t.name)
|
|
33
|
+
const metaOf = (tools: Array<NamedTool & { _meta?: unknown }>, n: string) =>
|
|
34
|
+
tools.find((t) => t.name === n)?._meta
|
|
35
|
+
|
|
36
|
+
describe('buildEffectiveToolSchemas — connection gating (A)', () => {
|
|
37
|
+
it('drops all linear_* tools when Linear is NOT enabled', () => {
|
|
38
|
+
const out = buildEffectiveToolSchemas(SAMPLE, { linearEnabled: false })
|
|
39
|
+
for (const t of LINEAR_TOOLS) expect(names(out)).not.toContain(t)
|
|
40
|
+
// non-linear tools all survive
|
|
41
|
+
expect(names(out)).toContain('reply')
|
|
42
|
+
expect(names(out)).toContain('ask_user')
|
|
43
|
+
expect(out.length).toBe(SAMPLE.length - LINEAR_TOOLS.size)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('keeps linear_* tools when Linear IS enabled', () => {
|
|
47
|
+
const out = buildEffectiveToolSchemas(SAMPLE, { linearEnabled: true })
|
|
48
|
+
for (const t of LINEAR_TOOLS) expect(names(out)).toContain(t)
|
|
49
|
+
expect(out.length).toBe(SAMPLE.length)
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
describe('buildEffectiveToolSchemas — per-tool deferral pins (B)', () => {
|
|
54
|
+
it('pins exactly the hot tools with _meta anthropic/alwaysLoad', () => {
|
|
55
|
+
const out = buildEffectiveToolSchemas(SAMPLE, { linearEnabled: true })
|
|
56
|
+
for (const hot of ALWAYS_LOAD_TOOLS) {
|
|
57
|
+
expect(metaOf(out, hot)).toEqual({ 'anthropic/alwaysLoad': true })
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('the reply path (reply/stream_reply) is ALWAYS pinned — never defers', () => {
|
|
62
|
+
const out = buildEffectiveToolSchemas(SAMPLE, { linearEnabled: false })
|
|
63
|
+
expect(metaOf(out, 'reply')).toEqual({ 'anthropic/alwaysLoad': true })
|
|
64
|
+
expect(metaOf(out, 'stream_reply')).toEqual({ 'anthropic/alwaysLoad': true })
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('cold tools carry NO _meta (so they defer under tool-search)', () => {
|
|
68
|
+
const out = buildEffectiveToolSchemas(SAMPLE, { linearEnabled: true })
|
|
69
|
+
for (const cold of ['ask_user', 'send_gif', 'vault_request_access', 'linear_create_issue']) {
|
|
70
|
+
expect(metaOf(out, cold)).toBeUndefined()
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
describe('buildEffectiveToolSchemas — purity', () => {
|
|
76
|
+
it('does not mutate the input array or its objects', () => {
|
|
77
|
+
const input: NamedTool[] = [{ name: 'reply' }, { name: 'send_gif' }]
|
|
78
|
+
const snapshot = JSON.stringify(input)
|
|
79
|
+
buildEffectiveToolSchemas(input, { linearEnabled: true })
|
|
80
|
+
expect(JSON.stringify(input)).toBe(snapshot)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('preserves order', () => {
|
|
84
|
+
const out = buildEffectiveToolSchemas(SAMPLE, { linearEnabled: true })
|
|
85
|
+
expect(names(out)).toEqual(names(SAMPLE))
|
|
86
|
+
})
|
|
87
|
+
})
|
|
@@ -237,7 +237,7 @@ describe('decideTurnFlush', () => {
|
|
|
237
237
|
// Regression guard for the redundant-follow-up-message fix: this reverts
|
|
238
238
|
// the #1291 post-reply-tail flush, which posted a duplicate recap on
|
|
239
239
|
// essentially every turn because the model habitually writes a closing
|
|
240
|
-
// summary after its final reply. See reference/conversational-pacing.md
|
|
240
|
+
// summary after its final reply. See reference/rfcs/conversational-pacing.md
|
|
241
241
|
// — "the framework owns the beat; the model authors the words".
|
|
242
242
|
describe('reply-called turns never flush trailing terminal text', () => {
|
|
243
243
|
it('skips even when a long substantive tail follows the reply', () => {
|
|
@@ -172,7 +172,7 @@ export interface FlushDecisionInput {
|
|
|
172
172
|
* message second-guesses an explicit reply and posts a redundant duplicate
|
|
173
173
|
* on essentially every turn, because the model habitually writes a closing
|
|
174
174
|
* summary. The framework owns the *beat*; the model authors the *words*
|
|
175
|
-
* and emits them via reply (`reference/conversational-pacing.md`).
|
|
175
|
+
* and emits them via reply (`reference/rfcs/conversational-pacing.md`).
|
|
176
176
|
*
|
|
177
177
|
* (This reverts the #1291 post-reply-tail flush. Its intent — catch a
|
|
178
178
|
* soft-commit reply followed by the real answer in terminal text only —
|
|
@@ -395,7 +395,7 @@ export async function waitForCardPhase(
|
|
|
395
395
|
* The actual card render uses emoji markers in the header: `✅` for
|
|
396
396
|
* done, `❌` for errors, `⚙️` while working (foreground), `🌀` for
|
|
397
397
|
* Background (parent done but fleet still running, see #862 /
|
|
398
|
-
* reference/conversational-pacing.md),
|
|
398
|
+
* reference/rfcs/conversational-pacing.md),
|
|
399
399
|
* and `⏳` during the boot-card window. These markers are stable
|
|
400
400
|
* enough to key on for UAT — finer parsing (checklist items,
|
|
401
401
|
* sub-agent row content) is out of scope.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Background sub-agent visibility scenario — closes #709 / #776 / #782 / #788
|
|
3
|
-
* (the four-issue family analysed in `reference/sub-agent-visibility
|
|
3
|
+
* (the four-issue family analysed in `reference/rfcs/sub-agent-visibility.md`).
|
|
4
4
|
*
|
|
5
5
|
* Verifies three acceptance criteria from the RFC in a single run because
|
|
6
6
|
* they share setup:
|
|
@@ -149,7 +149,7 @@ const FUZZ_CASES: readonly FuzzCase[] = [
|
|
|
149
149
|
// The conservative regex set in `telegram-plugin/inbound-classifier.ts`
|
|
150
150
|
// captures 10 standalone "ping" patterns that count toward the
|
|
151
151
|
// primary lagging KPI `inbound_status_query`. Each fire is a JTBD
|
|
152
|
-
// failure (`reference/know-what-my-agent-is-doing.md`), so we
|
|
152
|
+
// failure (`reference/jobs/know-what-my-agent-is-doing.md`), so we
|
|
153
153
|
// want every variant to (a) reach the agent unchanged, (b)
|
|
154
154
|
// produce a sensible reply (no crash, no loop, no ghosting).
|
|
155
155
|
// Tracks cause class CC-7 from
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* JTBD scenario — guaranteed fast acknowledgement (human-feel UX epic).
|
|
3
3
|
*
|
|
4
|
-
* Serves: `reference/conversational-pacing.md` and the JTBD
|
|
4
|
+
* Serves: `reference/rfcs/conversational-pacing.md` and the JTBD
|
|
5
5
|
* "talking to my agent feels like talking to a capable person".
|
|
6
6
|
*
|
|
7
7
|
* A person you message answers in a beat — "got it", "on it, checking
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* JTBD scenario — short happy path: trivial questions reply FAST.
|
|
3
3
|
*
|
|
4
|
-
* Serves: `reference/know-what-my-agent-is-doing.md` — the short-path
|
|
4
|
+
* Serves: `reference/jobs/know-what-my-agent-is-doing.md` — the short-path
|
|
5
5
|
* contract: a question with no real work should produce a plain reply
|
|
6
6
|
* with no ceremony (no soft-commit, no progress chunks) within a tight
|
|
7
7
|
* budget. Users judge agent speed on THIS path more than any other.
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* ## Targets
|
|
14
14
|
*
|
|
15
|
-
* From `reference/conversational-pacing.md` and the post-v0.12.22
|
|
15
|
+
* From `reference/rfcs/conversational-pacing.md` and the post-v0.12.22
|
|
16
16
|
* baseline measurements:
|
|
17
17
|
*
|
|
18
18
|
* - **TTFO p95 (vision target):** < 30s — the published contract.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* JTBD scenario — forwarded burst / split paste coalesces into ONE turn.
|
|
3
3
|
*
|
|
4
|
-
* Serves: `reference/steer-or-queue-mid-flight.md` — the "Forwarded
|
|
4
|
+
* Serves: `reference/jobs/steer-or-queue-mid-flight.md` — the "Forwarded
|
|
5
5
|
* burst / split paste" UAT prompt. When several messages land in quick
|
|
6
6
|
* succession from the same sender (a forward of 3-4 messages, or a long
|
|
7
7
|
* paste Telegram split into chunks), inbound coalescing must merge them
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* JTBD scenario — memory survives across restart (the "fleet differentiator").
|
|
3
3
|
*
|
|
4
|
-
* Serves: `reference/remember-across-sessions.md` — the JTBD says:
|
|
4
|
+
* Serves: `reference/jobs/remember-across-sessions.md` — the JTBD says:
|
|
5
5
|
*
|
|
6
6
|
* *Outcome:* The agent brings back relevant facts, preferences,
|
|
7
7
|
* decisions, and open threads from past conversations, in the right
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* JTBD scenario — rapid follow-ups (steering vs queued classification).
|
|
3
3
|
*
|
|
4
4
|
* Live contract codified in `_shared/telegram-style.md.hbs` and
|
|
5
|
-
* `reference/steer-or-queue-mid-flight.md` (default-flip commits
|
|
5
|
+
* `reference/jobs/steer-or-queue-mid-flight.md` (default-flip commits
|
|
6
6
|
* `4fff90bf` + `597a58af`, 2026-04-17):
|
|
7
7
|
*
|
|
8
8
|
* - A mid-turn follow-up with NO prefix is `queued="true"` — new
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* JTBD scenario — reflective status reaction (#1713).
|
|
3
3
|
*
|
|
4
4
|
* Serves the JTBD "know what my agent is actually doing" (see
|
|
5
|
-
* `reference/know-what-my-agent-is-doing.md`). The status reaction on
|
|
5
|
+
* `reference/jobs/know-what-my-agent-is-doing.md`). The status reaction on
|
|
6
6
|
* the user's inbound is the *primary* ambient liveness signal — the
|
|
7
7
|
* user reads it as "what is the agent doing right now". When it
|
|
8
8
|
* collapses straight to 👍 mid-turn, the signal evaporates and the
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* JTBD scenario — wake-audit content visibility post-restart.
|
|
3
3
|
*
|
|
4
|
-
* Serves: `reference/restart-and-know-what-im-running.md` — the JTBD:
|
|
4
|
+
* Serves: `reference/jobs/restart-and-know-what-im-running.md` — the JTBD:
|
|
5
5
|
*
|
|
6
6
|
* *Outcome:* After any restart, the user is told what config is live.
|
|
7
7
|
* Model, tools, skills, memory backend, auth state. **No need to ask.**
|