@vellumai/assistant 0.4.29 → 0.4.30

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 (174) hide show
  1. package/ARCHITECTURE.md +39 -37
  2. package/README.md +5 -6
  3. package/docs/runbook-trusted-contacts.md +79 -43
  4. package/package.json +1 -1
  5. package/scripts/ipc/check-swift-decoder-drift.ts +2 -3
  6. package/scripts/test.sh +1 -1
  7. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +4 -37
  8. package/src/__tests__/actor-token-service.test.ts +4 -3
  9. package/src/__tests__/app-executors.test.ts +7 -17
  10. package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -10
  11. package/src/__tests__/browser-skill-endstate.test.ts +10 -1
  12. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +1 -0
  13. package/src/__tests__/channel-approval-routes.test.ts +44 -44
  14. package/src/__tests__/channel-approval.test.ts +8 -0
  15. package/src/__tests__/channel-approvals.test.ts +39 -1
  16. package/src/__tests__/channel-guardian.test.ts +15 -5
  17. package/src/__tests__/channel-reply-delivery.test.ts +31 -0
  18. package/src/__tests__/commit-message-enrichment-service.test.ts +4 -0
  19. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +9 -0
  20. package/src/__tests__/gateway-only-guard.test.ts +1 -0
  21. package/src/__tests__/gemini-image-service.test.ts +2 -2
  22. package/src/__tests__/guardian-grant-minting.test.ts +6 -6
  23. package/src/__tests__/guardian-routing-invariants.test.ts +34 -11
  24. package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +4 -6
  25. package/src/__tests__/inbound-invite-redemption.test.ts +1 -1
  26. package/src/__tests__/integrations-cli.test.ts +3 -27
  27. package/src/__tests__/intent-routing.test.ts +3 -0
  28. package/src/__tests__/invite-redemption-service.test.ts +1 -1
  29. package/src/__tests__/{ingress-routes-http.test.ts → invite-routes-http.test.ts} +40 -320
  30. package/src/__tests__/ipc-snapshot.test.ts +4 -31
  31. package/src/__tests__/nl-approval-parser.test.ts +305 -0
  32. package/src/__tests__/oauth-provider-profiles.test.ts +34 -0
  33. package/src/__tests__/provider-error-scenarios.test.ts +68 -0
  34. package/src/__tests__/relay-server.test.ts +1 -1
  35. package/src/__tests__/retry-after-extraction.test.ts +111 -0
  36. package/src/__tests__/script-proxy-profile-template-fallback.test.ts +127 -0
  37. package/src/__tests__/session-media-retry.test.ts +147 -0
  38. package/src/__tests__/skill-feature-flags-integration.test.ts +9 -5
  39. package/src/__tests__/skill-feature-flags.test.ts +18 -12
  40. package/src/__tests__/skill-load-feature-flag.test.ts +4 -3
  41. package/src/__tests__/slack-block-formatting.test.ts +100 -0
  42. package/src/__tests__/slack-inbound-verification.test.ts +346 -0
  43. package/src/__tests__/slack-reaction-approvals.test.ts +77 -0
  44. package/src/__tests__/slack-skill.test.ts +3 -2
  45. package/src/__tests__/starter-task-flow.test.ts +0 -1
  46. package/src/__tests__/trusted-contact-verification.test.ts +3 -1
  47. package/src/__tests__/voice-invite-redemption.test.ts +1 -1
  48. package/src/amazon/client.ts +7 -24
  49. package/src/calls/relay-server.ts +39 -11
  50. package/src/channels/config.ts +1 -1
  51. package/src/cli/integrations.ts +10 -66
  52. package/src/config/bundled-skills/app-builder/SKILL.md +193 -1500
  53. package/src/config/bundled-skills/app-builder/TOOLS.json +70 -18
  54. package/src/config/bundled-skills/browser/TOOLS.json +59 -2
  55. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +4 -0
  56. package/src/config/bundled-skills/computer-use/TOOLS.json +50 -2
  57. package/src/config/bundled-skills/contacts/SKILL.md +42 -35
  58. package/src/config/bundled-skills/contacts/TOOLS.json +22 -2
  59. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +38 -58
  60. package/src/config/bundled-skills/contacts/tools/contact-search.ts +11 -31
  61. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +19 -37
  62. package/src/config/bundled-skills/document/TOOLS.json +8 -0
  63. package/src/config/bundled-skills/email-setup/SKILL.md +10 -7
  64. package/src/config/bundled-skills/followups/TOOLS.json +12 -0
  65. package/src/config/bundled-skills/google-calendar/TOOLS.json +124 -26
  66. package/src/config/bundled-skills/guardian-verify-setup/SKILL.md +54 -21
  67. package/src/config/bundled-skills/image-studio/TOOLS.json +12 -2
  68. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +14 -8
  69. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +13 -3
  70. package/src/config/bundled-skills/media-processing/SKILL.md +1 -1
  71. package/src/config/bundled-skills/media-processing/TOOLS.json +28 -0
  72. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +26 -6
  73. package/src/config/bundled-skills/messaging/TOOLS.json +228 -182
  74. package/src/config/bundled-skills/notifications/SKILL.md +3 -2
  75. package/src/config/bundled-skills/notifications/TOOLS.json +7 -13
  76. package/src/config/bundled-skills/phone-calls/TOOLS.json +13 -1
  77. package/src/config/bundled-skills/playbooks/TOOLS.json +16 -0
  78. package/src/config/bundled-skills/reminder/TOOLS.json +15 -2
  79. package/src/config/bundled-skills/schedule/SKILL.md +33 -15
  80. package/src/config/bundled-skills/schedule/TOOLS.json +17 -1
  81. package/src/config/bundled-skills/slack/SKILL.md +30 -1
  82. package/src/config/bundled-skills/slack/TOOLS.json +89 -2
  83. package/src/config/bundled-skills/slack/tools/slack-channel-permissions.ts +146 -0
  84. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +120 -0
  85. package/src/config/bundled-skills/slack-app-setup/SKILL.md +200 -0
  86. package/src/config/bundled-skills/subagent/TOOLS.json +22 -2
  87. package/src/config/bundled-skills/tasks/TOOLS.json +86 -14
  88. package/src/config/bundled-skills/transcribe/TOOLS.json +4 -0
  89. package/src/config/bundled-skills/watcher/TOOLS.json +20 -0
  90. package/src/config/bundled-skills/weather/TOOLS.json +4 -0
  91. package/src/config/bundled-tool-registry.ts +2 -0
  92. package/src/config/channel-permission-profiles.ts +155 -0
  93. package/src/config/env.ts +4 -1
  94. package/src/contacts/contact-store.ts +195 -4
  95. package/src/contacts/types.ts +26 -0
  96. package/src/daemon/assistant-attachments.ts +23 -3
  97. package/src/daemon/guardian-verification-intent.ts +7 -4
  98. package/src/daemon/handlers/apps.ts +1 -2
  99. package/src/daemon/handlers/config-inbox.ts +16 -134
  100. package/src/daemon/handlers/guardian-actions.ts +20 -87
  101. package/src/daemon/handlers/sessions.ts +0 -1
  102. package/src/daemon/ipc-contract/apps.ts +0 -1
  103. package/src/daemon/ipc-contract/inbox.ts +7 -66
  104. package/src/daemon/ipc-contract/sessions.ts +1 -0
  105. package/src/daemon/ipc-contract/surfaces.ts +0 -1
  106. package/src/daemon/ipc-contract-inventory.json +2 -4
  107. package/src/daemon/lifecycle.ts +14 -2
  108. package/src/daemon/session-agent-loop-handlers.ts +9 -0
  109. package/src/daemon/session-agent-loop.ts +1 -0
  110. package/src/daemon/session-attachments.ts +5 -1
  111. package/src/daemon/session-error.ts +18 -0
  112. package/src/daemon/session-lifecycle.ts +4 -5
  113. package/src/daemon/session-media-retry.ts +15 -1
  114. package/src/daemon/session-surfaces.ts +0 -1
  115. package/src/daemon/session-tool-setup.ts +7 -4
  116. package/src/events/domain-events.ts +2 -1
  117. package/src/home-base/prebuilt/seed.ts +0 -1
  118. package/src/influencer/client.ts +7 -24
  119. package/src/media/gemini-image-service.ts +48 -3
  120. package/src/memory/app-store.ts +0 -4
  121. package/src/memory/conversation-attention-store.ts +3 -1
  122. package/src/memory/db-init.ts +4 -0
  123. package/src/memory/migrations/133-assistant-contact-metadata.ts +21 -0
  124. package/src/memory/migrations/index.ts +1 -0
  125. package/src/memory/schema.ts +12 -0
  126. package/src/memory/slack-thread-store.ts +187 -0
  127. package/src/messaging/providers/slack/client.ts +84 -26
  128. package/src/messaging/providers/slack/types.ts +4 -0
  129. package/src/notifications/adapters/slack.ts +90 -0
  130. package/src/notifications/destination-resolver.ts +42 -1
  131. package/src/notifications/emit-signal.ts +17 -1
  132. package/src/oauth/provider-profiles.ts +22 -0
  133. package/src/providers/anthropic/client.ts +3 -0
  134. package/src/providers/openai/client.ts +3 -0
  135. package/src/providers/retry.ts +9 -1
  136. package/src/runtime/actor-trust-resolver.ts +8 -0
  137. package/src/runtime/auth/require-bound-guardian.ts +44 -0
  138. package/src/runtime/auth/route-policy.ts +4 -8
  139. package/src/runtime/channel-approval-types.ts +18 -0
  140. package/src/runtime/channel-approvals.ts +8 -0
  141. package/src/runtime/channel-invite-transport.ts +1 -1
  142. package/src/runtime/channel-reply-delivery.ts +62 -3
  143. package/src/runtime/gateway-client.ts +36 -2
  144. package/src/runtime/gateway-internal-client.ts +86 -0
  145. package/src/runtime/guardian-action-service.ts +127 -0
  146. package/src/runtime/guardian-verification-templates.ts +16 -1
  147. package/src/runtime/http-server.ts +20 -49
  148. package/src/runtime/invite-redemption-service.ts +1 -1
  149. package/src/runtime/{ingress-service.ts → invite-service.ts} +5 -157
  150. package/src/runtime/nl-approval-parser.ts +138 -0
  151. package/src/runtime/routes/approval-routes.ts +1 -40
  152. package/src/runtime/routes/channel-route-shared.ts +35 -1
  153. package/src/runtime/routes/contact-routes.ts +196 -28
  154. package/src/runtime/routes/guardian-action-routes.ts +19 -111
  155. package/src/runtime/routes/guardian-approval-interception.ts +76 -0
  156. package/src/runtime/routes/inbound-message-handler.ts +40 -12
  157. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +222 -0
  158. package/src/runtime/routes/inbound-stages/background-dispatch.ts +108 -0
  159. package/src/runtime/routes/{ingress-routes.ts → invite-routes.ts} +10 -110
  160. package/src/runtime/slack-block-formatting.ts +176 -0
  161. package/src/schedule/scheduler.ts +11 -2
  162. package/src/tools/apps/executors.ts +16 -15
  163. package/src/tools/calls/call-end.ts +1 -1
  164. package/src/tools/computer-use/definitions.ts +16 -0
  165. package/src/tools/credentials/vault.ts +86 -2
  166. package/src/tools/network/script-proxy/session-manager.ts +28 -3
  167. package/src/tools/permission-checker.ts +18 -0
  168. package/src/tools/terminal/shell.ts +15 -5
  169. package/src/tools/tool-approval-handler.ts +48 -4
  170. package/src/tools/types.ts +38 -1
  171. package/src/util/errors.ts +5 -1
  172. package/src/util/retry.ts +21 -0
  173. package/src/watcher/providers/slack.ts +33 -3
  174. /package/src/memory/{ingress-invite-store.ts → invite-store.ts} +0 -0
