@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.
- package/ARCHITECTURE.md +39 -37
- package/Dockerfile +14 -8
- package/README.md +7 -8
- package/docs/architecture/memory.md +28 -29
- package/docs/runbook-trusted-contacts.md +76 -43
- package/package.json +1 -1
- package/scripts/ipc/check-swift-decoder-drift.ts +2 -3
- package/scripts/test.sh +1 -1
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +4 -37
- package/src/__tests__/actor-token-service.test.ts +4 -3
- package/src/__tests__/app-executors.test.ts +7 -17
- package/src/__tests__/assistant-feature-flags-integration.test.ts +18 -10
- package/src/__tests__/browser-skill-endstate.test.ts +10 -1
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +1 -0
- package/src/__tests__/channel-approval-routes.test.ts +44 -44
- package/src/__tests__/channel-approval.test.ts +8 -0
- package/src/__tests__/channel-approvals.test.ts +39 -1
- package/src/__tests__/channel-guardian.test.ts +15 -5
- package/src/__tests__/channel-reply-delivery.test.ts +31 -0
- package/src/__tests__/config-schema.test.ts +0 -9
- package/src/__tests__/conflict-policy.test.ts +76 -0
- package/src/__tests__/conflict-store.test.ts +14 -20
- package/src/__tests__/contacts-tools.test.ts +8 -61
- package/src/__tests__/contradiction-checker.test.ts +5 -1
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +9 -0
- package/src/__tests__/gateway-only-guard.test.ts +1 -0
- package/src/__tests__/gemini-image-service.test.ts +2 -2
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-grant-minting.test.ts +6 -6
- package/src/__tests__/guardian-routing-invariants.test.ts +40 -15
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +4 -6
- package/src/__tests__/inbound-invite-redemption.test.ts +1 -1
- package/src/__tests__/integrations-cli.test.ts +3 -27
- package/src/__tests__/intent-routing.test.ts +3 -0
- package/src/__tests__/invite-redemption-service.test.ts +1 -1
- package/src/__tests__/{ingress-routes-http.test.ts → invite-routes-http.test.ts} +40 -320
- package/src/__tests__/ipc-snapshot.test.ts +4 -31
- package/src/__tests__/memory-lifecycle-e2e.test.ts +11 -10
- package/src/__tests__/nl-approval-parser.test.ts +305 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +34 -0
- package/src/__tests__/provider-error-scenarios.test.ts +68 -0
- package/src/__tests__/registry.test.ts +0 -10
- package/src/__tests__/relay-server.test.ts +1 -1
- package/src/__tests__/retry-after-extraction.test.ts +111 -0
- package/src/__tests__/script-proxy-profile-template-fallback.test.ts +127 -0
- package/src/__tests__/script-proxy-session-runtime.test.ts +6 -1
- package/src/__tests__/session-agent-loop.test.ts +0 -2
- package/src/__tests__/session-conflict-gate.test.ts +243 -388
- package/src/__tests__/session-media-retry.test.ts +147 -0
- package/src/__tests__/session-profile-injection.test.ts +0 -2
- package/src/__tests__/session-runtime-assembly.test.ts +2 -3
- package/src/__tests__/session-skill-tools.test.ts +0 -49
- package/src/__tests__/session-workspace-cache-state.test.ts +0 -1
- package/src/__tests__/session-workspace-injection.test.ts +0 -1
- package/src/__tests__/session-workspace-tool-tracking.test.ts +0 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +9 -5
- package/src/__tests__/skill-feature-flags.test.ts +18 -12
- package/src/__tests__/skill-load-feature-flag.test.ts +4 -3
- package/src/__tests__/slack-block-formatting.test.ts +100 -0
- package/src/__tests__/slack-inbound-verification.test.ts +346 -0
- package/src/__tests__/slack-reaction-approvals.test.ts +77 -0
- package/src/__tests__/slack-skill.test.ts +3 -2
- package/src/__tests__/starter-task-flow.test.ts +0 -1
- package/src/__tests__/tool-grant-request-escalation.test.ts +2 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -1
- package/src/__tests__/trusted-contact-verification.test.ts +3 -1
- package/src/__tests__/voice-invite-redemption.test.ts +1 -1
- package/src/amazon/client.ts +7 -24
- package/src/approvals/guardian-decision-primitive.ts +11 -7
- package/src/approvals/guardian-request-resolvers.ts +5 -3
- package/src/calls/relay-server.ts +44 -11
- package/src/channels/config.ts +1 -1
- package/src/cli/integrations.ts +10 -66
- package/src/config/bundled-skills/app-builder/SKILL.md +193 -1500
- package/src/config/bundled-skills/app-builder/TOOLS.json +70 -18
- package/src/config/bundled-skills/browser/TOOLS.json +59 -2
- package/src/config/bundled-skills/chatgpt-import/TOOLS.json +4 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +50 -2
- package/src/config/bundled-skills/contacts/SKILL.md +49 -53
- package/src/config/bundled-skills/contacts/TOOLS.json +26 -22
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +40 -62
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +17 -43
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +18 -57
- package/src/config/bundled-skills/document/TOOLS.json +8 -0
- package/src/config/bundled-skills/email-setup/SKILL.md +10 -7
- package/src/config/bundled-skills/followups/TOOLS.json +12 -0
- package/src/config/bundled-skills/google-calendar/TOOLS.json +124 -26
- package/src/config/bundled-skills/guardian-verify-setup/SKILL.md +54 -21
- package/src/config/bundled-skills/image-studio/TOOLS.json +12 -2
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +14 -8
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +13 -3
- package/src/config/bundled-skills/media-processing/SKILL.md +1 -1
- package/src/config/bundled-skills/media-processing/TOOLS.json +28 -0
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +26 -6
- package/src/config/bundled-skills/messaging/TOOLS.json +228 -182
- package/src/config/bundled-skills/notifications/SKILL.md +3 -2
- package/src/config/bundled-skills/notifications/TOOLS.json +7 -13
- package/src/config/bundled-skills/phone-calls/TOOLS.json +13 -1
- package/src/config/bundled-skills/playbooks/TOOLS.json +16 -0
- package/src/config/bundled-skills/reminder/TOOLS.json +15 -2
- package/src/config/bundled-skills/schedule/SKILL.md +33 -15
- package/src/config/bundled-skills/schedule/TOOLS.json +17 -1
- package/src/config/bundled-skills/slack/SKILL.md +30 -1
- package/src/config/bundled-skills/slack/TOOLS.json +89 -2
- package/src/config/bundled-skills/slack/tools/slack-channel-permissions.ts +146 -0
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +120 -0
- package/src/config/bundled-skills/slack-app-setup/SKILL.md +200 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +22 -2
- package/src/config/bundled-skills/tasks/TOOLS.json +86 -14
- package/src/config/bundled-skills/transcribe/TOOLS.json +4 -0
- package/src/config/bundled-skills/watcher/TOOLS.json +20 -0
- package/src/config/bundled-tool-registry.ts +2 -5
- package/src/config/channel-permission-profiles.ts +155 -0
- package/src/config/env.ts +4 -1
- package/src/config/memory-schema.ts +0 -10
- package/src/config/system-prompt.ts +6 -0
- package/src/contacts/contact-store.ts +221 -56
- package/src/contacts/contacts-write.ts +14 -3
- package/src/contacts/types.ts +35 -4
- package/src/daemon/assistant-attachments.ts +23 -3
- package/src/daemon/guardian-verification-intent.ts +7 -4
- package/src/daemon/handlers/apps.ts +1 -2
- package/src/daemon/handlers/config-heartbeat.ts +1 -2
- package/src/daemon/handlers/config-inbox.ts +16 -134
- package/src/daemon/handlers/contacts.ts +2 -2
- package/src/daemon/handlers/guardian-actions.ts +21 -88
- package/src/daemon/handlers/sessions.ts +2 -2
- package/src/daemon/ipc-contract/apps.ts +0 -1
- package/src/daemon/ipc-contract/contacts.ts +2 -2
- package/src/daemon/ipc-contract/inbox.ts +7 -66
- package/src/daemon/ipc-contract/sessions.ts +1 -0
- package/src/daemon/ipc-contract/surfaces.ts +0 -1
- package/src/daemon/ipc-contract-inventory.json +2 -4
- package/src/daemon/lifecycle.ts +14 -2
- package/src/daemon/session-agent-loop-handlers.ts +9 -0
- package/src/daemon/session-agent-loop.ts +2 -45
- package/src/daemon/session-attachments.ts +5 -1
- package/src/daemon/session-conflict-gate.ts +21 -82
- package/src/daemon/session-error.ts +18 -0
- package/src/daemon/session-lifecycle.ts +4 -5
- package/src/daemon/session-media-retry.ts +15 -1
- package/src/daemon/session-memory.ts +7 -52
- package/src/daemon/session-process.ts +3 -1
- package/src/daemon/session-runtime-assembly.ts +18 -35
- package/src/daemon/session-surfaces.ts +0 -1
- package/src/daemon/session-tool-setup.ts +7 -4
- package/src/events/domain-events.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +5 -1
- package/src/home-base/prebuilt/seed.ts +0 -1
- package/src/influencer/client.ts +7 -24
- package/src/media/gemini-image-service.ts +48 -3
- package/src/memory/app-store.ts +0 -4
- package/src/memory/conflict-intent.ts +3 -6
- package/src/memory/conflict-policy.ts +34 -0
- package/src/memory/conflict-store.ts +10 -18
- package/src/memory/contradiction-checker.ts +2 -2
- package/src/memory/conversation-attention-store.ts +3 -1
- package/src/memory/db-init.ts +8 -0
- package/src/memory/job-handlers/conflict.ts +0 -7
- package/src/memory/migrations/133-assistant-contact-metadata.ts +21 -0
- package/src/memory/migrations/134-contacts-notes-column.ts +51 -0
- package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +31 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/schema.ts +12 -17
- package/src/memory/slack-thread-store.ts +187 -0
- package/src/messaging/index.ts +0 -1
- package/src/messaging/providers/slack/client.ts +84 -26
- package/src/messaging/providers/slack/types.ts +4 -0
- package/src/messaging/types.ts +0 -38
- package/src/notifications/adapters/slack.ts +90 -0
- package/src/notifications/destination-resolver.ts +42 -1
- package/src/notifications/emit-signal.ts +17 -1
- package/src/oauth/provider-profiles.ts +22 -0
- package/src/providers/anthropic/client.ts +3 -0
- package/src/providers/openai/client.ts +3 -0
- package/src/providers/retry.ts +9 -1
- package/src/runtime/actor-trust-resolver.ts +8 -0
- package/src/runtime/auth/require-bound-guardian.ts +44 -0
- package/src/runtime/auth/route-policy.ts +4 -8
- package/src/runtime/channel-approval-types.ts +18 -0
- package/src/runtime/channel-approvals.ts +8 -0
- package/src/runtime/channel-invite-transport.ts +1 -1
- package/src/runtime/channel-reply-delivery.ts +62 -3
- package/src/runtime/gateway-client.ts +36 -2
- package/src/runtime/gateway-internal-client.ts +86 -0
- package/src/runtime/guardian-action-service.ts +128 -0
- package/src/runtime/guardian-outbound-actions.ts +3 -3
- package/src/runtime/guardian-reply-router.ts +4 -4
- package/src/runtime/guardian-verification-templates.ts +16 -1
- package/src/runtime/http-server.ts +29 -46
- package/src/runtime/invite-redemption-service.ts +1 -1
- package/src/runtime/{ingress-service.ts → invite-service.ts} +5 -157
- package/src/runtime/nl-approval-parser.ts +138 -0
- package/src/runtime/routes/approval-routes.ts +1 -40
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +6 -3
- package/src/runtime/routes/channel-route-shared.ts +35 -1
- package/src/runtime/routes/contact-routes.ts +494 -47
- package/src/runtime/routes/conversation-routes.ts +2 -1
- package/src/runtime/routes/global-search-routes.ts +2 -2
- package/src/runtime/routes/guardian-action-routes.ts +19 -111
- package/src/runtime/routes/guardian-approval-interception.ts +78 -1
- package/src/runtime/routes/guardian-bootstrap-routes.ts +6 -1
- package/src/runtime/routes/inbound-message-handler.ts +40 -12
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +227 -1
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +108 -0
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +2 -1
- package/src/runtime/routes/{ingress-routes.ts → invite-routes.ts} +10 -110
- package/src/runtime/routes/migration-routes.ts +17 -17
- package/src/runtime/slack-block-formatting.ts +176 -0
- package/src/schedule/scheduler.ts +11 -2
- package/src/tools/apps/executors.ts +16 -15
- package/src/tools/calls/call-end.ts +1 -1
- package/src/tools/computer-use/definitions.ts +16 -0
- package/src/tools/credentials/vault.ts +86 -2
- package/src/tools/network/script-proxy/session-manager.ts +28 -3
- package/src/tools/permission-checker.ts +18 -0
- package/src/tools/terminal/shell.ts +15 -5
- package/src/tools/tool-approval-handler.ts +48 -4
- package/src/tools/types.ts +38 -1
- package/src/util/errors.ts +5 -1
- package/src/util/retry.ts +21 -0
- package/src/watcher/providers/slack.ts +33 -3
- package/src/workspace/git-service.ts +6 -4
- package/src/__tests__/get-weather.test.ts +0 -393
- package/src/__tests__/weather-skill-regression.test.ts +0 -276
- package/src/autonomy/autonomy-resolver.ts +0 -62
- package/src/autonomy/autonomy-store.ts +0 -138
- package/src/autonomy/disposition-mapper.ts +0 -31
- package/src/autonomy/index.ts +0 -11
- package/src/autonomy/types.ts +0 -43
- package/src/config/bundled-skills/weather/SKILL.md +0 -38
- package/src/config/bundled-skills/weather/TOOLS.json +0 -32
- package/src/config/bundled-skills/weather/icon.svg +0 -24
- package/src/config/bundled-skills/weather/tools/get-weather.ts +0 -12
- package/src/messaging/triage-engine.ts +0 -344
- package/src/tools/weather/service.ts +0 -712
- /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.
|
|
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:
|
|
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
|
|
22
|
+
## 1. Inspect Trusted Contacts
|
|
18
23
|
|
|
19
24
|
### List all active trusted contacts
|
|
20
25
|
|
|
21
26
|
```bash
|
|
22
|
-
curl -s "$BASE/v1/
|
|
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/
|
|
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/
|
|
39
|
+
curl -s "$BASE/v1/contacts?channelType=sms" \
|
|
35
40
|
-H "Authorization: Bearer $TOKEN" | jq
|
|
36
41
|
```
|
|
37
42
|
|
|
38
|
-
### List all
|
|
43
|
+
### List all contacts (including revoked and blocked)
|
|
39
44
|
|
|
40
45
|
```bash
|
|
41
|
-
curl -s "$BASE/v1/
|
|
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
|
-
"
|
|
61
|
+
"contacts": [
|
|
51
62
|
{
|
|
52
63
|
"id": "uuid",
|
|
53
|
-
"sourceChannel": "telegram",
|
|
54
|
-
"externalUserId": "123456789",
|
|
55
|
-
"externalChatId": "123456789",
|
|
56
64
|
"displayName": "Alice",
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
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
|
|
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
|
|
116
|
-
|
|
117
|
-
-H "Authorization: Bearer $TOKEN" | jq -r '.
|
|
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
|
|
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
|
|
151
|
+
### Block a contact channel (stronger than revoke)
|
|
127
152
|
|
|
128
|
-
Blocking prevents the
|
|
153
|
+
Blocking prevents the contact from re-entering the flow without explicit unblocking.
|
|
129
154
|
|
|
130
155
|
```bash
|
|
131
|
-
curl -s -X
|
|
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
|
|
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/
|
|
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
|
-
"
|
|
244
|
-
"
|
|
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/
|
|
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
|
-
"
|
|
260
|
-
"
|
|
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
|
@@ -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
|
-
//
|
|
53
|
-
"
|
|
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
|
|
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": "
|
|
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
|
|
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": "
|
|
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
|
|
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
|
-
//
|
|
459
|
-
expect(ctx.actorPrincipalId).
|
|
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(
|
|
184
|
-
let
|
|
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
|
-
|
|
187
|
+
capturedHtml = params.htmlDefinition;
|
|
188
188
|
return makeApp({ name: params.name });
|
|
189
189
|
},
|
|
190
190
|
});
|
|
191
|
-
await executeAppCreate({ name: "
|
|
192
|
-
expect(
|
|
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: {
|
|
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("
|
|
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
|
-
|
|
173
|
-
expect(result).toContain(
|
|
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("
|
|
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
|
-
|
|
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:
|
|
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(
|
|
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("
|
|
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(
|
|
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
|
},
|