switchroom 0.13.52 → 0.13.54

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 (39) hide show
  1. package/dist/agent-scheduler/index.js +399 -213
  2. package/dist/auth-broker/index.js +576 -237
  3. package/dist/cli/drive-write-pretool.mjs +28 -13
  4. package/dist/cli/ms-365-write-pretool.mjs +259 -0
  5. package/dist/cli/skill-validate-pretool.mjs +72 -72
  6. package/dist/cli/switchroom.js +3241 -1382
  7. package/dist/host-control/main.js +396 -276
  8. package/dist/vault/approvals/kernel-server.js +8266 -8142
  9. package/dist/vault/broker/server.js +2894 -2770
  10. package/package.json +1 -1
  11. package/profiles/_base/start.sh.hbs +17 -0
  12. package/profiles/_shared/telegram-style.md.hbs +2 -0
  13. package/skills/switchroom-status/SKILL.md +8 -6
  14. package/telegram-plugin/chat-lock.ts +87 -19
  15. package/telegram-plugin/dist/bridge/bridge.js +112 -112
  16. package/telegram-plugin/dist/gateway/gateway.js +1283 -343
  17. package/telegram-plugin/dist/server.js +160 -160
  18. package/telegram-plugin/gateway/disconnect-flush.ts +32 -0
  19. package/telegram-plugin/gateway/gateway.ts +485 -72
  20. package/telegram-plugin/gateway/inbound-coalesce.ts +19 -6
  21. package/telegram-plugin/gateway/ipc-protocol.ts +37 -0
  22. package/telegram-plugin/gateway/ipc-server.ts +59 -0
  23. package/telegram-plugin/gateway/ms365-write-approval.test.ts +314 -0
  24. package/telegram-plugin/gateway/ms365-write-approval.ts +335 -0
  25. package/telegram-plugin/stream-reply-handler.ts +10 -8
  26. package/telegram-plugin/tests/gateway-disconnect-flush.test.ts +116 -0
  27. package/telegram-plugin/tests/inbound-coalesce.test.ts +20 -4
  28. package/telegram-plugin/tests/ipc-validator.test.ts +61 -0
  29. package/telegram-plugin/tests/outbound-ordering.test.ts +228 -0
  30. package/telegram-plugin/tests/parallel-turns-deadlock-fix.test.ts +217 -0
  31. package/telegram-plugin/tests/slash-command-smart-split.test.ts +115 -0
  32. package/telegram-plugin/tests/typing-wrap.test.ts +65 -8
  33. package/telegram-plugin/typing-wrap.ts +43 -21
  34. package/vendor/hindsight-memory/scripts/lib/gateway_ipc.py +35 -0
  35. package/vendor/hindsight-memory/scripts/recall.py +164 -4
  36. package/vendor/hindsight-memory/scripts/retain.py +52 -0
  37. package/vendor/hindsight-memory/scripts/tests/test_gateway_ipc.py +42 -0
  38. package/vendor/hindsight-memory/scripts/tests/test_recall_topic_filter.py +139 -0
  39. package/profiles/default/CLAUDE.md +0 -122
@@ -14,7 +14,7 @@ var __export = (target, all) => {
14
14
  };
15
15
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
16
16
 
17
- // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/util.js
17
+ // ../switchroom-sec-1417/node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/util.js
18
18
  var util, objectUtil, ZodParsedType, getParsedType = (data) => {
19
19
  const t = typeof data;
20
20
  switch (t) {
@@ -145,7 +145,7 @@ var init_util = __esm(() => {
145
145
  ]);
146
146
  });
147
147
 
148
- // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/ZodError.js
148
+ // ../switchroom-sec-1417/node_modules/.bun/zod@3.25.76/node_modules/zod/v3/ZodError.js
149
149
  var ZodIssueCode, quotelessJson = (obj) => {
150
150
  const json = JSON.stringify(obj, null, 2);
151
151
  return json.replace(/"([^"]+)":/g, "$1:");
@@ -266,7 +266,7 @@ var init_ZodError = __esm(() => {
266
266
  };
267
267
  });
268
268
 
