vellum 0.2.13 → 0.2.14

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 (207) hide show
  1. package/README.md +32 -0
  2. package/bun.lock +2 -2
  3. package/docs/skills.md +4 -4
  4. package/package.json +2 -2
  5. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +213 -3
  6. package/src/__tests__/app-git-history.test.ts +176 -0
  7. package/src/__tests__/app-git-service.test.ts +169 -0
  8. package/src/__tests__/assistant-events-sse-hardening.test.ts +315 -0
  9. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +8 -8
  10. package/src/__tests__/browser-skill-endstate.test.ts +6 -6
  11. package/src/__tests__/call-bridge.test.ts +105 -13
  12. package/src/__tests__/call-domain.test.ts +163 -0
  13. package/src/__tests__/call-orchestrator.test.ts +113 -0
  14. package/src/__tests__/call-routes-http.test.ts +246 -6
  15. package/src/__tests__/channel-approval-routes.test.ts +438 -0
  16. package/src/__tests__/channel-approval.test.ts +266 -0
  17. package/src/__tests__/channel-approvals.test.ts +393 -0
  18. package/src/__tests__/channel-delivery-store.test.ts +447 -0
  19. package/src/__tests__/checker.test.ts +607 -1048
  20. package/src/__tests__/cli.test.ts +1 -56
  21. package/src/__tests__/config-schema.test.ts +137 -18
  22. package/src/__tests__/conflict-intent-tokenization.test.ts +141 -0
  23. package/src/__tests__/conflict-policy.test.ts +121 -0
  24. package/src/__tests__/conflict-store.test.ts +2 -0
  25. package/src/__tests__/contacts-tools.test.ts +3 -3
  26. package/src/__tests__/contradiction-checker.test.ts +99 -1
  27. package/src/__tests__/credential-security-invariants.test.ts +22 -6
  28. package/src/__tests__/credential-vault-unit.test.ts +780 -0
  29. package/src/__tests__/elevenlabs-client.test.ts +62 -0
  30. package/src/__tests__/ephemeral-permissions.test.ts +73 -23
  31. package/src/__tests__/filesystem-tools.test.ts +579 -0
  32. package/src/__tests__/gateway-only-enforcement.test.ts +114 -4
  33. package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +202 -0
  34. package/src/__tests__/handlers-cu-observation-blob.test.ts +2 -1
  35. package/src/__tests__/handlers-ipc-blob-probe.test.ts +2 -1
  36. package/src/__tests__/handlers-slack-config.test.ts +2 -1
  37. package/src/__tests__/handlers-telegram-config.test.ts +855 -0
  38. package/src/__tests__/handlers-twitter-config.test.ts +141 -1
  39. package/src/__tests__/hooks-runner.test.ts +6 -2
  40. package/src/__tests__/host-file-edit-tool.test.ts +124 -0
  41. package/src/__tests__/host-file-read-tool.test.ts +62 -0
  42. package/src/__tests__/host-file-write-tool.test.ts +59 -0
  43. package/src/__tests__/host-shell-tool.test.ts +251 -0
  44. package/src/__tests__/ingress-reconcile.test.ts +581 -0
  45. package/src/__tests__/ipc-snapshot.test.ts +100 -41
  46. package/src/__tests__/ipc-validate.test.ts +50 -0
  47. package/src/__tests__/key-migration.test.ts +23 -0
  48. package/src/__tests__/memory-regressions.test.ts +99 -0
  49. package/src/__tests__/memory-retrieval.benchmark.test.ts +1 -1
  50. package/src/__tests__/oauth-callback-registry.test.ts +11 -4
  51. package/src/__tests__/playbook-execution.test.ts +502 -0
  52. package/src/__tests__/playbook-tools.test.ts +4 -6
  53. package/src/__tests__/public-ingress-urls.test.ts +34 -0
  54. package/src/__tests__/qdrant-manager.test.ts +267 -0
  55. package/src/__tests__/recurrence-engine-rruleset.test.ts +97 -0
  56. package/src/__tests__/recurrence-engine.test.ts +9 -0
  57. package/src/__tests__/recurrence-types.test.ts +8 -0
  58. package/src/__tests__/registry.test.ts +1 -1
  59. package/src/__tests__/runtime-runs.test.ts +1 -25
  60. package/src/__tests__/schedule-store.test.ts +16 -14
  61. package/src/__tests__/schedule-tools.test.ts +83 -0
  62. package/src/__tests__/scheduler-recurrence.test.ts +111 -10
  63. package/src/__tests__/secret-allowlist.test.ts +18 -17
  64. package/src/__tests__/secret-ingress-handler.test.ts +11 -0
  65. package/src/__tests__/secret-scanner.test.ts +43 -0
  66. package/src/__tests__/session-conflict-gate.test.ts +442 -6
  67. package/src/__tests__/session-init.benchmark.test.ts +3 -0
  68. package/src/__tests__/session-process-bridge.test.ts +242 -0
  69. package/src/__tests__/session-skill-tools.test.ts +1 -1
  70. package/src/__tests__/shell-identity.test.ts +256 -0
  71. package/src/__tests__/skill-projection.benchmark.test.ts +11 -1
  72. package/src/__tests__/subagent-tools.test.ts +637 -54
  73. package/src/__tests__/task-management-tools.test.ts +936 -0
  74. package/src/__tests__/task-runner.test.ts +2 -2
  75. package/src/__tests__/terminal-tools.test.ts +840 -0
  76. package/src/__tests__/tool-executor-shell-integration.test.ts +301 -0
  77. package/src/__tests__/tool-executor.test.ts +85 -151
  78. package/src/__tests__/tool-permission-simulate-handler.test.ts +336 -0
  79. package/src/__tests__/trust-store.test.ts +27 -453
  80. package/src/__tests__/twilio-provider.test.ts +153 -3
  81. package/src/__tests__/twilio-routes-elevenlabs.test.ts +375 -0
  82. package/src/__tests__/twilio-routes-twiml.test.ts +4 -4
  83. package/src/__tests__/twilio-routes.test.ts +17 -262
  84. package/src/__tests__/twitter-auth-handler.test.ts +2 -1
  85. package/src/__tests__/twitter-cli-error-shaping.test.ts +208 -0
  86. package/src/__tests__/twitter-cli-routing.test.ts +252 -0
  87. package/src/__tests__/twitter-oauth-client.test.ts +209 -0
  88. package/src/__tests__/workspace-policy.test.ts +213 -0
  89. package/src/calls/call-bridge.ts +92 -19
  90. package/src/calls/call-domain.ts +157 -5
  91. package/src/calls/call-orchestrator.ts +93 -7
  92. package/src/calls/call-store.ts +6 -0
  93. package/src/calls/elevenlabs-client.ts +8 -0
  94. package/src/calls/elevenlabs-config.ts +7 -5
  95. package/src/calls/twilio-provider.ts +91 -0
  96. package/src/calls/twilio-routes.ts +32 -37
  97. package/src/calls/types.ts +3 -1
  98. package/src/calls/voice-quality.ts +29 -7
  99. package/src/cli/twitter.ts +200 -21
  100. package/src/cli.ts +1 -20
  101. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +52 -4
  102. package/src/config/bundled-skills/contacts/tools/contact-search.ts +55 -4
  103. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +61 -4
  104. package/src/config/bundled-skills/messaging/SKILL.md +17 -2
  105. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +4 -1
  106. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -1
  107. package/src/config/bundled-skills/messaging/tools/shared.ts +5 -0
  108. package/src/config/bundled-skills/phone-calls/SKILL.md +142 -34
  109. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +95 -6
  110. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +51 -6
  111. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +73 -6
  112. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +110 -6
  113. package/src/config/bundled-skills/public-ingress/SKILL.md +22 -5
  114. package/src/config/bundled-skills/twitter/SKILL.md +103 -17
  115. package/src/config/defaults.ts +10 -4
  116. package/src/config/schema.ts +80 -21
  117. package/src/config/types.ts +1 -0
  118. package/src/config/vellum-skills/telegram-setup/SKILL.md +56 -61
  119. package/src/daemon/assistant-attachments.ts +4 -2
  120. package/src/daemon/handlers/apps.ts +69 -0
  121. package/src/daemon/handlers/config.ts +543 -24
  122. package/src/daemon/handlers/index.ts +1 -0
  123. package/src/daemon/handlers/sessions.ts +22 -6
  124. package/src/daemon/handlers/shared.ts +2 -1
  125. package/src/daemon/handlers/skills.ts +5 -20
  126. package/src/daemon/ipc-contract-inventory.json +28 -0
  127. package/src/daemon/ipc-contract.ts +168 -10
  128. package/src/daemon/ipc-validate.ts +17 -0
  129. package/src/daemon/lifecycle.ts +2 -0
  130. package/src/daemon/server.ts +78 -72
  131. package/src/daemon/session-attachments.ts +1 -1
  132. package/src/daemon/session-conflict-gate.ts +62 -6
  133. package/src/daemon/session-notifiers.ts +1 -1
  134. package/src/daemon/session-process.ts +62 -3
  135. package/src/daemon/session-tool-setup.ts +1 -2
  136. package/src/daemon/tls-certs.ts +189 -0
  137. package/src/daemon/video-thumbnail.ts +5 -3
  138. package/src/hooks/manager.ts +5 -9
  139. package/src/memory/app-git-service.ts +295 -0
  140. package/src/memory/app-store.ts +21 -0
  141. package/src/memory/conflict-intent.ts +47 -4
  142. package/src/memory/conflict-policy.ts +73 -0
  143. package/src/memory/conflict-store.ts +9 -1
  144. package/src/memory/contradiction-checker.ts +28 -0
  145. package/src/memory/conversation-key-store.ts +15 -0
  146. package/src/memory/db.ts +81 -0
  147. package/src/memory/embedding-local.ts +3 -13
  148. package/src/memory/external-conversation-store.ts +234 -0
  149. package/src/memory/job-handlers/conflict.ts +22 -2
  150. package/src/memory/jobs-worker.ts +67 -28
  151. package/src/memory/runs-store.ts +54 -7
  152. package/src/memory/schema.ts +20 -0
  153. package/src/messaging/provider.ts +9 -0
  154. package/src/messaging/providers/telegram-bot/adapter.ts +162 -0
  155. package/src/messaging/providers/telegram-bot/client.ts +104 -0
  156. package/src/messaging/providers/telegram-bot/types.ts +15 -0
  157. package/src/messaging/registry.ts +1 -0
  158. package/src/permissions/checker.ts +48 -44
  159. package/src/permissions/prompter.ts +0 -4
  160. package/src/permissions/shell-identity.ts +227 -0
  161. package/src/permissions/trust-store.ts +76 -53
  162. package/src/permissions/types.ts +0 -19
  163. package/src/permissions/workspace-policy.ts +114 -0
  164. package/src/providers/retry.ts +12 -37
  165. package/src/runtime/assistant-event-hub.ts +41 -4
  166. package/src/runtime/channel-approval-parser.ts +60 -0
  167. package/src/runtime/channel-approval-types.ts +71 -0
  168. package/src/runtime/channel-approvals.ts +145 -0
  169. package/src/runtime/gateway-client.ts +16 -0
  170. package/src/runtime/http-server.ts +29 -9
  171. package/src/runtime/routes/call-routes.ts +52 -2
  172. package/src/runtime/routes/channel-routes.ts +296 -16
  173. package/src/runtime/routes/events-routes.ts +97 -28
  174. package/src/runtime/routes/run-routes.ts +2 -7
  175. package/src/runtime/run-orchestrator.ts +0 -3
  176. package/src/schedule/recurrence-engine.ts +26 -2
  177. package/src/schedule/recurrence-types.ts +1 -1
  178. package/src/schedule/schedule-store.ts +12 -3
  179. package/src/security/secret-scanner.ts +7 -0
  180. package/src/tasks/ephemeral-permissions.ts +0 -2
  181. package/src/tasks/task-scheduler.ts +2 -1
  182. package/src/tools/calls/call-start.ts +8 -0
  183. package/src/tools/execution-target.ts +21 -0
  184. package/src/tools/execution-timeout.ts +49 -0
  185. package/src/tools/executor.ts +6 -135
  186. package/src/tools/network/web-search.ts +9 -32
  187. package/src/tools/policy-context.ts +29 -0
  188. package/src/tools/schedule/update.ts +8 -1
  189. package/src/tools/terminal/parser.ts +16 -18
  190. package/src/tools/types.ts +4 -11
  191. package/src/twitter/oauth-client.ts +102 -0
  192. package/src/twitter/router.ts +101 -0
  193. package/src/util/debounce.ts +88 -0
  194. package/src/util/network-info.ts +47 -0
  195. package/src/util/platform.ts +29 -4
  196. package/src/util/promise-guard.ts +37 -0
  197. package/src/util/retry.ts +98 -0
  198. package/src/util/truncate.ts +1 -1
  199. package/src/workspace/git-service.ts +129 -112
  200. package/src/tools/contacts/contact-merge.ts +0 -55
  201. package/src/tools/contacts/contact-search.ts +0 -58
  202. package/src/tools/contacts/contact-upsert.ts +0 -64
  203. package/src/tools/playbooks/index.ts +0 -4
  204. package/src/tools/playbooks/playbook-create.ts +0 -96
  205. package/src/tools/playbooks/playbook-delete.ts +0 -52
  206. package/src/tools/playbooks/playbook-list.ts +0 -74
  207. package/src/tools/playbooks/playbook-update.ts +0 -111
