@vellumai/assistant 0.4.29 → 0.4.31

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 (237) hide show
  1. package/ARCHITECTURE.md +39 -37
  2. package/Dockerfile +14 -8
  3. package/README.md +7 -8
  4. package/docs/architecture/memory.md +28 -29
  5. package/docs/runbook-trusted-contacts.md +76 -43
  6. package/package.json +1 -1
  7. package/scripts/ipc/check-swift-decoder-drift.ts +2 -3
  8. package/scripts/test.sh +1 -1
  9. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +4 -37
  10. package/src/__tests__/actor-token-service.test.ts +4 -3
  11. package/src/__tests__/app-executors.test.ts +7 -17
  12. package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -10
  13. package/src/__tests__/browser-skill-endstate.test.ts +10 -1
  14. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +1 -0
  15. package/src/__tests__/channel-approval-routes.test.ts +44 -44
  16. package/src/__tests__/channel-approval.test.ts +8 -0
  17. package/src/__tests__/channel-approvals.test.ts +39 -1
  18. package/src/__tests__/channel-guardian.test.ts +15 -5
  19. package/src/__tests__/channel-reply-delivery.test.ts +31 -0
  20. package/src/__tests__/config-schema.test.ts +0 -9
  21. package/src/__tests__/conflict-policy.test.ts +76 -0
  22. package/src/__tests__/conflict-store.test.ts +14 -20
  23. package/src/__tests__/contacts-tools.test.ts +8 -61
  24. package/src/__tests__/contradiction-checker.test.ts +5 -1
  25. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +9 -0
  26. package/src/__tests__/gateway-only-guard.test.ts +1 -0
  27. package/src/__tests__/gemini-image-service.test.ts +2 -2
  28. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
  29. package/src/__tests__/guardian-grant-minting.test.ts +6 -6
  30. package/src/__tests__/guardian-routing-invariants.test.ts +40 -15
  31. package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +4 -6
  32. package/src/__tests__/inbound-invite-redemption.test.ts +1 -1
  33. package/src/__tests__/integrations-cli.test.ts +3 -27
  34. package/src/__tests__/intent-routing.test.ts +3 -0
  35. package/src/__tests__/invite-redemption-service.test.ts +1 -1
  36. package/src/__tests__/{ingress-routes-http.test.ts → invite-routes-http.test.ts} +40 -320
  37. package/src/__tests__/ipc-snapshot.test.ts +4 -31
  38. package/src/__tests__/memory-lifecycle-e2e.test.ts +11 -10
  39. package/src/__tests__/nl-approval-parser.test.ts +305 -0
  40. package/src/__tests__/oauth-provider-profiles.test.ts +34 -0
  41. package/src/__tests__/provider-error-scenarios.test.ts +68 -0
  42. package/src/__tests__/registry.test.ts +0 -10
  43. package/src/__tests__/relay-server.test.ts +1 -1
  44. package/src/__tests__/retry-after-extraction.test.ts +111 -0
  45. package/src/__tests__/script-proxy-profile-template-fallback.test.ts +127 -0
  46. package/src/__tests__/script-proxy-session-runtime.test.ts +6 -1
  47. package/src/__tests__/session-agent-loop.test.ts +0 -2
  48. package/src/__tests__/session-conflict-gate.test.ts +243 -388
  49. package/src/__tests__/session-media-retry.test.ts +147 -0
  50. package/src/__tests__/session-profile-injection.test.ts +0 -2
  51. package/src/__tests__/session-runtime-assembly.test.ts +2 -3
  52. package/src/__tests__/session-skill-tools.test.ts +0 -49
  53. package/src/__tests__/session-workspace-cache-state.test.ts +0 -1
  54. package/src/__tests__/session-workspace-injection.test.ts +0 -1
  55. package/src/__tests__/session-workspace-tool-tracking.test.ts +0 -1
  56. package/src/__tests__/skill-feature-flags-integration.test.ts +9 -5
  57. package/src/__tests__/skill-feature-flags.test.ts +18 -12
  58. package/src/__tests__/skill-load-feature-flag.test.ts +4 -3
  59. package/src/__tests__/slack-block-formatting.test.ts +100 -0
  60. package/src/__tests__/slack-inbound-verification.test.ts +346 -0
  61. package/src/__tests__/slack-reaction-approvals.test.ts +77 -0
  62. package/src/__tests__/slack-skill.test.ts +3 -2
  63. package/src/__tests__/starter-task-flow.test.ts +0 -1
  64. package/src/__tests__/tool-grant-request-escalation.test.ts +2 -1
  65. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -1
  66. package/src/__tests__/trusted-contact-verification.test.ts +3 -1
  67. package/src/__tests__/voice-invite-redemption.test.ts +1 -1
  68. package/src/amazon/client.ts +7 -24
  69. package/src/approvals/guardian-decision-primitive.ts +11 -7
  70. package/src/approvals/guardian-request-resolvers.ts +5 -3
  71. package/src/calls/relay-server.ts +44 -11
  72. package/src/channels/config.ts +1 -1
  73. package/src/cli/integrations.ts +10 -66
  74. package/src/config/bundled-skills/app-builder/SKILL.md +193 -1500
  75. package/src/config/bundled-skills/app-builder/TOOLS.json +70 -18
  76. package/src/config/bundled-skills/browser/TOOLS.json +59 -2
  77. package/src/config/bundled-skills/chatgpt-import/TOOLS.json +4 -0
  78. package/src/config/bundled-skills/computer-use/TOOLS.json +50 -2
  79. package/src/config/bundled-skills/contacts/SKILL.md +49 -53
  80. package/src/config/bundled-skills/contacts/TOOLS.json +26 -22
  81. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +40 -62
  82. package/src/config/bundled-skills/contacts/tools/contact-search.ts +17 -43
  83. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +18 -57
  84. package/src/config/bundled-skills/document/TOOLS.json +8 -0
  85. package/src/config/bundled-skills/email-setup/SKILL.md +10 -7
  86. package/src/config/bundled-skills/followups/TOOLS.json +12 -0
  87. package/src/config/bundled-skills/google-calendar/TOOLS.json +124 -26
  88. package/src/config/bundled-skills/guardian-verify-setup/SKILL.md +54 -21
  89. package/src/config/bundled-skills/image-studio/TOOLS.json +12 -2
  90. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +14 -8
  91. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +13 -3
  92. package/src/config/bundled-skills/media-processing/SKILL.md +1 -1
  93. package/src/config/bundled-skills/media-processing/TOOLS.json +28 -0
  94. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +26 -6
  95. package/src/config/bundled-skills/messaging/TOOLS.json +228 -182
  96. package/src/config/bundled-skills/notifications/SKILL.md +3 -2
  97. package/src/config/bundled-skills/notifications/TOOLS.json +7 -13
  98. package/src/config/bundled-skills/phone-calls/TOOLS.json +13 -1
  99. package/src/config/bundled-skills/playbooks/TOOLS.json +16 -0
  100. package/src/config/bundled-skills/reminder/TOOLS.json +15 -2
  101. package/src/config/bundled-skills/schedule/SKILL.md +33 -15
  102. package/src/config/bundled-skills/schedule/TOOLS.json +17 -1
  103. package/src/config/bundled-skills/slack/SKILL.md +30 -1
  104. package/src/config/bundled-skills/slack/TOOLS.json +89 -2
  105. package/src/config/bundled-skills/slack/tools/slack-channel-permissions.ts +146 -0
  106. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +120 -0
  107. package/src/config/bundled-skills/slack-app-setup/SKILL.md +200 -0
  108. package/src/config/bundled-skills/subagent/TOOLS.json +22 -2
  109. package/src/config/bundled-skills/tasks/TOOLS.json +86 -14
  110. package/src/config/bundled-skills/transcribe/TOOLS.json +4 -0
  111. package/src/config/bundled-skills/watcher/TOOLS.json +20 -0
  112. package/src/config/bundled-tool-registry.ts +2 -5
  113. package/src/config/channel-permission-profiles.ts +155 -0
  114. package/src/config/env.ts +4 -1
  115. package/src/config/memory-schema.ts +0 -10
  116. package/src/config/system-prompt.ts +6 -0
  117. package/src/contacts/contact-store.ts +221 -56
  118. package/src/contacts/contacts-write.ts +14 -3
  119. package/src/contacts/types.ts +35 -4
  120. package/src/daemon/assistant-attachments.ts +23 -3
  121. package/src/daemon/guardian-verification-intent.ts +7 -4
  122. package/src/daemon/handlers/apps.ts +1 -2
  123. package/src/daemon/handlers/config-heartbeat.ts +1 -2
  124. package/src/daemon/handlers/config-inbox.ts +16 -134
  125. package/src/daemon/handlers/contacts.ts +2 -2
  126. package/src/daemon/handlers/guardian-actions.ts +21 -88
  127. package/src/daemon/handlers/sessions.ts +2 -2
  128. package/src/daemon/ipc-contract/apps.ts +0 -1
  129. package/src/daemon/ipc-contract/contacts.ts +2 -2
  130. package/src/daemon/ipc-contract/inbox.ts +7 -66
  131. package/src/daemon/ipc-contract/sessions.ts +1 -0
  132. package/src/daemon/ipc-contract/surfaces.ts +0 -1
  133. package/src/daemon/ipc-contract-inventory.json +2 -4
  134. package/src/daemon/lifecycle.ts +14 -2
  135. package/src/daemon/session-agent-loop-handlers.ts +9 -0
  136. package/src/daemon/session-agent-loop.ts +2 -45
  137. package/src/daemon/session-attachments.ts +5 -1
  138. package/src/daemon/session-conflict-gate.ts +21 -82
  139. package/src/daemon/session-error.ts +18 -0
  140. package/src/daemon/session-lifecycle.ts +4 -5
  141. package/src/daemon/session-media-retry.ts +15 -1
  142. package/src/daemon/session-memory.ts +7 -52
  143. package/src/daemon/session-process.ts +3 -1
  144. package/src/daemon/session-runtime-assembly.ts +18 -35
  145. package/src/daemon/session-surfaces.ts +0 -1
  146. package/src/daemon/session-tool-setup.ts +7 -4
  147. package/src/events/domain-events.ts +2 -1
  148. package/src/heartbeat/heartbeat-service.ts +5 -1
  149. package/src/home-base/prebuilt/seed.ts +0 -1
  150. package/src/influencer/client.ts +7 -24
  151. package/src/media/gemini-image-service.ts +48 -3
  152. package/src/memory/app-store.ts +0 -4
  153. package/src/memory/conflict-intent.ts +3 -6
  154. package/src/memory/conflict-policy.ts +34 -0
  155. package/src/memory/conflict-store.ts +10 -18
  156. package/src/memory/contradiction-checker.ts +2 -2
  157. package/src/memory/conversation-attention-store.ts +3 -1
  158. package/src/memory/db-init.ts +8 -0
  159. package/src/memory/job-handlers/conflict.ts +0 -7
  160. package/src/memory/migrations/133-assistant-contact-metadata.ts +21 -0
  161. package/src/memory/migrations/134-contacts-notes-column.ts +51 -0
  162. package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +31 -0
  163. package/src/memory/migrations/index.ts +3 -0
  164. package/src/memory/schema.ts +12 -17
  165. package/src/memory/slack-thread-store.ts +187 -0
  166. package/src/messaging/index.ts +0 -1
  167. package/src/messaging/providers/slack/client.ts +84 -26
  168. package/src/messaging/providers/slack/types.ts +4 -0
  169. package/src/messaging/types.ts +0 -38
  170. package/src/notifications/adapters/slack.ts +90 -0
  171. package/src/notifications/destination-resolver.ts +42 -1
  172. package/src/notifications/emit-signal.ts +17 -1
  173. package/src/oauth/provider-profiles.ts +22 -0
  174. package/src/providers/anthropic/client.ts +3 -0
  175. package/src/providers/openai/client.ts +3 -0
  176. package/src/providers/retry.ts +9 -1
  177. package/src/runtime/actor-trust-resolver.ts +8 -0
  178. package/src/runtime/auth/require-bound-guardian.ts +44 -0
  179. package/src/runtime/auth/route-policy.ts +4 -8
  180. package/src/runtime/channel-approval-types.ts +18 -0
  181. package/src/runtime/channel-approvals.ts +8 -0
  182. package/src/runtime/channel-invite-transport.ts +1 -1
  183. package/src/runtime/channel-reply-delivery.ts +62 -3
  184. package/src/runtime/gateway-client.ts +36 -2
  185. package/src/runtime/gateway-internal-client.ts +86 -0
  186. package/src/runtime/guardian-action-service.ts +128 -0
  187. package/src/runtime/guardian-outbound-actions.ts +3 -3
  188. package/src/runtime/guardian-reply-router.ts +4 -4
  189. package/src/runtime/guardian-verification-templates.ts +16 -1
  190. package/src/runtime/http-server.ts +29 -46
  191. package/src/runtime/invite-redemption-service.ts +1 -1
  192. package/src/runtime/{ingress-service.ts → invite-service.ts} +5 -157
  193. package/src/runtime/nl-approval-parser.ts +138 -0
  194. package/src/runtime/routes/approval-routes.ts +1 -40
  195. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +6 -3
  196. package/src/runtime/routes/channel-route-shared.ts +35 -1
  197. package/src/runtime/routes/contact-routes.ts +494 -47
  198. package/src/runtime/routes/conversation-routes.ts +2 -1
  199. package/src/runtime/routes/global-search-routes.ts +2 -2
  200. package/src/runtime/routes/guardian-action-routes.ts +19 -111
  201. package/src/runtime/routes/guardian-approval-interception.ts +78 -1
  202. package/src/runtime/routes/guardian-bootstrap-routes.ts +6 -1
  203. package/src/runtime/routes/inbound-message-handler.ts +40 -12
  204. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +227 -1
  205. package/src/runtime/routes/inbound-stages/background-dispatch.ts +108 -0
  206. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +2 -1
  207. package/src/runtime/routes/{ingress-routes.ts → invite-routes.ts} +10 -110
  208. package/src/runtime/routes/migration-routes.ts +17 -17
  209. package/src/runtime/slack-block-formatting.ts +176 -0
  210. package/src/schedule/scheduler.ts +11 -2
  211. package/src/tools/apps/executors.ts +16 -15
  212. package/src/tools/calls/call-end.ts +1 -1
  213. package/src/tools/computer-use/definitions.ts +16 -0
  214. package/src/tools/credentials/vault.ts +86 -2
  215. package/src/tools/network/script-proxy/session-manager.ts +28 -3
  216. package/src/tools/permission-checker.ts +18 -0
  217. package/src/tools/terminal/shell.ts +15 -5
  218. package/src/tools/tool-approval-handler.ts +48 -4
  219. package/src/tools/types.ts +38 -1
  220. package/src/util/errors.ts +5 -1
  221. package/src/util/retry.ts +21 -0
  222. package/src/watcher/providers/slack.ts +33 -3
  223. package/src/workspace/git-service.ts +6 -4
  224. package/src/__tests__/get-weather.test.ts +0 -393
  225. package/src/__tests__/weather-skill-regression.test.ts +0 -276
  226. package/src/autonomy/autonomy-resolver.ts +0 -62
  227. package/src/autonomy/autonomy-store.ts +0 -138
  228. package/src/autonomy/disposition-mapper.ts +0 -31
  229. package/src/autonomy/index.ts +0 -11
  230. package/src/autonomy/types.ts +0 -43
  231. package/src/config/bundled-skills/weather/SKILL.md +0 -38
  232. package/src/config/bundled-skills/weather/TOOLS.json +0 -32
  233. package/src/config/bundled-skills/weather/icon.svg +0 -24
  234. package/src/config/bundled-skills/weather/tools/get-weather.ts +0 -12
  235. package/src/messaging/triage-engine.ts +0 -344
  236. package/src/tools/weather/service.ts +0 -712
  237. /package/src/memory/{ingress-invite-store.ts → invite-store.ts} +0 -0
