@vellumai/assistant 0.10.3 → 0.10.4-staging.1
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/openapi.yaml +73 -56
- package/package.json +1 -1
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +83 -31
- package/src/__tests__/assistant-stream-state.test.ts +3 -76
- package/src/__tests__/background-workers-disk-pressure.test.ts +4 -2
- package/src/__tests__/channel-approval-routes.test.ts +21 -26
- package/src/__tests__/channel-delivery-store.test.ts +28 -0
- package/src/__tests__/channel-guardian.test.ts +82 -32
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +11 -19
- package/src/__tests__/channel-reply-delivery.test.ts +6 -2
- package/src/__tests__/compaction-ledger-store.test.ts +128 -0
- package/src/__tests__/config-loader-backfill.test.ts +148 -0
- package/src/__tests__/consult-deadline.test.ts +60 -0
- package/src/__tests__/contact-store-interaction-info.test.ts +156 -0
- package/src/__tests__/contact-store-user-file.test.ts +7 -10
- package/src/__tests__/contacts-relay-reads.test.ts +6 -9
- package/src/__tests__/contacts-write.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +4 -2
- package/src/__tests__/conversation-agent-loop.test.ts +98 -7
- package/src/__tests__/conversation-attention-telegram.test.ts +9 -11
- package/src/__tests__/conversation-error.test.ts +18 -0
- package/src/__tests__/conversation-fork-crud.test.ts +354 -24
- package/src/__tests__/conversation-title-service.test.ts +222 -201
- package/src/__tests__/db-compaction-events-migration.test.ts +129 -0
- package/src/__tests__/delete-propagation.test.ts +5 -3
- package/src/__tests__/dm-backfill.test.ts +6 -4
- package/src/__tests__/emit-signal-routing-intent.test.ts +2 -6
- package/src/__tests__/guardian-binding-drift-heal.test.ts +43 -23
- package/src/__tests__/guardian-dispatch.test.ts +50 -5
- package/src/__tests__/guardian-routing-state.test.ts +6 -10
- package/src/__tests__/helpers/channel-test-adapter.ts +45 -12
- package/src/__tests__/helpers/create-guardian-binding.ts +15 -23
- package/src/__tests__/helpers/mock-logger.ts +1 -0
- package/src/__tests__/helpers/seed-contact-channel.ts +96 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +87 -10
- package/src/__tests__/invite-redemption-service.test.ts +273 -53
- package/src/__tests__/invite-routes-http.test.ts +34 -0
- package/src/__tests__/invite-service-ipc.test.ts +65 -2
- package/src/__tests__/list-messages-page-latest.test.ts +173 -4
- package/src/__tests__/mcp-config-secret-boundary.test.ts +3 -0
- package/src/__tests__/non-member-access-request.test.ts +15 -13
- package/src/__tests__/onboarding-persona-write.test.ts +52 -22
- package/src/__tests__/persist-onboarding-artifacts.test.ts +1 -0
- package/src/__tests__/persona-resolver.test.ts +75 -45
- package/src/__tests__/plugin-bootstrap.test.ts +13 -5
- package/src/__tests__/plugin-disabled-state.test.ts +190 -0
- package/src/__tests__/provider-usage-tracking.test.ts +1 -1
- package/src/__tests__/reaction-intercept-cold-cache-warm.test.ts +135 -0
- package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +158 -0
- package/src/__tests__/reaction-persistence.test.ts +51 -4
- package/src/__tests__/relay-server.test.ts +88 -31
- package/src/__tests__/runtime-attachment-metadata.test.ts +9 -11
- package/src/__tests__/settings-routes.test.ts +32 -0
- package/src/__tests__/slack-block-formatting.test.ts +1 -38
- package/src/__tests__/sse-actor-principal-guardian-source.test.ts +13 -36
- package/src/__tests__/stt-hints.test.ts +6 -3
- package/src/__tests__/subagent-fork-prompt-role.test.ts +195 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +6 -7
- package/src/__tests__/subagent-role-registry.test.ts +17 -4
- package/src/__tests__/subagent-spawn-and-await.test.ts +546 -0
- package/src/__tests__/subagent-tools.test.ts +398 -3
- package/src/__tests__/thread-backfill.test.ts +3 -3
- package/src/__tests__/tool-preview-lifecycle.test.ts +26 -10
- package/src/__tests__/tool-start-timestamp.test.ts +4 -3
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +37 -51
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -2
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -7
- package/src/__tests__/trusted-contact-multichannel.test.ts +16 -7
- package/src/__tests__/trusted-contact-verification.test.ts +79 -54
- package/src/__tests__/voice-guardian-cold-cache-warm.test.ts +137 -0
- package/src/__tests__/voice-invite-redemption.test.ts +183 -20
- package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +3 -3
- package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +2 -2
- package/src/__tests__/workspace-migration-112-remove-advisor-callsite-override.test.ts +170 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +196 -238
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +35 -47
- package/src/agent/loop-exclusive-tool.test.ts +19 -15
- package/src/agent/loop-native-web-search.test.ts +200 -0
- package/src/agent/loop.ts +108 -1
- package/src/api/responses/conversation-message.ts +9 -0
- package/src/approvals/guardian-request-resolvers.ts +16 -4
- package/src/calls/__tests__/relay-setup-router.test.ts +10 -18
- package/src/calls/guardian-dispatch.ts +14 -11
- package/src/calls/inbound-trust-reader.ts +7 -1
- package/src/calls/relay-access-wait.ts +6 -6
- package/src/calls/relay-server.ts +22 -2
- package/src/calls/relay-setup-router.ts +10 -10
- package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
- package/src/cli/commands/contacts.ts +10 -7
- package/src/cli/commands/memory/__tests__/worker.test.ts +147 -17
- package/src/cli/commands/memory/worker.ts +97 -30
- package/src/cli/commands/plugins.ts +3 -146
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +17 -17
- package/src/cli/lib/__tests__/publish-plugin.test.ts +98 -0
- package/src/cli/lib/publish-plugin.ts +231 -1
- package/src/config/__tests__/sync-gated-profiles.test.ts +5 -7
- package/src/config/bundled-skills/subagent/SKILL.md +16 -1
- package/src/config/bundled-skills/subagent/TOOLS.json +5 -4
- package/src/config/call-site-defaults.ts +0 -6
- package/src/config/llm-resolver.ts +0 -3
- package/src/config/schemas/call-site-catalog.ts +0 -7
- package/src/config/schemas/heartbeat.ts +2 -5
- package/src/config/schemas/llm.ts +3 -12
- package/src/config/schemas/memory-lifecycle.ts +1 -1
- package/src/config/seed-inference-profiles.ts +76 -35
- package/src/config/sync-gated-profiles.ts +0 -3
- package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +7 -8
- package/src/contacts/__tests__/member-write-relay.test.ts +35 -11
- package/src/contacts/contact-store.ts +27 -237
- package/src/contacts/contacts-write.ts +18 -58
- package/src/contacts/gateway-channel-read.ts +51 -0
- package/src/contacts/member-write-relay.ts +25 -31
- package/src/contacts/types.ts +3 -15
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +0 -44
- package/src/daemon/conversation-agent-loop-handlers.ts +29 -10
- package/src/daemon/conversation-agent-loop.ts +68 -61
- package/src/daemon/conversation-error.ts +7 -10
- package/src/daemon/conversation-tool-setup.ts +0 -10
- package/src/daemon/conversation.ts +10 -0
- package/src/daemon/external-plugins-bootstrap.ts +8 -2
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -2
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -2
- package/src/daemon/handlers/__tests__/config-channels.test.ts +9 -14
- package/src/daemon/handlers/config-channels.ts +14 -29
- package/src/daemon/lifecycle.ts +16 -4
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/heartbeat/heartbeat-service.ts +5 -0
- package/src/home/relationship-state-writer.ts +5 -0
- package/src/memory/__tests__/embedding-cache.test.ts +136 -0
- package/src/memory/compaction-ledger-store.ts +107 -0
- package/src/memory/conversation-crud.ts +136 -61
- package/src/memory/conversation-title-service.ts +173 -24
- package/src/memory/embedding-backend.ts +8 -1
- package/src/memory/embedding-cache.ts +139 -0
- package/src/memory/jobs-worker.ts +75 -29
- package/src/memory/memory-retrospective-job.ts +5 -0
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +27 -5
- package/src/memory/migrations/302-create-compaction-events.ts +107 -0
- package/src/memory/migrations/303-add-conversation-creation-seq.ts +33 -0
- package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +79 -6
- package/src/memory/schema/contacts.ts +6 -2
- package/src/memory/schema/conversations.ts +39 -0
- package/src/memory/steps.ts +1090 -367
- package/src/memory/worker-control.ts +104 -18
- package/src/memory/worker-process.ts +17 -0
- package/src/messaging/channel-binding-metadata.ts +31 -0
- package/src/messaging/channel-binding-schema.ts +51 -0
- package/src/messaging/providers/__tests__/callback-routing.test.ts +45 -0
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +195 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +11 -0
- package/src/messaging/providers/a2a/deliver.ts +5 -1
- package/src/messaging/providers/a2a/transport.ts +10 -0
- package/src/messaging/providers/callback-routing.ts +48 -0
- package/src/messaging/providers/channel-transport.ts +55 -0
- package/src/messaging/providers/index.ts +65 -241
- package/src/messaging/providers/slack/binding-metadata.ts +62 -0
- package/src/messaging/providers/slack/transport.ts +92 -0
- package/src/messaging/providers/telegram-bot/transport.ts +51 -0
- package/src/messaging/providers/whatsapp/transport.ts +38 -0
- package/src/notifications/__tests__/broadcaster.test.ts +0 -8
- package/src/notifications/__tests__/connected-channels.test.ts +8 -36
- package/src/notifications/__tests__/destination-resolver.test.ts +12 -117
- package/src/notifications/destination-resolver.ts +7 -23
- package/src/notifications/emit-signal.ts +5 -11
- package/src/plugins/defaults/index.ts +0 -35
- package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +11 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +243 -2
- package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +167 -14
- package/src/plugins/disabled-state.ts +31 -0
- package/src/plugins/registry.ts +55 -12
- package/src/prompts/persona-resolver.ts +43 -11
- package/src/providers/call-site-routing.ts +41 -0
- package/src/providers/provider-send-message.ts +6 -0
- package/src/providers/ratelimit.ts +6 -0
- package/src/providers/registry.ts +1 -1
- package/src/providers/retry.ts +6 -0
- package/src/providers/types.ts +13 -0
- package/src/providers/usage-tracking.ts +6 -0
- package/src/runtime/__tests__/guardian-vellum-migration.test.ts +30 -27
- package/src/runtime/__tests__/local-principal-trust.test.ts +16 -18
- package/src/runtime/__tests__/member-verdict-cache.test.ts +119 -0
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +115 -168
- package/src/runtime/access-request-helper.ts +1 -2
- package/src/runtime/actor-trust-resolver.ts +44 -17
- package/src/runtime/anchored-guardian.test.ts +7 -54
- package/src/runtime/anchored-guardian.ts +4 -53
- package/src/runtime/assistant-stream-state.ts +12 -74
- package/src/runtime/channel-reply-delivery.ts +3 -8
- package/src/runtime/guardian-vellum-migration.ts +18 -16
- package/src/runtime/invite-redemption-service.ts +25 -10
- package/src/runtime/local-actor-identity.test.ts +108 -0
- package/src/runtime/local-actor-identity.ts +27 -20
- package/src/runtime/member-verdict-cache.ts +0 -0
- package/src/runtime/routes/__tests__/contact-routes.test.ts +100 -7
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +1 -2
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +2 -1
- package/src/runtime/routes/contact-routes.ts +40 -25
- package/src/runtime/routes/conversation-list-routes.ts +1 -29
- package/src/runtime/routes/conversation-routes.ts +27 -7
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -10
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -8
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +19 -0
- package/src/runtime/routes/settings-routes.ts +8 -3
- package/src/runtime/services/conversation-serializer.ts +6 -49
- package/src/runtime/slack-block-formatting.ts +0 -15
- package/src/runtime/trust-verdict-consumer.ts +36 -41
- package/src/subagent/__tests__/consult-prompt.test.ts +35 -0
- package/src/{plugins/defaults/advisor/__tests__/transcript.test.ts → subagent/__tests__/consult-transcript.test.ts} +47 -10
- package/src/{plugins/defaults/advisor/steering.ts → subagent/consult-prompt.ts} +17 -39
- package/src/{plugins/defaults/advisor/transcript.ts → subagent/consult-transcript.ts} +18 -8
- package/src/subagent/index.ts +1 -1
- package/src/subagent/manager.ts +245 -33
- package/src/subagent/types.ts +8 -1
- package/src/tools/registry.ts +10 -3
- package/src/tools/subagent/consult-deadline.ts +49 -0
- package/src/tools/subagent/spawn.ts +234 -5
- package/src/util/logger.ts +9 -0
- package/src/util/platform.ts +14 -0
- package/src/workspace/migrations/031-drop-user-md.ts +232 -148
- package/src/workspace/migrations/112-remove-advisor-callsite-override.ts +64 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +0 -56
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +0 -43
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +0 -137
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -314
- package/src/plugins/defaults/advisor/__tests__/context-pack-gating.test.ts +0 -106
- package/src/plugins/defaults/advisor/__tests__/context-pack.test.ts +0 -60
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +0 -138
- package/src/plugins/defaults/advisor/advisor-gate.ts +0 -29
- package/src/plugins/defaults/advisor/advisor-state-store.ts +0 -94
- package/src/plugins/defaults/advisor/config.ts +0 -21
- package/src/plugins/defaults/advisor/consult.ts +0 -197
- package/src/plugins/defaults/advisor/context-pack.ts +0 -288
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +0 -34
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +0 -30
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +0 -19
- package/src/plugins/defaults/advisor/package.json +0 -14
- package/src/plugins/defaults/advisor/tools/advisor.ts +0 -92
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test-only member seeding.
|
|
3
|
+
*
|
|
4
|
+
* Production identity upserts no longer write ACL columns (gateway-owned).
|
|
5
|
+
* Tests that simulate gateway-resolved members stamp the ACL columns directly
|
|
6
|
+
* and warm the member-verdict cache so verdict synthesis and the sync trust
|
|
7
|
+
* fallback resolve the intended trust.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { eq } from "drizzle-orm";
|
|
11
|
+
|
|
12
|
+
import { isChannelId } from "../../channels/types.js";
|
|
13
|
+
import { upsertContact } from "../../contacts/contact-store.js";
|
|
14
|
+
import type {
|
|
15
|
+
ChannelPolicy,
|
|
16
|
+
ChannelStatus,
|
|
17
|
+
ContactRole,
|
|
18
|
+
} from "../../contacts/types.js";
|
|
19
|
+
import { getDb } from "../../memory/db-connection.js";
|
|
20
|
+
import { contactChannels, contacts } from "../../memory/schema.js";
|
|
21
|
+
import { setMemberVerdict } from "../../runtime/member-verdict-cache.js";
|
|
22
|
+
|
|
23
|
+
export function seedContactChannel(params: {
|
|
24
|
+
sourceChannel: string;
|
|
25
|
+
externalUserId?: string;
|
|
26
|
+
externalChatId?: string;
|
|
27
|
+
displayName?: string;
|
|
28
|
+
username?: string;
|
|
29
|
+
contactId?: string;
|
|
30
|
+
role?: ContactRole;
|
|
31
|
+
status?: ChannelStatus;
|
|
32
|
+
policy?: string;
|
|
33
|
+
verifiedAt?: number | null;
|
|
34
|
+
verifiedVia?: string | null;
|
|
35
|
+
inviteId?: string | null;
|
|
36
|
+
revokedReason?: string | null;
|
|
37
|
+
blockedReason?: string | null;
|
|
38
|
+
principalId?: string | null;
|
|
39
|
+
}): { contactId: string; channelId: string } {
|
|
40
|
+
const address = params.externalUserId ?? params.externalChatId;
|
|
41
|
+
if (!address) throw new Error("seedContactChannel requires an address");
|
|
42
|
+
|
|
43
|
+
const contact = upsertContact({
|
|
44
|
+
id: params.contactId,
|
|
45
|
+
displayName: params.displayName ?? address,
|
|
46
|
+
channels: [
|
|
47
|
+
{
|
|
48
|
+
type: params.sourceChannel,
|
|
49
|
+
address,
|
|
50
|
+
externalChatId: params.externalChatId ?? null,
|
|
51
|
+
inviteId: params.inviteId ?? null,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
reassignConflictingChannels: !!params.contactId,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const db = getDb();
|
|
58
|
+
if (params.role !== undefined || params.principalId !== undefined) {
|
|
59
|
+
const set: Record<string, unknown> = {};
|
|
60
|
+
if (params.role !== undefined) set.role = params.role;
|
|
61
|
+
if (params.principalId !== undefined) set.principalId = params.principalId;
|
|
62
|
+
db.update(contacts).set(set).where(eq(contacts.id, contact.id)).run();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const channel = contact.channels.find(
|
|
66
|
+
(ch) => ch.type === params.sourceChannel,
|
|
67
|
+
)!;
|
|
68
|
+
const aclSet: Record<string, unknown> = { updatedAt: Date.now() };
|
|
69
|
+
if (params.status !== undefined) aclSet.status = params.status;
|
|
70
|
+
if (params.policy !== undefined) aclSet.policy = params.policy;
|
|
71
|
+
if (params.verifiedAt !== undefined) aclSet.verifiedAt = params.verifiedAt;
|
|
72
|
+
if (params.verifiedVia !== undefined) aclSet.verifiedVia = params.verifiedVia;
|
|
73
|
+
if (params.revokedReason !== undefined)
|
|
74
|
+
aclSet.revokedReason = params.revokedReason;
|
|
75
|
+
if (params.blockedReason !== undefined)
|
|
76
|
+
aclSet.blockedReason = params.blockedReason;
|
|
77
|
+
db.update(contactChannels)
|
|
78
|
+
.set(aclSet)
|
|
79
|
+
.where(eq(contactChannels.id, channel.id))
|
|
80
|
+
.run();
|
|
81
|
+
|
|
82
|
+
// Warm the verdict cache so the sync trust fallback resolves this member, as
|
|
83
|
+
// a gateway verdict fetch would in production.
|
|
84
|
+
if (isChannelId(params.sourceChannel)) {
|
|
85
|
+
setMemberVerdict(params.sourceChannel, address, {
|
|
86
|
+
trustClass: params.role === "guardian" ? "guardian" : "unknown",
|
|
87
|
+
canonicalSenderId: address,
|
|
88
|
+
contactId: contact.id,
|
|
89
|
+
channelId: channel.id,
|
|
90
|
+
status: (params.status ?? "active") as ChannelStatus,
|
|
91
|
+
policy: (params.policy ?? "allow") as ChannelPolicy,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return { contactId: contact.id, channelId: channel.id };
|
|
96
|
+
}
|
|
@@ -76,6 +76,9 @@ mock.module("../ipc/gateway-client.js", () => ({
|
|
|
76
76
|
params?: Record<string, unknown>,
|
|
77
77
|
) => {
|
|
78
78
|
gatewayIpcCalls.push({ method, params });
|
|
79
|
+
if (method === "contacts_get_rich") {
|
|
80
|
+
return richContactForId(params?.contactId as string);
|
|
81
|
+
}
|
|
79
82
|
if (method === "record_invite_redemption") {
|
|
80
83
|
return { ok: true, updated: true, mirrored: true };
|
|
81
84
|
}
|
|
@@ -86,15 +89,88 @@ mock.module("../ipc/gateway-client.js", () => ({
|
|
|
86
89
|
},
|
|
87
90
|
}));
|
|
88
91
|
|
|
92
|
+
// Serves contacts_get_rich (the gateway ACL read backing the gate-status
|
|
93
|
+
// fallback) from the seeded local contact, so gate resolution sources status
|
|
94
|
+
// from the gateway path rather than the local channel column.
|
|
95
|
+
function richContactForId(contactId: string | undefined) {
|
|
96
|
+
if (!contactId) return undefined;
|
|
97
|
+
const contact = getContact(contactId);
|
|
98
|
+
if (!contact) return undefined;
|
|
99
|
+
// ACL columns live on the still-present DB rows, not the slimmed interfaces;
|
|
100
|
+
// read them raw to build the gateway-rich response the production read parses.
|
|
101
|
+
const contactRole = (
|
|
102
|
+
getSqlite()
|
|
103
|
+
.query("SELECT role FROM contacts WHERE id = ?")
|
|
104
|
+
.get(contact.id) as { role: string } | undefined
|
|
105
|
+
)?.role;
|
|
106
|
+
return {
|
|
107
|
+
ok: true,
|
|
108
|
+
contact: {
|
|
109
|
+
id: contact.id,
|
|
110
|
+
displayName: contact.displayName,
|
|
111
|
+
role: contactRole ?? "contact",
|
|
112
|
+
interactionCount: contact.interactionCount,
|
|
113
|
+
createdAt: contact.createdAt,
|
|
114
|
+
updatedAt: contact.updatedAt,
|
|
115
|
+
channels: contact.channels.map((c) => {
|
|
116
|
+
const acl = rawChannelAcl(c.id);
|
|
117
|
+
return {
|
|
118
|
+
id: c.id,
|
|
119
|
+
contactId: c.contactId,
|
|
120
|
+
type: c.type,
|
|
121
|
+
address: c.address,
|
|
122
|
+
isPrimary: c.isPrimary,
|
|
123
|
+
externalUserId: c.externalChatId,
|
|
124
|
+
status: acl.status,
|
|
125
|
+
policy: acl.policy,
|
|
126
|
+
verifiedAt: acl.verifiedAt,
|
|
127
|
+
verifiedVia: acl.verifiedVia,
|
|
128
|
+
lastSeenAt: acl.lastSeenAt,
|
|
129
|
+
interactionCount: acl.interactionCount,
|
|
130
|
+
lastInteraction: acl.lastInteraction,
|
|
131
|
+
revokedReason: acl.revokedReason,
|
|
132
|
+
blockedReason: acl.blockedReason,
|
|
133
|
+
};
|
|
134
|
+
}),
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Read a channel's ACL columns straight off the still-present DB row. */
|
|
140
|
+
function rawChannelAcl(channelId: string) {
|
|
141
|
+
return getSqlite()
|
|
142
|
+
.query(
|
|
143
|
+
`SELECT status, policy, verified_at AS verifiedAt, verified_via AS verifiedVia,
|
|
144
|
+
last_seen_at AS lastSeenAt, interaction_count AS interactionCount,
|
|
145
|
+
last_interaction AS lastInteraction, revoked_reason AS revokedReason,
|
|
146
|
+
blocked_reason AS blockedReason
|
|
147
|
+
FROM contact_channels WHERE id = ?`,
|
|
148
|
+
)
|
|
149
|
+
.get(channelId) as {
|
|
150
|
+
status: string;
|
|
151
|
+
policy: string;
|
|
152
|
+
verifiedAt: number | null;
|
|
153
|
+
verifiedVia: string | null;
|
|
154
|
+
lastSeenAt: number | null;
|
|
155
|
+
interactionCount: number;
|
|
156
|
+
lastInteraction: number | null;
|
|
157
|
+
revokedReason: string | null;
|
|
158
|
+
blockedReason: string | null;
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
89
162
|
import {
|
|
90
163
|
findContactChannel,
|
|
164
|
+
getContact,
|
|
91
165
|
upsertContact,
|
|
92
166
|
} from "../contacts/contact-store.js";
|
|
93
|
-
import {
|
|
94
|
-
import { getDb } from "../memory/db-connection.js";
|
|
167
|
+
import { getDb, getSqlite } from "../memory/db-connection.js";
|
|
95
168
|
import { initializeDb } from "../memory/db-init.js";
|
|
96
169
|
import { createInvite, revokeInvite } from "../memory/invite-store.js";
|
|
97
|
-
import {
|
|
170
|
+
import {
|
|
171
|
+
handleChannelInbound,
|
|
172
|
+
seedContactChannel,
|
|
173
|
+
} from "./helpers/channel-test-adapter.js";
|
|
98
174
|
|
|
99
175
|
await initializeDb();
|
|
100
176
|
|
|
@@ -192,13 +268,13 @@ describe("inbound invite redemption intercept", () => {
|
|
|
192
268
|
expect(json.memberId).toEqual(expect.any(String));
|
|
193
269
|
expect(json.denied).toBeUndefined();
|
|
194
270
|
|
|
195
|
-
//
|
|
271
|
+
// The local mirror persists the member identity row; the gateway owns the
|
|
272
|
+
// active ACL verdict.
|
|
196
273
|
const result = findContactChannel({
|
|
197
274
|
channelType: "telegram",
|
|
198
275
|
address: "user-invite-123",
|
|
199
276
|
});
|
|
200
277
|
expect(result).not.toBeNull();
|
|
201
|
-
expect(result!.channel.status).toBe("active");
|
|
202
278
|
|
|
203
279
|
// The activation is written to the gateway via upsert_verified_channel.
|
|
204
280
|
expect(
|
|
@@ -337,7 +413,7 @@ describe("inbound invite redemption intercept", () => {
|
|
|
337
413
|
|
|
338
414
|
test("existing active member sending normal message is unaffected", async () => {
|
|
339
415
|
// Pre-create an active member
|
|
340
|
-
|
|
416
|
+
seedContactChannel({
|
|
341
417
|
sourceChannel: "telegram",
|
|
342
418
|
externalUserId: "user-active-member",
|
|
343
419
|
externalChatId: "chat-active",
|
|
@@ -391,7 +467,7 @@ describe("inbound invite redemption intercept", () => {
|
|
|
391
467
|
});
|
|
392
468
|
|
|
393
469
|
// Pre-create an active member that will click the invite link
|
|
394
|
-
|
|
470
|
+
seedContactChannel({
|
|
395
471
|
sourceChannel: "telegram",
|
|
396
472
|
externalUserId: "user-already-active",
|
|
397
473
|
externalChatId: "chat-invite-test",
|
|
@@ -414,7 +490,7 @@ describe("inbound invite redemption intercept", () => {
|
|
|
414
490
|
test("reactivation via invite preserves existing guardian-managed member display name", async () => {
|
|
415
491
|
// Pre-create a revoked member named "Jeff" — the invite should preserve
|
|
416
492
|
// that guardian-assigned name rather than overwriting with the Telegram name.
|
|
417
|
-
|
|
493
|
+
seedContactChannel({
|
|
418
494
|
sourceChannel: "telegram",
|
|
419
495
|
externalUserId: "user-invite-123",
|
|
420
496
|
externalChatId: "chat-invite-test",
|
|
@@ -423,7 +499,7 @@ describe("inbound invite redemption intercept", () => {
|
|
|
423
499
|
displayName: "Jeff",
|
|
424
500
|
});
|
|
425
501
|
|
|
426
|
-
// Look up the contact that
|
|
502
|
+
// Look up the contact that the seed created so we can use
|
|
427
503
|
// its ID as the invite's contactId (satisfies the FK constraint).
|
|
428
504
|
const existing = findContactChannel({
|
|
429
505
|
channelType: "telegram",
|
|
@@ -453,7 +529,8 @@ describe("inbound invite redemption intercept", () => {
|
|
|
453
529
|
externalChatId: "chat-invite-test",
|
|
454
530
|
});
|
|
455
531
|
expect(result).not.toBeNull();
|
|
456
|
-
|
|
532
|
+
// The gateway owns reactivation; the local mirror preserves the
|
|
533
|
+
// guardian-assigned display name rather than the raw platform identity.
|
|
457
534
|
expect(result!.contact.displayName).toBe("Jeff");
|
|
458
535
|
});
|
|
459
536
|
});
|