@vellumai/assistant 0.4.45 → 0.4.48

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 (236) hide show
  1. package/ARCHITECTURE.md +6 -6
  2. package/docs/architecture/memory.md +1 -1
  3. package/docs/architecture/scheduling.md +2 -3
  4. package/docs/architecture/security.md +5 -5
  5. package/docs/trusted-contact-access.md +5 -6
  6. package/package.json +4 -1
  7. package/src/__tests__/avatar-e2e.test.ts +18 -219
  8. package/src/__tests__/avatar-generator.test.ts +5 -57
  9. package/src/__tests__/browser-fill-credential.test.ts +5 -2
  10. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +2 -1
  11. package/src/__tests__/channel-readiness-routes.test.ts +20 -19
  12. package/src/__tests__/cli.test.ts +23 -0
  13. package/src/__tests__/credential-broker-browser-fill.test.ts +23 -22
  14. package/src/__tests__/credential-broker-server-use.test.ts +22 -21
  15. package/src/__tests__/credential-broker.test.ts +2 -1
  16. package/src/__tests__/credential-metadata-store.test.ts +240 -18
  17. package/src/__tests__/credential-resolve.test.ts +5 -4
  18. package/src/__tests__/credential-security-e2e.test.ts +8 -8
  19. package/src/__tests__/credential-security-invariants.test.ts +104 -7
  20. package/src/__tests__/credential-vault-unit.test.ts +22 -20
  21. package/src/__tests__/credential-vault.test.ts +284 -12
  22. package/src/__tests__/credentials-cli.test.ts +11 -6
  23. package/src/__tests__/gateway-only-enforcement.test.ts +4 -2
  24. package/src/__tests__/gemini-image-service.test.ts +75 -45
  25. package/src/__tests__/gemini-provider.test.ts +9 -6
  26. package/src/__tests__/guardian-action-conversation-turn.test.ts +1 -33
  27. package/src/__tests__/guardian-action-copy-generator.test.ts +0 -20
  28. package/src/__tests__/guardian-action-followup-executor.test.ts +1 -28
  29. package/src/__tests__/guardian-action-followup-store.test.ts +1 -1
  30. package/src/__tests__/guardian-grant-minting.test.ts +35 -0
  31. package/src/__tests__/integration-status.test.ts +53 -21
  32. package/src/__tests__/managed-proxy-context.test.ts +5 -3
  33. package/src/__tests__/media-generate-image.test.ts +63 -2
  34. package/src/__tests__/media-reuse-story.e2e.test.ts +7 -3
  35. package/src/__tests__/messaging-send-tool.test.ts +4 -6
  36. package/src/__tests__/provider-fail-open-selection.test.ts +3 -1
  37. package/src/__tests__/provider-managed-proxy-integration.test.ts +70 -6
  38. package/src/__tests__/schedule-store.test.ts +1 -1
  39. package/src/__tests__/schema-transforms.test.ts +226 -0
  40. package/src/__tests__/script-proxy-injection-runtime.test.ts +23 -13
  41. package/src/__tests__/script-proxy-policy-runtime.test.ts +1 -1
  42. package/src/__tests__/script-proxy-session-manager.test.ts +1 -1
  43. package/src/__tests__/secret-onetime-send.test.ts +5 -3
  44. package/src/__tests__/session-messaging-secret-redirect.test.ts +5 -4
  45. package/src/__tests__/skills-uninstall.test.ts +2 -2
  46. package/src/__tests__/skills.test.ts +0 -9
  47. package/src/__tests__/slack-channel-config.test.ts +9 -8
  48. package/src/__tests__/slack-share-routes.test.ts +11 -6
  49. package/src/__tests__/telegram-bot-username-resolution.test.ts +3 -0
  50. package/src/__tests__/twilio-config.test.ts +2 -1
  51. package/src/__tests__/twilio-provider.test.ts +4 -2
  52. package/src/__tests__/twilio-routes.test.ts +5 -4
  53. package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
  54. package/src/approvals/AGENTS.md +1 -1
  55. package/src/calls/call-domain.ts +7 -4
  56. package/src/calls/twilio-config.ts +2 -1
  57. package/src/calls/twilio-provider.ts +2 -1
  58. package/src/calls/twilio-rest.ts +2 -2
  59. package/src/cli/commands/browser-relay.ts +40 -15
  60. package/src/cli/commands/credentials.ts +9 -8
  61. package/src/cli/commands/oauth.ts +1 -1
  62. package/src/cli.ts +3 -2
  63. package/src/config/bundled-skills/claude-code/TOOLS.json +0 -4
  64. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +29 -32
  65. package/src/config/bundled-skills/gmail/SKILL.md +4 -4
  66. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +54 -61
  67. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +25 -28
  68. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +14 -17
  69. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +39 -44
  70. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +61 -58
  71. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +50 -49
  72. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +11 -13
  73. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +148 -146
  74. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +4 -7
  75. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +175 -173
  76. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +4 -7
  77. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +71 -76
  78. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +32 -38
  79. package/src/config/bundled-skills/google-calendar/SKILL.md +2 -2
  80. package/src/config/bundled-skills/google-calendar/calendar-client.ts +70 -29
  81. package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +9 -10
  82. package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +5 -6
  83. package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +4 -5
  84. package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +14 -15
  85. package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +37 -37
  86. package/src/config/bundled-skills/google-calendar/tools/shared.ts +4 -9
  87. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +24 -3
  88. package/src/config/bundled-skills/messaging/SKILL.md +6 -6
  89. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +62 -63
  90. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +15 -16
  91. package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +4 -5
  92. package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +6 -7
  93. package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +4 -5
  94. package/src/config/bundled-skills/messaging/tools/messaging-read.ts +14 -15
  95. package/src/config/bundled-skills/messaging/tools/messaging-search.ts +4 -5
  96. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +128 -128
  97. package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +33 -34
  98. package/src/config/bundled-skills/messaging/tools/shared.ts +11 -11
  99. package/src/config/bundled-skills/notifications/SKILL.md +1 -1
  100. package/src/config/bundled-skills/phone-calls/SKILL.md +5 -5
  101. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  102. package/src/config/bundled-skills/skill-management/SKILL.md +1 -1
  103. package/src/config/bundled-skills/slack/tools/shared.ts +4 -10
  104. package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +4 -5
  105. package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +15 -16
  106. package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +4 -5
  107. package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +4 -5
  108. package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +4 -5
  109. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +95 -92
  110. package/src/config/loader.ts +6 -0
  111. package/src/daemon/computer-use-session.ts +7 -1
  112. package/src/daemon/guardian-action-generators.ts +4 -5
  113. package/src/daemon/handlers/config-slack-channel.ts +37 -20
  114. package/src/daemon/handlers/config-telegram.ts +33 -20
  115. package/src/daemon/lifecycle.ts +9 -1
  116. package/src/daemon/message-types/integrations.ts +1 -0
  117. package/src/daemon/ride-shotgun-handler.ts +3 -1
  118. package/src/daemon/session-messaging.ts +3 -1
  119. package/src/daemon/session-tool-setup.ts +18 -2
  120. package/src/daemon/session.ts +1 -1
  121. package/src/email/providers/index.ts +2 -1
  122. package/src/instrument.ts +15 -1
  123. package/src/media/app-icon-generator.ts +30 -4
  124. package/src/media/avatar-router.ts +28 -62
  125. package/src/media/gemini-image-service.ts +28 -2
  126. package/src/memory/canonical-guardian-store.ts +1 -1
  127. package/src/memory/guardian-action-store.ts +1 -1
  128. package/src/memory/schema/guardian.ts +1 -1
  129. package/src/messaging/provider.ts +16 -10
  130. package/src/messaging/providers/gmail/adapter.ts +40 -23
  131. package/src/messaging/providers/gmail/client.ts +203 -122
  132. package/src/messaging/providers/gmail/people-client.ts +26 -18
  133. package/src/messaging/providers/slack/adapter.ts +29 -19
  134. package/src/messaging/providers/slack/client.ts +265 -78
  135. package/src/messaging/providers/telegram-bot/adapter.ts +5 -4
  136. package/src/messaging/providers/whatsapp/adapter.ts +6 -3
  137. package/src/messaging/registry.ts +2 -1
  138. package/src/oauth/byo-connection.test.ts +436 -0
  139. package/src/oauth/byo-connection.ts +112 -0
  140. package/src/oauth/connect-orchestrator.ts +27 -0
  141. package/src/oauth/connection-resolver.ts +34 -0
  142. package/src/oauth/connection.ts +38 -0
  143. package/src/oauth/platform-connection.test.ts +163 -0
  144. package/src/oauth/platform-connection.ts +110 -0
  145. package/src/oauth/provider-base-urls.ts +21 -0
  146. package/src/oauth/provider-profiles.ts +1 -1
  147. package/src/oauth/token-persistence.ts +20 -20
  148. package/src/permissions/checker.ts +6 -1
  149. package/src/prompts/system-prompt.ts +52 -15
  150. package/src/prompts/templates/BOOTSTRAP.md +1 -1
  151. package/src/providers/gemini/client.ts +15 -6
  152. package/src/providers/managed-proxy/constants.ts +2 -2
  153. package/src/providers/managed-proxy/context.ts +5 -1
  154. package/src/providers/ratelimit.ts +17 -0
  155. package/src/providers/registry.ts +2 -2
  156. package/src/runtime/AGENTS.md +18 -1
  157. package/src/runtime/auth/route-policy.ts +1 -0
  158. package/src/runtime/channel-invite-transports/telegram.ts +2 -1
  159. package/src/runtime/channel-readiness-service.ts +168 -195
  160. package/src/runtime/channel-readiness-types.ts +4 -0
  161. package/src/runtime/guardian-action-conversation-turn.ts +1 -3
  162. package/src/runtime/guardian-action-followup-executor.ts +1 -2
  163. package/src/runtime/guardian-action-message-composer.ts +3 -23
  164. package/src/runtime/http-server.ts +9 -4
  165. package/src/runtime/http-types.ts +0 -1
  166. package/src/runtime/middleware/rate-limiter.ts +74 -20
  167. package/src/runtime/middleware/twilio-validation.ts +1 -3
  168. package/src/runtime/routes/channel-readiness-routes.ts +2 -0
  169. package/src/runtime/routes/diagnostics-routes.ts +11 -9
  170. package/src/runtime/routes/guardian-approval-interception.ts +20 -5
  171. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +71 -25
  172. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +12 -5
  173. package/src/runtime/routes/integrations/slack/share.ts +3 -2
  174. package/src/runtime/routes/integrations/twilio.ts +6 -5
  175. package/src/runtime/routes/secret-routes.ts +3 -2
  176. package/src/runtime/routes/settings-routes.ts +75 -17
  177. package/src/runtime/telegram-streaming-delivery.test.ts +132 -0
  178. package/src/runtime/telegram-streaming-delivery.ts +11 -1
  179. package/src/schedule/integration-status.ts +5 -4
  180. package/src/security/credential-key.ts +170 -0
  181. package/src/security/token-manager.ts +36 -7
  182. package/src/tools/apps/definitions.ts +0 -5
  183. package/src/tools/assets/materialize.ts +0 -5
  184. package/src/tools/assets/search.ts +0 -5
  185. package/src/tools/browser/headless-browser.ts +1 -67
  186. package/src/tools/claude-code/claude-code.ts +0 -5
  187. package/src/tools/computer-use/request-computer-control.ts +0 -5
  188. package/src/tools/credentials/broker.ts +6 -4
  189. package/src/tools/credentials/metadata-store.ts +72 -20
  190. package/src/tools/credentials/resolve.ts +2 -1
  191. package/src/tools/credentials/vault.ts +77 -16
  192. package/src/tools/filesystem/edit.ts +1 -6
  193. package/src/tools/filesystem/read.ts +0 -5
  194. package/src/tools/filesystem/write.ts +1 -6
  195. package/src/tools/host-filesystem/edit.ts +1 -6
  196. package/src/tools/host-filesystem/read.ts +1 -6
  197. package/src/tools/host-filesystem/write.ts +1 -6
  198. package/src/tools/mcp/mcp-tool-factory.ts +18 -1
  199. package/src/tools/memory/definitions.ts +0 -5
  200. package/src/tools/network/web-fetch.ts +0 -5
  201. package/src/tools/network/web-search.ts +0 -5
  202. package/src/tools/schema-transforms.ts +99 -0
  203. package/src/tools/skills/load.ts +0 -5
  204. package/src/tools/swarm/delegate.ts +0 -5
  205. package/src/tools/system/avatar-generator.ts +3 -44
  206. package/src/tools/ui-surface/definitions.ts +0 -15
  207. package/src/tools/watch/screen-watch.ts +0 -5
  208. package/src/version.ts +10 -0
  209. package/src/watcher/providers/github.ts +51 -52
  210. package/src/watcher/providers/gmail.ts +88 -80
  211. package/src/watcher/providers/google-calendar.ts +93 -86
  212. package/src/watcher/providers/linear.ts +87 -93
  213. package/src/__tests__/avatar-router.test.ts +0 -149
  214. package/src/__tests__/managed-avatar-client.test.ts +0 -337
  215. package/src/config/bundled-skills/doordash/SKILL.md +0 -170
  216. package/src/config/bundled-skills/doordash/__tests__/doordash-client.test.ts +0 -205
  217. package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +0 -74
  218. package/src/config/bundled-skills/doordash/doordash-cli.ts +0 -1081
  219. package/src/config/bundled-skills/doordash/doordash-entry.ts +0 -22
  220. package/src/config/bundled-skills/doordash/lib/cart-queries.ts +0 -787
  221. package/src/config/bundled-skills/doordash/lib/client.ts +0 -1069
  222. package/src/config/bundled-skills/doordash/lib/order-queries.ts +0 -85
  223. package/src/config/bundled-skills/doordash/lib/queries.ts +0 -28
  224. package/src/config/bundled-skills/doordash/lib/query-extractor.ts +0 -94
  225. package/src/config/bundled-skills/doordash/lib/search-queries.ts +0 -203
  226. package/src/config/bundled-skills/doordash/lib/session.ts +0 -96
  227. package/src/config/bundled-skills/doordash/lib/shared/errors.ts +0 -61
  228. package/src/config/bundled-skills/doordash/lib/shared/network-recorder.ts +0 -380
  229. package/src/config/bundled-skills/doordash/lib/shared/platform.ts +0 -55
  230. package/src/config/bundled-skills/doordash/lib/shared/recording-store.ts +0 -43
  231. package/src/config/bundled-skills/doordash/lib/shared/recording-types.ts +0 -49
  232. package/src/config/bundled-skills/doordash/lib/shared/truncate.ts +0 -6
  233. package/src/config/bundled-skills/doordash/lib/store-queries.ts +0 -246
  234. package/src/config/bundled-skills/doordash/lib/types.ts +0 -367
  235. package/src/media/avatar-types.ts +0 -53
  236. package/src/media/managed-avatar-client.ts +0 -225