@@ -1,12 +1,17 @@
1
1
  # Trusted Contacts — Operator Runbook
2
2
 
3
- Operational procedures for inspecting, managing, and debugging the trusted contact access flow. All HTTP commands use the gateway API (default `http://localhost:7830`) with bearer authentication.
3
+ Operational procedures for inspecting, managing, and debugging the trusted contact access flow. HTTP commands use the assistant runtime API (default `http://localhost:7821`) with bearer authentication.
4
+
5
+ > **Note:** The `/v1/contacts` endpoints are served by the assistant runtime, not
6
+ > the gateway. If you prefer to route through the gateway (`localhost:7830`), set
7
+ > `GATEWAY_RUNTIME_PROXY_ENABLED=true` in the gateway environment — the proxy is
8
+ > disabled by default and these routes will 404 without it.
4
9
 
5
10
  ## Prerequisites
6
11
 
7
12
  ```bash
8
- # Base URL (adjust if using a non-default port)
9
- BASE=http://localhost:7830
13
+ # Base URL — assistant runtime (adjust if using a non-default port)
14
+ BASE=http://localhost:7821
10
15
 
11
16
  # Bearer token: if running via the assistant's shell tools, $GATEWAY_AUTH_TOKEN
12
17
  # is injected automatically. For manual operator use, mint a token via the CLI
@@ -14,51 +19,71 @@ BASE=http://localhost:7830
14
19
  TOKEN=$GATEWAY_AUTH_TOKEN
15
20
  ```
