@vellumai/assistant 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +109 -0
- package/docs/architecture/memory.md +105 -0
- package/docs/skills.md +100 -0
- package/package.json +1 -1
- package/src/__tests__/archive-recall.test.ts +560 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +7 -0
- package/src/__tests__/conversation-agent-loop.test.ts +7 -0
- package/src/__tests__/conversation-clear-safety.test.ts +259 -0
- package/src/__tests__/conversation-memory-dirty-tail.test.ts +150 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +7 -0
- package/src/__tests__/conversation-switch-memory-reduction.test.ts +474 -0
- package/src/__tests__/conversation-wipe.test.ts +226 -0
- package/src/__tests__/db-memory-archive-migration.test.ts +372 -0
- package/src/__tests__/db-memory-brief-state-migration.test.ts +213 -0
- package/src/__tests__/db-memory-reducer-checkpoints.test.ts +273 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +3 -0
- package/src/__tests__/inline-command-runner.test.ts +311 -0
- package/src/__tests__/inline-skill-authoring-guard.test.ts +220 -0
- package/src/__tests__/inline-skill-load-permissions.test.ts +435 -0
- package/src/__tests__/list-messages-attachments.test.ts +96 -0
- package/src/__tests__/memory-brief-open-loops.test.ts +530 -0
- package/src/__tests__/memory-brief-time.test.ts +285 -0
- package/src/__tests__/memory-brief-wrapper.test.ts +311 -0
- package/src/__tests__/memory-chunk-archive.test.ts +400 -0
- package/src/__tests__/memory-chunk-dual-write.test.ts +453 -0
- package/src/__tests__/memory-episode-archive.test.ts +370 -0
- package/src/__tests__/memory-episode-dual-write.test.ts +626 -0
- package/src/__tests__/memory-observation-archive.test.ts +375 -0
- package/src/__tests__/memory-observation-dual-write.test.ts +318 -0
- package/src/__tests__/memory-recall-quality.test.ts +2 -2
- package/src/__tests__/memory-reducer-job.test.ts +538 -0
- package/src/__tests__/memory-reducer-scheduling.test.ts +473 -0
- package/src/__tests__/memory-reducer-store.test.ts +728 -0
- package/src/__tests__/memory-reducer-types.test.ts +707 -0
- package/src/__tests__/memory-reducer.test.ts +704 -0
- package/src/__tests__/memory-regressions.test.ts +30 -8
- package/src/__tests__/memory-simplified-config.test.ts +281 -0
- package/src/__tests__/parse-identity-fields.test.ts +129 -0
- package/src/__tests__/simplified-memory-e2e.test.ts +666 -0
- package/src/__tests__/simplified-memory-runtime.test.ts +616 -0
- package/src/__tests__/skill-load-inline-command.test.ts +598 -0
- package/src/__tests__/skill-load-inline-includes.test.ts +644 -0
- package/src/__tests__/skills-inline-command-expansions.test.ts +301 -0
- package/src/__tests__/skills-transitive-hash.test.ts +333 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +320 -0
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +4 -4
- package/src/cli/commands/conversations.ts +18 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +8 -8
- package/src/config/bundled-skills/schedule/TOOLS.json +8 -0
- package/src/config/bundled-skills/skill-management/SKILL.md +1 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/raw-config-utils.ts +28 -0
- package/src/config/schema.ts +12 -0
- package/src/config/schemas/memory-simplified.ts +101 -0
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/skills.ts +50 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +8 -3
- package/src/daemon/conversation-agent-loop.ts +71 -1
- package/src/daemon/conversation-lifecycle.ts +11 -1
- package/src/daemon/conversation-memory.ts +117 -0
- package/src/daemon/conversation-runtime-assembly.ts +3 -1
- package/src/daemon/conversation-surfaces.ts +31 -8
- package/src/daemon/conversation.ts +40 -23
- package/src/daemon/handlers/config-embeddings.ts +10 -2
- package/src/daemon/handlers/config-model.ts +0 -9
- package/src/daemon/handlers/conversations.ts +11 -0
- package/src/daemon/handlers/identity.ts +12 -1
- package/src/daemon/lifecycle.ts +52 -1
- package/src/daemon/message-types/conversations.ts +0 -1
- package/src/daemon/server.ts +1 -1
- package/src/followups/followup-store.ts +47 -1
- package/src/memory/archive-recall.ts +516 -0
- package/src/memory/archive-store.ts +400 -0
- package/src/memory/brief-formatting.ts +33 -0
- package/src/memory/brief-open-loops.ts +266 -0
- package/src/memory/brief-time.ts +162 -0
- package/src/memory/brief.ts +75 -0
- package/src/memory/conversation-crud.ts +455 -101
- package/src/memory/conversation-key-store.ts +33 -4
- package/src/memory/db-init.ts +16 -0
- package/src/memory/indexer.ts +106 -15
- package/src/memory/job-handlers/backfill-simplified-memory.ts +462 -0
- package/src/memory/job-handlers/conversation-starters.ts +9 -3
- package/src/memory/job-handlers/embedding.test.ts +1 -0
- package/src/memory/job-handlers/embedding.ts +83 -0
- package/src/memory/job-handlers/reduce-conversation-memory.ts +229 -0
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +8 -0
- package/src/memory/jobs-worker.ts +20 -0
- package/src/memory/migrations/036-normalize-phone-identities.ts +49 -14
- package/src/memory/migrations/135-backfill-contact-interaction-stats.ts +9 -1
- package/src/memory/migrations/141-rename-verification-table.ts +8 -0
- package/src/memory/migrations/142-rename-verification-session-id-column.ts +7 -2
- package/src/memory/migrations/174-rename-thread-starters-table.ts +8 -0
- package/src/memory/migrations/185-memory-brief-state.ts +52 -0
- package/src/memory/migrations/186-memory-archive.ts +109 -0
- package/src/memory/migrations/187-memory-reducer-checkpoints.ts +19 -0
- package/src/memory/migrations/188-schedule-quiet-flag.ts +13 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/qdrant-client.ts +23 -4
- package/src/memory/reducer-scheduler.ts +242 -0
- package/src/memory/reducer-store.ts +271 -0
- package/src/memory/reducer-types.ts +106 -0
- package/src/memory/reducer.ts +467 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/index.ts +2 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/schema/memory-archive.ts +121 -0
- package/src/memory/schema/memory-brief.ts +55 -0
- package/src/memory/search/semantic.ts +17 -4
- package/src/oauth/oauth-store.ts +3 -1
- package/src/permissions/checker.ts +89 -6
- package/src/permissions/defaults.ts +14 -0
- package/src/runtime/auth/route-policy.ts +10 -1
- package/src/runtime/routes/conversation-management-routes.ts +94 -2
- package/src/runtime/routes/conversation-query-routes.ts +7 -0
- package/src/runtime/routes/conversation-routes.ts +52 -5
- package/src/runtime/routes/guardian-bootstrap-routes.ts +19 -7
- package/src/runtime/routes/identity-routes.ts +2 -35
- package/src/runtime/routes/llm-context-normalization.ts +14 -1
- package/src/runtime/routes/memory-item-routes.ts +90 -5
- package/src/runtime/routes/secret-routes.ts +3 -0
- package/src/runtime/routes/surface-action-routes.ts +68 -1
- package/src/schedule/schedule-store.ts +28 -0
- package/src/schedule/scheduler.ts +6 -2
- package/src/skills/inline-command-expansions.ts +204 -0
- package/src/skills/inline-command-render.ts +127 -0
- package/src/skills/inline-command-runner.ts +242 -0
- package/src/skills/transitive-version-hash.ts +88 -0
- package/src/tasks/task-store.ts +43 -1
- package/src/telemetry/usage-telemetry-reporter.ts +1 -1
- package/src/tools/filesystem/edit.ts +6 -1
- package/src/tools/filesystem/read.ts +6 -1
- package/src/tools/filesystem/write.ts +6 -1
- package/src/tools/memory/handlers.ts +129 -1
- package/src/tools/permission-checker.ts +8 -1
- package/src/tools/schedule/create.ts +3 -0
- package/src/tools/schedule/list.ts +5 -1
- package/src/tools/schedule/update.ts +6 -0
- package/src/tools/skills/load.ts +140 -6
- package/src/util/platform.ts +18 -0
- package/src/workspace/migrations/{002-backfill-installation-id.ts → 011-backfill-installation-id.ts} +1 -1
- package/src/workspace/migrations/registry.ts +1 -1
|
@@ -134,6 +134,7 @@ export function getOrCreateConversation(
|
|
|
134
134
|
opts?: { conversationType?: "standard" | "private" },
|
|
135
135
|
): {
|
|
136
136
|
conversationId: string;
|
|
137
|
+
conversationType: string;
|
|
137
138
|
created: boolean;
|
|
138
139
|
} {
|
|
139
140
|
const db = getDb();
|
|
@@ -147,7 +148,16 @@ export function getOrCreateConversation(
|
|
|
147
148
|
.get();
|
|
148
149
|
|
|
149
150
|
if (existing) {
|
|
150
|
-
|
|
151
|
+
const conv = tx
|
|
152
|
+
.select({ conversationType: conversations.conversationType })
|
|
153
|
+
.from(conversations)
|
|
154
|
+
.where(eq(conversations.id, existing.conversationId))
|
|
155
|
+
.get();
|
|
156
|
+
return {
|
|
157
|
+
conversationId: existing.conversationId,
|
|
158
|
+
conversationType: conv?.conversationType ?? "standard",
|
|
159
|
+
created: false as const,
|
|
160
|
+
};
|
|
151
161
|
}
|
|
152
162
|
|
|
153
163
|
// Check if the conversationKey itself is an existing conversation ID.
|
|
@@ -168,7 +178,16 @@ export function getOrCreateConversation(
|
|
|
168
178
|
createdAt: Date.now(),
|
|
169
179
|
})
|
|
170
180
|
.run();
|
|
171
|
-
|
|
181
|
+
const conv = tx
|
|
182
|
+
.select({ conversationType: conversations.conversationType })
|
|
183
|
+
.from(conversations)
|
|
184
|
+
.where(eq(conversations.id, existingConversation.id))
|
|
185
|
+
.get();
|
|
186
|
+
return {
|
|
187
|
+
conversationId: existingConversation.id,
|
|
188
|
+
conversationType: conv?.conversationType ?? "standard",
|
|
189
|
+
created: false as const,
|
|
190
|
+
};
|
|
172
191
|
}
|
|
173
192
|
|
|
174
193
|
const now = Date.now();
|
|
@@ -205,8 +224,14 @@ export function getOrCreateConversation(
|
|
|
205
224
|
|
|
206
225
|
return {
|
|
207
226
|
conversationId,
|
|
227
|
+
conversationType,
|
|
208
228
|
created: true as const,
|
|
209
|
-
conversation: {
|
|
229
|
+
conversation: {
|
|
230
|
+
id: conversationId,
|
|
231
|
+
title,
|
|
232
|
+
createdAt: now,
|
|
233
|
+
conversationType,
|
|
234
|
+
},
|
|
210
235
|
};
|
|
211
236
|
});
|
|
212
237
|
|
|
@@ -214,5 +239,9 @@ export function getOrCreateConversation(
|
|
|
214
239
|
initConversationDir({ ...result.conversation, originChannel: null });
|
|
215
240
|
}
|
|
216
241
|
|
|
217
|
-
return {
|
|
242
|
+
return {
|
|
243
|
+
conversationId: result.conversationId,
|
|
244
|
+
conversationType: result.conversationType,
|
|
245
|
+
created: result.created,
|
|
246
|
+
};
|
|
218
247
|
}
|
package/src/memory/db-init.ts
CHANGED
|
@@ -82,7 +82,10 @@ import {
|
|
|
82
82
|
migrateInviteContactId,
|
|
83
83
|
migrateLlmRequestLogMessageId,
|
|
84
84
|
migrateLlmRequestLogProvider,
|
|
85
|
+
migrateMemoryArchiveTables,
|
|
86
|
+
migrateMemoryBriefState,
|
|
85
87
|
migrateMemoryItemSupersession,
|
|
88
|
+
migrateMemoryReducerCheckpoints,
|
|
86
89
|
migrateMessagesFtsBackfill,
|
|
87
90
|
migrateNormalizePhoneIdentities,
|
|
88
91
|
migrateNotificationDeliveryThreadDecision,
|
|
@@ -108,6 +111,7 @@ import {
|
|
|
108
111
|
migrateRenameVerificationTable,
|
|
109
112
|
migrateRenameVoiceToPhone,
|
|
110
113
|
migrateScheduleOneShotRouting,
|
|
114
|
+
migrateScheduleQuietFlag,
|
|
111
115
|
migrateSchemaIndexesAndColumns,
|
|
112
116
|
migrateUsageDashboardIndexes,
|
|
113
117
|
migrateVoiceInviteColumns,
|
|
@@ -484,6 +488,18 @@ export function initializeDb(): void {
|
|
|
484
488
|
// 84. Add nullable conversation fork lineage columns and parent lookup index
|
|
485
489
|
migrateConversationForkLineage(database);
|
|
486
490
|
|
|
491
|
+
// 85. Memory brief state tables (time_contexts, open_loops) for simplified memory system
|
|
492
|
+
migrateMemoryBriefState(database);
|
|
493
|
+
|
|
494
|
+
// 86. Memory archive tables (observations, chunks, episodes) for simplified memory v1
|
|
495
|
+
migrateMemoryArchiveTables(database);
|
|
496
|
+
|
|
497
|
+
// 87. Add memory reducer checkpoint columns to conversations
|
|
498
|
+
migrateMemoryReducerCheckpoints(database);
|
|
499
|
+
|
|
500
|
+
// 88. Add quiet flag to schedule jobs
|
|
501
|
+
migrateScheduleQuietFlag(database);
|
|
502
|
+
|
|
487
503
|
validateMigrationState(database);
|
|
488
504
|
|
|
489
505
|
if (process.env.BUN_TEST === "1") {
|
package/src/memory/indexer.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { getConfig } from "../config/loader.js";
|
|
|
5
5
|
import type { MemoryConfig } from "../config/types.js";
|
|
6
6
|
import type { TrustClass } from "../runtime/actor-trust-resolver.js";
|
|
7
7
|
import { getLogger } from "../util/logger.js";
|
|
8
|
+
import { computeChunkContentHash } from "./archive-store.js";
|
|
8
9
|
import { getDb } from "./db.js";
|
|
9
10
|
import { selectedBackendSupportsMultimodal } from "./embedding-backend.js";
|
|
10
11
|
import { enqueueMemoryJob } from "./jobs-store.js";
|
|
@@ -12,7 +13,7 @@ import {
|
|
|
12
13
|
extractMediaBlockMeta,
|
|
13
14
|
extractTextFromStoredMessageContent,
|
|
14
15
|
} from "./message-content.js";
|
|
15
|
-
import { memorySegments } from "./schema.js";
|
|
16
|
+
import { memoryChunks, memoryObservations, memorySegments } from "./schema.js";
|
|
16
17
|
import { segmentText } from "./segmenter.js";
|
|
17
18
|
|
|
18
19
|
const log = getLogger("memory-indexer");
|
|
@@ -53,7 +54,12 @@ export async function indexMessageNow(
|
|
|
53
54
|
input.provenanceTrustClass === undefined;
|
|
54
55
|
|
|
55
56
|
const text = extractTextFromStoredMessageContent(input.content);
|
|
56
|
-
|
|
57
|
+
const hasText = text.length > 0;
|
|
58
|
+
const candidateMediaMeta = extractMediaBlockMeta(input.content).filter(
|
|
59
|
+
(b) => b.type === "image",
|
|
60
|
+
);
|
|
61
|
+
const hasMedia = candidateMediaMeta.length > 0;
|
|
62
|
+
if (!hasText && !hasMedia) {
|
|
57
63
|
enqueueMemoryJob("build_conversation_summary", {
|
|
58
64
|
conversationId: input.conversationId,
|
|
59
65
|
});
|
|
@@ -62,11 +68,13 @@ export async function indexMessageNow(
|
|
|
62
68
|
|
|
63
69
|
const db = getDb();
|
|
64
70
|
const now = Date.now();
|
|
65
|
-
const segments =
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
const segments = hasText
|
|
72
|
+
? segmentText(
|
|
73
|
+
text,
|
|
74
|
+
config.segmentation.targetTokens,
|
|
75
|
+
config.segmentation.overlapTokens,
|
|
76
|
+
)
|
|
77
|
+
: [];
|
|
70
78
|
const shouldExtract =
|
|
71
79
|
input.role === "user" ||
|
|
72
80
|
(input.role === "assistant" && config.extraction.extractFromAssistant);
|
|
@@ -76,9 +84,6 @@ export async function indexMessageNow(
|
|
|
76
84
|
// overhead for messages on non-multimodal backends.
|
|
77
85
|
// selectedBackendSupportsMultimodal requires async key resolution, so we
|
|
78
86
|
// skip it entirely for text-only messages.
|
|
79
|
-
const candidateMediaMeta = extractMediaBlockMeta(input.content).filter(
|
|
80
|
-
(b) => b.type === "image",
|
|
81
|
-
);
|
|
82
87
|
const mediaBlocks =
|
|
83
88
|
candidateMediaMeta.length > 0 &&
|
|
84
89
|
(await selectedBackendSupportsMultimodal(getConfig()))
|
|
@@ -88,7 +93,10 @@ export async function indexMessageNow(
|
|
|
88
93
|
// Wrap all segment inserts and job enqueues in a single transaction so they
|
|
89
94
|
// either all succeed or all roll back, preventing partial/orphaned state.
|
|
90
95
|
let skippedEmbedJobs = 0;
|
|
96
|
+
let skippedChunkEmbedJobs = 0;
|
|
97
|
+
const scopeId = input.scopeId ?? "default";
|
|
91
98
|
db.transaction((tx) => {
|
|
99
|
+
// ── Legacy segment path (kept intact for parallel validation) ───
|
|
92
100
|
for (const segment of segments) {
|
|
93
101
|
const segmentId = buildSegmentId(input.messageId, segment.segmentIndex);
|
|
94
102
|
const hash = createHash("sha256").update(segment.text).digest("hex");
|
|
@@ -109,7 +117,7 @@ export async function indexMessageNow(
|
|
|
109
117
|
segmentIndex: segment.segmentIndex,
|
|
110
118
|
text: segment.text,
|
|
111
119
|
tokenEstimate: segment.tokenEstimate,
|
|
112
|
-
scopeId
|
|
120
|
+
scopeId,
|
|
113
121
|
contentHash: hash,
|
|
114
122
|
createdAt: input.createdAt,
|
|
115
123
|
updatedAt: now,
|
|
@@ -119,7 +127,7 @@ export async function indexMessageNow(
|
|
|
119
127
|
set: {
|
|
120
128
|
text: segment.text,
|
|
121
129
|
tokenEstimate: segment.tokenEstimate,
|
|
122
|
-
scopeId
|
|
130
|
+
scopeId,
|
|
123
131
|
contentHash: hash,
|
|
124
132
|
updatedAt: now,
|
|
125
133
|
},
|
|
@@ -133,6 +141,65 @@ export async function indexMessageNow(
|
|
|
133
141
|
}
|
|
134
142
|
}
|
|
135
143
|
|
|
144
|
+
// ── Archive chunk dual-write (mirrors segment boundaries) ──────
|
|
145
|
+
// Create a single observation per message, then create one chunk per
|
|
146
|
+
// segment using the same segmentation boundaries. Chunks are
|
|
147
|
+
// deduplicated by (scopeId, contentHash) via onConflictDoNothing so
|
|
148
|
+
// unchanged content does not enqueue duplicate embed_chunk jobs.
|
|
149
|
+
const observationId = buildObservationId(input.messageId);
|
|
150
|
+
tx.insert(memoryObservations)
|
|
151
|
+
.values({
|
|
152
|
+
id: observationId,
|
|
153
|
+
scopeId,
|
|
154
|
+
conversationId: input.conversationId,
|
|
155
|
+
messageId: input.messageId,
|
|
156
|
+
role: input.role,
|
|
157
|
+
content: hasText ? text : input.content,
|
|
158
|
+
modality: hasMedia ? "multimodal" : "text",
|
|
159
|
+
source: null,
|
|
160
|
+
createdAt: input.createdAt,
|
|
161
|
+
})
|
|
162
|
+
.onConflictDoNothing({ target: memoryObservations.id })
|
|
163
|
+
.run();
|
|
164
|
+
|
|
165
|
+
for (const segment of segments) {
|
|
166
|
+
const chunkId = buildChunkId(input.messageId, segment.segmentIndex);
|
|
167
|
+
const chunkHash = computeChunkContentHash(scopeId, segment.text);
|
|
168
|
+
|
|
169
|
+
// Check if this chunk already exists with the same content hash
|
|
170
|
+
const existingChunk = tx
|
|
171
|
+
.select({ contentHash: memoryChunks.contentHash })
|
|
172
|
+
.from(memoryChunks)
|
|
173
|
+
.where(eq(memoryChunks.id, chunkId))
|
|
174
|
+
.get();
|
|
175
|
+
|
|
176
|
+
tx.insert(memoryChunks)
|
|
177
|
+
.values({
|
|
178
|
+
id: chunkId,
|
|
179
|
+
scopeId,
|
|
180
|
+
observationId,
|
|
181
|
+
content: segment.text,
|
|
182
|
+
tokenEstimate: segment.tokenEstimate,
|
|
183
|
+
contentHash: chunkHash,
|
|
184
|
+
createdAt: input.createdAt,
|
|
185
|
+
})
|
|
186
|
+
.onConflictDoUpdate({
|
|
187
|
+
target: memoryChunks.id,
|
|
188
|
+
set: {
|
|
189
|
+
content: segment.text,
|
|
190
|
+
tokenEstimate: segment.tokenEstimate,
|
|
191
|
+
contentHash: chunkHash,
|
|
192
|
+
},
|
|
193
|
+
})
|
|
194
|
+
.run();
|
|
195
|
+
|
|
196
|
+
if (existingChunk?.contentHash === chunkHash) {
|
|
197
|
+
skippedChunkEmbedJobs++;
|
|
198
|
+
} else {
|
|
199
|
+
enqueueMemoryJob("embed_chunk", { chunkId, scopeId }, Date.now(), tx);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
136
203
|
// Enqueue embed_attachment jobs for image content blocks when the
|
|
137
204
|
// embedding provider supports multimodal (Gemini only).
|
|
138
205
|
for (const block of mediaBlocks) {
|
|
@@ -147,7 +214,7 @@ export async function indexMessageNow(
|
|
|
147
214
|
if (shouldExtract && isTrustedActor && !input.automated) {
|
|
148
215
|
enqueueMemoryJob(
|
|
149
216
|
"extract_items",
|
|
150
|
-
{ messageId: input.messageId, scopeId
|
|
217
|
+
{ messageId: input.messageId, scopeId },
|
|
151
218
|
Date.now(),
|
|
152
219
|
tx,
|
|
153
220
|
);
|
|
@@ -166,6 +233,12 @@ export async function indexMessageNow(
|
|
|
166
233
|
);
|
|
167
234
|
}
|
|
168
235
|
|
|
236
|
+
if (skippedChunkEmbedJobs > 0) {
|
|
237
|
+
log.debug(
|
|
238
|
+
`Skipped ${skippedChunkEmbedJobs}/${segments.length} embed_chunk jobs (content unchanged)`,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
169
242
|
if (!isTrustedActor && shouldExtract) {
|
|
170
243
|
log.info(
|
|
171
244
|
`Skipping extraction jobs for untrusted actor (trustClass=${input.provenanceTrustClass})`,
|
|
@@ -177,9 +250,11 @@ export async function indexMessageNow(
|
|
|
177
250
|
}
|
|
178
251
|
|
|
179
252
|
const extractionGated = !isTrustedActor || !!input.automated;
|
|
253
|
+
const segmentEmbedJobs = segments.length - skippedEmbedJobs;
|
|
254
|
+
const chunkEmbedJobs = segments.length - skippedChunkEmbedJobs;
|
|
180
255
|
const enqueuedJobs =
|
|
181
|
-
|
|
182
|
-
|
|
256
|
+
segmentEmbedJobs +
|
|
257
|
+
chunkEmbedJobs +
|
|
183
258
|
mediaBlocks.length +
|
|
184
259
|
(shouldExtract && !extractionGated ? 2 : 1);
|
|
185
260
|
return {
|
|
@@ -213,3 +288,19 @@ export function getRecentSegmentsForConversation(
|
|
|
213
288
|
function buildSegmentId(messageId: string, segmentIndex: number): string {
|
|
214
289
|
return `${messageId}:${segmentIndex}`;
|
|
215
290
|
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Deterministic observation ID derived from the messageId so repeated
|
|
294
|
+
* indexer runs for the same message converge on the same observation row.
|
|
295
|
+
*/
|
|
296
|
+
function buildObservationId(messageId: string): string {
|
|
297
|
+
return `obs:${messageId}`;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Deterministic chunk ID derived from the messageId and segment index so
|
|
302
|
+
* the dual-write path mirrors the legacy segment identity scheme exactly.
|
|
303
|
+
*/
|
|
304
|
+
function buildChunkId(messageId: string, segmentIndex: number): string {
|
|
305
|
+
return `chunk:${messageId}:${segmentIndex}`;
|
|
306
|
+
}
|