269
- // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/locales/en.js
269
+ // ../switchroom-sec-1417/node_modules/.bun/zod@3.25.76/node_modules/zod/v3/locales/en.js
270
270
  var errorMap = (issue, _ctx) => {
271
271
  let message;
272
272
  switch (issue.code) {
@@ -373,7 +373,7 @@ var init_en = __esm(() => {
373
373
  en_default = errorMap;
374
374
  });
375
375
 
376
- // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/errors.js
376
+ // ../switchroom-sec-1417/node_modules/.bun/zod@3.25.76/node_modules/zod/v3/errors.js
377
377
  function setErrorMap(map) {
378
378
  overrideErrorMap = map;
379
379
  }
@@ -386,7 +386,7 @@ var init_errors = __esm(() => {
386
386
  overrideErrorMap = en_default;
387
387
  });
388
388
 
389
- // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
389
+ // ../switchroom-sec-1417/node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
390
390
  function addIssueToContext(ctx, issueData) {
391
391
  const overrideMap = getErrorMap();
392
392
  const issue = makeIssue({
@@ -491,10 +491,10 @@ var init_parseUtil = __esm(() => {
491
491
  });
492
492
  });
493
493
 
494
- // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/typeAliases.js
494
+ // ../switchroom-sec-1417/node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/typeAliases.js
495
495
  var init_typeAliases = () => {};
496
496
 
497
- // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js
497
+ // ../switchroom-sec-1417/node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js
498
498
  var errorUtil;
499
499
  var init_errorUtil = __esm(() => {
500
500
  (function(errorUtil2) {
@@ -503,7 +503,7 @@ var init_errorUtil = __esm(() => {
503
503
  })(errorUtil || (errorUtil = {}));
504
504
  });
505
505
 
506
- // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/types.js
506
+ // ../switchroom-sec-1417/node_modules/.bun/zod@3.25.76/node_modules/zod/v3/types.js
507
507
  class ParseInputLazyPath {
508
508
  constructor(parent, value, path, key) {
509
509
  this._cachedPath = [];
@@ -3854,7 +3854,7 @@ var init_types = __esm(() => {
3854
3854
  NEVER = INVALID;
3855
3855
  });
3856
3856
 
3857
- // node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
3857
+ // ../switchroom-sec-1417/node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
3858
3858
  var exports_external = {};
3859
3859
  __export(exports_external, {
3860
3860
  void: () => voidType,
@@ -3974,7 +3974,7 @@ var init_external = __esm(() => {
3974
3974
  init_ZodError();
3975
3975
  });
3976
3976
 
3977
- // node_modules/.bun/zod@3.25.76/node_modules/zod/index.js
3977
+ // ../switchroom-sec-1417/node_modules/.bun/zod@3.25.76/node_modules/zod/index.js
3978
3978
  var init_zod = __esm(() => {
3979
3979
  init_external();
3980
3980
  init_external();
@@ -4000,11 +4000,11 @@ function decodeResponse(line) {
4000
4000
  }
4001
4001
  return ResponseSchema.parse(parsed);
4002
4002
  }
4003
- var MAX_FRAME_BYTES, PROTOCOL_VERSION = 1, ProviderNameSchema, GetCredentialsRequestSchema, ListStateRequestSchema, SetActiveRequestSchema, MarkExhaustedRequestSchema, RefreshAccountRequestSchema, AnthropicCredentialsSchema, GoogleCredentialsSchema, ProviderCredentialsSchema, AddAccountRequestSchema, RmAccountRequestSchema, SetOverrideRequestSchema, ListGoogleAccountsRequestSchema, ProbeQuotaRequestSchema, RequestSchema, GetCredentialsDataSchema, AccountStateSchema, AgentStateSchema, ConsumerStateSchema, ListStateDataSchema, SetActiveDataSchema, MarkExhaustedDataSchema, RefreshAccountDataSchema, AddAccountDataSchema, RmAccountDataSchema, SetOverrideDataSchema, GoogleAccountStateSchema, ListGoogleAccountsDataSchema, ErrorBodySchema, SuccessResponseSchema, ErrorResponseSchema, ResponseSchema;
4003
+ var MAX_FRAME_BYTES, PROTOCOL_VERSION = 1, ProviderNameSchema, GetCredentialsRequestSchema, ListStateRequestSchema, SetActiveRequestSchema, MarkExhaustedRequestSchema, RefreshAccountRequestSchema, AnthropicCredentialsSchema, GoogleCredentialsSchema, MicrosoftCredentialsSchema, ProviderCredentialsSchema, AddAccountRequestSchema, RmAccountRequestSchema, SetOverrideRequestSchema, ListGoogleAccountsRequestSchema, ProbeQuotaRequestSchema, RequestSchema, GetCredentialsDataSchema, AccountStateSchema, AgentStateSchema, ConsumerStateSchema, ListStateDataSchema, SetActiveDataSchema, MarkExhaustedDataSchema, RefreshAccountDataSchema, AddAccountDataSchema, RmAccountDataSchema, SetOverrideDataSchema, GoogleAccountStateSchema, ListGoogleAccountsDataSchema, ErrorBodySchema, SuccessResponseSchema, ErrorResponseSchema, ResponseSchema;
4004
4004
  var init_protocol = __esm(() => {
4005
4005
  init_zod();
4006
4006
  MAX_FRAME_BYTES = 64 * 1024;
4007
- ProviderNameSchema = exports_external.enum(["anthropic", "google"]);
4007
+ ProviderNameSchema = exports_external.enum(["anthropic", "google", "microsoft"]);
4008
4008
  GetCredentialsRequestSchema = exports_external.object({
4009
4009
  v: exports_external.literal(PROTOCOL_VERSION),
4010
4010
  op: exports_external.literal("get-credentials"),
@@ -4057,9 +4057,24 @@ var init_protocol = __esm(() => {
4057
4057
  tokenType: exports_external.literal("Bearer")
4058
4058
  })
4059
4059
  });
4060
+ MicrosoftCredentialsSchema = exports_external.object({
4061
+ microsoftOauth: exports_external.object({
4062
+ accessToken: exports_external.string(),
4063
+ refreshToken: exports_external.string(),
4064
+ expiresAt: exports_external.number(),
4065
+ scope: exports_external.string(),
4066
+ clientId: exports_external.string(),
4067
+ accountEmail: exports_external.string(),
4068
+ tokenType: exports_external.literal("Bearer"),
4069
+ tenantId: exports_external.string(),
4070
+ accountType: exports_external.enum(["personal", "work"]),
4071
+ homeAccountId: exports_external.string()
4072
+ })
4073
+ });
4060
4074
  ProviderCredentialsSchema = exports_external.union([
4061
4075
  AnthropicCredentialsSchema,
4062
- GoogleCredentialsSchema
4076
+ GoogleCredentialsSchema,
4077
+ MicrosoftCredentialsSchema
4063
4078
  ]);
4064
4079
  AddAccountRequestSchema = exports_external.object({
4065
4080
  v: exports_external.literal(PROTOCOL_VERSION),
@@ -0,0 +1,259 @@
1
+ import { createRequire } from "node:module";
2
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
+
4
+ // src/cli/ms-365-write-pretool.ts
5
+ import { readFileSync } from "node:fs";
6
+ import { createConnection } from "node:net";
7
+ import { homedir } from "node:os";
8
+ import { join } from "node:path";
9
+ import { randomBytes } from "node:crypto";
10
+ var HOOK_TIMEOUT_MS = 5 * 60 * 1000;
11
+ var KERNEL_POLL_INTERVAL_MS = 2000;
12
+ var IPC_CONNECT_TIMEOUT_MS = 3000;
13
+ var IPC_REPLY_TIMEOUT_MS = 1e4;
14
+ var KERNEL_RPC_TIMEOUT_MS = 3000;
15
+ var GATEWAY_SOCKET = process.env.SWITCHROOM_GATEWAY_SOCKET ?? (process.env.TELEGRAM_STATE_DIR !== undefined ? join(process.env.TELEGRAM_STATE_DIR, "gateway.sock") : join(homedir(), ".claude", "channels", "telegram", "gateway.sock"));
16
+ var KERNEL_SOCKET = process.env.SWITCHROOM_KERNEL_SOCKET ?? "/run/switchroom/kernel/sock";
17
+ var TOOL_PREFIX = "mcp__ms-365__";
18
+ var GATED_MS365_WRITE_TOOLS = new Set([
19
+ "upload-file-content",
20
+ "create-upload-session",
21
+ "create-event",
22
+ "update-event",
23
+ "delete-event",
24
+ "update-message",
25
+ "delete-message"
26
+ ]);
27
+ function isGatedMs365Tool(toolName) {
28
+ if (!toolName.startsWith(TOOL_PREFIX))
29
+ return false;
30
+ return GATED_MS365_WRITE_TOOLS.has(toolName.slice(TOOL_PREFIX.length));
31
+ }
32
+ function readStdin() {
33
+ try {
34
+ return readFileSync(0, "utf8");
35
+ } catch {
36
+ return "";
37
+ }
38
+ }
39
+ function parseHookInput() {
40
+ const raw = readStdin();
41
+ if (!raw)
42
+ return null;
43
+ try {
44
+ const obj = JSON.parse(raw);
45
+ return obj && typeof obj === "object" ? obj : null;
46
+ } catch {
47
+ return null;
48
+ }
49
+ }
50
+ function extractMs365Preview(toolName, toolInput) {
51
+ const o = toolInput && typeof toolInput === "object" ? toolInput : {};
52
+ let itemId = "(new)";
53
+ for (const k of ["itemId", "item_id", "id", "driveItemId", "eventId", "messageId", "message_id"]) {
54
+ if (typeof o[k] === "string" && o[k].length > 0) {
55
+ itemId = o[k];
56
+ break;
57
+ }
58
+ }
59
+ let itemDisplayName = "(unknown)";
60
+ for (const k of ["name", "fileName", "file_name", "displayName", "subject", "title"]) {
61
+ if (typeof o[k] === "string" && o[k].length > 0) {
62
+ itemDisplayName = o[k];
63
+ break;
64
+ }
65
+ }
66
+ let deepLink;
67
+ for (const k of ["webUrl", "web_url", "url", "link"]) {
68
+ if (typeof o[k] === "string" && o[k].startsWith("http")) {
69
+ deepLink = o[k];
70
+ break;
71
+ }
72
+ }
73
+ let sizeBytesAfter;
74
+ for (const k of ["contentSize", "content_size", "fileSize", "size"]) {
75
+ if (typeof o[k] === "number" && Number.isFinite(o[k])) {
76
+ sizeBytesAfter = o[k];
77
+ break;
78
+ }
79
+ }
80
+ if (sizeBytesAfter === undefined && typeof o.content === "string") {
81
+ const len = o.content.length;
82
+ sizeBytesAfter = Math.floor(len * 3 / 4);
83
+ }
84
+ return { itemId, itemDisplayName, deepLink, sizeBytesAfter };
85
+ }
86
+ async function sendGatewayRequest(socket, payload, matchType, correlationId) {
87
+ return new Promise((resolve) => {
88
+ const conn = createConnection(socket);
89
+ let buf = "";
90
+ const cleanup = () => {
91
+ try {
92
+ conn.destroy();
93
+ } catch {}
94
+ };
95
+ const replyTimer = setTimeout(() => {
96
+ cleanup();
97
+ resolve({ ok: false, reason: "gateway reply timeout" });
98
+ }, IPC_REPLY_TIMEOUT_MS);
99
+ const connectTimer = setTimeout(() => {
100
+ cleanup();
101
+ resolve({ ok: false, reason: "gateway connect timeout" });
102
+ }, IPC_CONNECT_TIMEOUT_MS);
103
+ conn.once("error", (err) => {
104
+ clearTimeout(replyTimer);
105
+ clearTimeout(connectTimer);
106
+ resolve({ ok: false, reason: `gateway: ${err.message}` });
107
+ });
108
+ conn.once("connect", () => {
109
+ clearTimeout(connectTimer);
110
+ conn.write(JSON.stringify(payload) + `
111
+ `);
112
+ });
113
+ conn.on("data", (chunk) => {
114
+ buf += chunk.toString("utf8");
115
+ while (true) {
116
+ const lineEnd = buf.indexOf(`
117
+ `);
118
+ if (lineEnd === -1)
119
+ break;
120
+ const line = buf.slice(0, lineEnd);
121
+ buf = buf.slice(lineEnd + 1);
122
+ try {
123
+ const parsed = JSON.parse(line);
124
+ if (parsed?.type === matchType && parsed?.correlationId === correlationId) {
125
+ clearTimeout(replyTimer);
126
+ cleanup();
127
+ resolve({ ok: true, value: parsed });
128
+ return;
129
+ }
130
+ } catch {}
131
+ }
132
+ });
133
+ });
134
+ }
135
+ async function rpcKernel(payload) {
136
+ return new Promise((resolve) => {
137
+ const conn = createConnection(KERNEL_SOCKET);
138
+ let buf = "";
139
+ const cleanup = () => {
140
+ try {
141
+ conn.destroy();
142
+ } catch {}
143
+ };
144
+ const timer = setTimeout(() => {
145
+ cleanup();
146
+ resolve({ ok: false, reason: "kernel rpc timeout" });
147
+ }, KERNEL_RPC_TIMEOUT_MS);
148
+ conn.once("error", (err) => {
149
+ clearTimeout(timer);
150
+ resolve({ ok: false, reason: `kernel: ${err.message}` });
151
+ });
152
+ conn.once("connect", () => {
153
+ conn.write(JSON.stringify(payload) + `
154
+ `);
155
+ });
156
+ conn.on("data", (chunk) => {
157
+ buf += chunk.toString("utf8");
158
+ const lineEnd = buf.indexOf(`
159
+ `);
160
+ if (lineEnd === -1)
161
+ return;
162
+ const line = buf.slice(0, lineEnd);
163
+ clearTimeout(timer);
164
+ cleanup();
165
+ try {
166
+ resolve({ ok: true, value: JSON.parse(line) });
167
+ } catch (err) {
168
+ resolve({ ok: false, reason: `kernel parse: ${String(err)}` });
169
+ }
170
+ });
171
+ });
172
+ }
173
+ async function approvalLookup(agentUnit, scope, approverSet) {
174
+ const res = await rpcKernel({
175
+ v: 1,
176
+ op: "approval_lookup",
177
+ agent_unit: agentUnit,
178
+ scope,
179
+ action: "write",
180
+ current_approver_set: approverSet
181
+ });
182
+ if (!res.ok)
183
+ return null;
184
+ return res.value;
185
+ }
186
+ function fail(reason) {
187
+ process.stdout.write(JSON.stringify({
188
+ decision: "block",
189
+ reason: `ms-365 write blocked: ${reason}`
190
+ }));
191
+ process.exit(0);
192
+ }
193
+ function allow() {
194
+ process.exit(0);
195
+ }
196
+ async function main() {
197
+ const input = parseHookInput();
198
+ if (!input)
199
+ fail("malformed stdin");
200
+ const toolName = input.tool_name;
201
+ if (typeof toolName !== "string" || !isGatedMs365Tool(toolName)) {
202
+ allow();
203
+ }
204
+ const agentName = process.env.SWITCHROOM_AGENT_NAME;
205
+ if (!agentName) {
206
+ allow();
207
+ }
208
+ const accountEmail = process.env.SWITCHROOM_MICROSOFT_ACCOUNT ?? "(unknown)";
209
+ const extract = extractMs365Preview(toolName, input.tool_input);
210
+ const preview = {
211
+ agentName,
212
+ toolName,
213
+ itemId: extract.itemId,
214
+ itemDisplayName: extract.itemDisplayName,
215
+ accountEmail,
216
+ deepLink: extract.deepLink,
217
+ sizeBytesAfter: extract.sizeBytesAfter
218
+ };
219
+ const correlationId = randomBytes(16).toString("hex");
220
+ const requestResult = await sendGatewayRequest(GATEWAY_SOCKET, {
221
+ type: "request_ms365_approval",
222
+ correlationId,
223
+ agentName,
224
+ preview,
225
+ ttlMs: HOOK_TIMEOUT_MS
226
+ }, "ms365_approval_posted", correlationId);
227
+ if (!requestResult.ok)
228
+ fail(requestResult.reason);
229
+ const response = requestResult.value;
230
+ if (!response.ok || !response.requestId) {
231
+ fail(response.reason ?? "gateway returned ok=false");
232
+ }
233
+ const deadline = response.expiresAtMs ?? Date.now() + HOOK_TIMEOUT_MS;
234
+ const scope = `ms-365:write:${preview.itemId}`;
235
+ while (Date.now() < deadline) {
236
+ await new Promise((r) => setTimeout(r, KERNEL_POLL_INTERVAL_MS));
237
+ const lookup = await approvalLookup(agentName, scope, []);
238
+ if (!lookup)
239
+ continue;
240
+ const state = lookup.state;
241
+ if (state === "granted")
242
+ allow();
243
+ if (state === "denied" || state === "drift_revoked" || state === "expired") {
244
+ fail(`operator ${state}`);
245
+ }
246
+ }
247
+ fail("approval timed out");
248
+ }
249
+ if (__require.main == __require.module) {
250
+ main().catch((err) => {
251
+ const m = err instanceof Error ? err.message : String(err);
252
+ fail(`hook error: ${m}`);
253
+ });
254
+ }
255
+ export {
256
+ isGatedMs365Tool,
257
+ extractMs365Preview,
258
+ GATED_MS365_WRITE_TOOLS
259
+ };