16
21
 
17
- ## 1. Inspect Trusted Contacts (Members)
22
+ ## 1. Inspect Trusted Contacts
18
23
 
19
24
  ### List all active trusted contacts
20
25
 
21
26
  ```bash
22
- curl -s "$BASE/v1/ingress/members?status=active" \
27
+ curl -s "$BASE/v1/contacts?role=contact" \
23
28
  -H "Authorization: Bearer $TOKEN" | jq
24
29
  ```
25
30
 
26
- ### Filter by channel
31
+ ### Filter by channel type
27
32
 
28
33
  ```bash
29
34
  # Telegram contacts only
30
- curl -s "$BASE/v1/ingress/members?sourceChannel=telegram&status=active" \
35
+ curl -s "$BASE/v1/contacts?channelType=telegram" \
31
36
  -H "Authorization: Bearer $TOKEN" | jq
32
37
 
33
38
  # SMS contacts only
34
- curl -s "$BASE/v1/ingress/members?sourceChannel=sms&status=active" \
39
+ curl -s "$BASE/v1/contacts?channelType=sms" \
35
40
  -H "Authorization: Bearer $TOKEN" | jq
36
41
  ```
37
42
 
38
- ### List all members (including revoked and blocked)
43
+ ### List all contacts (including revoked and blocked)
39
44
 