@@ -27,13 +27,8 @@ class HostFileWriteTool implements Tool {
27
27
  type: "string",
28
28
  description: "The content to write to the file",
29
29
  },
30
- reason: {
31
- type: "string",
32
- description:
33
- "Brief non-technical explanation of why this file is being written, shown to the user as a status update. Use simple language a non-technical person would understand.",
34
- },
35
30
  },
36
- required: ["path", "content", "reason"],
31
+ required: ["path", "content"],
37
32
  },
38
33
  };
39
34
  }
@@ -2,6 +2,7 @@ import type { McpServerConfig } from "../../config/schemas/mcp.js";
2
2
  import type { McpServerManager } from "../../mcp/manager.js";
3
3
  import { RiskLevel } from "../../permissions/types.js";
4
4
  import type { ToolDefinition } from "../../providers/types.js";
5
+ import { schemaDefinesProperty } from "../schema-transforms.js";
5
6
  import type { Tool, ToolContext, ToolExecutionResult } from "../types.js";
6
7
 
7
8
  const riskMap: Record<string, RiskLevel> = {
@@ -36,6 +37,10 @@ export function createMcpTool(
36
37
  ): Tool {
37
38
  const namespacedName = mcpToolName(serverId, metadata.name);
38
39
  const riskLevel = riskMap[serverConfig.defaultRiskLevel] ?? RiskLevel.High;
40
+ const serverDefinesReason = schemaDefinesProperty(
41
+ metadata.inputSchema,
42
+ "reason",
43
+ );
39
44
 
40
45
  return {
41
46
  name: namespacedName,
@@ -59,7 +64,19 @@ export function createMcpTool(
59
64
  _context: ToolContext,
60
65
  ): Promise<ToolExecutionResult> {
61
66
  try {
62
- const result = await manager.callTool(serverId, metadata.name, input);
67
+ // Strip injected reason before sending to MCP server
68
+ const { reason: _reason, ...mcpInput } = input as Record<
69
+ string,
70
+ unknown
71
+ > & {
72
+ reason?: unknown;
73
+ };
74
+ const forwardInput = serverDefinesReason ? input : mcpInput;
75
+ const result = await manager.callTool(
76
+ serverId,
77
+ metadata.name,
78
+ forwardInput,
79
+ );
63
80
  return {
64
81
  content: result.content,
65
82
  isError: result.isError,
@@ -62,11 +62,6 @@ const memoryManageProperties = {
62
62
  type: "string" as const,
63
63
  description: "Short subject/topic label, 2-8 words (optional, save only)",
64
64
  },
65
- reason: {
66
- type: "string" as const,
67
- description:
68
- "Brief non-technical explanation shown to the user as a status update",
69
- },
70
65
  };
71
66
 
72
67
  export const memoryManageDefinition: ToolDefinition = {
@@ -863,11 +863,6 @@ class WebFetchTool implements Tool {
863
863
  description:
864
864
  "If true, allows requests to localhost/private-network hosts. Disabled by default for SSRF safety.",
865
865
  },
866
- reason: {
867
- type: "string",
868
- description:
869
- "Brief non-technical explanation of what you are fetching and why, shown to the user as a status update. Use simple language a non-technical person would understand.",
870
- },
871
866
  },
872
867
  required: ["url"],
873
868
  },
@@ -302,11 +302,6 @@ class WebSearchTool implements Tool {
302
302
  description:
303
303
  'Filter by recency: "pd" (past day), "pw" (past week), "pm" (past month), "py" (past year). Only used with Brave provider.',
304
304
  },
305
- reason: {
306
- type: "string",
307
- description:
308
- "Brief non-technical explanation of what you are searching for and why, shown to the user as a status update. Use simple language a non-technical person would understand.",
309
- },
310
305
  },
311
306
  required: ["query"],
312
307
  },
@@ -0,0 +1,99 @@
1
+ import type { ToolDefinition } from "../providers/types.js";
2
+
3
+ /**
4
+ * Tools that should never have a `reason` field injected into their schema.
5
+ */
6
+ export const REASON_SKIP_SET = new Set<string>([
7
+ "skill_execute",
8
+ "bash",
9
+ "host_bash",
10
+ "request_system_permission",
11
+ ]);
12
+
13
+ /**
14
+ * Injects a `reason` string property into each tool definition's input schema,
15
+ * unless the tool is in the skip set, already has a reason field, or has a
16
+ * non-object schema.
17
+ *
18
+ * CRITICAL: Never mutates the input definitions — always returns deep clones
19
+ * for any modified definition, since `getDefinition()` returns shared refs.
20
+ */
21
+ export function injectReasonField(
22
+ definitions: ToolDefinition[],
23
+ skip: Set<string> = REASON_SKIP_SET,
24
+ ): ToolDefinition[] {
25
+ return definitions.map((def) => {
26
+ if (skip.has(def.name)) {
27
+ return def;
28
+ }
29
+
30
+ const schema = def.input_schema as Record<string, unknown>;
31
+ if (schema.type !== "object" || !schema.properties) {
32
+ return def;
33
+ }
34
+
35
+ const properties = schema.properties as Record<string, unknown>;
36
+ if (schemaDefinesProperty(schema, "reason")) {
37
+ return def;
38
+ }
39
+
40
+ // Deep clone to avoid mutating shared refs
41
+ const newProperties = { ...properties, reason: { type: "string" } };
42
+ const existingRequired = Array.isArray(schema.required)
43
+ ? [...schema.required, "reason"]
44
+ : ["reason"];
45
+
46
+ return {
47
+ ...def,
48
+ input_schema: {
49
+ ...schema,
50
+ properties: newProperties,
51
+ required: existingRequired,
52
+ },
53
+ };
54
+ });
55
+ }
56
+
57
+ /**
58
+ * Checks whether a JSON Schema defines a given property name.
59
+ * Walks `allOf`, `oneOf`, `anyOf` recursively.
60
+ * Fail-closed on `$ref`: returns false if a `$ref` is encountered.
61
+ */
62
+ export function schemaDefinesProperty(
63
+ schema: unknown,
64
+ propertyName: string,
65
+ ): boolean {
66
+ if (schema == null || typeof schema !== "object") {
67
+ return false;
68
+ }
69
+
70
+ const s = schema as Record<string, unknown>;
71
+
72
+ // Fail-closed on $ref — we can't resolve it, so treat as "doesn't define it"
73
+ if ("$ref" in s) {
74
+ return false;
75
+ }
76
+
77
+ // Check direct properties
78
+ if (
79
+ s.properties &&
80
+ typeof s.properties === "object" &&
81
+ propertyName in (s.properties as Record<string, unknown>)
82
+ ) {
83
+ return true;
84
+ }
85
+
86
+ // Walk composite keywords
87
+ for (const keyword of ["allOf", "oneOf", "anyOf"] as const) {
88
+ const arr = s[keyword];
89
+ if (Array.isArray(arr)) {
90
+ for (const member of arr) {
91
+ if (schemaDefinesProperty(member, propertyName)) {
92
+ return true;
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ return false;
99
+ }
@@ -119,11 +119,6 @@ export class SkillLoadTool implements Tool {
119
119
  type: "string",
120
120
  description: "The skill id or skill name to load.",
121
121
  },
122
- reason: {
123
- type: "string",
124
- description:
125
- "Brief non-technical explanation of what you are loading and why, shown to the user as a status update. Use simple language a non-technical person would understand.",
126
- },
127
122
  },
128
123
  required: ["skill"],
129
124
  },
@@ -44,11 +44,6 @@ export const swarmDelegateTool: Tool = {
44
44
  description:
45
45
  "Maximum concurrent workers (1-6, default from config)",
46
46
  },
47
- reason: {
48
- type: "string",
49
- description:
50
- "Brief non-technical explanation of what you are doing and why, shown to the user as a status update. Use simple language a non-technical person would understand.",
51
- },
52
47
  },
53
48
  required: ["objective"],
54
49
  },
@@ -2,8 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
  import { mkdirSync, renameSync, writeFileSync } from "node:fs";
3
3
  import { dirname, join } from "node:path";
4
4
 
5
- import { routedGenerateAvatar } from "../../media/avatar-router.js";
6
- import { ManagedAvatarError } from "../../media/avatar-types.js";
5
+ import { generateAvatar } from "../../media/avatar-router.js";
7
6
  import { mapGeminiError } from "../../media/gemini-image-service.js";
8
7
  import { RiskLevel } from "../../permissions/types.js";
9
8
  import type { ToolDefinition } from "../../providers/types.js";
@@ -41,11 +40,6 @@ export const setAvatarTool: Tool = {
41
40
  "A text description of the desired avatar appearance, " +
42
41
  'e.g. "a friendly purple cat with green eyes wearing a tiny hat".',
43
42
  },
44
- reason: {
45
- type: "string",
46
- description:
47
- "Brief non-technical explanation of what you are creating and why, shown to the user as a status update. Use simple language a non-technical person would understand.",
48
- },
49
43
  },
50
44
  required: ["description"],
51
45
  },