@@ -51,9 +51,18 @@ mock.module('../util/logger.js', () => ({
51
51
  }),
52
52
  }));
53
53
 
54
+ let mockConflictableKinds: string[] = [
55
+ 'preference', 'profile', 'constraint', 'instruction', 'style',
56
+ ];
57
+
54
58
  mock.module('../config/loader.js', () => ({
55
59
  getConfig: () => ({
56
60
  apiKeys: { anthropic: 'test-key' },
61
+ memory: {
62
+ conflicts: {
63
+ conflictableKinds: mockConflictableKinds,
64
+ },
65
+ },
57
66
  }),
58
67
  }));
59
68
 
@@ -67,6 +76,9 @@ beforeAll(() => {
67
76
 
68
77
  beforeEach(() => {
69
78
  classifyCallCount = 0;
79
+ mockConflictableKinds = [
80
+ 'preference', 'profile', 'constraint', 'instruction', 'style',
81
+ ];
70
82
  const db = getDb();
71
83
  db.run('DELETE FROM memory_item_conflicts');
72
84
  db.run('DELETE FROM memory_item_sources');
@@ -88,12 +100,13 @@ function insertMemoryItem(params: {
88
100
  statement: string;
89
101
  scopeId?: string;
90
102
  status?: 'active' | 'pending_clarification';
103
+ kind?: string;
91
104
  }): void {
92
105
  const now = Date.now();
93
106
  const db = getDb();
94
107
  db.insert(memoryItems).values({
95
108
  id: params.id,
96
- kind: 'preference',
109
+ kind: params.kind ?? 'preference',
97
110
  subject: 'framework preference',
98
111
  statement: params.statement,
99
112
  status: params.status ?? 'active',
@@ -213,4 +226,89 @@ describe('checkContradictions', () => {
213
226
  expect(candidate?.status).toBe('active');
214
227
  expect(conflicts).toHaveLength(0);
215
228
  });
229
+
230
+ test('project kind ambiguous contradiction does not generate pending conflict with default config', async () => {
231
+ nextRelationship = 'ambiguous_contradiction';
232
+ nextExplanation = 'Project items may conflict but are not durable.';
233
+
234
+ insertMemoryItem({
235
+ id: 'item-existing-project',
236
+ statement: 'The backend uses Node.js.',
237
+ kind: 'project',
238
+ });
239
+ insertMemoryItem({
240
+ id: 'item-candidate-project',
241
+ statement: 'The backend uses Deno.',
242
+ kind: 'project',
243
+ });
244
+
245
+ await checkContradictions('item-candidate-project');
246
+
247
+ expect(classifyCallCount).toBe(0);
248
+ const db = getDb();
249
+ const conflicts = db.select().from(memoryItemConflicts).all();
250
+ expect(conflicts).toHaveLength(0);
251
+ });
252
+
253
+ test('skips classification when item kind is not in conflictableKinds', async () => {
254
+ mockConflictableKinds = ['instruction', 'style'];
255
+ nextRelationship = 'ambiguous_contradiction';
256
+
257
+ insertMemoryItem({
258
+ id: 'item-existing-ineligible',
259
+ statement: 'User prefers React for frontend work.',
260
+ });
261
+ insertMemoryItem({
262
+ id: 'item-candidate-ineligible',
263
+ statement: 'User prefers Vue for frontend work.',
264
+ });
265
+
266
+ await checkContradictions('item-candidate-ineligible');
267
+
268
+ expect(classifyCallCount).toBe(0);
269
+ const db = getDb();
270
+ const conflicts = db.select().from(memoryItemConflicts).all();
271
+ expect(conflicts).toHaveLength(0);
272
+ });
273
+
274
+ test('skips classification when candidate statement contains PR-tracking content', async () => {
275
+ nextRelationship = 'ambiguous_contradiction';
276
+
277
+ insertMemoryItem({
278
+ id: 'item-existing-pr-tracking',
279
+ statement: 'Track PR #5526 for review.',
280
+ });
281
+ insertMemoryItem({
282
+ id: 'item-candidate-pr-tracking',
283
+ statement: 'Track PR #5525 for review.',
284
+ });
285
+
286
+ await checkContradictions('item-candidate-pr-tracking');
287
+
288
+ expect(classifyCallCount).toBe(0);
289
+ const db = getDb();
290
+ const conflicts = db.select().from(memoryItemConflicts).all();
291
+ expect(conflicts).toHaveLength(0);
292
+ });
293
+
294
+ test('durable preference contradiction still runs normal flow', async () => {
295
+ nextRelationship = 'ambiguous_contradiction';
296
+ nextExplanation = 'Both are valid preferences that conflict.';
297
+
298
+ insertMemoryItem({
299
+ id: 'item-existing-durable',
300
+ statement: 'User prefers React for frontend work.',
301
+ });
302
+ insertMemoryItem({
303
+ id: 'item-candidate-durable',
304
+ statement: 'User prefers Vue for frontend work.',
305
+ });
306
+
307
+ await checkContradictions('item-candidate-durable');
308
+
309
+ expect(classifyCallCount).toBe(1);
310
+ const db = getDb();
311
+ const conflicts = db.select().from(memoryItemConflicts).all();
312
+ expect(conflicts).toHaveLength(1);
313
+ });
216
314
  });
@@ -185,10 +185,17 @@ describe('Invariant 2: no generic plaintext secret read API', () => {
185
185
  'email/providers/index.ts', // email provider API key lookup
186
186
  'tools/network/script-proxy/session-manager.ts', // proxy credential injection at runtime
187
187
  'messaging/registry.ts', // checks stored credentials for connected providers
188
+ 'calls/call-domain.ts', // caller identity resolution (user phone number lookup)
189
+ 'calls/elevenlabs-config.ts', // ElevenLabs voice quality API key lookup
188
190
  'calls/twilio-config.ts', // call infrastructure credential lookup
189
191
  'calls/twilio-provider.ts', // call infrastructure credential lookup
192
+ 'cli/config-commands.ts', // CLI credential management commands
190
193
  'runtime/http-server.ts', // HTTP server credential lookup
191
194
  'daemon/handlers/twitter-auth.ts', // Twitter OAuth token storage
195
+ 'twitter/oauth-client.ts', // Twitter OAuth API client (reads access token for API calls)
196
+ 'calls/elevenlabs-config.ts', // ElevenLabs credential lookup
197
+ 'cli/config-commands.ts', // CLI config management
198
+ 'messaging/providers/telegram-bot/adapter.ts', // Telegram bot token lookup for connectivity check
192
199
  ]);
193
200
 
194
201
  const thisDir = dirname(fileURLToPath(import.meta.url));
@@ -267,18 +274,27 @@ describe('Invariant 3: secrets never logged in plaintext', () => {
267
274
  });
268
275
  } else if (tc.component === 'ipc_decode') {
269
276
  // PR 24 — IPC decode log hygiene: the TS daemon's IPC parser must
270
- // not have any logging that could leak raw message content
277
+ // not log raw message content that could contain secrets.
278
+ // Logging metadata (line length, error type) is acceptable; logging
279
+ // the raw line, trimmed content, or error.message is not.
271
280
  test(`${tc.label}`, () => {
272
281
  const thisDir = dirname(fileURLToPath(import.meta.url));
273
282
  const ipcSrc = readFileSync(
274
283
  resolve(thisDir, '../daemon/ipc-protocol.ts'),
275
284
  'utf-8',
276
285
  );
277
- // The IPC parser must not use a logger at all it handles raw
278
- // bytes that could contain secrets in malformed messages. Verify
279
- // no getLogger import and no log.* calls exist in the source.
280
- expect(ipcSrc).not.toContain('getLogger');
281
- expect(ipcSrc).not.toMatch(/\blog\.\w+\(/);
286
+ // Verify log calls never include raw content fieldsonly safe
287
+ // metadata like lineLength and errorType are permitted.
288
+ // `trimmed.length` is safe (numeric); `trimmed` alone would leak raw content.
289
+ // Use [^\n]* instead of [^)]* so that inner parentheses (e.g.
290
+ // helper calls like formatErr(err)) don't terminate the match
291
+ // early — avoiding false negatives — while still scoping each
292
+ // pattern to a single line (no cross-statement matching).
293
+ expect(ipcSrc).not.toMatch(/\blog\.\w+\([^\n]*[{,]\s*trimmed[^.]/);
294
+ expect(ipcSrc).not.toMatch(/\blog\.\w+\([^\n]*[{,]\s*line[^L]/);
295
+ expect(ipcSrc).not.toMatch(/\blog\.\w+\([^\n]*[{,]\s*data\b/);
296
+ expect(ipcSrc).not.toMatch(/\blog\.\w+\([^\n]*[{,]\s*buffer\b/);
297
+ expect(ipcSrc).not.toMatch(/\blog\.\w+\([^\n]*err\.message\b/);
282
298
  });
283
299
  } else {
284
300
  // PR 25 — secret prompter log hygiene: verify the prompter source