40
45
  ```bash
41
- curl -s "$BASE/v1/ingress/members" \
46
+ curl -s "$BASE/v1/contacts" \
42
47
  -H "Authorization: Bearer $TOKEN" | jq
43
48
  ```
44
49
 
50
+ ### Via CLI
51
+
52
+ ```bash
53
+ vellum contacts list --role contact
54
+ ```
55
+
45
56
  Response shape:
46
57
 
47
58
  ```json
48
59
  {
49
60
  "ok": true,
50
- "members": [
61
+ "contacts": [
51
62
  {
52
63
  "id": "uuid",
53
- "sourceChannel": "telegram",
54
- "externalUserId": "123456789",
55
- "externalChatId": "123456789",
56
64
  "displayName": "Alice",
57
- "username": "alice_handle",
58
- "status": "active",
59
- "policy": "allow",
60
- "lastSeenAt": 1700000000000,
61
- "createdAt": 1699000000000
65
+ "notes": null,
66
+ "lastInteraction": 1700000000000,
67
+ "interactionCount": 12,
68
+ "createdAt": 1699000000000,
69
+ "updatedAt": 1700000000000,
70
+ "role": "contact",
71
+ "channels": [
72
+ {
73
+ "id": "channel-uuid",
74
+ "contactId": "uuid",
75
+ "type": "telegram",
76
+ "address": "alice_handle",
77
+ "isPrimary": true,
78
+ "externalUserId": "123456789",
79
+ "externalChatId": "123456789",
80
+ "status": "active",
81
+ "policy": "allow",
82
+ "verifiedAt": 1699500000000,
83
+ "lastSeenAt": 1700000000000,
84
+ "createdAt": 1699000000000
85
+ }
86
+ ]
62
87
  }
63
88
  ]
64
89
  }
@@ -109,29 +134,29 @@ sqlite3 ~/.vellum/workspace/data/db/assistant.db \
109
134
 
110
135
  ### Via HTTP API
111
136
 
112
- First, find the member's `id` from the list endpoint, then revoke:
137
+ First, find the contact and its channel ID from the list endpoint, then revoke the channel:
113
138
 
114
139
  ```bash