@@ -75,7 +69,7 @@ export const setAvatarTool: Tool = {
75
69
  "Circular or rounded composition filling the canvas. " +
76
70
  "Subtle background color (not white or transparent).";
77
71
 
78
- const result = await routedGenerateAvatar(prompt);
72
+ const result = await generateAvatar(prompt);
79
73
  if (!result.imageBase64) {
80
74
  return {
81
75
  content: "Error: No image data returned. Please try again.",
@@ -92,14 +86,7 @@ export const setAvatarTool: Tool = {
92
86
  writeFileSync(tmpPath, pngBuffer);
93
87
  renameSync(tmpPath, avatarPath);
94
88
 
95
- log.info(
96
- {
97
- avatarPath,
98
- pathUsed: result.pathUsed,
99
- correlationId: result.correlationId,
100
- },
101
- "Avatar saved successfully",
102
- );
89
+ log.info({ avatarPath }, "Avatar saved successfully");
103
90
 
104
91
  // Side-effect hook in tool-side-effects.ts broadcasts avatar_updated to all clients.
105
92
 
@@ -108,34 +95,6 @@ export const setAvatarTool: Tool = {
108
95
  isError: false,
109
96
  };
110
97
  } catch (error) {
111
- if (error instanceof ManagedAvatarError) {
112
- log.error(
113
- {
114
- error: error.message,
115
- statusCode: error.statusCode,
116
- code: error.code,
117
- },
118
- "Avatar generation failed (managed)",
119
- );
120
- if (error.statusCode === 429) {
121
- return {
122
- content:
123
- "Avatar generation is currently rate limited. Please try again in a moment.",
124
- isError: true,
125
- };
126
- }
127
- if (error.statusCode >= 500) {
128
- return {
129
- content:
130
- "Avatar generation is temporarily unavailable. Please try again later.",
131
- isError: true,
132
- };
133
- }
134
- return {
135
- content: `Avatar generation failed: ${error.message}`,
136
- isError: true,
137
- };
138
- }
139
98
  const message = mapGeminiError(error);
140
99
  log.error({ error: message }, "Avatar generation failed");
141
100
  return {
@@ -126,11 +126,6 @@ export const uiShowTool: Tool = {
126
126
  description:
127
127
  "Whether to block until the user interacts with an action. Defaults to true when actions are provided.",
128
128
  },
129
- reason: {
130
- type: "string",
131
- description:
132
- "Brief non-technical explanation of what you are showing and why, shown to the user as a status update. Use simple language a non-technical person would understand.",
133
- },
134
129
  },
135
130
  required: ["surface_type", "data"],
136
131
  },
@@ -168,11 +163,6 @@ export const uiUpdateTool: Tool = {
168
163
  type: "object",
169
164
  description: "Partial data to merge into the existing surface data",
170
165
  },
171
- reason: {
172
- type: "string",
173
- description:
174
- "Brief non-technical explanation of what you are updating and why, shown to the user as a status update. Use simple language a non-technical person would understand.",
175
- },
176
166
  },
177
167
  required: ["surface_id", "data"],
178
168
  },
@@ -204,11 +194,6 @@ export const uiDismissTool: Tool = {
204
194
  type: "string",
205
195
  description: "The ID of the surface to dismiss",
206
196
  },
207
- reason: {
208
- type: "string",
209
- description:
210
- "Brief non-technical explanation of what you are dismissing and why, shown to the user as a status update. Use simple language a non-technical person would understand.",
211
- },
212
197
  },
