@vellumai/assistant 0.10.3-staging.2 → 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,129 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
5
|
+
|
|
6
|
+
import { getSqliteFrom } from "../memory/db-connection.js";
|
|
7
|
+
import { migrateCreateCompactionEvents } from "../memory/migrations/302-create-compaction-events.js";
|
|
8
|
+
import * as schema from "../memory/schema.js";
|
|
9
|
+
|
|
10
|
+
interface EventRow {
|
|
11
|
+
conversation_id: string;
|
|
12
|
+
compacted_at: number;
|
|
13
|
+
summary: string;
|
|
14
|
+
compacted_message_count: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function createTestDb() {
|
|
18
|
+
const sqlite = new Database(":memory:");
|
|
19
|
+
sqlite.exec("PRAGMA journal_mode=WAL");
|
|
20
|
+
sqlite.exec("PRAGMA foreign_keys = ON");
|
|
21
|
+
return drizzle(sqlite, { schema });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function bootstrapCheckpointsTable(raw: Database): void {
|
|
25
|
+
raw.exec(/*sql*/ `
|
|
26
|
+
CREATE TABLE IF NOT EXISTS memory_checkpoints (
|
|
27
|
+
key TEXT PRIMARY KEY,
|
|
28
|
+
value TEXT NOT NULL,
|
|
29
|
+
updated_at INTEGER NOT NULL
|
|
30
|
+
)
|
|
31
|
+
`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function bootstrapConversations(raw: Database): void {
|
|
35
|
+
raw.exec(/*sql*/ `
|
|
36
|
+
CREATE TABLE conversations (
|
|
37
|
+
id TEXT PRIMARY KEY,
|
|
38
|
+
title TEXT,
|
|
39
|
+
created_at INTEGER NOT NULL,
|
|
40
|
+
updated_at INTEGER NOT NULL,
|
|
41
|
+
context_summary TEXT,
|
|
42
|
+
context_compacted_message_count INTEGER NOT NULL DEFAULT 0,
|
|
43
|
+
context_compacted_at INTEGER
|
|
44
|
+
)
|
|
45
|
+
`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function insertConversation(
|
|
49
|
+
raw: Database,
|
|
50
|
+
id: string,
|
|
51
|
+
summary: string | null,
|
|
52
|
+
count: number,
|
|
53
|
+
compactedAt: number | null,
|
|
54
|
+
): void {
|
|
55
|
+
raw
|
|
56
|
+
.query(
|
|
57
|
+
/*sql*/ `
|
|
58
|
+
INSERT INTO conversations (
|
|
59
|
+
id, title, created_at, updated_at,
|
|
60
|
+
context_summary, context_compacted_message_count, context_compacted_at
|
|
61
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
62
|
+
`,
|
|
63
|
+
)
|
|
64
|
+
.run(id, id, 1000, 2000, summary, count, compactedAt);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function getEvents(raw: Database): EventRow[] {
|
|
68
|
+
return raw
|
|
69
|
+
.query(
|
|
70
|
+
/*sql*/ `
|
|
71
|
+
SELECT conversation_id, compacted_at, summary, compacted_message_count
|
|
72
|
+
FROM conversation_compaction_events
|
|
73
|
+
ORDER BY conversation_id
|
|
74
|
+
`,
|
|
75
|
+
)
|
|
76
|
+
.all() as EventRow[];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
describe("migrateCreateCompactionEvents", () => {
|
|
80
|
+
test("creates the ledger and backfills one event per compacted conversation", () => {
|
|
81
|
+
const db = createTestDb();
|
|
82
|
+
const raw = getSqliteFrom(db);
|
|
83
|
+
bootstrapCheckpointsTable(raw);
|
|
84
|
+
bootstrapConversations(raw);
|
|
85
|
+
|
|
86
|
+
insertConversation(raw, "compacted", "Summary text", 5, 3000);
|
|
87
|
+
insertConversation(raw, "uncompacted", null, 0, null);
|
|
88
|
+
// count>0 but never actually compacted (no timestamp) — must be skipped.
|
|
89
|
+
insertConversation(raw, "count-without-at", "Orphan summary", 4, null);
|
|
90
|
+
|
|
91
|
+
migrateCreateCompactionEvents(db);
|
|
92
|
+
|
|
93
|
+
expect(getEvents(raw)).toEqual([
|
|
94
|
+
{
|
|
95
|
+
conversation_id: "compacted",
|
|
96
|
+
compacted_at: 3000,
|
|
97
|
+
summary: "Summary text",
|
|
98
|
+
compacted_message_count: 5,
|
|
99
|
+
},
|
|
100
|
+
]);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("is idempotent — re-running does not duplicate backfilled events", () => {
|
|
104
|
+
const db = createTestDb();
|
|
105
|
+
const raw = getSqliteFrom(db);
|
|
106
|
+
bootstrapCheckpointsTable(raw);
|
|
107
|
+
bootstrapConversations(raw);
|
|
108
|
+
insertConversation(raw, "compacted", "Summary text", 5, 3000);
|
|
109
|
+
|
|
110
|
+
migrateCreateCompactionEvents(db);
|
|
111
|
+
migrateCreateCompactionEvents(db);
|
|
112
|
+
|
|
113
|
+
expect(getEvents(raw)).toHaveLength(1);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("the NOT EXISTS guard prevents duplicates even if the checkpoint is lost", () => {
|
|
117
|
+
const db = createTestDb();
|
|
118
|
+
const raw = getSqliteFrom(db);
|
|
119
|
+
bootstrapCheckpointsTable(raw);
|
|
120
|
+
bootstrapConversations(raw);
|
|
121
|
+
insertConversation(raw, "compacted", "Summary text", 5, 3000);
|
|
122
|
+
|
|
123
|
+
migrateCreateCompactionEvents(db);
|
|
124
|
+
raw.exec(`DELETE FROM memory_checkpoints`);
|
|
125
|
+
migrateCreateCompactionEvents(db);
|
|
126
|
+
|
|
127
|
+
expect(getEvents(raw)).toHaveLength(1);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -23,7 +23,6 @@ mock.module("../config/env.js", () => ({
|
|
|
23
23
|
|
|
24
24
|
import { eq } from "drizzle-orm";
|
|
25
25
|
|
|
26
|
-
import { upsertContactChannel } from "../contacts/contacts-write.js";
|
|
27
26
|
import { getDb } from "../memory/db-connection.js";
|
|
28
27
|
import { initializeDb } from "../memory/db-init.js";
|
|
29
28
|
import { linkMessage, recordInbound } from "../memory/delivery-crud.js";
|
|
@@ -33,7 +32,10 @@ import {
|
|
|
33
32
|
writeSlackMetadata,
|
|
34
33
|
} from "../messaging/providers/slack/message-metadata.js";
|
|
35
34
|
import { _setDeleteLookupConfigForTests } from "../runtime/routes/inbound-message-handler.js";
|
|
36
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
handleChannelInbound,
|
|
37
|
+
seedContactChannel,
|
|
38
|
+
} from "./helpers/channel-test-adapter.js";
|
|
37
39
|
|
|
38
40
|
await initializeDb();
|
|
39
41
|
|
|
@@ -56,7 +58,7 @@ function resetState(): void {
|
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
function seedActiveDeleteActor(externalChatId: string): void {
|
|
59
|
-
|
|
61
|
+
seedContactChannel({
|
|
60
62
|
sourceChannel: "slack",
|
|
61
63
|
externalUserId: SLACK_DELETE_ACTOR_ID,
|
|
62
64
|
externalChatId,
|
|
@@ -78,7 +78,6 @@ import {
|
|
|
78
78
|
saveRawConfig,
|
|
79
79
|
setNestedValue,
|
|
80
80
|
} from "../config/loader.js";
|
|
81
|
-
import { upsertContactChannel } from "../contacts/contacts-write.js";
|
|
82
81
|
import { getDb } from "../memory/db-connection.js";
|
|
83
82
|
import { initializeDb } from "../memory/db-init.js";
|
|
84
83
|
import { messages } from "../memory/schema/conversations.js";
|
|
@@ -86,7 +85,10 @@ import {
|
|
|
86
85
|
readSlackMetadata,
|
|
87
86
|
type SlackMessageMetadata,
|
|
88
87
|
} from "../messaging/providers/slack/message-metadata.js";
|
|
89
|
-
import {
|
|
88
|
+
import {
|
|
89
|
+
handleChannelInbound,
|
|
90
|
+
seedContactChannel,
|
|
91
|
+
} from "./helpers/channel-test-adapter.js";
|
|
90
92
|
|
|
91
93
|
await initializeDb();
|
|
92
94
|
|
|
@@ -117,7 +119,7 @@ function setConfiguredSlackBotUserId(botUserId: string): void {
|
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
function seedActiveMember(): void {
|
|
120
|
-
|
|
122
|
+
seedContactChannel({
|
|
121
123
|
sourceChannel: "slack",
|
|
122
124
|
externalUserId: SLACK_DM_USER_ID,
|
|
123
125
|
externalChatId: SLACK_DM_CHANNEL_ID,
|
|
@@ -128,7 +130,7 @@ function seedActiveMember(): void {
|
|
|
128
130
|
}
|
|
129
131
|
|
|
130
132
|
function seedSlackGuardian(): void {
|
|
131
|
-
|
|
133
|
+
seedContactChannel({
|
|
132
134
|
sourceChannel: "slack",
|
|
133
135
|
externalUserId: SLACK_DM_USER_ID,
|
|
134
136
|
externalChatId: SLACK_DM_CHANNEL_ID,
|
|
@@ -21,17 +21,13 @@ mock.module("../channels/config.js", () => ({
|
|
|
21
21
|
|
|
22
22
|
// Guardian connectivity is resolved from the gateway pull. No active guardian
|
|
23
23
|
// binding ⇒ binding-based channels (telegram) are not reported connected.
|
|
24
|
+
// Guardian connectivity resolves solely from the gateway delivery; an empty
|
|
25
|
+
// list ⇒ telegram stays disconnected.
|
|
24
26
|
mock.module("../contacts/guardian-delivery-reader.js", () => ({
|
|
25
27
|
getGuardianDelivery: async () => [],
|
|
26
28
|
guardianForChannel: () => undefined,
|
|
27
29
|
}));
|
|
28
30
|
|
|
29
|
-
// connectivity falls back to the local contacts read on a per-channel gateway
|
|
30
|
-
// no-match; no local binding ⇒ telegram stays disconnected.
|
|
31
|
-
mock.module("../contacts/contact-store.js", () => ({
|
|
32
|
-
findGuardianForChannel: () => null,
|
|
33
|
-
}));
|
|
34
|
-
|
|
35
31
|
mock.module("../notifications/adapters/macos.js", () => ({
|
|
36
32
|
VellumAdapter: class {
|
|
37
33
|
constructor(_broadcastFn: unknown) {}
|
|
@@ -17,7 +17,7 @@ mock.module("../contacts/guardian-delivery-reader.js", () => ({
|
|
|
17
17
|
list.find((g) => g.channelType === channelType && g.status === "active"),
|
|
18
18
|
}));
|
|
19
19
|
|
|
20
|
-
import {
|
|
20
|
+
import { findContactByAddress } from "../contacts/contact-store.js";
|
|
21
21
|
import { getDb } from "../memory/db-connection.js";
|
|
22
22
|
import { initializeDb } from "../memory/db-init.js";
|
|
23
23
|
import { healGuardianBindingDrift } from "../runtime/guardian-vellum-migration.js";
|
|
@@ -31,17 +31,31 @@ function resetTables(): void {
|
|
|
31
31
|
db.run("DELETE FROM contacts");
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
/**
|
|
35
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Gateway delivery for the vellum guardian. `principalId` is the gateway-owned
|
|
36
|
+
* binding principal; `address` is the local channel address the heal resolves
|
|
37
|
+
* its write target by (defaults to the principal for same-principal bindings).
|
|
38
|
+
*/
|
|
39
|
+
function gatewayGuardian(
|
|
40
|
+
principalId: string,
|
|
41
|
+
address: string = principalId,
|
|
42
|
+
): GuardianDelivery {
|
|
36
43
|
return {
|
|
37
44
|
channelType: "vellum",
|
|
38
45
|
contactId: "guardian-contact",
|
|
39
46
|
principalId,
|
|
40
|
-
address
|
|
47
|
+
address,
|
|
41
48
|
status: "active",
|
|
42
49
|
};
|
|
43
50
|
}
|
|
44
51
|
|
|
52
|
+
/** Read the local vellum guardian channel/contact by its channel address. */
|
|
53
|
+
function localVellumGuardian(address: string) {
|
|
54
|
+
const contact = findContactByAddress("vellum", address);
|
|
55
|
+
const channel = contact?.channels.find((c) => c.type === "vellum");
|
|
56
|
+
return contact && channel ? { contact, channel } : null;
|
|
57
|
+
}
|
|
58
|
+
|
|
45
59
|
describe("healGuardianBindingDrift", () => {
|
|
46
60
|
beforeEach(() => {
|
|
47
61
|
resetTables();
|
|
@@ -63,18 +77,17 @@ describe("healGuardianBindingDrift", () => {
|
|
|
63
77
|
const healed = await healGuardianBindingDrift("vellum-principal-old-uuid");
|
|
64
78
|
expect(healed).toBe(true);
|
|
65
79
|
|
|
66
|
-
//
|
|
67
|
-
|
|
80
|
+
// The heal repairs the channel identity address to match the JWT. The
|
|
81
|
+
// principalId column is gateway-owned and no longer written locally.
|
|
82
|
+
const guardian = localVellumGuardian("vellum-principal-old-uuid");
|
|
68
83
|
expect(guardian).not.toBeNull();
|
|
69
|
-
expect(guardian!.contact.principalId).toBe("vellum-principal-old-uuid");
|
|
70
84
|
expect(guardian!.channel.address).toBe("vellum-principal-old-uuid");
|
|
71
85
|
});
|
|
72
86
|
|
|
73
|
-
test("repairs the
|
|
74
|
-
// Gateway
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
// a stale row must be repaired or the actor stays classified `unknown`.
|
|
87
|
+
test("repairs the local mirror toward the JWT when the gateway diverges", async () => {
|
|
88
|
+
// Gateway principal diverges from the incoming JWT — the drift signal. The
|
|
89
|
+
// /v1/messages trust path still reads the local mirror in this plan, so the
|
|
90
|
+
// mirror is repaired toward the JWT or the actor stays `unknown`.
|
|
78
91
|
createGuardianBinding({
|
|
79
92
|
channel: "vellum",
|
|
80
93
|
guardianExternalUserId: "vellum-principal-stale-local",
|
|
@@ -82,15 +95,19 @@ describe("healGuardianBindingDrift", () => {
|
|
|
82
95
|
guardianPrincipalId: "vellum-principal-stale-local",
|
|
83
96
|
verifiedVia: "startup-migration",
|
|
84
97
|
});
|
|
85
|
-
|
|
98
|
+
// Gateway address matches the local channel address so the heal can resolve
|
|
99
|
+
// its local write target; only the gateway principal has drifted.
|
|
100
|
+
mockGuardians = [
|
|
101
|
+
gatewayGuardian("vellum-principal-gateway", "vellum-principal-stale-local"),
|
|
102
|
+
];
|
|
86
103
|
|
|
87
104
|
const healed = await healGuardianBindingDrift("vellum-principal-jwt");
|
|
88
105
|
expect(healed).toBe(true);
|
|
89
106
|
|
|
90
|
-
//
|
|
91
|
-
// resolution classifies the actor as guardian rather than
|
|
92
|
-
|
|
93
|
-
|
|
107
|
+
// The local mirror's identity address now matches the JWT, so a subsequent
|
|
108
|
+
// local trust resolution classifies the actor as guardian rather than
|
|
109
|
+
// unknown. The principalId column is gateway-owned and not written locally.
|
|
110
|
+
const guardian = localVellumGuardian("vellum-principal-jwt");
|
|
94
111
|
expect(guardian!.channel.address).toBe("vellum-principal-jwt");
|
|
95
112
|
});
|
|
96
113
|
|
|
@@ -122,12 +139,13 @@ describe("healGuardianBindingDrift", () => {
|
|
|
122
139
|
const healed = await healGuardianBindingDrift("platform-user-12345");
|
|
123
140
|
expect(healed).toBe(false);
|
|
124
141
|
|
|
125
|
-
// Guardian unchanged
|
|
126
|
-
|
|
127
|
-
|
|
142
|
+
// Guardian unchanged: the local channel identity address still resolves to
|
|
143
|
+
// the original binding (principalId is gateway-owned, no longer local).
|
|
144
|
+
const guardian = localVellumGuardian("vellum-principal-aaa");
|
|
145
|
+
expect(guardian!.channel.address).toBe("vellum-principal-aaa");
|
|
128
146
|
});
|
|
129
147
|
|
|
130
|
-
test("refuses to heal when
|
|
148
|
+
test("refuses to heal when the gateway principal lacks vellum-principal- prefix", async () => {
|
|
131
149
|
createGuardianBinding({
|
|
132
150
|
channel: "vellum",
|
|
133
151
|
guardianExternalUserId: "verified-phone-guardian",
|
|
@@ -141,8 +159,10 @@ describe("healGuardianBindingDrift", () => {
|
|
|
141
159
|
const healed = await healGuardianBindingDrift("vellum-principal-attacker");
|
|
142
160
|
expect(healed).toBe(false);
|
|
143
161
|
|
|
144
|
-
|
|
145
|
-
|
|
162
|
+
// Guardian unchanged: the local channel identity address still resolves to
|
|
163
|
+
// the original binding (principalId is gateway-owned, no longer local).
|
|
164
|
+
const guardian = localVellumGuardian("verified-phone-guardian");
|
|
165
|
+
expect(guardian!.channel.address).toBe("verified-phone-guardian");
|
|
146
166
|
});
|
|
147
167
|
|
|
148
168
|
test("returns false when gateway reports no guardian binding", async () => {
|
|
@@ -24,11 +24,56 @@ mock.module("../config/loader.js", () => ({
|
|
|
24
24
|
}),
|
|
25
25
|
}));
|
|
26
26
|
|
|
27
|
-
// The pending_question request principal is resolved via the
|
|
28
|
-
// the Vellum actor uses —
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
27
|
+
// The pending_question request principal is resolved via the gateway guardian
|
|
28
|
+
// delivery for the vellum channel — the SAME source the Vellum actor uses — so
|
|
29
|
+
// the stamped principal always equals the submitting actor principal. The real
|
|
30
|
+
// contacts DB is seeded in resetTables(); the reader mock below derives the
|
|
31
|
+
// gateway delivery from that DB binding so tests model drift / missing guardian
|
|
32
|
+
// by reseeding or clearing the local binding directly.
|
|
33
|
+
mock.module("../contacts/guardian-delivery-reader.js", () => ({
|
|
34
|
+
getGuardianDelivery: async (input?: { channelTypes?: string[] }) => {
|
|
35
|
+
const { getDb } = await import("../memory/db-connection.js");
|
|
36
|
+
const { contacts, contactChannels } = await import("../memory/schema.js");
|
|
37
|
+
const { and, eq } = await import("drizzle-orm");
|
|
38
|
+
const channels = input?.channelTypes ?? [];
|
|
39
|
+
return channels
|
|
40
|
+
.map((channelType) => {
|
|
41
|
+
const row = getDb()
|
|
42
|
+
.select({ contact: contacts, channel: contactChannels })
|
|
43
|
+
.from(contacts)
|
|
44
|
+
.innerJoin(
|
|
45
|
+
contactChannels,
|
|
46
|
+
eq(contacts.id, contactChannels.contactId),
|
|
47
|
+
)
|
|
48
|
+
.where(
|
|
49
|
+
and(
|
|
50
|
+
eq(contacts.role, "guardian"),
|
|
51
|
+
eq(contactChannels.type, channelType),
|
|
52
|
+
eq(contactChannels.status, "active"),
|
|
53
|
+
),
|
|
54
|
+
)
|
|
55
|
+
.get();
|
|
56
|
+
if (!row) return null;
|
|
57
|
+
return {
|
|
58
|
+
channelType,
|
|
59
|
+
contactId: row.contact.id,
|
|
60
|
+
principalId: row.contact.principalId ?? null,
|
|
61
|
+
displayName: row.contact.displayName ?? null,
|
|
62
|
+
address: row.channel.address,
|
|
63
|
+
externalChatId: row.channel.externalChatId ?? null,
|
|
64
|
+
status: "active",
|
|
65
|
+
verifiedAt: row.channel.verifiedAt ?? null,
|
|
66
|
+
};
|
|
67
|
+
})
|
|
68
|
+
.filter((g) => g !== null);
|
|
69
|
+
},
|
|
70
|
+
guardianForChannel: (
|
|
71
|
+
list: Array<{ channelType: string; status: string }>,
|
|
72
|
+
channelType: string,
|
|
73
|
+
) =>
|
|
74
|
+
list.find((g) => g.channelType === channelType && g.status === "active"),
|
|
75
|
+
}));
|
|
76
|
+
|
|
32
77
|
const emitCalls: unknown[] = [];
|
|
33
78
|
let conversationCreatedFromMock: ConversationCreatedInfo | null = null;
|
|
34
79
|
let mockEmitResult: {
|
|
@@ -14,7 +14,6 @@ mock.module("../util/logger.js", () => ({
|
|
|
14
14
|
}),
|
|
15
15
|
}));
|
|
16
16
|
|
|
17
|
-
import { upsertContact } from "../contacts/contact-store.js";
|
|
18
17
|
import type { TrustContext } from "../daemon/trust-context.js";
|
|
19
18
|
import { getDb } from "../memory/db-connection.js";
|
|
20
19
|
import { initializeDb } from "../memory/db-init.js";
|
|
@@ -27,6 +26,7 @@ import {
|
|
|
27
26
|
} from "../runtime/trust-context-resolver.js";
|
|
28
27
|
import {
|
|
29
28
|
handleChannelInbound,
|
|
29
|
+
seedContactChannel,
|
|
30
30
|
setAdapterProcessMessage,
|
|
31
31
|
} from "./helpers/channel-test-adapter.js";
|
|
32
32
|
import { createGuardianBinding } from "./helpers/create-guardian-binding.js";
|
|
@@ -155,16 +155,12 @@ describe("inbound-message-handler trusted-contact interactivity", () => {
|
|
|
155
155
|
resetTables();
|
|
156
156
|
setAdapterProcessMessage(undefined);
|
|
157
157
|
// Insert a test contact so the contacts-based ACL lookup passes
|
|
158
|
-
|
|
158
|
+
seedContactChannel({
|
|
159
|
+
sourceChannel: "telegram",
|
|
160
|
+
externalUserId: "telegram-user-default",
|
|
159
161
|
displayName: "Test User",
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
type: "telegram",
|
|
163
|
-
address: "telegram-user-default",
|
|
164
|
-
status: "active",
|
|
165
|
-
policy: "allow",
|
|
166
|
-
},
|
|
167
|
-
],
|
|
162
|
+
status: "active",
|
|
163
|
+
policy: "allow",
|
|
168
164
|
});
|
|
169
165
|
});
|
|
170
166
|
|
|
@@ -62,11 +62,11 @@ mock.module("../../daemon/approval-generators.js", () => ({
|
|
|
62
62
|
}));
|
|
63
63
|
|
|
64
64
|
import type { TrustClass, TrustVerdict } from "@vellumai/gateway-client";
|
|
65
|
+
import { and, desc, eq } from "drizzle-orm";
|
|
65
66
|
|
|
66
|
-
import {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
} from "../../contacts/contact-store.js";
|
|
67
|
+
import { findContactChannel } from "../../contacts/contact-store.js";
|
|
68
|
+
import { getDb } from "../../memory/db-connection.js";
|
|
69
|
+
import { contactChannels, contacts } from "../../memory/schema.js";
|
|
70
70
|
import type {
|
|
71
71
|
ApprovalConversationGenerator,
|
|
72
72
|
ApprovalCopyGenerator,
|
|
@@ -147,6 +147,27 @@ function stampTrustVerdict(body: Record<string, unknown>): void {
|
|
|
147
147
|
body.sourceMetadata = { ...(meta ?? {}), trustVerdict: verdict };
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Local mirror of the gateway's active-guardian-channel resolution, querying
|
|
152
|
+
* the contact store directly (the production query now lives in the gateway).
|
|
153
|
+
*/
|
|
154
|
+
function localGuardianForChannel(channelType: string) {
|
|
155
|
+
const row = getDb()
|
|
156
|
+
.select({ contact: contacts, channel: contactChannels })
|
|
157
|
+
.from(contacts)
|
|
158
|
+
.innerJoin(contactChannels, eq(contacts.id, contactChannels.contactId))
|
|
159
|
+
.where(
|
|
160
|
+
and(
|
|
161
|
+
eq(contacts.role, "guardian"),
|
|
162
|
+
eq(contactChannels.type, channelType),
|
|
163
|
+
eq(contactChannels.status, "active"),
|
|
164
|
+
),
|
|
165
|
+
)
|
|
166
|
+
.orderBy(desc(contactChannels.verifiedAt))
|
|
167
|
+
.get();
|
|
168
|
+
return row ? { contact: row.contact, channel: row.channel } : null;
|
|
169
|
+
}
|
|
170
|
+
|
|
150
171
|
/** Local mirror of the gateway resolver, reading the daemon contact store. */
|
|
151
172
|
export function resolveLocalTrustVerdict(input: {
|
|
152
173
|
channelType: string;
|
|
@@ -161,18 +182,28 @@ export function resolveLocalTrustVerdict(input: {
|
|
|
161
182
|
address: input.actorExternalId,
|
|
162
183
|
})
|
|
163
184
|
: null;
|
|
164
|
-
const guardian =
|
|
185
|
+
const guardian = localGuardianForChannel(input.channelType);
|
|
165
186
|
|
|
166
187
|
const isGuardian =
|
|
167
188
|
!!guardian &&
|
|
168
189
|
!!canonicalSenderId &&
|
|
169
190
|
guardian.channel.address.toLowerCase() === canonicalSenderId.toLowerCase();
|
|
170
191
|
|
|
192
|
+
// ACL columns are no longer on the slimmed ContactChannel type; read the raw
|
|
193
|
+
// row (columns still exist) so this local mirror sees status/policy/verified.
|
|
194
|
+
const memberRow = member
|
|
195
|
+
? (getDb()
|
|
196
|
+
.select()
|
|
197
|
+
.from(contactChannels)
|
|
198
|
+
.where(eq(contactChannels.id, member.channel.id))
|
|
199
|
+
.get() ?? null)
|
|
200
|
+
: null;
|
|
201
|
+
|
|
171
202
|
let trustClass: TrustClass;
|
|
172
203
|
if (isGuardian) {
|
|
173
204
|
trustClass = "guardian";
|
|
174
|
-
} else if (
|
|
175
|
-
const status =
|
|
205
|
+
} else if (memberRow) {
|
|
206
|
+
const status = memberRow.status;
|
|
176
207
|
if (status === "active") trustClass = "trusted_contact";
|
|
177
208
|
else if (status === "unverified" || status === "pending")
|
|
178
209
|
trustClass = "unverified_contact";
|
|
@@ -191,22 +222,24 @@ export function resolveLocalTrustVerdict(input: {
|
|
|
191
222
|
verdict.guardianDisplayName = guardian.contact.displayName;
|
|
192
223
|
}
|
|
193
224
|
|
|
194
|
-
if (member) {
|
|
225
|
+
if (member && memberRow) {
|
|
195
226
|
verdict.contactId = member.channel.contactId;
|
|
196
227
|
verdict.channelId = member.channel.id;
|
|
197
228
|
verdict.type = member.channel.type;
|
|
198
229
|
verdict.address = member.channel.address;
|
|
199
230
|
verdict.externalChatId = member.channel.externalChatId;
|
|
200
|
-
verdict.status =
|
|
201
|
-
verdict.policy =
|
|
202
|
-
verdict.verifiedAt =
|
|
203
|
-
verdict.verifiedVia =
|
|
231
|
+
verdict.status = memberRow.status;
|
|
232
|
+
verdict.policy = memberRow.policy;
|
|
233
|
+
verdict.verifiedAt = memberRow.verifiedAt;
|
|
234
|
+
verdict.verifiedVia = memberRow.verifiedVia;
|
|
204
235
|
verdict.memberDisplayName = member.contact.displayName;
|
|
205
236
|
}
|
|
206
237
|
|
|
207
238
|
return verdict;
|
|
208
239
|
}
|
|
209
240
|
|
|
241
|
+
export { seedContactChannel } from "./seed-contact-channel.js";
|
|
242
|
+
|
|
210
243
|
// ---------------------------------------------------------------------------
|
|
211
244
|
// handleDeleteConversation adapter
|
|
212
245
|
// ---------------------------------------------------------------------------
|
|
@@ -4,11 +4,10 @@
|
|
|
4
4
|
* path was moved to the gateway.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import
|
|
8
|
-
import { upsertContact } from "../../contacts/contact-store.js";
|
|
7
|
+
import { getContact } from "../../contacts/contact-store.js";
|
|
9
8
|
import type { GuardianBinding } from "../../memory/channel-verification-sessions.js";
|
|
10
9
|
import { ensureGuardianPersonaFile } from "../../prompts/persona-resolver.js";
|
|
11
|
-
import {
|
|
10
|
+
import { seedContactChannel } from "./seed-contact-channel.js";
|
|
12
11
|
|
|
13
12
|
function parseDisplayNameFromMetadata(
|
|
14
13
|
metadataJson: string | null | undefined,
|
|
@@ -36,37 +35,30 @@ export function createGuardianBinding(params: {
|
|
|
36
35
|
verifiedVia?: string;
|
|
37
36
|
metadataJson?: string | null;
|
|
38
37
|
}): GuardianBinding {
|
|
39
|
-
const canonicalId =
|
|
40
|
-
canonicalizeInboundIdentity(
|
|
41
|
-
params.channel as ChannelId,
|
|
42
|
-
params.guardianExternalUserId,
|
|
43
|
-
) ?? params.guardianExternalUserId;
|
|
44
|
-
|
|
45
38
|
const displayName =
|
|
46
39
|
parseDisplayNameFromMetadata(params.metadataJson) ??
|
|
47
40
|
params.guardianExternalUserId;
|
|
48
41
|
|
|
49
|
-
|
|
42
|
+
// The production identity upsert no longer writes ACL columns (gateway-owned);
|
|
43
|
+
// seedContactChannel stamps the guardian ACL state directly so the local
|
|
44
|
+
// guardian-resolution reads still under test resolve this binding.
|
|
45
|
+
const { contactId } = seedContactChannel({
|
|
46
|
+
sourceChannel: params.channel,
|
|
47
|
+
externalUserId: params.guardianExternalUserId,
|
|
48
|
+
externalChatId: params.guardianDeliveryChatId,
|
|
50
49
|
displayName,
|
|
51
50
|
role: "guardian",
|
|
52
|
-
notes: "guardian",
|
|
53
51
|
principalId: params.guardianPrincipalId,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
address: canonicalId,
|
|
58
|
-
externalChatId: params.guardianDeliveryChatId,
|
|
59
|
-
status: "active",
|
|
60
|
-
verifiedAt: Date.now(),
|
|
61
|
-
verifiedVia: params.verifiedVia ?? "challenge",
|
|
62
|
-
},
|
|
63
|
-
],
|
|
52
|
+
status: "active",
|
|
53
|
+
verifiedAt: Date.now(),
|
|
54
|
+
verifiedVia: params.verifiedVia ?? "challenge",
|
|
64
55
|
});
|
|
65
56
|
|
|
66
57
|
// Seed persona file (mirrors gateway's production behavior)
|
|
67
|
-
|
|
58
|
+
const userFile = getContact(contactId)?.userFile;
|
|
59
|
+
if (userFile) {
|
|
68
60
|
try {
|
|
69
|
-
ensureGuardianPersonaFile(
|
|
61
|
+
ensureGuardianPersonaFile(userFile);
|
|
70
62
|
} catch {
|
|
71
63
|
// Tolerate filesystem failures in tests
|
|
72
64
|
}
|
|
@@ -56,6 +56,7 @@ export function createMockLoggerModule(
|
|
|
56
56
|
initLogger: () => {},
|
|
57
57
|
truncateForLog: (value: string) => value,
|
|
58
58
|
pruneOldLogFiles: () => 0,
|
|
59
|
+
getCurrentLogFilePath: () => "/tmp/assistant-test-mock.log",
|
|
59
60
|
LOG_FILE_PATTERN: /^assistant-(\d{4}-\d{2}-\d{2})\.log$/,
|
|
60
61
|
...overrides,
|
|
61
62
|
};
|