115
- # Find the member
116
- MEMBER_ID=$(curl -s "$BASE/v1/ingress/members?sourceChannel=telegram&status=active" \
117
- -H "Authorization: Bearer $TOKEN" | jq -r '.members[] | select(.externalUserId == "TARGET_USER_ID") | .id')
140
+ # Find the contact's channel ID
141
+ CHANNEL_ID=$(curl -s "$BASE/v1/contacts?channelType=telegram" \
142
+ -H "Authorization: Bearer $TOKEN" | jq -r '.contacts[] | select(.channels[] | select(.externalUserId == "TARGET_USER_ID")) | .channels[] | select(.externalUserId == "TARGET_USER_ID") | .id')
118
143
 
119
144
  # Revoke with reason
120
- curl -s -X DELETE "$BASE/v1/ingress/members/$MEMBER_ID" \
145
+ curl -s -X PATCH "$BASE/v1/contacts/channels/$CHANNEL_ID" \
121
146
  -H "Authorization: Bearer $TOKEN" \
122
147
  -H "Content-Type: application/json" \
123
- -d '{"reason": "Revoked by operator"}' | jq
148
+ -d '{"status": "revoked", "reason": "Revoked by operator"}' | jq
124
149
  ```
125
150
 
126
- ### Block a member (stronger than revoke)
151
+ ### Block a contact channel (stronger than revoke)
127
152
 
128
- Blocking prevents the member from re-entering the flow without explicit unblocking.
153
+ Blocking prevents the contact from re-entering the flow without explicit unblocking.
129
154
 
130
155
  ```bash
131
- curl -s -X POST "$BASE/v1/ingress/members/$MEMBER_ID/block" \
156
+ curl -s -X PATCH "$BASE/v1/contacts/channels/$CHANNEL_ID" \
132
157
  -H "Authorization: Bearer $TOKEN" \
133
158
  -H "Content-Type: application/json" \
134
- -d '{"reason": "Blocked by operator"}' | jq
159
+ -d '{"status": "blocked", "reason": "Blocked by operator"}' | jq
135
160
  ```
136
161
 
137
162
  ### Via SQLite (emergency)
@@ -229,35 +254,43 @@ sqlite3 ~/.vellum/workspace/data/db/assistant.db \
229
254
 
230
255
  ## 7. Manually Add a Trusted Contact (Bypass Verification)
231
256
 
232
- If the verification flow cannot be completed, an operator can directly create an active member:
257
+ If the verification flow cannot be completed, an operator can directly create an active contact:
233
258
 
234
259
  ```bash
