@vellumai/assistant 0.5.4 → 0.5.6
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/Dockerfile +17 -27
- package/node_modules/@vellumai/ces-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +42 -0
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +113 -0
- package/src/__tests__/config-schema.test.ts +2 -2
- package/src/__tests__/context-window-manager.test.ts +78 -0
- package/src/__tests__/conversation-title-service.test.ts +30 -1
- package/src/__tests__/credential-security-invariants.test.ts +2 -0
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +207 -0
- package/src/__tests__/memory-regressions.test.ts +8 -30
- package/src/__tests__/openai-whisper.test.ts +93 -0
- package/src/__tests__/require-fresh-approval.test.ts +4 -0
- package/src/__tests__/slack-messaging-token-resolution.test.ts +319 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +4 -0
- package/src/__tests__/tool-executor.test.ts +4 -0
- package/src/__tests__/volume-security-guard.test.ts +155 -0
- package/src/cli/commands/conversations.ts +0 -18
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -0
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +16 -37
- package/src/config/env-registry.ts +9 -0
- package/src/config/env.ts +8 -2
- package/src/config/feature-flag-registry.json +8 -8
- package/src/config/schema.ts +0 -12
- package/src/config/schemas/memory.ts +0 -4
- package/src/config/schemas/platform.ts +1 -1
- package/src/config/schemas/security.ts +4 -0
- package/src/context/window-manager.ts +53 -2
- package/src/credential-execution/managed-catalog.ts +5 -15
- package/src/daemon/conversation-agent-loop.ts +0 -60
- package/src/daemon/conversation-memory.ts +0 -117
- package/src/daemon/conversation-runtime-assembly.ts +0 -2
- package/src/daemon/daemon-control.ts +7 -0
- package/src/daemon/handlers/conversations.ts +0 -11
- package/src/daemon/lifecycle.ts +10 -47
- package/src/daemon/providers-setup.ts +2 -1
- package/src/followups/followup-store.ts +5 -2
- package/src/hooks/manager.ts +7 -0
- package/src/instrument.ts +33 -1
- package/src/memory/conversation-crud.ts +0 -236
- package/src/memory/conversation-title-service.ts +26 -10
- package/src/memory/db-init.ts +5 -13
- package/src/memory/embedding-local.ts +11 -5
- package/src/memory/indexer.ts +15 -106
- package/src/memory/job-handlers/conversation-starters.ts +24 -36
- package/src/memory/job-handlers/embedding.ts +0 -79
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +0 -8
- package/src/memory/jobs-worker.ts +0 -20
- package/src/memory/migrations/189-drop-simplified-memory.ts +42 -0
- package/src/memory/migrations/index.ts +1 -3
- package/src/memory/qdrant-client.ts +4 -6
- package/src/memory/schema/conversations.ts +0 -3
- package/src/memory/schema/index.ts +0 -2
- package/src/messaging/draft-store.ts +2 -2
- package/src/messaging/provider.ts +9 -0
- package/src/messaging/providers/slack/adapter.ts +29 -2
- package/src/oauth/connection-resolver.test.ts +22 -18
- package/src/oauth/connection-resolver.ts +92 -7
- package/src/oauth/platform-connection.test.ts +78 -69
- package/src/oauth/platform-connection.ts +12 -19
- package/src/permissions/defaults.ts +3 -3
- package/src/permissions/trust-client.ts +332 -0
- package/src/permissions/trust-store-interface.ts +105 -0
- package/src/permissions/trust-store.ts +531 -39
- package/src/platform/client.test.ts +148 -0
- package/src/platform/client.ts +71 -0
- package/src/providers/speech-to-text/openai-whisper.test.ts +190 -0
- package/src/providers/speech-to-text/openai-whisper.ts +68 -0
- package/src/providers/speech-to-text/resolve.ts +9 -0
- package/src/providers/speech-to-text/types.ts +17 -0
- package/src/runtime/auth/route-policy.ts +14 -0
- package/src/runtime/auth/token-service.ts +133 -0
- package/src/runtime/http-server.ts +4 -2
- package/src/runtime/routes/conversation-management-routes.ts +0 -36
- package/src/runtime/routes/conversation-query-routes.ts +44 -2
- package/src/runtime/routes/conversation-routes.ts +2 -1
- package/src/runtime/routes/inbound-message-handler.ts +27 -3
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +16 -1
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +287 -0
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +122 -0
- package/src/runtime/routes/log-export-routes.ts +1 -0
- package/src/runtime/routes/memory-item-routes.test.ts +221 -3
- package/src/runtime/routes/memory-item-routes.ts +124 -2
- package/src/runtime/routes/secret-routes.ts +4 -1
- package/src/runtime/routes/upgrade-broadcast-routes.ts +151 -0
- package/src/schedule/schedule-store.ts +0 -21
- package/src/security/ces-credential-client.ts +173 -0
- package/src/security/secure-keys.ts +65 -22
- package/src/signals/bash.ts +3 -0
- package/src/signals/cancel.ts +3 -0
- package/src/signals/confirm.ts +3 -0
- package/src/signals/conversation-undo.ts +3 -0
- package/src/signals/event-stream.ts +7 -0
- package/src/signals/shotgun.ts +3 -0
- package/src/signals/trust-rule.ts +3 -0
- package/src/skills/inline-command-render.ts +5 -1
- package/src/skills/inline-command-runner.ts +30 -2
- package/src/telemetry/usage-telemetry-reporter.test.ts +23 -36
- package/src/telemetry/usage-telemetry-reporter.ts +21 -19
- package/src/tools/memory/handlers.ts +1 -129
- package/src/tools/permission-checker.ts +18 -0
- package/src/tools/skills/load.ts +9 -2
- package/src/util/device-id.ts +70 -7
- package/src/util/logger.ts +35 -9
- package/src/util/platform.ts +29 -5
- package/src/util/xml.ts +8 -0
- package/src/workspace/heartbeat-service.ts +5 -24
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +113 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/__tests__/archive-recall.test.ts +0 -560
- package/src/__tests__/conversation-memory-dirty-tail.test.ts +0 -150
- package/src/__tests__/conversation-switch-memory-reduction.test.ts +0 -474
- package/src/__tests__/db-memory-archive-migration.test.ts +0 -372
- package/src/__tests__/db-memory-brief-state-migration.test.ts +0 -213
- package/src/__tests__/db-memory-reducer-checkpoints.test.ts +0 -273
- package/src/__tests__/memory-brief-open-loops.test.ts +0 -530
- package/src/__tests__/memory-brief-time.test.ts +0 -285
- package/src/__tests__/memory-brief-wrapper.test.ts +0 -311
- package/src/__tests__/memory-chunk-archive.test.ts +0 -400
- package/src/__tests__/memory-chunk-dual-write.test.ts +0 -453
- package/src/__tests__/memory-episode-archive.test.ts +0 -370
- package/src/__tests__/memory-episode-dual-write.test.ts +0 -626
- package/src/__tests__/memory-observation-archive.test.ts +0 -375
- package/src/__tests__/memory-observation-dual-write.test.ts +0 -318
- package/src/__tests__/memory-reducer-job.test.ts +0 -538
- package/src/__tests__/memory-reducer-scheduling.test.ts +0 -473
- package/src/__tests__/memory-reducer-store.test.ts +0 -728
- package/src/__tests__/memory-reducer-types.test.ts +0 -707
- package/src/__tests__/memory-reducer.test.ts +0 -704
- package/src/__tests__/memory-simplified-config.test.ts +0 -281
- package/src/__tests__/simplified-memory-e2e.test.ts +0 -666
- package/src/__tests__/simplified-memory-runtime.test.ts +0 -616
- package/src/config/schemas/memory-simplified.ts +0 -101
- package/src/memory/archive-recall.ts +0 -516
- package/src/memory/archive-store.ts +0 -400
- package/src/memory/brief-formatting.ts +0 -33
- package/src/memory/brief-open-loops.ts +0 -266
- package/src/memory/brief-time.ts +0 -162
- package/src/memory/brief.ts +0 -75
- package/src/memory/job-handlers/backfill-simplified-memory.ts +0 -462
- package/src/memory/job-handlers/reduce-conversation-memory.ts +0 -229
- package/src/memory/migrations/185-memory-brief-state.ts +0 -52
- package/src/memory/migrations/186-memory-archive.ts +0 -109
- package/src/memory/migrations/187-memory-reducer-checkpoints.ts +0 -19
- package/src/memory/reducer-scheduler.ts +0 -242
- package/src/memory/reducer-store.ts +0 -271
- package/src/memory/reducer-types.ts +0 -106
- package/src/memory/reducer.ts +0 -467
- package/src/memory/schema/memory-archive.ts +0 -121
- package/src/memory/schema/memory-brief.ts +0 -55
|
@@ -176,11 +176,11 @@ async function generateStarters(scopeId: string): Promise<GeneratedStarter[]> {
|
|
|
176
176
|
? truncate(rawIdentityContext, 2000, "\n…[truncated]")
|
|
177
177
|
: null;
|
|
178
178
|
|
|
179
|
-
const systemPrompt = `You are generating 4 conversation starters for a personal assistant app. These appear as clickable chips on the empty conversation page — the first thing the user sees when they open the app.
|
|
179
|
+
const systemPrompt = `You are generating 4 conversation starters for a personal assistant app. These appear as clickable chips on the empty conversation page — the first thing the user sees when they open the app. Clicking a chip sends its prompt as a message from the user.
|
|
180
180
|
|
|
181
181
|
${timeContext}
|
|
182
182
|
|
|
183
|
-
Your goal:
|
|
183
|
+
Your goal: suggest the 4 most useful things this person could ask you to do right now.
|
|
184
184
|
|
|
185
185
|
${identityContext ? `## Assistant identity & user profile\n\n${identityContext}\n\n` : ""}## What you know
|
|
186
186
|
|
|
@@ -188,7 +188,9 @@ ${rollup}
|
|
|
188
188
|
${diff}
|
|
189
189
|
${skills}
|
|
190
190
|
|
|
191
|
-
##
|
|
191
|
+
## Selection
|
|
192
|
+
|
|
193
|
+
Generate exactly 4 starters, ranked #1 (best) to #4.
|
|
192
194
|
|
|
193
195
|
Start from the user's situation, not from the skill list. Ask yourself:
|
|
194
196
|
- What is this person likely dealing with right now (given the day/time and their context)?
|
|
@@ -197,11 +199,7 @@ Start from the user's situation, not from the skill list. Ask yourself:
|
|
|
197
199
|
|
|
198
200
|
The skills list tells you what the assistant CAN do — use it to filter out suggestions the assistant can't actually help with, not as a menu to generate suggestions from.
|
|
199
201
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
Generate exactly 4 starters, ranked #1 (best) to #4.
|
|
203
|
-
|
|
204
|
-
For each, you must be able to clearly answer:
|
|
202
|
+
For each starter, you must clearly answer:
|
|
205
203
|
- Why now? (timing — day of week, recent activity, upcoming deadline)
|
|
206
204
|
- Why this user? (grounded in their specific context, not generic)
|
|
207
205
|
- Why would they be glad I suggested this? (genuine usefulness, not just relevance)
|
|
@@ -218,44 +216,34 @@ Favor what is live over what is merely true. Recent changes matter more than old
|
|
|
218
216
|
|
|
219
217
|
## Output format
|
|
220
218
|
|
|
221
|
-
Return exactly 4 starters in rank order (best first).
|
|
222
|
-
|
|
223
219
|
Each starter has:
|
|
224
|
-
- label: 3-6 words, max 40 chars, starts with a verb.
|
|
225
|
-
- prompt: 1-2 natural sentences,
|
|
220
|
+
- label: 3-6 words, max 40 chars, starts with a verb. Written in the user's voice — something they'd want to do, not something the assistant is offering.
|
|
221
|
+
- prompt: 1-2 natural sentences, as the user would actually say them.
|
|
226
222
|
- category: one of ${CONVERSATION_STARTER_CATEGORIES.join(", ")}
|
|
227
223
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
Never include a chip whose primary meaning is configuration, setup, workflow creation, or "set up X for Y" unless it solves an urgent pain the user is actively feeling right now. Prefer the outcome over the mechanism — "Catch the emails that matter" beats "Set up a playbook for inbox."
|
|
231
|
-
|
|
232
|
-
## Topic diversity
|
|
233
|
-
|
|
234
|
-
Each chip should cover a distinct topic or concern. Never have two chips about the same tool, project, or theme — even if there are multiple related issues. Pick the single most impactful angle and give the other slot to something different. Four chips about three topics is too narrow; four chips about four topics is right.
|
|
224
|
+
## Constraints
|
|
235
225
|
|
|
236
|
-
|
|
226
|
+
**Voice**: The user clicks these chips to send a message. Every label must read as something the user is asking to do, never something the assistant is saying to the user.
|
|
237
227
|
|
|
238
|
-
|
|
228
|
+
**Coherence**: The 4 starters should feel like one set — similar abstraction level, no jarring mix of mundane chores and life strategy.
|
|
239
229
|
|
|
240
|
-
|
|
230
|
+
**Diversity**: Each chip covers a distinct topic. Never two chips about the same tool, project, or theme. Four topics, four chips.
|
|
241
231
|
|
|
242
|
-
|
|
232
|
+
**No setup chips**: Never include a chip whose primary meaning is configuration or "set up X for Y" unless it solves an urgent pain the user is actively feeling. Prefer the outcome over the mechanism.
|
|
243
233
|
|
|
244
|
-
|
|
234
|
+
**Natural language**: No jargon, project names, or raw memory phrases in labels unless they already sound natural in conversation. If a label sounds like a ticket title or backlog item, rewrite it as something the user would actually say.
|
|
245
235
|
|
|
246
|
-
Examples
|
|
247
|
-
- BAD: "Fix Slack Socket Mode blocker" → GOOD: "Fix Slack so it just works"
|
|
248
|
-
- BAD: "Rewire messaging for Socket Mode" → GOOD: "Get Socket Mode stable"
|
|
249
|
-
- BAD: "Review this week's calendar" → GOOD: "Protect this week's focus"
|
|
250
|
-
- BAD: "Model the coaching transition" → GOOD: "Plan the coaching transition"
|
|
251
|
-
- BAD: "Restore outgoing Slack messages" → GOOD: "Get Slack messages flowing"
|
|
252
|
-
- BAD: "Set up a playbook for inbox" → GOOD: "Catch the emails that matter"
|
|
236
|
+
## Examples
|
|
253
237
|
|
|
254
|
-
|
|
255
|
-
-
|
|
256
|
-
-
|
|
238
|
+
Bad → Good (ticket-speak → natural):
|
|
239
|
+
- "Fix Slack Socket Mode blocker" → "Fix Slack so it just works"
|
|
240
|
+
- "Restore outgoing Slack messages" → "Get Slack messages flowing"
|
|
241
|
+
- "Review this week's calendar" → "Protect this week's focus"
|
|
242
|
+
- "Set up a playbook for inbox" → "Triage my inbox"
|
|
257
243
|
|
|
258
|
-
|
|
244
|
+
Bad → Good (assistant voice → user voice):
|
|
245
|
+
- "You've got a busy week ahead" → "Plan my week ahead"
|
|
246
|
+
- "Let me check your calendar" → "Check my Thursday schedule"`;
|
|
259
247
|
|
|
260
248
|
const { signal, cleanup } = createTimeout(20000);
|
|
261
249
|
try {
|
|
@@ -280,7 +268,7 @@ The good versions emphasize the user's payoff in the user's own voice, not the i
|
|
|
280
268
|
label: {
|
|
281
269
|
type: "string",
|
|
282
270
|
description:
|
|
283
|
-
"User-voice chip
|
|
271
|
+
"User-voice chip label (2-7 words, max 40 chars, verb-first)",
|
|
284
272
|
},
|
|
285
273
|
prompt: {
|
|
286
274
|
type: "string",
|
|
@@ -11,10 +11,7 @@ import type { MemoryJob } from "../jobs-store.js";
|
|
|
11
11
|
import { extractMediaBlocks } from "../message-content.js";
|
|
12
12
|
import {
|
|
13
13
|
mediaAssets,
|
|
14
|
-
memoryChunks,
|
|
15
|
-
memoryEpisodes,
|
|
16
14
|
memoryItems,
|
|
17
|
-
memoryObservations,
|
|
18
15
|
memorySegments,
|
|
19
16
|
memorySummaries,
|
|
20
17
|
messages,
|
|
@@ -93,26 +90,6 @@ export async function embedSummaryJob(
|
|
|
93
90
|
);
|
|
94
91
|
}
|
|
95
92
|
|
|
96
|
-
export async function embedChunkJob(
|
|
97
|
-
job: MemoryJob,
|
|
98
|
-
config: AssistantConfig,
|
|
99
|
-
): Promise<void> {
|
|
100
|
-
const chunkId = asString(job.payload.chunkId);
|
|
101
|
-
if (!chunkId) return;
|
|
102
|
-
const db = getDb();
|
|
103
|
-
const chunk = db
|
|
104
|
-
.select()
|
|
105
|
-
.from(memoryChunks)
|
|
106
|
-
.where(eq(memoryChunks.id, chunkId))
|
|
107
|
-
.get();
|
|
108
|
-
if (!chunk) return;
|
|
109
|
-
await embedAndUpsert(config, "chunk", chunk.id, chunk.content, {
|
|
110
|
-
observation_id: chunk.observationId,
|
|
111
|
-
created_at: chunk.createdAt,
|
|
112
|
-
memory_scope_id: chunk.scopeId,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
93
|
export async function embedMediaJob(
|
|
117
94
|
job: MemoryJob,
|
|
118
95
|
config: AssistantConfig,
|
|
@@ -146,40 +123,6 @@ export async function embedMediaJob(
|
|
|
146
123
|
});
|
|
147
124
|
}
|
|
148
125
|
|
|
149
|
-
export async function embedObservationJob(
|
|
150
|
-
job: MemoryJob,
|
|
151
|
-
config: AssistantConfig,
|
|
152
|
-
): Promise<void> {
|
|
153
|
-
const observationId = asString(job.payload.observationId);
|
|
154
|
-
const chunkId = asString(job.payload.chunkId);
|
|
155
|
-
if (!observationId || !chunkId) return;
|
|
156
|
-
|
|
157
|
-
const db = getDb();
|
|
158
|
-
const observation = db
|
|
159
|
-
.select()
|
|
160
|
-
.from(memoryObservations)
|
|
161
|
-
.where(eq(memoryObservations.id, observationId))
|
|
162
|
-
.get();
|
|
163
|
-
if (!observation) return;
|
|
164
|
-
|
|
165
|
-
const chunk = db
|
|
166
|
-
.select()
|
|
167
|
-
.from(memoryChunks)
|
|
168
|
-
.where(eq(memoryChunks.id, chunkId))
|
|
169
|
-
.get();
|
|
170
|
-
if (!chunk) return;
|
|
171
|
-
|
|
172
|
-
await embedAndUpsert(config, "observation", chunk.id, chunk.content, {
|
|
173
|
-
observation_id: observationId,
|
|
174
|
-
conversation_id: observation.conversationId,
|
|
175
|
-
role: observation.role,
|
|
176
|
-
modality: observation.modality,
|
|
177
|
-
source: observation.source,
|
|
178
|
-
created_at: observation.createdAt,
|
|
179
|
-
memory_scope_id: observation.scopeId,
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
|
|
183
126
|
export async function embedAttachmentJob(
|
|
184
127
|
job: MemoryJob,
|
|
185
128
|
config: AssistantConfig,
|
|
@@ -216,25 +159,3 @@ export async function embedAttachmentJob(
|
|
|
216
159
|
memory_scope_id: memoryScopeId,
|
|
217
160
|
});
|
|
218
161
|
}
|
|
219
|
-
|
|
220
|
-
export async function embedEpisodeJob(
|
|
221
|
-
job: MemoryJob,
|
|
222
|
-
config: AssistantConfig,
|
|
223
|
-
): Promise<void> {
|
|
224
|
-
const episodeId = asString(job.payload.episodeId);
|
|
225
|
-
if (!episodeId) return;
|
|
226
|
-
const db = getDb();
|
|
227
|
-
const episode = db
|
|
228
|
-
.select()
|
|
229
|
-
.from(memoryEpisodes)
|
|
230
|
-
.where(eq(memoryEpisodes.id, episodeId))
|
|
231
|
-
.get();
|
|
232
|
-
if (!episode) return;
|
|
233
|
-
const text = `[episode] ${episode.title}: ${episode.summary}`;
|
|
234
|
-
await embedAndUpsert(config, "episode", episode.id, text, {
|
|
235
|
-
conversation_id: episode.conversationId,
|
|
236
|
-
created_at: episode.startAt,
|
|
237
|
-
last_seen_at: episode.endAt,
|
|
238
|
-
memory_scope_id: episode.scopeId,
|
|
239
|
-
});
|
|
240
|
-
}
|
package/src/memory/job-utils.ts
CHANGED
|
@@ -142,7 +142,7 @@ export function truncate(text: string, max: number): string {
|
|
|
142
142
|
|
|
143
143
|
export async function embedAndUpsert(
|
|
144
144
|
config: AssistantConfig,
|
|
145
|
-
targetType: "segment" | "item" | "summary" | "
|
|
145
|
+
targetType: "segment" | "item" | "summary" | "media",
|
|
146
146
|
targetId: string,
|
|
147
147
|
input: EmbeddingInput,
|
|
148
148
|
extraPayload?: Record<string, unknown>,
|
package/src/memory/jobs-store.ts
CHANGED
|
@@ -12,9 +12,6 @@ export type MemoryJobType =
|
|
|
12
12
|
| "embed_segment"
|
|
13
13
|
| "embed_item"
|
|
14
14
|
| "embed_summary"
|
|
15
|
-
| "embed_chunk"
|
|
16
|
-
| "embed_episode"
|
|
17
|
-
| "embed_observation"
|
|
18
15
|
| "extract_items"
|
|
19
16
|
| "extract_entities"
|
|
20
17
|
| "cleanup_stale_superseded_items"
|
|
@@ -30,8 +27,6 @@ export type MemoryJobType =
|
|
|
30
27
|
| "embed_media"
|
|
31
28
|
| "embed_attachment"
|
|
32
29
|
| "generate_conversation_starters"
|
|
33
|
-
| "reduce_conversation_memory"
|
|
34
|
-
| "backfill_simplified_memory"
|
|
35
30
|
| "generate_capability_cards" // legacy compat — silently dropped by worker (capability cards removed)
|
|
36
31
|
| "generate_thread_starters"; // legacy compat — silently dropped by worker (renamed to generate_conversation_starters)
|
|
37
32
|
|
|
@@ -39,9 +34,6 @@ const EMBED_JOB_TYPES: MemoryJobType[] = [
|
|
|
39
34
|
"embed_segment",
|
|
40
35
|
"embed_item",
|
|
41
36
|
"embed_summary",
|
|
42
|
-
"embed_chunk",
|
|
43
|
-
"embed_episode",
|
|
44
|
-
"embed_observation",
|
|
45
37
|
"embed_media",
|
|
46
38
|
"embed_attachment",
|
|
47
39
|
];
|
|
@@ -3,7 +3,6 @@ import type { AssistantConfig } from "../config/types.js";
|
|
|
3
3
|
import { getLogger } from "../util/logger.js";
|
|
4
4
|
import { rawRun } from "./db.js";
|
|
5
5
|
import { backfillJob } from "./job-handlers/backfill.js";
|
|
6
|
-
import { backfillSimplifiedMemoryJob } from "./job-handlers/backfill-simplified-memory.js";
|
|
7
6
|
import {
|
|
8
7
|
cleanupStaleSupersededItemsJob,
|
|
9
8
|
pruneOldConversationsJob,
|
|
@@ -12,11 +11,8 @@ import { generateConversationStartersJob } from "./job-handlers/conversation-sta
|
|
|
12
11
|
// ── Per-job-type handlers ──────────────────────────────────────────
|
|
13
12
|
import {
|
|
14
13
|
embedAttachmentJob,
|
|
15
|
-
embedChunkJob,
|
|
16
|
-
embedEpisodeJob,
|
|
17
14
|
embedItemJob,
|
|
18
15
|
embedMediaJob,
|
|
19
|
-
embedObservationJob,
|
|
20
16
|
embedSegmentJob,
|
|
21
17
|
embedSummaryJob,
|
|
22
18
|
} from "./job-handlers/embedding.js";
|
|
@@ -26,7 +22,6 @@ import {
|
|
|
26
22
|
rebuildIndexJob,
|
|
27
23
|
} from "./job-handlers/index-maintenance.js";
|
|
28
24
|
import { mediaProcessingJob } from "./job-handlers/media-processing.js";
|
|
29
|
-
import { reduceConversationMemoryJob } from "./job-handlers/reduce-conversation-memory.js";
|
|
30
25
|
import { buildConversationSummaryJob } from "./job-handlers/summarization.js";
|
|
31
26
|
import {
|
|
32
27
|
BackendUnavailableError,
|
|
@@ -272,15 +267,6 @@ async function processJob(
|
|
|
272
267
|
case "embed_summary":
|
|
273
268
|
await embedSummaryJob(job, config);
|
|
274
269
|
return;
|
|
275
|
-
case "embed_chunk":
|
|
276
|
-
await embedChunkJob(job, config);
|
|
277
|
-
return;
|
|
278
|
-
case "embed_episode":
|
|
279
|
-
await embedEpisodeJob(job, config);
|
|
280
|
-
return;
|
|
281
|
-
case "embed_observation":
|
|
282
|
-
await embedObservationJob(job, config);
|
|
283
|
-
return;
|
|
284
270
|
case "extract_items":
|
|
285
271
|
await extractItemsJob(job);
|
|
286
272
|
return;
|
|
@@ -321,12 +307,6 @@ async function processJob(
|
|
|
321
307
|
case "embed_attachment":
|
|
322
308
|
await embedAttachmentJob(job, config);
|
|
323
309
|
return;
|
|
324
|
-
case "reduce_conversation_memory":
|
|
325
|
-
await reduceConversationMemoryJob(job);
|
|
326
|
-
return;
|
|
327
|
-
case "backfill_simplified_memory":
|
|
328
|
-
await backfillSimplifiedMemoryJob(job);
|
|
329
|
-
return;
|
|
330
310
|
case "generate_conversation_starters":
|
|
331
311
|
await generateConversationStartersJob(job);
|
|
332
312
|
return;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { DrizzleDb } from "../db-connection.js";
|
|
2
|
+
import { getSqliteFrom } from "../db-connection.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Drop simplified-memory tables and reducer checkpoint columns added by
|
|
6
|
+
* the simplified-memory-v1 plan, reverting to the legacy item/tier/XML
|
|
7
|
+
* memory system.
|
|
8
|
+
*/
|
|
9
|
+
export function migrateDropSimplifiedMemory(database: DrizzleDb): void {
|
|
10
|
+
const raw = getSqliteFrom(database);
|
|
11
|
+
|
|
12
|
+
// Drop simplified-memory tables (idempotent — IF EXISTS).
|
|
13
|
+
raw.exec(`DROP TABLE IF EXISTS time_contexts`);
|
|
14
|
+
raw.exec(`DROP TABLE IF EXISTS open_loops`);
|
|
15
|
+
raw.exec(`DROP TABLE IF EXISTS memory_observations`);
|
|
16
|
+
raw.exec(`DROP TABLE IF EXISTS memory_chunks`);
|
|
17
|
+
raw.exec(`DROP TABLE IF EXISTS memory_episodes`);
|
|
18
|
+
|
|
19
|
+
// Remove reducer checkpoint columns from conversations.
|
|
20
|
+
// SQLite doesn't support DROP COLUMN before 3.35.0, but Bun's built-in
|
|
21
|
+
// SQLite is >= 3.38, so this is safe.
|
|
22
|
+
for (const col of [
|
|
23
|
+
"memory_reduced_through_message_id",
|
|
24
|
+
"memory_dirty_tail_since_message_id",
|
|
25
|
+
"memory_last_reduced_at",
|
|
26
|
+
]) {
|
|
27
|
+
try {
|
|
28
|
+
raw.exec(`ALTER TABLE conversations DROP COLUMN ${col}`);
|
|
29
|
+
} catch {
|
|
30
|
+
// Column doesn't exist — already cleaned up.
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Remove embedding rows for archive target types that no longer exist.
|
|
35
|
+
try {
|
|
36
|
+
raw.exec(
|
|
37
|
+
`DELETE FROM memory_embeddings WHERE target_type IN ('observation', 'chunk', 'episode')`,
|
|
38
|
+
);
|
|
39
|
+
} catch {
|
|
40
|
+
// Column doesn't exist — table was never migrated to include target_type.
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -126,10 +126,8 @@ export { migrateRenameThreadStartersCheckpoints } from "./181-rename-thread-star
|
|
|
126
126
|
export { migrateOAuthProvidersDisplayMetadata } from "./182-oauth-providers-display-metadata.js";
|
|
127
127
|
export { migrateConversationForkLineage } from "./183-add-conversation-fork-lineage.js";
|
|
128
128
|
export { migrateLlmRequestLogProvider } from "./184-llm-request-log-provider.js";
|
|
129
|
-
export { migrateMemoryBriefState } from "./185-memory-brief-state.js";
|
|
130
|
-
export { migrateMemoryArchiveTables } from "./186-memory-archive.js";
|
|
131
|
-
export { migrateMemoryReducerCheckpoints } from "./187-memory-reducer-checkpoints.js";
|
|
132
129
|
export { migrateScheduleQuietFlag } from "./188-schedule-quiet-flag.js";
|
|
130
|
+
export { migrateDropSimplifiedMemory } from "./189-drop-simplified-memory.js";
|
|
133
131
|
export {
|
|
134
132
|
MIGRATION_REGISTRY,
|
|
135
133
|
type MigrationRegistryEntry,
|
|
@@ -20,7 +20,7 @@ export interface QdrantClientConfig {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export interface QdrantPointPayload {
|
|
23
|
-
target_type: "segment" | "item" | "summary" | "
|
|
23
|
+
target_type: "segment" | "item" | "summary" | "media";
|
|
24
24
|
target_id: string;
|
|
25
25
|
text: string;
|
|
26
26
|
kind?: string;
|
|
@@ -230,7 +230,7 @@ export class VellumQdrantClient {
|
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
async upsert(
|
|
233
|
-
targetType: "segment" | "item" | "summary" | "
|
|
233
|
+
targetType: "segment" | "item" | "summary" | "media",
|
|
234
234
|
targetId: string,
|
|
235
235
|
vector: number[],
|
|
236
236
|
payload: Omit<QdrantPointPayload, "target_type" | "target_id">,
|
|
@@ -324,9 +324,7 @@ export class VellumQdrantClient {
|
|
|
324
324
|
async searchWithFilter(
|
|
325
325
|
vector: number[],
|
|
326
326
|
limit: number,
|
|
327
|
-
targetTypes: Array<
|
|
328
|
-
"segment" | "item" | "summary" | "media" | "chunk" | "episode"
|
|
329
|
-
>,
|
|
327
|
+
targetTypes: Array<"segment" | "item" | "summary" | "media">,
|
|
330
328
|
excludeMessageIds?: string[],
|
|
331
329
|
scopeIds?: string[],
|
|
332
330
|
): Promise<QdrantSearchResult[]> {
|
|
@@ -349,7 +347,7 @@ export class VellumQdrantClient {
|
|
|
349
347
|
},
|
|
350
348
|
{
|
|
351
349
|
key: "target_type",
|
|
352
|
-
match: { any: ["segment", "summary", "media"
|
|
350
|
+
match: { any: ["segment", "summary", "media"] },
|
|
353
351
|
},
|
|
354
352
|
],
|
|
355
353
|
});
|
|
@@ -30,9 +30,6 @@ export const conversations = sqliteTable(
|
|
|
30
30
|
forkParentMessageId: text("fork_parent_message_id"),
|
|
31
31
|
isAutoTitle: integer("is_auto_title").notNull().default(1),
|
|
32
32
|
scheduleJobId: text("schedule_job_id"),
|
|
33
|
-
memoryReducedThroughMessageId: text("memory_reduced_through_message_id"),
|
|
34
|
-
memoryDirtyTailSinceMessageId: text("memory_dirty_tail_since_message_id"),
|
|
35
|
-
memoryLastReducedAt: integer("memory_last_reduced_at"),
|
|
36
33
|
},
|
|
37
34
|
(table) => [
|
|
38
35
|
index("idx_conversations_updated_at").on(table.updatedAt),
|
|
@@ -3,8 +3,6 @@ export * from "./contacts.js";
|
|
|
3
3
|
export * from "./conversations.js";
|
|
4
4
|
export * from "./guardian.js";
|
|
5
5
|
export * from "./infrastructure.js";
|
|
6
|
-
export * from "./memory-archive.js";
|
|
7
|
-
export * from "./memory-brief.js";
|
|
8
6
|
export * from "./memory-core.js";
|
|
9
7
|
export * from "./notifications.js";
|
|
10
8
|
export * from "./oauth.js";
|
|
@@ -10,7 +10,7 @@ import { readdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
|
10
10
|
import { join } from "node:path";
|
|
11
11
|
|
|
12
12
|
import { ensureDir, pathExists } from "../util/fs.js";
|
|
13
|
-
import {
|
|
13
|
+
import { getWorkspaceDir } from "../util/platform.js";
|
|
14
14
|
|
|
15
15
|
export interface Draft {
|
|
16
16
|
id: string;
|
|
@@ -25,7 +25,7 @@ export interface Draft {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
function getDraftsDir(platform: string): string {
|
|
28
|
-
const dir = join(
|
|
28
|
+
const dir = join(getWorkspaceDir(), "data", "drafts", platform);
|
|
29
29
|
ensureDir(dir);
|
|
30
30
|
return dir;
|
|
31
31
|
}
|
|
@@ -89,6 +89,15 @@ export interface MessagingProvider {
|
|
|
89
89
|
*/
|
|
90
90
|
isConnected?(): Promise<boolean>;
|
|
91
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Custom credential resolution for providers with non-standard credential
|
|
94
|
+
* paths (e.g. Slack Socket Mode stores tokens under "slack_channel" rather
|
|
95
|
+
* than the OAuth provider key). When present, getProviderConnection() calls
|
|
96
|
+
* this instead of resolveOAuthConnection(), giving the provider full control
|
|
97
|
+
* over credential lookup including fallback strategies.
|
|
98
|
+
*/
|
|
99
|
+
resolveConnection?(account?: string): Promise<OAuthConnection | string>;
|
|
100
|
+
|
|
92
101
|
/** Platform-specific capabilities for tool routing (e.g. 'reactions', 'threads', 'labels'). */
|
|
93
102
|
capabilities: Set<string>;
|
|
94
103
|
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Slack messaging provider adapter.
|
|
3
3
|
*
|
|
4
|
-
* Maps Slack API responses to the platform-agnostic messaging types
|
|
5
|
-
*
|
|
4
|
+
* Maps Slack API responses to the platform-agnostic messaging types and
|
|
5
|
+
* implements the MessagingProvider interface.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { OAuthConnection } from "../../../oauth/connection.js";
|
|
9
|
+
import { resolveOAuthConnection } from "../../../oauth/connection-resolver.js";
|
|
10
|
+
import { isProviderConnected } from "../../../oauth/oauth-store.js";
|
|
11
|
+
import { credentialKey } from "../../../security/credential-key.js";
|
|
12
|
+
import { getSecureKeyAsync } from "../../../security/secure-keys.js";
|
|
9
13
|
import type { MessagingProvider } from "../../provider.js";
|
|
10
14
|
import type {
|
|
11
15
|
ConnectionInfo,
|
|
@@ -112,6 +116,29 @@ export const slackProvider: MessagingProvider = {
|
|
|
112
116
|
credentialService: "integration:slack",
|
|
113
117
|
capabilities: new Set(["reactions", "threads", "leave_channel"]),
|
|
114
118
|
|
|
119
|
+
async isConnected(): Promise<boolean> {
|
|
120
|
+
// Socket Mode: check for bot token directly in credential store.
|
|
121
|
+
// The token is the source of truth; the slack_channel connection row
|
|
122
|
+
// is advisory (backfill can fail non-fatally on startup).
|
|
123
|
+
const botToken = await getSecureKeyAsync(
|
|
124
|
+
credentialKey("slack_channel", "bot_token"),
|
|
125
|
+
);
|
|
126
|
+
if (botToken) return true;
|
|
127
|
+
// Preserve existing OAuth path (integration:slack) for backwards compat.
|
|
128
|
+
return isProviderConnected("integration:slack");
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
async resolveConnection(account?: string): Promise<OAuthConnection | string> {
|
|
132
|
+
// Socket Mode: return raw bot token if available.
|
|
133
|
+
// Token presence is sufficient — no connection row required.
|
|
134
|
+
const botToken = await getSecureKeyAsync(
|
|
135
|
+
credentialKey("slack_channel", "bot_token"),
|
|
136
|
+
);
|
|
137
|
+
if (botToken) return botToken;
|
|
138
|
+
// Preserve existing OAuth path (integration:slack) for backwards compat.
|
|
139
|
+
return resolveOAuthConnection("integration:slack", { account });
|
|
140
|
+
},
|
|
141
|
+
|
|
115
142
|
async testConnection(
|
|
116
143
|
connectionOrToken: OAuthConnection | string,
|
|
117
144
|
): Promise<ConnectionInfo> {
|
|
@@ -8,12 +8,7 @@ let mockProvider: Record<string, unknown> | undefined;
|
|
|
8
8
|
let mockConnection: Record<string, unknown> | undefined;
|
|
9
9
|
let mockAccessToken: string | undefined;
|
|
10
10
|
let mockConfig: Record<string, unknown> = {};
|
|
11
|
-
let
|
|
12
|
-
enabled: false,
|
|
13
|
-
platformBaseUrl: "",
|
|
14
|
-
assistantApiKey: "",
|
|
15
|
-
};
|
|
16
|
-
let mockAssistantId = "";
|
|
11
|
+
let mockPlatformClient: Record<string, unknown> | null = null;
|
|
17
12
|
|
|
18
13
|
// ---------------------------------------------------------------------------
|
|
19
14
|
// Module mocks (must precede imports of the module under test)
|
|
@@ -48,12 +43,10 @@ mock.module("../config/loader.js", () => ({
|
|
|
48
43
|
getConfig: () => mockConfig,
|
|
49
44
|
}));
|
|
50
45
|
|
|
51
|
-
mock.module("../
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
mock.module("../providers/managed-proxy/context.js", () => ({
|
|
56
|
-
resolveManagedProxyContext: async () => mockManagedProxyCtx,
|
|
46
|
+
mock.module("../platform/client.js", () => ({
|
|
47
|
+
VellumPlatformClient: {
|
|
48
|
+
create: async () => mockPlatformClient,
|
|
49
|
+
},
|
|
57
50
|
}));
|
|
58
51
|
|
|
59
52
|
// ---------------------------------------------------------------------------
|
|
@@ -68,6 +61,22 @@ import { PlatformOAuthConnection } from "./platform-connection.js";
|
|
|
68
61
|
// Helpers
|
|
69
62
|
// ---------------------------------------------------------------------------
|
|
70
63
|
|
|
64
|
+
function makeMockClient() {
|
|
65
|
+
return {
|
|
66
|
+
baseUrl: "https://platform.example.com",
|
|
67
|
+
assistantApiKey: "sk-test-key",
|
|
68
|
+
platformAssistantId: "asst-123",
|
|
69
|
+
fetch: mock(async () => {
|
|
70
|
+
return new Response(
|
|
71
|
+
JSON.stringify({
|
|
72
|
+
results: [{ id: "platform-conn-1", account_label: null }],
|
|
73
|
+
}),
|
|
74
|
+
{ status: 200 },
|
|
75
|
+
);
|
|
76
|
+
}),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
71
80
|
function setupDefaults(): void {
|
|
72
81
|
mockProvider = {
|
|
73
82
|
providerKey: "integration:google",
|
|
@@ -100,12 +109,7 @@ function setupDefaults(): void {
|
|
|
100
109
|
"google-oauth": { mode: "managed" },
|
|
101
110
|
},
|
|
102
111
|
};
|
|
103
|
-
|
|
104
|
-
enabled: true,
|
|
105
|
-
platformBaseUrl: "https://platform.example.com",
|
|
106
|
-
assistantApiKey: "sk-test-key",
|
|
107
|
-
};
|
|
108
|
-
mockAssistantId = "asst-123";
|
|
112
|
+
mockPlatformClient = makeMockClient();
|
|
109
113
|
}
|
|
110
114
|
|
|
111
115
|
// ---------------------------------------------------------------------------
|