switchroom 0.15.37 → 0.15.39
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 +89 -89
- package/dist/auth-broker/index.js +89 -89
- package/dist/cli/autoaccept-poll.js +13 -7
- package/dist/cli/drive-write-pretool.mjs +10 -10
- package/dist/cli/notion-write-pretool.mjs +91 -91
- package/dist/cli/skill-validate-pretool.mjs +72 -72
- package/dist/cli/switchroom.js +857 -572
- package/dist/cli/ui/index.html +87 -17
- package/dist/host-control/main.js +158 -158
- package/dist/vault/approvals/kernel-server.js +91 -91
- package/dist/vault/broker/server.js +92 -92
- package/package.json +1 -1
- package/profiles/_base/cron-session.sh.hbs +1 -1
- package/profiles/_base/start.sh.hbs +1 -1
- package/profiles/default/CLAUDE.md.hbs +2 -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 +18 -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 +141 -115
- package/telegram-plugin/dist/gateway/gateway.js +318 -207
- package/telegram-plugin/dist/server.js +193 -164
- 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 +133 -12
- 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/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/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
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Tier-1 cheap cron SESSION launcher —
|
|
2
|
+
# Tier-1 cheap cron SESSION launcher — reference/rfcs/cheap-cron-sessions.md §2.2.
|
|
3
3
|
#
|
|
4
4
|
# A SECOND interactive `claude` (no -p — compliance pillar 3) in the agent
|
|
5
5
|
# container, dedicated to cheap cron fires. It registers to the SAME gateway
|
|
@@ -164,7 +164,7 @@ if [ "$SWITCHROOM_RUNTIME" = "docker" ] && [ -z "$SWITCHROOM_DOCKER_TMUX_INNER"
|
|
|
164
164
|
fi
|
|
165
165
|
|
|
166
166
|
{{#if cronSessionEnabled}}
|
|
167
|
-
# 4) cheap cron SESSION (Tier 1,
|
|
167
|
+
# 4) cheap cron SESSION (Tier 1, reference/rfcs/cheap-cron-sessions.md §2.2).
|
|
168
168
|
# A SECOND interactive claude (no -p) dedicated to context:fresh cron
|
|
169
169
|
# fires, registering to the gateway as the cron-suffixed bridge. This
|
|
170
170
|
# block is rendered ONLY for an agent that has a Tier-1 cron entry; for
|
|
@@ -28,6 +28,8 @@ You are operating in the **{{topicName}}** {{#if topicEmoji}}{{topicEmoji}} {{/i
|
|
|
28
28
|
- Prefer `trash` over `rm` when available (recoverable beats gone forever).
|
|
29
29
|
- Safe to do freely: read files, explore, organize, search the web, check calendars, work within this workspace.
|
|
30
30
|
- Ask first: sending emails, tweets, public posts, anything that leaves the machine, anything you're uncertain about.
|
|
31
|
+
- **Batch foreseeable approvals; don't drip surprises.** When you can already see that several actions will each need the user's approval, tell them up front which approvals are coming and why. Request independent ones together so they can decide once; for dependent ones (one's input comes from another), say what you're doing first and what approval comes next — a permission card should never arrive out of the blue.
|
|
32
|
+
- **A timed-out approval isn't a denial.** If a request came back denied only because the user was away (a timeout, not an explicit "no"), don't silently abandon it. When they're back, remind them it's still pending and re-offer it if they still want it.
|
|
31
33
|
|
|
32
34
|
## Execution Bias
|
|
33
35
|
|
|
@@ -38,7 +38,7 @@ When the user says "add a new agent", "add an agent to my switchroom setup", or
|
|
|
38
38
|
|
|
39
39
|
### Anthropic accounts (one OAuth, many agents)
|
|
40
40
|
|
|
41
|
-
The auth model treats the Anthropic account as the unit of authentication: one OAuth flow per account, then every agent in the fleet inherits the fleet-wide active account. The `switchroom-auth-broker` daemon owns the refresh loop and is the sole writer of every `credentials.json`. See `docs/auth.md` for the operator guide and `reference/share-auth-across-the-fleet.md` for the design.
|
|
41
|
+
The auth model treats the Anthropic account as the unit of authentication: one OAuth flow per account, then every agent in the fleet inherits the fleet-wide active account. The `switchroom-auth-broker` daemon owns the refresh loop and is the sole writer of every `credentials.json`. See `docs/auth.md` for the operator guide and `reference/jobs/share-auth-across-the-fleet.md` for the design.
|
|
42
42
|
|
|
43
43
|
**Bootstrap flow when the user wants to share one Pro/Max subscription across agents:**
|
|
44
44
|
|
|
@@ -145,7 +145,7 @@ Doubled `!!` (typo / emphasis) reaches you verbatim. Empty `!` gets a "Send your
|
|
|
145
145
|
|
|
146
146
|
## "status?" / "still there?" — UX-failure signal
|
|
147
147
|
|
|
148
|
-
**Trigger:** the user sends a short, low-content message asking whether you're alive — "status?", "still there?", "any update?", "you working?". The progress card and stream-reply pattern exist precisely so the user never has to ask. When you see one of those messages, treat it as a defect signal: something about the in-flight turn made the user feel uncertain. The product expectation (per `reference/know-what-my-agent-is-doing.md`) is that this rate trends to zero.
|
|
148
|
+
**Trigger:** the user sends a short, low-content message asking whether you're alive — "status?", "still there?", "any update?", "you working?". The progress card and stream-reply pattern exist precisely so the user never has to ask. When you see one of those messages, treat it as a defect signal: something about the in-flight turn made the user feel uncertain. The product expectation (per `reference/jobs/know-what-my-agent-is-doing.md`) is that this rate trends to zero.
|
|
149
149
|
|
|
150
150
|
Your response should:
|
|
151
151
|
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
* Answer-lane incremental streaming for long Telegram replies.
|
|
12
12
|
*
|
|
13
13
|
* This module implements the "narrative" liveness layer described in
|
|
14
|
-
* `reference/know-what-my-agent-is-doing.md`:
|
|
14
|
+
* `reference/jobs/know-what-my-agent-is-doing.md`:
|
|
15
15
|
*
|
|
16
16
|
* ambient → 👀 ack reaction
|
|
17
17
|
* structured → progress card (existing, via stream-reply-handler.ts lane:'progress')
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
type PtyTailHandle,
|
|
28
28
|
} from '../pty-tail.js'
|
|
29
29
|
import { createIpcClient, type IpcClientHandle } from './ipc-client.js'
|
|
30
|
+
import { buildEffectiveToolSchemas, LINEAR_ENV } from './tool-filter.js'
|
|
30
31
|
import type { InboundMessage, PermissionEvent, StatusEvent } from '../gateway/ipc-protocol.js'
|
|
31
32
|
import { matchesAllowRule } from '../permission-rule.js'
|
|
32
33
|
|
|
@@ -544,7 +545,17 @@ const TOOL_SCHEMAS = [
|
|
|
544
545
|
},
|
|
545
546
|
]
|
|
546
547
|
|
|
547
|
-
|
|
548
|
+
// Tool-surface right-sizing (P4): connection-gate linear_* + per-tool
|
|
549
|
+
// alwaysLoad pins for the hot path. See tool-filter.ts for the rationale.
|
|
550
|
+
// Computed once at startup — SWITCHROOM_TELEGRAM_LINEAR is fixed for the
|
|
551
|
+
// process lifetime (set by the gateway in .mcp.json when Linear is wired).
|
|
552
|
+
const EFFECTIVE_TOOL_SCHEMAS = buildEffectiveToolSchemas(TOOL_SCHEMAS, {
|
|
553
|
+
linearEnabled: process.env[LINEAR_ENV] === '1',
|
|
554
|
+
})
|
|
555
|
+
|
|
556
|
+
mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
557
|
+
tools: EFFECTIVE_TOOL_SCHEMAS,
|
|
558
|
+
}))
|
|
548
559
|
|
|
549
560
|
// ─── MCP CallTool → IPC forward ─────────────────────────────────────────
|
|
550
561
|
|
|
@@ -691,6 +702,12 @@ function onPermission(msg: PermissionEvent): void {
|
|
|
691
702
|
params: {
|
|
692
703
|
request_id: msg.requestId,
|
|
693
704
|
behavior: msg.behavior,
|
|
705
|
+
// `message` (deny only) is rendered by claude's channel as
|
|
706
|
+
// "…the user said: ${message}". We use it to tell the model a deny was
|
|
707
|
+
// a TIMEOUT, not a human denial — so it doesn't retry the identical
|
|
708
|
+
// call and re-raise a duplicate card. Omitted → claude's default
|
|
709
|
+
// "Denied" (safe degradation).
|
|
710
|
+
...(msg.message ? { message: msg.message } : {}),
|
|
694
711
|
},
|
|
695
712
|
}).catch((err) => {
|
|
696
713
|
process.stderr.write(`telegram bridge: failed to deliver permission to Claude: ${err}\n`)
|
|
@@ -26,7 +26,10 @@ export function validateGatewayMessage(msg: unknown): msg is GatewayToClient {
|
|
|
26
26
|
&& (m.behavior === "allow" || m.behavior === "deny")
|
|
27
27
|
// `rule` is optional (only sent on "🔁 Always allow"); when present
|
|
28
28
|
// it must be a non-empty string. #1138 wire extension.
|
|
29
|
-
&& (m.rule === undefined || (typeof m.rule === "string" && m.rule.length > 0))
|
|
29
|
+
&& (m.rule === undefined || (typeof m.rule === "string" && m.rule.length > 0))
|
|
30
|
+
// `message` is optional (deny only — the timeout-vs-denial reason);
|
|
31
|
+
// when present it must be a non-empty string.
|
|
32
|
+
&& (m.message === undefined || (typeof m.message === "string" && m.message.length > 0));
|
|
30
33
|
case "status":
|
|
31
34
|
return typeof m.status === "string";
|
|
32
35
|
case "tool_call_result":
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-surface right-sizing for the switchroom-telegram MCP server (P4).
|
|
3
|
+
*
|
|
4
|
+
* switchroom-telegram is the primary interface, so its tool schemas are
|
|
5
|
+
* always-loaded on EVERY agent, every turn (~8k tokens). Two reductions,
|
|
6
|
+
* both native to Claude Code tool-search (honored in 2.1.177; the MCP SDK
|
|
7
|
+
* preserves `_meta` on tools/list — @modelcontextprotocol/sdk ToolSchema
|
|
8
|
+
* has `_meta: z.record(...).optional()`):
|
|
9
|
+
*
|
|
10
|
+
* A. Connection gating — linear_* tools are advertised only when the
|
|
11
|
+
* agent has the Linear connection configured (the gateway sets
|
|
12
|
+
* SWITCHROOM_TELEGRAM_LINEAR=1 from channels.telegram.linear_agent.
|
|
13
|
+
* enabled). A non-Linear agent never carries their schema at all.
|
|
14
|
+
*
|
|
15
|
+
* B. Per-tool deferral — the server is no longer pinned wholesale
|
|
16
|
+
* (scaffold sets alwaysLoad:false). We pin ONLY the load-bearing hot
|
|
17
|
+
* tools via `_meta["anthropic/alwaysLoad"]` so they never defer (the
|
|
18
|
+
* reply path must never pay a ToolSearch round-trip before it can
|
|
19
|
+
* answer); the ~14 cold tools defer and load on first use.
|
|
20
|
+
*
|
|
21
|
+
* Pure + side-effect free so it's unit-testable in isolation (bridge.ts
|
|
22
|
+
* has import-time side effects). Both reductions are non-destructive — every
|
|
23
|
+
* tool the agent is entitled to still works; cold ones just load on demand.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/** Minimal shape we need from a tool schema. */
|
|
27
|
+
export interface NamedTool {
|
|
28
|
+
name: string
|
|
29
|
+
[k: string]: unknown
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Hot tools pinned loaded — must never defer. The reply path
|
|
34
|
+
* (reply/stream_reply) plus the frequently-used early-turn ops. Everything
|
|
35
|
+
* NOT in this set defers under tool-search.
|
|
36
|
+
*/
|
|
37
|
+
export const ALWAYS_LOAD_TOOLS: ReadonlySet<string> = new Set([
|
|
38
|
+
'reply',
|
|
39
|
+
'stream_reply',
|
|
40
|
+
'get_recent_messages',
|
|
41
|
+
'react',
|
|
42
|
+
'edit_message',
|
|
43
|
+
'send_typing',
|
|
44
|
+
'download_attachment',
|
|
45
|
+
])
|
|
46
|
+
|
|
47
|
+
/** Linear tools — advertised only when the Linear connection is configured. */
|
|
48
|
+
export const LINEAR_TOOLS: ReadonlySet<string> = new Set([
|
|
49
|
+
'linear_agent_activity',
|
|
50
|
+
'linear_create_issue',
|
|
51
|
+
'linear_agent_setup',
|
|
52
|
+
])
|
|
53
|
+
|
|
54
|
+
/** The env var the gateway sets (per .mcp.json) when Linear is configured. */
|
|
55
|
+
export const LINEAR_ENV = 'SWITCHROOM_TELEGRAM_LINEAR'
|
|
56
|
+
|
|
57
|
+
export interface ToolFilterOpts {
|
|
58
|
+
/** True when the Linear connection is configured for this agent. */
|
|
59
|
+
linearEnabled: boolean
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Apply connection gating (A) + per-tool deferral pins (B) to a tool list.
|
|
64
|
+
* Returns a NEW array; inputs are not mutated. Order is preserved.
|
|
65
|
+
*/
|
|
66
|
+
export function buildEffectiveToolSchemas<T extends NamedTool>(
|
|
67
|
+
schemas: ReadonlyArray<T>,
|
|
68
|
+
opts: ToolFilterOpts,
|
|
69
|
+
): Array<T & { _meta?: { 'anthropic/alwaysLoad': true } }> {
|
|
70
|
+
return schemas
|
|
71
|
+
.filter((t) => opts.linearEnabled || !LINEAR_TOOLS.has(t.name))
|
|
72
|
+
.map((t) =>
|
|
73
|
+
ALWAYS_LOAD_TOOLS.has(t.name)
|
|
74
|
+
? { ...t, _meta: { 'anthropic/alwaysLoad': true as const } }
|
|
75
|
+
: t,
|
|
76
|
+
)
|
|
77
|
+
}
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
* autoRetry transformer; if two topics in the same chat both burst
|
|
42
42
|
* past the limit, grammY backs off per the Retry-After header — same
|
|
43
43
|
* worst-case behavior as the old per-chat lock, just hit differently.
|
|
44
|
-
* See
|
|
44
|
+
* See reference/rfcs/supergroup-mode.md and the
|
|
45
45
|
* `tests/outbound-ordering.test.ts` 429-isolation row.
|
|
46
46
|
*/
|
|
47
47
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* silently failed (stdout to /dev/null), and the user only noticed
|
|
14
14
|
* hours later when they wondered why their morning brief never came.
|
|
15
15
|
* Direct violation of the #1 product principle (silent failure is
|
|
16
|
-
* the worst case — see reference/know-what-my-agent-is-doing.md).
|
|
16
|
+
* the worst case — see reference/jobs/know-what-my-agent-is-doing.md).
|
|
17
17
|
*
|
|
18
18
|
* This module is a pure decision layer. It reads the file, compares
|
|
19
19
|
* against the last-notified state on disk, and tells the caller
|