235
- curl -s -X POST "$BASE/v1/ingress/members" \
260
+ curl -s -X POST "$BASE/v1/contacts" \
236
261
  -H "Authorization: Bearer $TOKEN" \
237
262
  -H "Content-Type: application/json" \
238
263
  -d '{
239
- "sourceChannel": "telegram",
240
- "externalUserId": "123456789",
241
- "externalChatId": "123456789",
242
264
  "displayName": "Alice",
243
- "policy": "allow",
244
- "status": "active"
265
+ "role": "contact",
266
+ "channels": [{
267
+ "type": "telegram",
268
+ "address": "alice_handle",
269
+ "externalUserId": "123456789",
270
+ "externalChatId": "123456789",
271
+ "status": "active",
272
+ "policy": "allow"
273
+ }]
245
274
  }' | jq
246
275
  ```
247
276
 
248
- For SMS contacts, use the E.164 phone number as the external user/chat ID:
277
+ For SMS contacts, use the E.164 phone number as the address and external user/chat ID:
249
278
 
250
279
  ```bash
251
- curl -s -X POST "$BASE/v1/ingress/members" \
280
+ curl -s -X POST "$BASE/v1/contacts" \
252
281
  -H "Authorization: Bearer $TOKEN" \
253
282
  -H "Content-Type: application/json" \
254
283
  -d '{
255
- "sourceChannel": "sms",
256
- "externalUserId": "+15551234567",
257
- "externalChatId": "+15551234567",
258
284
  "displayName": "Bob",
259
- "policy": "allow",
260
- "status": "active"
285
+ "role": "contact",
286
+ "channels": [{
287
+ "type": "sms",
288
+ "address": "+15551234567",
289
+ "externalUserId": "+15551234567",
290
+ "externalChatId": "+15551234567",
291
+ "status": "active",
292
+ "policy": "allow"
293
+ }]
261
294
  }' | jq
262
295
  ```
263
296
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.4.29",
3
+ "version": "0.4.31",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "vellum": "./src/index.ts"
@@ -49,9 +49,8 @@ const SWIFT_OMIT_ALLOWLIST = new Set<string>([
49
49
  "heartbeat_alert",
50
50
  // Guardian verification — daemon-internal for Telegram channel setup
51
51
  "guardian_verification_response",
52
- // Ingress invite/member management — not yet consumed by the macOS client
53
- "ingress_invite_response",
54
- "ingress_member_response",
52
+ // Contacts invite management — not yet consumed by the macOS client
53
+ "contacts_invite_response",
55
54
  // Inbox escalation — not yet consumed by the macOS client
56
55
  "assistant_inbox_escalation_response",
57
56
  // Work item messages — not yet consumed by the macOS client
package/scripts/test.sh CHANGED
@@ -166,7 +166,7 @@ printf '%s\n' "${test_files[@]}" | xargs -P "${WORKERS}" -I {} bash -c '
166
166
  if grep -q "^(fail)" "${out_file}" 2>/dev/null; then
167
167
  echo "${test_file}" >> "${results_dir}/failures"
168
168
  echo " ✗ ${base} (killed after ${per_test_timeout}s — tests failed and process hung)"
169
- elif grep -qE "^Ran [0-9]+ tests across" "${out_file}" 2>/dev/null; then
169
+ elif grep -qE "^Ran [0-9]+ tests? across" "${out_file}" 2>/dev/null; then
170
170
  echo " ⚠ ${base} (tests passed but process hung after ${per_test_timeout}s — likely open handles)"
171
171
  else
172
172
  echo "${test_file}" >> "${results_dir}/failures"
@@ -967,28 +967,14 @@ exports[`IPC message snapshots ClientMessage types dictation_request serializes
967
967
  }
968
968
  `;
969
969
 
970
- exports[`IPC message snapshots ClientMessage types ingress_invite serializes to expected JSON 1`] = `
970
+ exports[`IPC message snapshots ClientMessage types contacts_invite serializes to expected JSON 1`] = `
971
971
  {
972
972
  "action": "create",
973
973
  "expiresInMs": 86400000,
974
974
  "maxUses": 5,
975
975
  "note": "Test invite",
976
976
  "sourceChannel": "telegram",
977
- "type": "ingress_invite",
978
- }
979
- `;
980
-
981
- exports[`IPC message snapshots ClientMessage types ingress_member serializes to expected JSON 1`] = `
982
- {
983
- "action": "upsert",
984
- "displayName": "Test User",
985
- "externalChatId": "chat-456",
986
- "externalUserId": "user-123",
987
- "policy": "allow",
988
- "sourceChannel": "telegram",
989
- "status": "active",
990
- "type": "ingress_member",
991
- "username": "testuser",
977
+ "type": "contacts_invite",
992
978
  }
993
979
  `;
