@vellumai/assistant 0.3.3 → 0.3.5

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 (163) hide show
  1. package/Dockerfile +2 -0
  2. package/README.md +45 -18
  3. package/package.json +1 -1
  4. package/scripts/ipc/generate-swift.ts +13 -0
  5. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +100 -0
  6. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +41 -0
  7. package/src/__tests__/approval-message-composer.test.ts +253 -0
  8. package/src/__tests__/call-domain.test.ts +12 -2
  9. package/src/__tests__/call-orchestrator.test.ts +391 -1
  10. package/src/__tests__/call-routes-http.test.ts +27 -2
  11. package/src/__tests__/channel-approval-routes.test.ts +397 -135
  12. package/src/__tests__/channel-approvals.test.ts +99 -3
  13. package/src/__tests__/channel-delivery-store.test.ts +30 -4
  14. package/src/__tests__/channel-guardian.test.ts +261 -22
  15. package/src/__tests__/channel-readiness-service.test.ts +257 -0
  16. package/src/__tests__/config-schema.test.ts +2 -1
  17. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  18. package/src/__tests__/daemon-lifecycle.test.ts +636 -0
  19. package/src/__tests__/dictation-mode-detection.test.ts +63 -0
  20. package/src/__tests__/entity-search.test.ts +615 -0
  21. package/src/__tests__/gateway-only-enforcement.test.ts +19 -13
  22. package/src/__tests__/handlers-twilio-config.test.ts +480 -0
  23. package/src/__tests__/ipc-snapshot.test.ts +63 -0
  24. package/src/__tests__/messaging-send-tool.test.ts +65 -0
  25. package/src/__tests__/run-orchestrator-assistant-events.test.ts +4 -0
  26. package/src/__tests__/run-orchestrator.test.ts +22 -0
  27. package/src/__tests__/secret-scanner.test.ts +223 -0
  28. package/src/__tests__/session-runtime-assembly.test.ts +85 -1
  29. package/src/__tests__/shell-parser-property.test.ts +357 -2
  30. package/src/__tests__/sms-messaging-provider.test.ts +125 -0
  31. package/src/__tests__/system-prompt.test.ts +25 -1
  32. package/src/__tests__/tool-executor-lifecycle-events.test.ts +34 -1
  33. package/src/__tests__/twilio-routes.test.ts +39 -3
  34. package/src/__tests__/twitter-cli-error-shaping.test.ts +2 -2
  35. package/src/__tests__/user-reference.test.ts +68 -0
  36. package/src/__tests__/web-search.test.ts +1 -1
  37. package/src/__tests__/work-item-output.test.ts +110 -0
  38. package/src/calls/call-domain.ts +8 -5
  39. package/src/calls/call-orchestrator.ts +85 -22
  40. package/src/calls/twilio-config.ts +17 -11
  41. package/src/calls/twilio-rest.ts +276 -0
  42. package/src/calls/twilio-routes.ts +39 -1
  43. package/src/cli/map.ts +6 -0
  44. package/src/commands/__tests__/cc-command-registry.test.ts +67 -0
  45. package/src/commands/cc-command-registry.ts +14 -1
  46. package/src/config/bundled-skills/claude-code/TOOLS.json +10 -3
  47. package/src/config/bundled-skills/knowledge-graph/SKILL.md +15 -0
  48. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +56 -0
  49. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +185 -0
  50. package/src/config/bundled-skills/media-processing/SKILL.md +199 -0
  51. package/src/config/bundled-skills/media-processing/TOOLS.json +320 -0
  52. package/src/config/bundled-skills/media-processing/services/capability-registry.ts +137 -0
  53. package/src/config/bundled-skills/media-processing/services/event-detection-service.ts +280 -0
  54. package/src/config/bundled-skills/media-processing/services/feedback-aggregation.ts +144 -0
  55. package/src/config/bundled-skills/media-processing/services/feedback-store.ts +136 -0
  56. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +261 -0
  57. package/src/config/bundled-skills/media-processing/services/retrieval-service.ts +95 -0
  58. package/src/config/bundled-skills/media-processing/services/timeline-service.ts +267 -0
  59. package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +301 -0
  60. package/src/config/bundled-skills/media-processing/tools/detect-events.ts +110 -0
  61. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +190 -0
  62. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +195 -0
  63. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +197 -0
  64. package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +166 -0
  65. package/src/config/bundled-skills/media-processing/tools/media-status.ts +75 -0
  66. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +300 -0
  67. package/src/config/bundled-skills/media-processing/tools/recalibrate.ts +235 -0
  68. package/src/config/bundled-skills/media-processing/tools/select-tracking-profile.ts +142 -0
  69. package/src/config/bundled-skills/media-processing/tools/submit-feedback.ts +150 -0
  70. package/src/config/bundled-skills/messaging/SKILL.md +24 -5
  71. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -1
  72. package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
  73. package/src/config/bundled-skills/twitter/SKILL.md +19 -3
  74. package/src/config/defaults.ts +2 -1
  75. package/src/config/schema.ts +9 -3
  76. package/src/config/skills.ts +5 -32
  77. package/src/config/system-prompt.ts +40 -0
  78. package/src/config/templates/IDENTITY.md +2 -2
  79. package/src/config/user-reference.ts +29 -0
  80. package/src/config/vellum-skills/catalog.json +58 -0
  81. package/src/config/vellum-skills/google-oauth-setup/SKILL.md +3 -3
  82. package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +3 -3
  83. package/src/config/vellum-skills/sms-setup/SKILL.md +118 -0
  84. package/src/config/vellum-skills/telegram-setup/SKILL.md +6 -1
  85. package/src/config/vellum-skills/twilio-setup/SKILL.md +76 -6
  86. package/src/daemon/auth-manager.ts +103 -0
  87. package/src/daemon/computer-use-session.ts +8 -1
  88. package/src/daemon/config-watcher.ts +253 -0
  89. package/src/daemon/handlers/config.ts +819 -22
  90. package/src/daemon/handlers/dictation.ts +182 -0
  91. package/src/daemon/handlers/identity.ts +14 -23
  92. package/src/daemon/handlers/index.ts +2 -0
  93. package/src/daemon/handlers/sessions.ts +2 -0
  94. package/src/daemon/handlers/shared.ts +3 -0
  95. package/src/daemon/handlers/skills.ts +6 -7
  96. package/src/daemon/handlers/work-items.ts +15 -7
  97. package/src/daemon/ipc-contract-inventory.json +10 -0
  98. package/src/daemon/ipc-contract.ts +114 -4
  99. package/src/daemon/ipc-handler.ts +87 -0
  100. package/src/daemon/lifecycle.ts +18 -4
  101. package/src/daemon/ride-shotgun-handler.ts +11 -1
  102. package/src/daemon/server.ts +111 -504
  103. package/src/daemon/session-agent-loop.ts +10 -15
  104. package/src/daemon/session-runtime-assembly.ts +115 -44
  105. package/src/daemon/session-tool-setup.ts +2 -0
  106. package/src/daemon/session.ts +19 -2
  107. package/src/inbound/public-ingress-urls.ts +3 -3
  108. package/src/memory/channel-guardian-store.ts +2 -1
  109. package/src/memory/db-connection.ts +28 -0
  110. package/src/memory/db-init.ts +1163 -0
  111. package/src/memory/db.ts +2 -2007
  112. package/src/memory/embedding-backend.ts +79 -11
  113. package/src/memory/indexer.ts +2 -0
  114. package/src/memory/job-handlers/media-processing.ts +100 -0
  115. package/src/memory/job-utils.ts +64 -4
  116. package/src/memory/jobs-store.ts +2 -1
  117. package/src/memory/jobs-worker.ts +11 -1
  118. package/src/memory/media-store.ts +759 -0
  119. package/src/memory/recall-cache.ts +107 -0
  120. package/src/memory/retriever.ts +36 -2
  121. package/src/memory/schema-migration.ts +984 -0
  122. package/src/memory/schema.ts +99 -0
  123. package/src/memory/search/entity.ts +208 -25
  124. package/src/memory/search/ranking.ts +6 -1
  125. package/src/memory/search/types.ts +26 -0
  126. package/src/messaging/provider-types.ts +2 -0
  127. package/src/messaging/providers/sms/adapter.ts +204 -0
  128. package/src/messaging/providers/sms/client.ts +93 -0
  129. package/src/messaging/providers/sms/types.ts +7 -0
  130. package/src/permissions/checker.ts +16 -2
  131. package/src/permissions/prompter.ts +14 -3
  132. package/src/permissions/trust-store.ts +7 -0
  133. package/src/runtime/approval-message-composer.ts +143 -0
  134. package/src/runtime/channel-approvals.ts +29 -7
  135. package/src/runtime/channel-guardian-service.ts +44 -18
  136. package/src/runtime/channel-readiness-service.ts +292 -0
  137. package/src/runtime/channel-readiness-types.ts +29 -0
  138. package/src/runtime/gateway-client.ts +2 -1
  139. package/src/runtime/http-server.ts +65 -28
  140. package/src/runtime/http-types.ts +3 -0
  141. package/src/runtime/routes/call-routes.ts +2 -1
  142. package/src/runtime/routes/channel-routes.ts +237 -103
  143. package/src/runtime/routes/run-routes.ts +7 -1
  144. package/src/runtime/run-orchestrator.ts +43 -3
  145. package/src/security/secret-scanner.ts +218 -0
  146. package/src/skills/frontmatter.ts +63 -0
  147. package/src/skills/slash-commands.ts +23 -0
  148. package/src/skills/vellum-catalog-remote.ts +107 -0
  149. package/src/tools/assets/materialize.ts +2 -2
  150. package/src/tools/browser/auto-navigate.ts +132 -24
  151. package/src/tools/browser/browser-manager.ts +67 -61
  152. package/src/tools/calls/call-start.ts +1 -0
  153. package/src/tools/claude-code/claude-code.ts +55 -3
  154. package/src/tools/credentials/vault.ts +1 -1
  155. package/src/tools/execution-target.ts +11 -1
  156. package/src/tools/executor.ts +10 -2
  157. package/src/tools/network/web-search.ts +1 -1
  158. package/src/tools/skills/vellum-catalog.ts +61 -156
  159. package/src/tools/terminal/parser.ts +21 -5
  160. package/src/tools/types.ts +2 -0
  161. package/src/twitter/router.ts +1 -1
  162. package/src/util/platform.ts +43 -1
  163. package/src/util/retry.ts +4 -4