213
198
  required: ["surface_id"],
214
199
  },
@@ -42,11 +42,6 @@ class ScreenWatchTool implements Tool {
42
42
  type: "string",
43
43
  description: "What to focus on observing",
44
44
  },
45
- reason: {
46
- type: "string",
47
- description:
48
- "Brief non-technical explanation of what you are watching and why, shown to the user as a status update. Use simple language a non-technical person would understand.",
49
- },
50
45
  },
51
46
  required: ["focus_area"],
52
47
  },
package/src/version.ts CHANGED
@@ -32,3 +32,13 @@ function resolveVersion(): string {
32
32
  // Falls back to "0.0.0-dev" for local development, or resolves the dev
33
33
  // placeholder to package.json version when explicitly set in CI.
34
34
  export const APP_VERSION: string = resolveVersion();
35
+
36
+ // Commit SHA is embedded at compile time via --define in CI.
37
+ // Falls back to "unknown" for local development.
38
+ function resolveCommitSha(): string {
39
+ const sha = process.env.COMMIT_SHA;
40
+ if (!sha || sha === "unknown") return "unknown";
41
+ return sha;
42
+ }
43
+
44
+ export const COMMIT_SHA: string = resolveCommitSha();
@@ -10,7 +10,8 @@
10
10
  * `notifications` scope (classic) or Notification read permission (fine-grained).
11
11
  */
12
12
 
13
- import { withValidToken } from "../../security/token-manager.js";
13
+ import type { OAuthConnection } from "../../oauth/connection.js";
14
+ import { resolveOAuthConnection } from "../../oauth/connection-resolver.js";
14
15
  import { getLogger } from "../../util/logger.js";
15
16
  import { truncate } from "../../util/truncate.js";
16
17
  import type {
@@ -21,8 +22,6 @@ import type {
21
22
 
22
23
  const log = getLogger("watcher:github");
23
24
 
24
- const GITHUB_API_BASE = "https://api.github.com";
25
-
26
25
  // ── API types ──────────────────────────────────────────────────────────────────
27
26
 
28
27
  interface GitHubNotification {
@@ -82,31 +81,32 @@ function notificationToItem(n: GitHubNotification): WatcherItem {
82
81
 
83
82
  /** Fetch a single page of notifications since a timestamp. */
84
83
  async function fetchNotificationsPage(
85
- token: string,
84
+ connection: OAuthConnection,
86
85
  since: string,
87
86
  page: number,
88
87
  ): Promise<{ items: GitHubNotification[]; hasMore: boolean }> {
89
- const params = new URLSearchParams({
90
- all: "false", // only unread
91
- since,
92
- per_page: "50",
93
- page: String(page),
94
- });
95
-
96
- const resp = await fetch(`${GITHUB_API_BASE}/notifications?${params}`, {
88
+ const resp = await connection.request({
89
+ method: "GET",
90
+ path: "/notifications",
91
+ query: {
92
+ all: "false", // only unread
93
+ since,
94
+ per_page: "50",
95
+ page: String(page),
96
+ },
97
97
  headers: {
98
- Authorization: `Bearer ${token}`,
99
98
  Accept: "application/vnd.github+json",
100
99
  "X-GitHub-Api-Version": "2022-11-28",
101
100
  },
102
101
  });
103
102
 
104
- if (!resp.ok) {
105
- const body = await resp.text().catch(() => "");
103
+ if (resp.status >= 400) {
104
+ const body =
105
+ typeof resp.body === "string" ? resp.body : JSON.stringify(resp.body);
106
106
  throw new Error(`GitHub Notifications API ${resp.status}: ${body}`);
107
107
  }
108
108
 
109
- const items = (await resp.json()) as GitHubNotification[];
109
+ const items = resp.body as GitHubNotification[];
110
110
  // GitHub returns 50 per page; if we got a full page there may be more
111
111
  const hasMore = items.length === 50;
112
112
  return { items, hasMore };
@@ -130,44 +130,43 @@ export const githubProvider: WatcherProvider = {
130
130
  _config: Record<string, unknown>,
131
131
  _watcherKey: string,
132
132
  ): Promise<FetchResult> {
133
- return withValidToken(credentialService, async (token) => {
134
- const since = watermark ?? new Date().toISOString();
135
- const items: WatcherItem[] = [];
136
- let page = 1;
137
-
138
- while (true) {
139
- const { items: pageItems, hasMore } = await fetchNotificationsPage(
140
- token,
141
- since,
142
- page,
143
- );
144
-
145
- for (const n of pageItems) {
146
- // Only surface notifications for reasons that warrant attention
147
- const relevantReasons = new Set([
148
- "assign",
149
- "mention",
150
- "review_requested",
151
- "team_mention",
152
- ]);
153
- if (!relevantReasons.has(n.reason)) continue;
154
-
155
- items.push(notificationToItem(n));
156
- }
157
-
158
- if (!hasMore) break;
159
- page++;
133
+ const connection = resolveOAuthConnection(credentialService);
134
+ const since = watermark ?? new Date().toISOString();
135
+ const items: WatcherItem[] = [];
136
+ let page = 1;
137
+
138
+ while (true) {
139
+ const { items: pageItems, hasMore } = await fetchNotificationsPage(
140
+ connection,
141
+ since,
142
+ page,
143
+ );
144
+
145
+ for (const n of pageItems) {
146
+ // Only surface notifications for reasons that warrant attention
147
+ const relevantReasons = new Set([
148
+ "assign",
149
+ "mention",
150
+ "review_requested",
151
+ "team_mention",
152
+ ]);
153
+ if (!relevantReasons.has(n.reason)) continue;
154
+
155
+ items.push(notificationToItem(n));
160
156
  }
161
157
 
162
- // New watermark: the time just before we fetched so we don't miss events
163
- // that arrive between poll cycles.
164
- const newWatermark = new Date().toISOString();
158
+ if (!hasMore) break;
159
+ page++;
160
+ }
165
161
 
166
- log.info(
167
- { count: items.length, watermark: newWatermark },
168
- "GitHub: fetched new notifications",
169
- );
170
- return { items, watermark: newWatermark };
171
- });
162
+ // New watermark: the time just before we fetched so we don't miss events
163
+ // that arrive between poll cycles.
164
+ const newWatermark = new Date().toISOString();
165
+
166
+ log.info(
167
+ { count: items.length, watermark: newWatermark },
168
+ "GitHub: fetched new notifications",
169
+ );
170
+ return { items, watermark: newWatermark };
172
171
  },
173
172
  };