@@ -46,6 +46,15 @@ mock.module("../util/logger.js", () => ({
46
46
  truncateForLog: (v: string) => v,
47
47
  }));
48
48
 
49
+ mock.module("../config/loader.js", () => ({
50
+ getConfig: () => ({
51
+ sandbox: { enabled: false, backend: "native" },
52
+ assistantFeatureFlagValues: {
53
+ "feature_flags.browser.enabled": true,
54
+ },
55
+ }),
56
+ }));
57
+
49
58
  const { buildSystemPrompt } = await import("../config/system-prompt.js");
50
59
 
51
60
  describe("Dynamic Skill Authoring Workflow prompt section", () => {
@@ -32,6 +32,7 @@ const ALLOWLIST = new Set([
32
32
 
33
33
  // --- Documentation and comments that mention the port for explanatory purposes ---
34
34
  "AGENTS.md", // documents the gateway-only rule itself
35
+ "assistant/docs/runbook-trusted-contacts.md", // operator runbook targeting runtime-only /v1/contacts endpoints
35
36
  "assistant/src/runtime/middleware/twilio-validation.ts", // comment explaining proxy URL rewriting
36
37
  ]);
37
38
 
@@ -137,8 +137,8 @@ describe("generateImage", () => {
137
137
  .contents as Array<Record<string, unknown>>;
138
138
  const parts = contents[0].parts as Array<Record<string, unknown>>;
139
139
 
140
- // First part is the text prompt
141
- expect(parts[0]).toEqual({ text: "remove background" });
140
+ // First part is the text prompt (with appended title instruction)
141
+ expect((parts[0] as { text: string }).text).toContain("remove background");
142
142
  // Second part is the source image
143
143
  expect(parts[1]).toEqual({
144
144
  inlineData: { mimeType: "image/jpeg", data: "srcdata" },
@@ -189,9 +189,9 @@ describe("guardian grant minting on tool-approval decisions", () => {
189
189
 
190
190
  beforeEach(() => {
191
191
  resetTables();
192
- deliverSpy = spyOn(gatewayClient, "deliverChannelReply").mockResolvedValue(
193
- undefined,
194
- );
192
+ deliverSpy = spyOn(gatewayClient, "deliverChannelReply").mockResolvedValue({
193
+ ok: true,
194
+ });
195
195
  composeSpy = spyOn(
196
196
  approvalMessageComposer,
197
197
  "composeApprovalMessageGenerative",
@@ -593,9 +593,9 @@ describe("approval interception trust-class regression coverage", () => {
593
593
 
594
594
  beforeEach(() => {
595
595
  resetTables();
596
- deliverSpy = spyOn(gatewayClient, "deliverChannelReply").mockResolvedValue(
597
- undefined,
598
- );
596
+ deliverSpy = spyOn(gatewayClient, "deliverChannelReply").mockResolvedValue({
597
+ ok: true,
598
+ });
599
599
  composeSpy = spyOn(
600
600
  approvalMessageComposer,
601
601
  "composeApprovalMessageGenerative",
@@ -169,22 +169,45 @@ function registerPendingToolApprovalInteraction(
169
169
  describe("routing invariant: all decision paths reference applyCanonicalGuardianDecision", () => {
170
170
  const srcRoot = resolve(__dirname, "..");
171
171
 
172
- // The files that constitute decision entrypoints. Each must import
173
- // `applyCanonicalGuardianDecision` from the guardian-decision-primitive.
174
- const DECISION_ENTRYPOINTS = [
172
+ // The files that constitute decision entrypoints. Each must reference
173
+ // `applyCanonicalGuardianDecision` (directly) or `processGuardianDecision`
174
+ // (shared wrapper that calls applyCanonicalGuardianDecision internally).
175
+ const DECISION_ENTRYPOINTS: Array<{
176
+ path: string;
177
+ symbols: string[];
178
+ }> = [
175
179
  // Inbound channel router (Telegram/SMS/WhatsApp)
176
- "runtime/guardian-reply-router.ts",
177
- // HTTP API route handler (desktop and API clients)
178
- "runtime/routes/guardian-action-routes.ts",
179
- // IPC handler (desktop socket clients)
180
- "daemon/handlers/guardian-actions.ts",
180
+ {
181
+ path: "runtime/guardian-reply-router.ts",
182
+ symbols: ["applyCanonicalGuardianDecision"],
183
+ },
184
+ // HTTP API route handler (desktop and API clients) — uses processGuardianDecision
185
+ // which is a shared wrapper around applyCanonicalGuardianDecision
186
+ {
187
+ path: "runtime/routes/guardian-action-routes.ts",
188
+ symbols: ["processGuardianDecision"],
189
+ },
190
+ // IPC handler (desktop socket clients) — uses processGuardianDecision
191
+ // which is a shared wrapper around applyCanonicalGuardianDecision
192
+ {
193
+ path: "daemon/handlers/guardian-actions.ts",
194
+ symbols: ["processGuardianDecision"],
195
+ },
196
+ // Shared service where processGuardianDecision is defined — must route
197
+ // through the canonical primitive to complete the chain:
198
+ // entrypoint → processGuardianDecision → applyCanonicalGuardianDecision
199
+ {
200
+ path: "runtime/guardian-action-service.ts",
201
+ symbols: ["applyCanonicalGuardianDecision"],
202
+ },
181
203
  ];
182
204
 
183
- for (const relPath of DECISION_ENTRYPOINTS) {
184
- test(`${relPath} imports applyCanonicalGuardianDecision`, () => {
205
+ for (const { path: relPath, symbols } of DECISION_ENTRYPOINTS) {
206
+ test(`${relPath} imports ${symbols.join(" or ")}`, () => {
185
207
  const fullPath = join(srcRoot, relPath);
186
208
  const source = readFileSync(fullPath, "utf-8");
187
- expect(source).toContain("applyCanonicalGuardianDecision");
209
+ const found = symbols.some((s) => source.includes(s));
210
+ expect(found).toBe(true);
188
211
  });
189
212
  }
190
213
 
@@ -107,14 +107,12 @@ describe("guardian-verify-setup skill — voice auto-followup", () => {
107
107
  ?.split("## Step 6")[0] ?? "";
108
108
  // Must mention rebind guard concept
109
109
  expect(pollingSection).toContain("Rebind guard");
110
- // Must instruct not to trust the first bound: true in a rebind flow
110
+ // Must instruct not to trust bound: true alone in a rebind flow
111
111
  expect(pollingSection).toContain(
112
- "do NOT treat the first `bound: true` poll result as success",
112
+ "do NOT treat `bound: true` alone as success",
113
113
  );
114
- // Must reference bound_at timestamp comparison as the primary mechanism
115
- expect(pollingSection).toContain("bound_at");
116
- // Must have a fallback for when bound_at is unavailable
117
- expect(pollingSection).toContain("second poll onward");
114
+ // Must reference verificationSessionId as the mechanism to detect fresh binding
115
+ expect(pollingSection).toContain("verificationSessionId");
118
116
  // Must clarify non-rebind flows are unaffected
119
117
  expect(pollingSection).toContain("Non-rebind flows");
120
118
  });
@@ -90,7 +90,7 @@ mock.module("../runtime/approval-message-composer.js", () => ({
90
90
  import { findContactChannel } from "../contacts/contact-store.js";
91
91
  import { upsertMember } from "../contacts/contacts-write.js";
92
92
  import { getDb, initializeDb, resetDb } from "../memory/db.js";
93
- import { createInvite, revokeInvite } from "../memory/ingress-invite-store.js";
93
+ import { createInvite, revokeInvite } from "../memory/invite-store.js";
94
94
  import { handleChannelInbound } from "../runtime/routes/channel-routes.js";
95
95
 
96
96
  initializeDb();
@@ -108,7 +108,6 @@ describe("vellum integrations CLI", () => {
108
108
 
109
109
  afterEach(() => {
110
110
  delete process.env.GATEWAY_AUTH_TOKEN;
111
- delete process.env.INTERNAL_GATEWAY_BASE_URL;
112
111
  process.exitCode = 0;
113
112
  });
114
113
 
@@ -141,8 +140,8 @@ describe("vellum integrations CLI", () => {
141
140
  expect(mintCalls).toBe(1);
142
141
  });
143
142
 
144
- test("prefers INTERNAL_GATEWAY_BASE_URL when it is injected", async () => {
145
- process.env.INTERNAL_GATEWAY_BASE_URL = "http://gateway.internal:9900/";
143
+ test("uses configured gateway base for requests", async () => {
144
+ gatewayBase = "http://gateway.internal:9900";
146
145
  const result = await runCli(["--json", "twilio", "config"], {
147
146
  success: true,
148
147
  });
@@ -163,28 +162,6 @@ describe("vellum integrations CLI", () => {
163
162
  );
164
163
  });
165
164
 
166
- test("passes filters for ingress members (now calls /v1/contacts)", async () => {
167
- const result = await runCli(
168
- ["--json", "ingress", "members", "--role", "contact", "--limit", "20"],
169
- { ok: true, contacts: [] },
170
- );
171
- expect(result.exitCode).toBe(0);
172
- expect(result.fetchCalls[0]?.url).toBe(
173
- "http://gateway.test/v1/contacts?role=contact&limit=20",
174
- );
175
- });
176
-
177
- test("ingress members defaults role to contact", async () => {
178
- const result = await runCli(["--json", "ingress", "members"], {
179
- ok: true,
180
- contacts: [],
181
- });
182
- expect(result.exitCode).toBe(0);
183
- expect(result.fetchCalls[0]?.url).toBe(
184
- "http://gateway.test/v1/contacts?role=contact",
185
- );
186
- });
187
-
188
165
  test("reads ingress config without gateway fetch", async () => {
189
166
  rawConfig = {
190
167
  ingress: {
@@ -192,7 +169,6 @@ describe("vellum integrations CLI", () => {
192
169
  publicBaseUrl: "https://public.example.com",
193
170
  },
194
171
  };
195
- process.env.INTERNAL_GATEWAY_BASE_URL = "http://gateway.internal:9900/";
196
172
  const result = await runCli(["--json", "ingress", "config"], { ok: true });
197
173
 
198
174
  expect(result.exitCode).toBe(0);
@@ -201,7 +177,7 @@ describe("vellum integrations CLI", () => {
201
177
  success: true,
202
178
  enabled: true,
203
179
  publicBaseUrl: "https://public.example.com",
204
- localGatewayTarget: "http://gateway.internal:9900",
180
+ localGatewayTarget: "http://gateway.test",
205
181
  });
206
182
  });
207
183
 
@@ -50,6 +50,9 @@ mock.module("../config/loader.js", () => ({
50
50
  ui: {},
51
51
 
52
52
  sandbox: { enabled: false, backend: "local" },
53
+ assistantFeatureFlagValues: {
54
+ "feature_flags.guardian-verify-setup.enabled": true,
55
+ },
53
56
  }),
54
57
  }));
55
58
 
@@ -29,7 +29,7 @@ import { getSqlite, initializeDb, resetDb } from "../memory/db.js";
29
29
  import {
30
30
  createInvite,
31
31
  revokeInvite as revokeStoreFn,
32
- } from "../memory/ingress-invite-store.js";
32
+ } from "../memory/invite-store.js";
33
33
  import {
34
34
  type InviteRedemptionOutcome,
35
35
  redeemInvite,