@@ -0,0 +1,257 @@
1
+ import { describe, test, expect, beforeEach, mock } from 'bun:test';
2
+ import { ChannelReadinessService, REMOTE_TTL_MS } from '../runtime/channel-readiness-service.js';
3
+ import type { ChannelProbe, ReadinessCheckResult } from '../runtime/channel-readiness-types.js';
4
+
5
+ // ── Test helpers ────────────────────────────────────────────────────────────
6
+
7
+ function makeProbe(
8
+ channel: string,
9
+ localResults: ReadinessCheckResult[],
10
+ remoteResults?: ReadinessCheckResult[],
11
+ ): ChannelProbe & { localCallCount: number; remoteCallCount: number } {
12
+ const probe = {
13
+ channel,
14
+ localCallCount: 0,
15
+ remoteCallCount: 0,
16
+ runLocalChecks(): ReadinessCheckResult[] {
17
+ probe.localCallCount++;
18
+ return localResults;
19
+ },
20
+ ...(remoteResults !== undefined
21
+ ? {
22
+ async runRemoteChecks(): Promise<ReadinessCheckResult[]> {
23
+ probe.remoteCallCount++;
24
+ return remoteResults;
25
+ },
26
+ }
27
+ : {}),
28
+ };
29
+ return probe;
30
+ }
31
+
32
+ // ── Tests ───────────────────────────────────────────────────────────────────
33
+
34
+ describe('ChannelReadinessService', () => {
35
+ let service: ChannelReadinessService;
36
+
37
+ beforeEach(() => {
38
+ service = new ChannelReadinessService();
39
+ });
40
+
41
+ test('local checks run on every call (no caching of local results)', async () => {
42
+ const probe = makeProbe('sms', [
43
+ { name: 'creds', passed: true, message: 'ok' },
44
+ ]);
45
+ service.registerProbe(probe);
46
+
47
+ await service.getReadiness('sms');
48
+ await service.getReadiness('sms');
49
+
50
+ expect(probe.localCallCount).toBe(2);
51
+ });
52
+
53
+ test('cache miss runs local checks and returns snapshot', async () => {
54
+ const probe = makeProbe('sms', [
55
+ { name: 'creds', passed: true, message: 'ok' },
56
+ { name: 'phone', passed: false, message: 'missing' },
57
+ ]);
58
+ service.registerProbe(probe);
59
+
60
+ const [snapshot] = await service.getReadiness('sms');
61
+
62
+ expect(probe.localCallCount).toBe(1);
63
+ expect(snapshot.channel).toBe('sms');
64
+ expect(snapshot.ready).toBe(false);
65
+ expect(snapshot.localChecks).toHaveLength(2);
66
+ expect(snapshot.reasons).toEqual([
67
+ { code: 'phone', text: 'missing' },
68
+ ]);
69
+ });
70
+
71
+ test('includeRemote=true runs remote checks on cache miss', async () => {
72
+ const probe = makeProbe(
73
+ 'sms',
74
+ [{ name: 'creds', passed: true, message: 'ok' }],
75
+ [{ name: 'api_check', passed: true, message: 'remote ok' }],
76
+ );
77
+ service.registerProbe(probe);
78
+
79
+ const [snapshot] = await service.getReadiness('sms', true);
80
+
81
+ expect(probe.remoteCallCount).toBe(1);
82
+ expect(snapshot.remoteChecks).toHaveLength(1);
83
+ expect(snapshot.remoteChecks![0].passed).toBe(true);
84
+ });
85
+
86
+ test('cached remote checks reused within TTL', async () => {
87
+ const probe = makeProbe(
88
+ 'sms',
89
+ [{ name: 'creds', passed: true, message: 'ok' }],
90
+ [{ name: 'api_check', passed: true, message: 'remote ok' }],
91
+ );
92
+ service.registerProbe(probe);
93
+
94
+ // First call populates cache
95
+ await service.getReadiness('sms', true);
96
+ expect(probe.remoteCallCount).toBe(1);
97
+
98
+ // Second call within TTL should reuse cache
99
+ const [snapshot] = await service.getReadiness('sms', true);
100
+ expect(probe.remoteCallCount).toBe(1);
101
+ expect(snapshot.remoteChecks).toHaveLength(1);
102
+ });
103
+
104
+ test('stale cache triggers remote check re-run', async () => {
105
+ const probe = makeProbe(
106
+ 'sms',
107
+ [{ name: 'creds', passed: true, message: 'ok' }],
108
+ [{ name: 'api_check', passed: true, message: 'remote ok' }],
109
+ );
110
+ service.registerProbe(probe);
111
+
112
+ // First call
113
+ await service.getReadiness('sms', true);
114
+ expect(probe.remoteCallCount).toBe(1);
115
+
116
+ // Manually age the cached snapshot beyond TTL
117
+ const cached = (service as unknown as { snapshots: Map<string, { checkedAt: number }> }).snapshots.get('sms')!;
118
+ cached.checkedAt = Date.now() - REMOTE_TTL_MS - 1;
119
+
120
+ // Second call should re-run remote checks
121
+ await service.getReadiness('sms', true);
122
+ expect(probe.remoteCallCount).toBe(2);
123
+ });
124
+
125
+ test('invalidateChannel clears cache for specific channel', async () => {
126
+ const probe = makeProbe(
127
+ 'sms',
128
+ [{ name: 'creds', passed: true, message: 'ok' }],
129
+ [{ name: 'api_check', passed: true, message: 'remote ok' }],
130
+ );
131
+ service.registerProbe(probe);
132
+
133
+ await service.getReadiness('sms', true);
134
+ expect(probe.remoteCallCount).toBe(1);
135
+
136
+ service.invalidateChannel('sms');
137
+
138
+ // After invalidation, remote checks should run again
139
+ await service.getReadiness('sms', true);
140
+ expect(probe.remoteCallCount).toBe(2);
141
+ });
142
+
143
+ test('invalidateAll clears all cached snapshots', async () => {
144
+ const smsProbe = makeProbe(
145
+ 'sms',
146
+ [{ name: 'creds', passed: true, message: 'ok' }],
147
+ [{ name: 'api', passed: true, message: 'ok' }],
148
+ );
149
+ const telegramProbe = makeProbe(
150
+ 'telegram',
151
+ [{ name: 'token', passed: true, message: 'ok' }],
152
+ [{ name: 'webhook', passed: true, message: 'ok' }],
153
+ );
154
+ service.registerProbe(smsProbe);
155
+ service.registerProbe(telegramProbe);
156
+
157
+ await service.getReadiness(undefined, true);
158
+ expect(smsProbe.remoteCallCount).toBe(1);
159
+ expect(telegramProbe.remoteCallCount).toBe(1);
160
+
161
+ service.invalidateAll();
162
+
163
+ await service.getReadiness(undefined, true);
164
+ expect(smsProbe.remoteCallCount).toBe(2);
165
+ expect(telegramProbe.remoteCallCount).toBe(2);
166
+ });
167
+
168
+ test('unknown channel returns unsupported_channel reason', async () => {
169
+ const [snapshot] = await service.getReadiness('carrier_pigeon');
170
+
171
+ expect(snapshot.channel).toBe('carrier_pigeon');
172
+ expect(snapshot.ready).toBe(false);
173
+ expect(snapshot.reasons).toEqual([
174
+ { code: 'unsupported_channel', text: 'Channel carrier_pigeon is not supported' },
175
+ ]);
176
+ expect(snapshot.localChecks).toHaveLength(0);
177
+ });
178
+
179
+ test('all checks passing yields ready=true', async () => {
180
+ const probe = makeProbe('test', [
181
+ { name: 'a', passed: true, message: 'ok' },
182
+ { name: 'b', passed: true, message: 'ok' },
183
+ ]);
184
+ service.registerProbe(probe);
185
+
186
+ const [snapshot] = await service.getReadiness('test');
187
+
188
+ expect(snapshot.ready).toBe(true);
189
+ expect(snapshot.reasons).toHaveLength(0);
190
+ });
191
+
192
+ test('getReadiness with no channel returns all registered channels', async () => {
193
+ service.registerProbe(makeProbe('sms', [{ name: 'a', passed: true, message: 'ok' }]));
194
+ service.registerProbe(makeProbe('telegram', [{ name: 'b', passed: true, message: 'ok' }]));
195
+
196
+ const snapshots = await service.getReadiness();
197
+
198
+ expect(snapshots).toHaveLength(2);
199
+ const channels = snapshots.map((s) => s.channel).sort();
200
+ expect(channels).toEqual(['sms', 'telegram']);
201
+ });
202
+
203
+ test('cached remote checks preserve original checkedAt (TTL not reset on reuse)', async () => {
204
+ const probe = makeProbe(
205
+ 'sms',
206
+ [{ name: 'creds', passed: true, message: 'ok' }],
207
+ [{ name: 'api_check', passed: true, message: 'remote ok' }],
208
+ );
209
+ service.registerProbe(probe);
210
+
211
+ // First call populates cache with freshly fetched remote checks
212
+ const [first] = await service.getReadiness('sms', true);
213
+ const originalCheckedAt = first.checkedAt;
214
+ expect(probe.remoteCallCount).toBe(1);
215
+
216
+ // Second call within TTL reuses cache — checkedAt must stay at the original value
217
+ const [second] = await service.getReadiness('sms', true);
218
+ expect(probe.remoteCallCount).toBe(1);
219
+ expect(second.checkedAt).toBe(originalCheckedAt);
220
+ });
221
+
222
+ test('includeRemote runs remote checks when cache exists without remote data', async () => {
223
+ const probe = makeProbe(
224
+ 'sms',
225
+ [{ name: 'creds', passed: true, message: 'ok' }],
226
+ [{ name: 'api_check', passed: true, message: 'remote ok' }],
227
+ );
228
+ service.registerProbe(probe);
229
+
230
+ // First call without includeRemote — cache has no remote data
231
+ await service.getReadiness('sms', false);
232
+ expect(probe.remoteCallCount).toBe(0);
233
+
234
+ // Second call with includeRemote — should run remote checks even though
235
+ // the cached snapshot exists (because it has no remoteChecks)
236
+ const [snapshot] = await service.getReadiness('sms', true);
237
+ expect(probe.remoteCallCount).toBe(1);
238
+ expect(snapshot.remoteChecks).toHaveLength(1);
239
+ expect(snapshot.remoteChecks![0].passed).toBe(true);
240
+ });
241
+
242
+ test('failed remote check makes channel not ready', async () => {
243
+ const probe = makeProbe(
244
+ 'sms',
245
+ [{ name: 'creds', passed: true, message: 'ok' }],
246
+ [{ name: 'api_check', passed: false, message: 'API unreachable' }],
247
+ );
248
+ service.registerProbe(probe);
249
+
250
+ const [snapshot] = await service.getReadiness('sms', true);
251
+
252
+ expect(snapshot.ready).toBe(false);
253
+ expect(snapshot.reasons).toEqual([
254
+ { code: 'api_check', text: 'API unreachable' },
255
+ ]);
256
+ });
257
+ });
@@ -206,6 +206,7 @@ describe('AssistantConfigSchema', () => {
206
206
  maxEdges: 40,
207
207
  neighborScoreMultiplier: 0.7,
208
208
  maxDepth: 3,
209
+ depthDecay: true,
209
210
  });
210
211
  });
211
212
 
@@ -681,7 +682,7 @@ describe('AssistantConfigSchema', () => {
681
682
  userConsultTimeoutSeconds: 120,
682
683
  disclosure: {
683
684
  enabled: true,
684
- text: 'At the very beginning of the call, disclose that you are an AI assistant calling on behalf of the user.',
685
+ text: 'At the very beginning of the call, introduce yourself as an assistant calling on behalf of the person you represent. Do not say "AI assistant".',
685
686
  },
686
687
  safety: {
687
688
  denyCategories: [],
@@ -197,6 +197,7 @@ describe('Invariant 2: no generic plaintext secret read API', () => {
197
197
  'calls/elevenlabs-config.ts', // ElevenLabs credential lookup
198
198
  'cli/config-commands.ts', // CLI config management
199
199
  'messaging/providers/telegram-bot/adapter.ts', // Telegram bot token lookup for connectivity check
200
+ 'messaging/providers/sms/adapter.ts', // Twilio credential lookup for SMS connectivity check
200
201
  ]);
201
202
 
202
203
  const thisDir = dirname(fileURLToPath(import.meta.url));