@vellumai/assistant 0.9.0-dev.202606181735.ae6e1d8 → 0.9.0-dev.202606181935.328e5f4
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/docs/activation-funnel-telemetry.md +24 -18
- package/openapi.yaml +240 -0
- package/package.json +1 -1
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +6 -8
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/channel-approval-routes.test.ts +0 -4
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +0 -2
- package/src/__tests__/contact-store-user-file.test.ts +0 -6
- package/src/__tests__/contacts-tools.test.ts +29 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/credential-prompt-route.test.ts +1 -0
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
- package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
- package/src/__tests__/invite-redemption-service.test.ts +0 -3
- package/src/__tests__/mcp-health-check.test.ts +6 -7
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/tool-audit-listener.test.ts +7 -7
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
- package/src/__tests__/trusted-contact-verification.test.ts +2 -4
- package/src/__tests__/voice-invite-redemption.test.ts +0 -1
- package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
- package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
- package/src/config/feature-flag-registry.json +8 -8
- package/src/config/schema.ts +6 -6
- package/src/config/schemas/channels.ts +11 -0
- package/src/contacts/contact-store.ts +2 -10
- package/src/contacts/contacts-write.ts +1 -2
- package/src/contacts/types.ts +0 -1
- package/src/context/token-estimator.ts +1 -1
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
- package/src/daemon/handlers/config-a2a.ts +0 -2
- package/src/daemon/handlers/config-channels.ts +5 -10
- package/src/daemon/handlers/config-slack-channel.ts +20 -0
- package/src/daemon/lifecycle.ts +23 -12
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/events/tool-audit-listener.ts +2 -2
- package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
- package/src/memory/auth-fallback-events-store.ts +2 -2
- package/src/memory/conversation-key-store.ts +17 -2
- package/src/memory/db-init.ts +2 -0
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
- package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +10 -0
- package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
- package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +30 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -1
- package/src/memory/skill-loaded-events-store.test.ts +7 -15
- package/src/memory/skill-loaded-events-store.ts +2 -2
- package/src/memory/tool-executed-events-store.test.ts +7 -7
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/permissions/checker.ts +22 -7
- package/src/permissions/ipc-risk-types.ts +10 -0
- package/src/platform/client.test.ts +96 -0
- package/src/platform/client.ts +52 -0
- package/src/platform/consent-cache.test.ts +159 -0
- package/src/platform/consent-cache.ts +107 -0
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +4 -3
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +6 -3
- package/src/plugins/defaults/advisor/config.ts +3 -7
- package/src/plugins/defaults/advisor/consult.ts +4 -6
- package/src/plugins/defaults/advisor/steering.ts +21 -5
- package/src/plugins/defaults/advisor/tools/advisor.ts +7 -4
- package/src/prompts/persona-resolver.ts +2 -2
- package/src/runtime/access-request-helper.ts +2 -3
- package/src/runtime/actor-trust-resolver.ts +1 -2
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/channel-verification-service.ts +1 -2
- package/src/runtime/invite-redemption-service.ts +6 -22
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
- package/src/runtime/routes/contact-routes.ts +31 -8
- package/src/runtime/routes/conversation-management-routes.ts +22 -1
- package/src/runtime/routes/conversation-routes.ts +12 -0
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
- package/src/runtime/routes/integrations/slack/channel.ts +36 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +233 -41
- package/src/telemetry/usage-telemetry-reporter.test.ts +40 -25
- package/src/telemetry/usage-telemetry-reporter.ts +24 -23
- package/src/tools/registry.ts +20 -0
- package/src/util/platform.ts +8 -1
- package/src/watcher/telemetry.ts +2 -2
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
- package/src/workspace/migrations/registry.ts +2 -0
|
@@ -50,8 +50,9 @@ Flow, end to end:
|
|
|
50
50
|
`assistant/src/telemetry/activation-funnel.ts`;
|
|
51
51
|
- emission is best-effort (wrapped in try/catch) and never blocks or alters
|
|
52
52
|
the surface-action flow;
|
|
53
|
-
- `recordActivationEvent` respects the `
|
|
54
|
-
|
|
53
|
+
- `recordActivationEvent` respects the platform `share_analytics` consent gate
|
|
54
|
+
via `getCachedShareAnalytics()` (returns `null` / no row when the platform
|
|
55
|
+
user has not consented; default-off when there is no platform session).
|
|
55
56
|
2. **Reporter flushes** every ~5 min: `usage-telemetry-reporter.ts`
|
|
56
57
|
(`REPORT_INTERVAL_MS = 5 * 60 * 1000`, with a one-time
|
|
57
58
|
`INITIAL_FLUSH_DELAY_MS = 30_000` after startup) POSTs unreported onboarding
|
|
@@ -172,22 +173,27 @@ When the flag is ON, the web prechat context selects
|
|
|
172
173
|
`BOOTSTRAP-ACTIVATION-RAIL.md` as the bootstrap template, and the daemon marks
|
|
173
174
|
the conversation in `activation_sessions` on first build of the system prompt.
|
|
174
175
|
|
|
175
|
-
### 5.2 Confirm
|
|
176
|
-
|
|
177
|
-
Two
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
`
|
|
190
|
-
|
|
176
|
+
### 5.2 Confirm share_analytics consent is on — and pick the right daemon mode
|
|
177
|
+
|
|
178
|
+
Two distinct concerns: whether record-time rows reach SQLite (consent gate), and
|
|
179
|
+
whether those rows reach BigQuery (flush gate, dev-disabled).
|
|
180
|
+
|
|
181
|
+
1. `recordActivationEvent` no-ops when the platform `share_analytics` consent is
|
|
182
|
+
off (it reads `getCachedShareAnalytics()`), and the consent is **default-off
|
|
183
|
+
when there is no platform session**. So the dev session must be signed in to
|
|
184
|
+
the platform with `share_analytics` consent enabled, or no rows are written to
|
|
185
|
+
SQLite. The consent cache is refreshed by `startConsentRefresh()` in
|
|
186
|
+
`assistant/src/daemon/lifecycle.ts`, which runs **regardless of dev mode** — so
|
|
187
|
+
a dev session signed in with `share_analytics` consent enabled writes
|
|
188
|
+
record-time rows to SQLite, where they can be inspected directly.
|
|
189
|
+
2. **Dev mode disables the flush entirely.** When the daemon runs in dev mode
|
|
190
|
+
(`VELLUM_DEV=1`), `assistant/src/daemon/lifecycle.ts` never starts the
|
|
191
|
+
`UsageTelemetryReporter` — rows accumulate in SQLite (consent permitting) but
|
|
192
|
+
are never POSTed, and the "wait/restart for flush" steps below will never reach
|
|
193
|
+
BigQuery. For an end-to-end smoke test, run the daemon **outside dev mode** (so
|
|
194
|
+
the reporter starts), or explicitly invoke the reporter's `flush()` via a dev
|
|
195
|
+
hook. The SQLite rows can be inspected directly in dev mode, but the BigQuery
|
|
196
|
+
verification (§6) requires a real flush.
|
|
191
197
|
|
|
192
198
|
### 5.3 Start a fresh activation conversation and capture its id
|
|
193
199
|
|
package/openapi.yaml
CHANGED
|
@@ -6152,6 +6152,12 @@ paths:
|
|
|
6152
6152
|
description: Only standard conversations are created by this endpoint
|
|
6153
6153
|
type: string
|
|
6154
6154
|
const: standard
|
|
6155
|
+
title:
|
|
6156
|
+
description:
|
|
6157
|
+
Explicit title for the conversation. When provided on creation, it is persisted as a user-set title (never
|
|
6158
|
+
overwritten by the auto-titler). Used by flows that mint a conversation up-front and don't want an
|
|
6159
|
+
auto-generated title.
|
|
6160
|
+
type: string
|
|
6155
6161
|
responses:
|
|
6156
6162
|
"200":
|
|
6157
6163
|
description: Successful response
|
|
@@ -13839,6 +13845,11 @@ paths:
|
|
|
13839
13845
|
type: string
|
|
13840
13846
|
botUsername:
|
|
13841
13847
|
type: string
|
|
13848
|
+
threadMode:
|
|
13849
|
+
type: string
|
|
13850
|
+
enum:
|
|
13851
|
+
- mention_only
|
|
13852
|
+
- mention_then_thread
|
|
13842
13853
|
error:
|
|
13843
13854
|
type: string
|
|
13844
13855
|
warning:
|
|
@@ -13849,6 +13860,7 @@ paths:
|
|
|
13849
13860
|
- hasAppToken
|
|
13850
13861
|
- hasUserToken
|
|
13851
13862
|
- connected
|
|
13863
|
+
- threadMode
|
|
13852
13864
|
additionalProperties: false
|
|
13853
13865
|
get:
|
|
13854
13866
|
operationId: integrations_slack_channel_config_get
|
|
@@ -13884,6 +13896,11 @@ paths:
|
|
|
13884
13896
|
type: string
|
|
13885
13897
|
botUsername:
|
|
13886
13898
|
type: string
|
|
13899
|
+
threadMode:
|
|
13900
|
+
type: string
|
|
13901
|
+
enum:
|
|
13902
|
+
- mention_only
|
|
13903
|
+
- mention_then_thread
|
|
13887
13904
|
error:
|
|
13888
13905
|
type: string
|
|
13889
13906
|
warning:
|
|
@@ -13894,6 +13911,71 @@ paths:
|
|
|
13894
13911
|
- hasAppToken
|
|
13895
13912
|
- hasUserToken
|
|
13896
13913
|
- connected
|
|
13914
|
+
- threadMode
|
|
13915
|
+
additionalProperties: false
|
|
13916
|
+
patch:
|
|
13917
|
+
operationId: integrations_slack_channel_config_patch
|
|
13918
|
+
summary: Update Slack channel settings
|
|
13919
|
+
description: Update Slack channel behavior settings (e.g. thread mode).
|
|
13920
|
+
tags:
|
|
13921
|
+
- integrations
|
|
13922
|
+
requestBody:
|
|
13923
|
+
required: true
|
|
13924
|
+
content:
|
|
13925
|
+
application/json:
|
|
13926
|
+
schema:
|
|
13927
|
+
type: object
|
|
13928
|
+
properties:
|
|
13929
|
+
threadMode:
|
|
13930
|
+
type: string
|
|
13931
|
+
enum:
|
|
13932
|
+
- mention_only
|
|
13933
|
+
- mention_then_thread
|
|
13934
|
+
description: Controls whether the bot follows threads after an initial @mention
|
|
13935
|
+
responses:
|
|
13936
|
+
"200":
|
|
13937
|
+
description: Successful response
|
|
13938
|
+
content:
|
|
13939
|
+
application/json:
|
|
13940
|
+
schema:
|
|
13941
|
+
type: object
|
|
13942
|
+
properties:
|
|
13943
|
+
success:
|
|
13944
|
+
type: boolean
|
|
13945
|
+
hasBotToken:
|
|
13946
|
+
type: boolean
|
|
13947
|
+
hasAppToken:
|
|
13948
|
+
type: boolean
|
|
13949
|
+
hasUserToken:
|
|
13950
|
+
type: boolean
|
|
13951
|
+
connected:
|
|
13952
|
+
type: boolean
|
|
13953
|
+
teamId:
|
|
13954
|
+
type: string
|
|
13955
|
+
teamName:
|
|
13956
|
+
type: string
|
|
13957
|
+
teamUrl:
|
|
13958
|
+
type: string
|
|
13959
|
+
botUserId:
|
|
13960
|
+
type: string
|
|
13961
|
+
botUsername:
|
|
13962
|
+
type: string
|
|
13963
|
+
threadMode:
|
|
13964
|
+
type: string
|
|
13965
|
+
enum:
|
|
13966
|
+
- mention_only
|
|
13967
|
+
- mention_then_thread
|
|
13968
|
+
error:
|
|
13969
|
+
type: string
|
|
13970
|
+
warning:
|
|
13971
|
+
type: string
|
|
13972
|
+
required:
|
|
13973
|
+
- success
|
|
13974
|
+
- hasBotToken
|
|
13975
|
+
- hasAppToken
|
|
13976
|
+
- hasUserToken
|
|
13977
|
+
- connected
|
|
13978
|
+
- threadMode
|
|
13897
13979
|
additionalProperties: false
|
|
13898
13980
|
post:
|
|
13899
13981
|
operationId: integrations_slack_channel_config_post
|
|
@@ -13945,6 +14027,11 @@ paths:
|
|
|
13945
14027
|
type: string
|
|
13946
14028
|
botUsername:
|
|
13947
14029
|
type: string
|
|
14030
|
+
threadMode:
|
|
14031
|
+
type: string
|
|
14032
|
+
enum:
|
|
14033
|
+
- mention_only
|
|
14034
|
+
- mention_then_thread
|
|
13948
14035
|
error:
|
|
13949
14036
|
type: string
|
|
13950
14037
|
warning:
|
|
@@ -13955,6 +14042,7 @@ paths:
|
|
|
13955
14042
|
- hasAppToken
|
|
13956
14043
|
- hasUserToken
|
|
13957
14044
|
- connected
|
|
14045
|
+
- threadMode
|
|
13958
14046
|
additionalProperties: false
|
|
13959
14047
|
/v1/integrations/telegram/commands:
|
|
13960
14048
|
post:
|
|
@@ -14569,6 +14657,54 @@ paths:
|
|
|
14569
14657
|
responses:
|
|
14570
14658
|
"200":
|
|
14571
14659
|
description: Successful response
|
|
14660
|
+
content:
|
|
14661
|
+
application/json:
|
|
14662
|
+
schema:
|
|
14663
|
+
type: object
|
|
14664
|
+
properties:
|
|
14665
|
+
servers:
|
|
14666
|
+
type: array
|
|
14667
|
+
items:
|
|
14668
|
+
type: object
|
|
14669
|
+
properties:
|
|
14670
|
+
id:
|
|
14671
|
+
type: string
|
|
14672
|
+
status:
|
|
14673
|
+
type: string
|
|
14674
|
+
transport:
|
|
14675
|
+
type: object
|
|
14676
|
+
properties:
|
|
14677
|
+
type:
|
|
14678
|
+
type: string
|
|
14679
|
+
enum:
|
|
14680
|
+
- stdio
|
|
14681
|
+
- sse
|
|
14682
|
+
- streamable-http
|
|
14683
|
+
required:
|
|
14684
|
+
- type
|
|
14685
|
+
additionalProperties: {}
|
|
14686
|
+
enabled:
|
|
14687
|
+
type: boolean
|
|
14688
|
+
defaultRiskLevel:
|
|
14689
|
+
type: string
|
|
14690
|
+
allowedTools:
|
|
14691
|
+
type: array
|
|
14692
|
+
items:
|
|
14693
|
+
type: string
|
|
14694
|
+
blockedTools:
|
|
14695
|
+
type: array
|
|
14696
|
+
items:
|
|
14697
|
+
type: string
|
|
14698
|
+
required:
|
|
14699
|
+
- id
|
|
14700
|
+
- status
|
|
14701
|
+
- transport
|
|
14702
|
+
- enabled
|
|
14703
|
+
- defaultRiskLevel
|
|
14704
|
+
additionalProperties: false
|
|
14705
|
+
required:
|
|
14706
|
+
- servers
|
|
14707
|
+
additionalProperties: false
|
|
14572
14708
|
/v1/internal/mcp/reload:
|
|
14573
14709
|
post:
|
|
14574
14710
|
operationId: internal_mcp_reload_post
|
|
@@ -14600,6 +14736,104 @@ paths:
|
|
|
14600
14736
|
responses:
|
|
14601
14737
|
"200":
|
|
14602
14738
|
description: Successful response
|
|
14739
|
+
/v1/internal/mcp/tools-summary:
|
|
14740
|
+
get:
|
|
14741
|
+
operationId: internal_mcp_toolssummary_get
|
|
14742
|
+
summary: Per-server MCP tool counts and token estimates
|
|
14743
|
+
description:
|
|
14744
|
+
Returns registered tool counts, individual tool details, and estimated token overhead for each connected
|
|
14745
|
+
MCP server.
|
|
14746
|
+
tags:
|
|
14747
|
+
- internal
|
|
14748
|
+
responses:
|
|
14749
|
+
"200":
|
|
14750
|
+
description: Successful response
|
|
14751
|
+
content:
|
|
14752
|
+
application/json:
|
|
14753
|
+
schema:
|
|
14754
|
+
type: object
|
|
14755
|
+
properties:
|
|
14756
|
+
servers:
|
|
14757
|
+
type: array
|
|
14758
|
+
items:
|
|
14759
|
+
type: object
|
|
14760
|
+
properties:
|
|
14761
|
+
serverId:
|
|
14762
|
+
type: string
|
|
14763
|
+
toolCount:
|
|
14764
|
+
type: number
|
|
14765
|
+
estimatedTokens:
|
|
14766
|
+
type: number
|
|
14767
|
+
tools:
|
|
14768
|
+
type: array
|
|
14769
|
+
items:
|
|
14770
|
+
type: object
|
|
14771
|
+
properties:
|
|
14772
|
+
name:
|
|
14773
|
+
type: string
|
|
14774
|
+
description:
|
|
14775
|
+
type: string
|
|
14776
|
+
estimatedTokens:
|
|
14777
|
+
type: number
|
|
14778
|
+
required:
|
|
14779
|
+
- name
|
|
14780
|
+
- description
|
|
14781
|
+
- estimatedTokens
|
|
14782
|
+
additionalProperties: false
|
|
14783
|
+
required:
|
|
14784
|
+
- serverId
|
|
14785
|
+
- toolCount
|
|
14786
|
+
- estimatedTokens
|
|
14787
|
+
- tools
|
|
14788
|
+
additionalProperties: false
|
|
14789
|
+
totalToolCount:
|
|
14790
|
+
type: number
|
|
14791
|
+
totalEstimatedTokens:
|
|
14792
|
+
type: number
|
|
14793
|
+
required:
|
|
14794
|
+
- servers
|
|
14795
|
+
- totalToolCount
|
|
14796
|
+
- totalEstimatedTokens
|
|
14797
|
+
additionalProperties: false
|
|
14798
|
+
/v1/internal/mcp/update:
|
|
14799
|
+
post:
|
|
14800
|
+
operationId: internal_mcp_update_post
|
|
14801
|
+
summary: Update an MCP server configuration
|
|
14802
|
+
description: Updates fields on an existing MCP server config entry and triggers a reload.
|
|
14803
|
+
tags:
|
|
14804
|
+
- internal
|
|
14805
|
+
requestBody:
|
|
14806
|
+
required: true
|
|
14807
|
+
content:
|
|
14808
|
+
application/json:
|
|
14809
|
+
schema:
|
|
14810
|
+
type: object
|
|
14811
|
+
properties:
|
|
14812
|
+
name:
|
|
14813
|
+
type: string
|
|
14814
|
+
enabled:
|
|
14815
|
+
type: boolean
|
|
14816
|
+
defaultRiskLevel:
|
|
14817
|
+
type: string
|
|
14818
|
+
maxTools:
|
|
14819
|
+
type: number
|
|
14820
|
+
allowedTools:
|
|
14821
|
+
anyOf:
|
|
14822
|
+
- type: array
|
|
14823
|
+
items:
|
|
14824
|
+
type: string
|
|
14825
|
+
- type: "null"
|
|
14826
|
+
blockedTools:
|
|
14827
|
+
anyOf:
|
|
14828
|
+
- type: array
|
|
14829
|
+
items:
|
|
14830
|
+
type: string
|
|
14831
|
+
- type: "null"
|
|
14832
|
+
required:
|
|
14833
|
+
- name
|
|
14834
|
+
responses:
|
|
14835
|
+
"200":
|
|
14836
|
+
description: Successful response
|
|
14603
14837
|
/v1/internal/oauth/callback:
|
|
14604
14838
|
post:
|
|
14605
14839
|
operationId: internal_oauth_callback_post
|
|
@@ -16854,6 +17088,12 @@ paths:
|
|
|
16854
17088
|
type: array
|
|
16855
17089
|
items:
|
|
16856
17090
|
type: string
|
|
17091
|
+
title:
|
|
17092
|
+
description:
|
|
17093
|
+
Explicit title for the conversation minted on this first message. Persisted as a user-set title (never
|
|
17094
|
+
overwritten by the auto-titler). Used by onboarding flows that mint a conversation behind the
|
|
17095
|
+
scenes.
|
|
17096
|
+
type: string
|
|
16857
17097
|
required:
|
|
16858
17098
|
- tools
|
|
16859
17099
|
- tasks
|
package/package.json
CHANGED
|
@@ -43,7 +43,6 @@ const PHONE = "+15559871234";
|
|
|
43
43
|
function makeContact(
|
|
44
44
|
role: "guardian" | "contact" = "contact",
|
|
45
45
|
status: "unverified" | "active" = "unverified",
|
|
46
|
-
externalUserId: string | null = null,
|
|
47
46
|
): ContactWithChannels {
|
|
48
47
|
const channelId = "ch-test";
|
|
49
48
|
return {
|
|
@@ -64,7 +63,6 @@ function makeContact(
|
|
|
64
63
|
contactId: "contact-test",
|
|
65
64
|
type: "phone",
|
|
66
65
|
address: PHONE,
|
|
67
|
-
externalUserId,
|
|
68
66
|
externalChatId: null,
|
|
69
67
|
isPrimary: true,
|
|
70
68
|
status,
|
|
@@ -94,7 +92,7 @@ describe("resolveActorTrust — address fallback", () => {
|
|
|
94
92
|
|
|
95
93
|
test("finds unverified channel via address when externalUserId is null", () => {
|
|
96
94
|
// Simulate a contact registered by name-capture: address set, externalUserId null.
|
|
97
|
-
_byAddress = makeContact("contact", "unverified"
|
|
95
|
+
_byAddress = makeContact("contact", "unverified");
|
|
98
96
|
|
|
99
97
|
const result = resolveActorTrust({
|
|
100
98
|
assistantId: "asst-1",
|
|
@@ -112,7 +110,7 @@ describe("resolveActorTrust — address fallback", () => {
|
|
|
112
110
|
});
|
|
113
111
|
|
|
114
112
|
test("address lookup is the sole member resolution path", () => {
|
|
115
|
-
_byAddress = makeContact("contact", "active"
|
|
113
|
+
_byAddress = makeContact("contact", "active");
|
|
116
114
|
|
|
117
115
|
const result = resolveActorTrust({
|
|
118
116
|
assistantId: "asst-1",
|
|
@@ -142,7 +140,7 @@ describe("resolveActorTrust — address fallback", () => {
|
|
|
142
140
|
test("address-found active channel elevates trust to trusted_contact", () => {
|
|
143
141
|
// An active channel found via address (e.g. after manual verify without externalUserId set)
|
|
144
142
|
// should still yield trusted_contact trust class.
|
|
145
|
-
_byAddress = makeContact("contact", "active"
|
|
143
|
+
_byAddress = makeContact("contact", "active");
|
|
146
144
|
|
|
147
145
|
const result = resolveActorTrust({
|
|
148
146
|
assistantId: "asst-1",
|
|
@@ -159,7 +157,7 @@ describe("resolveActorTrust — address fallback", () => {
|
|
|
159
157
|
test("pending-status member is classified as unverified_contact", () => {
|
|
160
158
|
// Mirrors the unverified branch but for `pending` status (e.g. a phone
|
|
161
159
|
// contact registered by name-capture awaiting the DTMF challenge).
|
|
162
|
-
const contact = makeContact("contact", "unverified"
|
|
160
|
+
const contact = makeContact("contact", "unverified");
|
|
163
161
|
// Override status to "pending" — makeContact only accepts unverified/active
|
|
164
162
|
contact.channels[0]!.status = "pending";
|
|
165
163
|
_byAddress = contact;
|
|
@@ -178,7 +176,7 @@ describe("resolveActorTrust — address fallback", () => {
|
|
|
178
176
|
test("blocked-status member is classified as unknown (not unverified_contact)", () => {
|
|
179
177
|
// Hard-deny statuses (blocked, revoked) stay `unknown` — admission-layer
|
|
180
178
|
// re-checks channel.status and emits the hard-deny reasons.
|
|
181
|
-
const contact = makeContact("contact", "unverified"
|
|
179
|
+
const contact = makeContact("contact", "unverified");
|
|
182
180
|
contact.channels[0]!.status = "blocked";
|
|
183
181
|
_byAddress = contact;
|
|
184
182
|
|
|
@@ -194,7 +192,7 @@ describe("resolveActorTrust — address fallback", () => {
|
|
|
194
192
|
});
|
|
195
193
|
|
|
196
194
|
test("revoked-status member is classified as unknown", () => {
|
|
197
|
-
const contact = makeContact("contact", "unverified"
|
|
195
|
+
const contact = makeContact("contact", "unverified");
|
|
198
196
|
contact.channels[0]!.status = "revoked";
|
|
199
197
|
_byAddress = contact;
|
|
200
198
|
|
|
@@ -7,18 +7,10 @@ mock.module("../util/logger.js", () => ({
|
|
|
7
7
|
}),
|
|
8
8
|
}));
|
|
9
9
|
|
|
10
|
-
let
|
|
10
|
+
let shareAnalytics = true;
|
|
11
11
|
|
|
12
|
-
mock.module("../
|
|
13
|
-
|
|
14
|
-
ui: {},
|
|
15
|
-
model: "test",
|
|
16
|
-
provider: "test",
|
|
17
|
-
memory: { enabled: false },
|
|
18
|
-
rateLimit: { maxRequestsPerMinute: 0 },
|
|
19
|
-
secretDetection: { enabled: false },
|
|
20
|
-
collectUsageData,
|
|
21
|
-
}),
|
|
12
|
+
mock.module("../platform/consent-cache.js", () => ({
|
|
13
|
+
getCachedShareAnalytics: () => shareAnalytics,
|
|
22
14
|
}));
|
|
23
15
|
|
|
24
16
|
import {
|
|
@@ -53,7 +45,7 @@ const SAMPLE: AuthFallbackCount[] = [
|
|
|
53
45
|
|
|
54
46
|
describe("auth-fallback-events-store", () => {
|
|
55
47
|
beforeEach(() => {
|
|
56
|
-
|
|
48
|
+
shareAnalytics = true;
|
|
57
49
|
resetTable();
|
|
58
50
|
});
|
|
59
51
|
|
|
@@ -78,8 +70,8 @@ describe("auth-fallback-events-store", () => {
|
|
|
78
70
|
});
|
|
79
71
|
});
|
|
80
72
|
|
|
81
|
-
test("honors the
|
|
82
|
-
|
|
73
|
+
test("honors the share_analytics opt-out (records nothing)", () => {
|
|
74
|
+
shareAnalytics = false;
|
|
83
75
|
const recorded = recordAuthFallbackCounts(1000, 2000, SAMPLE);
|
|
84
76
|
expect(recorded).toBe(0);
|
|
85
77
|
expect(queryUnreportedAuthFallbackEvents(0, undefined, 100).length).toBe(0);
|
|
@@ -224,14 +224,12 @@ function ensureTestContact(): void {
|
|
|
224
224
|
{
|
|
225
225
|
type: "telegram",
|
|
226
226
|
address: "telegram-user-default",
|
|
227
|
-
externalUserId: "telegram-user-default",
|
|
228
227
|
status: "active",
|
|
229
228
|
policy: "allow",
|
|
230
229
|
},
|
|
231
230
|
{
|
|
232
231
|
type: "slack",
|
|
233
232
|
address: "slack-user-default",
|
|
234
|
-
externalUserId: "slack-user-default",
|
|
235
233
|
status: "active",
|
|
236
234
|
policy: "allow",
|
|
237
235
|
},
|
|
@@ -2054,7 +2052,6 @@ describe("requester cancel of guardian-gated pending request", () => {
|
|
|
2054
2052
|
{
|
|
2055
2053
|
type: "telegram",
|
|
2056
2054
|
address: "requester-cancel-user",
|
|
2057
|
-
externalUserId: "requester-cancel-user",
|
|
2058
2055
|
status: "active",
|
|
2059
2056
|
policy: "allow",
|
|
2060
2057
|
},
|
|
@@ -2981,7 +2978,6 @@ describe("trusted-contact self-approval blocked before guardian approval row exi
|
|
|
2981
2978
|
{
|
|
2982
2979
|
type: "telegram",
|
|
2983
2980
|
address: "tc-selfapproval-user",
|
|
2984
|
-
externalUserId: "tc-selfapproval-user",
|
|
2985
2981
|
status: "active",
|
|
2986
2982
|
policy: "allow",
|
|
2987
2983
|
},
|
|
@@ -97,7 +97,6 @@ function seedTrustedContact(policy: "allow" | "escalate" = "allow"): void {
|
|
|
97
97
|
{
|
|
98
98
|
type: "telegram",
|
|
99
99
|
address: "telegram-user-1",
|
|
100
|
-
externalUserId: "telegram-user-1",
|
|
101
100
|
status: "active",
|
|
102
101
|
policy,
|
|
103
102
|
},
|
|
@@ -244,7 +243,6 @@ describe("channel inbound disk pressure gate", () => {
|
|
|
244
243
|
{
|
|
245
244
|
type: "slack",
|
|
246
245
|
address: "slack-user-1",
|
|
247
|
-
externalUserId: "slack-user-1",
|
|
248
246
|
status: "active",
|
|
249
247
|
policy: "allow",
|
|
250
248
|
},
|
|
@@ -75,7 +75,6 @@ describe("upsertContact user_file selection", () => {
|
|
|
75
75
|
{
|
|
76
76
|
type: "vellum",
|
|
77
77
|
address: "vellum-principal-abc",
|
|
78
|
-
externalUserId: "vellum-principal-abc",
|
|
79
78
|
},
|
|
80
79
|
],
|
|
81
80
|
});
|
|
@@ -91,7 +90,6 @@ describe("upsertContact user_file selection", () => {
|
|
|
91
90
|
{
|
|
92
91
|
type: "slack",
|
|
93
92
|
address: "u123456",
|
|
94
|
-
externalUserId: "U123456",
|
|
95
93
|
externalChatId: "D987654",
|
|
96
94
|
},
|
|
97
95
|
],
|
|
@@ -109,7 +107,6 @@ describe("upsertContact user_file selection", () => {
|
|
|
109
107
|
{
|
|
110
108
|
type: "slack",
|
|
111
109
|
address: "ualice",
|
|
112
|
-
externalUserId: "UALICE",
|
|
113
110
|
externalChatId: "DALICE",
|
|
114
111
|
},
|
|
115
112
|
],
|
|
@@ -125,7 +122,6 @@ describe("upsertContact user_file selection", () => {
|
|
|
125
122
|
{
|
|
126
123
|
type: "slack",
|
|
127
124
|
address: "ubob1",
|
|
128
|
-
externalUserId: "UBOB1",
|
|
129
125
|
externalChatId: "DBOB1",
|
|
130
126
|
},
|
|
131
127
|
],
|
|
@@ -137,7 +133,6 @@ describe("upsertContact user_file selection", () => {
|
|
|
137
133
|
{
|
|
138
134
|
type: "slack",
|
|
139
135
|
address: "ubob2",
|
|
140
|
-
externalUserId: "UBOB2",
|
|
141
136
|
externalChatId: "DBOB2",
|
|
142
137
|
},
|
|
143
138
|
],
|
|
@@ -164,7 +159,6 @@ describe("upsertContact user_file selection", () => {
|
|
|
164
159
|
{
|
|
165
160
|
type: "phone",
|
|
166
161
|
address: "+15550000",
|
|
167
|
-
externalUserId: "+15550000",
|
|
168
162
|
externalChatId: "+15550000",
|
|
169
163
|
},
|
|
170
164
|
],
|
|
@@ -240,6 +240,35 @@ describe("contact_search tool", () => {
|
|
|
240
240
|
});
|
|
241
241
|
});
|
|
242
242
|
|
|
243
|
+
// ── search_contacts route (HTTP/IPC compat shim) ─────────────────────
|
|
244
|
+
|
|
245
|
+
describe("search_contacts route", () => {
|
|
246
|
+
beforeEach(clearContacts);
|
|
247
|
+
|
|
248
|
+
test("includes externalUserId (= address) on channels for older clients", () => {
|
|
249
|
+
const seeded = upsertFixture({
|
|
250
|
+
display_name: "Dana",
|
|
251
|
+
channels: [{ type: "slack", address: "U12345ABC" }],
|
|
252
|
+
});
|
|
253
|
+
const seededAddress = seeded.channels[0]!.address;
|
|
254
|
+
|
|
255
|
+
const searchRoute = ROUTES.find(
|
|
256
|
+
(r) => r.operationId === "search_contacts",
|
|
257
|
+
)!;
|
|
258
|
+
const contacts = searchRoute.handler({
|
|
259
|
+
body: { channelAddress: seededAddress },
|
|
260
|
+
}) as unknown as Array<{
|
|
261
|
+
channels: Array<{ address: string; externalUserId?: string }>;
|
|
262
|
+
}>;
|
|
263
|
+
|
|
264
|
+
expect(contacts.length).toBeGreaterThanOrEqual(1);
|
|
265
|
+
const channel = contacts[0]!.channels[0]!;
|
|
266
|
+
// The route re-derives the compat field from address, so SDK/macOS
|
|
267
|
+
// clients that read externalUserId keep working.
|
|
268
|
+
expect(channel.externalUserId).toBe(channel.address);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
243
272
|
// ── contact_merge ───────────────────────────────────────────────────
|
|
244
273
|
|
|
245
274
|
describe("contact_merge tool", () => {
|
|
@@ -10,9 +10,11 @@ mock.module("../util/logger.js", () => ({
|
|
|
10
10
|
}),
|
|
11
11
|
}));
|
|
12
12
|
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
// Analytics consent is granted so recordActivationEvent writes rows.
|
|
14
|
+
let shareAnalytics = true;
|
|
15
|
+
|
|
16
|
+
mock.module("../platform/consent-cache.js", () => ({
|
|
17
|
+
getCachedShareAnalytics: () => shareAnalytics,
|
|
16
18
|
}));
|
|
17
19
|
|
|
18
20
|
let broadcastedMessages: ServerMessage[] = [];
|
|
@@ -117,6 +119,7 @@ async function showTaggedChoice(
|
|
|
117
119
|
|
|
118
120
|
describe("activation moment emission from ui_show surface commits", () => {
|
|
119
121
|
beforeEach(() => {
|
|
122
|
+
shareAnalytics = true;
|
|
120
123
|
broadcastedMessages = [];
|
|
121
124
|
resetTables();
|
|
122
125
|
});
|
|
@@ -44,7 +44,7 @@ describe("healGuardianBindingDrift", () => {
|
|
|
44
44
|
const guardian = findGuardianForChannel("vellum");
|
|
45
45
|
expect(guardian).not.toBeNull();
|
|
46
46
|
expect(guardian!.contact.principalId).toBe("vellum-principal-old-uuid");
|
|
47
|
-
expect(guardian!.channel.
|
|
47
|
+
expect(guardian!.channel.address).toBe("vellum-principal-old-uuid");
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
test("no-op when principals already match", () => {
|