994
980
 
@@ -2844,7 +2830,7 @@ exports[`IPC message snapshots ServerMessage types dictation_response serializes
2844
2830
  }
2845
2831
  `;
2846
2832
 
2847
- exports[`IPC message snapshots ServerMessage types ingress_invite_response serializes to expected JSON 1`] = `
2833
+ exports[`IPC message snapshots ServerMessage types contacts_invite_response serializes to expected JSON 1`] = `
2848
2834
  {
2849
2835
  "invite": {
2850
2836
  "createdAt": 1700000000,
@@ -2859,26 +2845,7 @@ exports[`IPC message snapshots ServerMessage types ingress_invite_response seria
2859
2845
  "useCount": 0,
2860
2846
  },
2861
2847
  "success": true,
2862
- "type": "ingress_invite_response",
2863
- }
2864
- `;
2865
-
2866
- exports[`IPC message snapshots ServerMessage types ingress_member_response serializes to expected JSON 1`] = `
2867
- {
2868
- "member": {
2869
- "createdAt": 1700000000,
2870
- "displayName": "Test User",
2871
- "externalChatId": "chat-456",
2872
- "externalUserId": "user-123",
2873
- "id": "mem-001",
2874
- "lastSeenAt": 1700000000,
2875
- "policy": "allow",
2876
- "sourceChannel": "telegram",
2877
- "status": "active",
2878
- "username": "testuser",
2879
- },
2880
- "success": true,
2881
- "type": "ingress_member_response",
2848
+ "type": "contacts_invite_response",
2882
2849
  }
2883
2850
  `;
2884
2851
 
@@ -449,14 +449,15 @@ describe("resolveLocalIpcAuthContext", () => {
449
449
  );
450
450
  });
451
451
 
452
- test("actorPrincipalId is undefined when no vellum binding exists", () => {
452
+ test("actorPrincipalId is auto-created via self-heal when no vellum binding exists", () => {
453
453
  // Reset DB to ensure no binding
454
454
  resetDb();
455
455
  initializeDb();
456
456
 
457
457
  const ctx = resolveLocalIpcAuthContext("session-123");
458
- // When no binding exists, actorPrincipalId is not set
459
- expect(ctx.actorPrincipalId).toBeUndefined();
458
+ // Self-heal creates a vellum guardian binding automatically
459
+ expect(ctx.actorPrincipalId).toBeDefined();
460
+ expect(ctx.actorPrincipalId).toMatch(/^vellum-principal-/);
460
461
  });
461
462
 
462
463
  test("sessionId matches the provided argument", () => {
@@ -180,28 +180,18 @@ describe("executeAppCreate", () => {
180
180
  expect(capturedPages).toEqual({ "settings.html": "<div/>" });
181
181
  });
182
182
 
183
- test('maps type "site" to appType "site"', async () => {
184
- let capturedType: "app" | "site" | undefined;
183
+ test("defaults html to minimal scaffold when omitted", async () => {
184
+ let capturedHtml: string | undefined;
185
185
  const store = makeMockStore({
186
186
  createApp: (params) => {
187
- capturedType = params.appType;
187
+ capturedHtml = params.htmlDefinition;
188
188
  return makeApp({ name: params.name });
189
189
  },
190
190
  });
191
- await executeAppCreate({ name: "Site", html: "<p/>", type: "site" }, store);
192
- expect(capturedType).toBe("site");
193
- });
194
-
195
- test('defaults appType to "app" when type is not "site"', async () => {
196
- let capturedType: "app" | "site" | undefined;
197
- const store = makeMockStore({
198
- createApp: (params) => {
199
- capturedType = params.appType;
200
- return makeApp({ name: params.name });
201
- },
202
- });
203
- await executeAppCreate({ name: "App", html: "<p/>" }, store);
204
- expect(capturedType).toBe("app");
191
+ await executeAppCreate({ name: "No HTML" }, store);
192
+ expect(capturedHtml).toBe(
193
+ "<!DOCTYPE html><html><head></head><body></body></html>",
194
+ );
205
195
  });
206
196
  });
207
197
 
@@ -146,16 +146,20 @@ describe("buildSystemPrompt assistant feature flag filtering", () => {
146
146
 
147
147
  currentConfig = {
148
148
  sandbox: { enabled: false, backend: "native" },
149
- assistantFeatureFlagValues: { [DECLARED_FLAG_KEY]: false },
149
+ assistantFeatureFlagValues: {
150
+ [DECLARED_FLAG_KEY]: false,
151
+ "feature_flags.twitter.enabled": true,
152
+ },
150
153
  };
151
154
 
152
155
  const result = buildSystemPrompt();
153
156
 
157
+ // twitter is explicitly enabled, declared flagged skill is explicitly off
154
158
  expect(result).toContain('id="twitter"');
155
159
  expect(result).not.toContain(`id="${DECLARED_SKILL_ID}"`);
156
160
  });
157
161
 
158
- test("all skills visible when no flag overrides set", () => {
162
+ test("declared skills hidden when no flag overrides set (registry defaults to false)", () => {
159
163
  createSkillOnDisk(
160
164
  DECLARED_SKILL_ID,
161
165
  "Hatch New Assistant",
@@ -169,8 +173,9 @@ describe("buildSystemPrompt assistant feature flag filtering", () => {
169
173
 
170
174
  const result = buildSystemPrompt();
171
175
 
172
- expect(result).toContain(`id="${DECLARED_SKILL_ID}"`);
173
- expect(result).toContain('id="twitter"');
176
+ // Both skills are declared in the registry with defaultEnabled: false
177
+ expect(result).not.toContain(`id="${DECLARED_SKILL_ID}"`);
178
+ expect(result).not.toContain('id="twitter"');
174
179
  });
175
180
 
176
181
  test("flagged-off skills hidden when all flags are OFF", () => {
@@ -227,7 +232,7 @@ describe("buildSystemPrompt assistant feature flag filtering", () => {
227
232
  expect(result).not.toContain('id="browser"');
228
233
  });
229
234
 
230
- test("undeclared flags with no persisted override default to enabled", () => {
235
+ test("declared flags with no persisted override use registry default", () => {
231
236
  createSkillOnDisk("browser", "Browser", "Web browsing automation");
232
237
 
233
238
  currentConfig = {
@@ -236,7 +241,8 @@ describe("buildSystemPrompt assistant feature flag filtering", () => {
236
241
 
237
242
  const result = buildSystemPrompt();
238
243
 
239
- expect(result).toContain('id="browser"');
244
+ // browser is declared in the registry with defaultEnabled: false
245
+ expect(result).not.toContain('id="browser"');
240
246
  });
241
247
  });
242
248
 
@@ -265,10 +271,12 @@ describe("isAssistantFeatureFlagEnabled", () => {
265
271
 
266
272
  test("missing persisted value falls back to defaults registry defaultEnabled", () => {
267
273
  // No explicit config at all — should fall back to defaults registry
268
- // which has defaultEnabled: true for hatch-new-assistant
274
+ // which has defaultEnabled: false for hatch-new-assistant
269
275
  const config = {} as any;
270
276
 
271
- expect(isAssistantFeatureFlagEnabled(DECLARED_FLAG_KEY, config)).toBe(true);
277
+ expect(isAssistantFeatureFlagEnabled(DECLARED_FLAG_KEY, config)).toBe(
278
+ false,
279
+ );
272
280
  });
273
281
 
274
282
  test("unknown flag defaults to true when no persisted override", () => {
@@ -302,9 +310,9 @@ describe("legacy isSkillFeatureEnabled backward compat", () => {
302
310
  expect(isSkillFeatureEnabled(DECLARED_SKILL_ID, config)).toBe(false);
303
311
  });
304
312
 
305
- test("enabled when no override set", () => {
313
+ test("disabled when no override set (registry default is false)", () => {
306
314
  const config = {} as any;
307
315
 
308
- expect(isSkillFeatureEnabled(DECLARED_SKILL_ID, config)).toBe(true);
316
+ expect(isSkillFeatureEnabled(DECLARED_SKILL_ID, config)).toBe(false);
309
317
  });
310
318
  });
@@ -4,7 +4,16 @@
4
4
  * Locks the final invariants from the BROWSER_SKILL plan so that future
5
5
  * changes cannot silently regress any of the migration guarantees.
6
6
  */
7
- import { afterAll, beforeAll, describe, expect, test } from "bun:test";
7
+ import { afterAll, beforeAll, describe, expect, mock, test } from "bun:test";
8
+
9
+ mock.module("../config/loader.js", () => ({
10
+ getConfig: () => ({
11
+ sandbox: { enabled: false, backend: "native" },
12
+ assistantFeatureFlagValues: {
13
+ "feature_flags.browser.enabled": true,
14
+ },
15
+ }),
16
+ }));
8
17
 
9
18
  import {
10
19
  projectSkillTools,
@@ -62,6 +62,7 @@ const GATEWAY_RETRIEVAL_BANLIST: Array<{
62
62
  bannedSnippets: [
63
63
  'curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/ingress/members',
64
64
  'curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/ingress/invites',
65
+ 'curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/contacts/invites',
65
66
  'curl -s "$INTERNAL_GATEWAY_BASE_URL/v1/integrations/telegram/config',
66
67
  ],
67
68
  },