@vellumai/assistant 0.3.3 → 0.3.5
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 +2 -0
- package/README.md +45 -18
- package/package.json +1 -1
- package/scripts/ipc/generate-swift.ts +13 -0
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +100 -0
- package/src/__tests__/approval-hardcoded-copy-guard.test.ts +41 -0
- package/src/__tests__/approval-message-composer.test.ts +253 -0
- package/src/__tests__/call-domain.test.ts +12 -2
- package/src/__tests__/call-orchestrator.test.ts +391 -1
- package/src/__tests__/call-routes-http.test.ts +27 -2
- package/src/__tests__/channel-approval-routes.test.ts +397 -135
- package/src/__tests__/channel-approvals.test.ts +99 -3
- package/src/__tests__/channel-delivery-store.test.ts +30 -4
- package/src/__tests__/channel-guardian.test.ts +261 -22
- package/src/__tests__/channel-readiness-service.test.ts +257 -0
- package/src/__tests__/config-schema.test.ts +2 -1
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/daemon-lifecycle.test.ts +636 -0
- package/src/__tests__/dictation-mode-detection.test.ts +63 -0
- package/src/__tests__/entity-search.test.ts +615 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +19 -13
- package/src/__tests__/handlers-twilio-config.test.ts +480 -0
- package/src/__tests__/ipc-snapshot.test.ts +63 -0
- package/src/__tests__/messaging-send-tool.test.ts +65 -0
- package/src/__tests__/run-orchestrator-assistant-events.test.ts +4 -0
- package/src/__tests__/run-orchestrator.test.ts +22 -0
- package/src/__tests__/secret-scanner.test.ts +223 -0
- package/src/__tests__/session-runtime-assembly.test.ts +85 -1
- package/src/__tests__/shell-parser-property.test.ts +357 -2
- package/src/__tests__/sms-messaging-provider.test.ts +125 -0
- package/src/__tests__/system-prompt.test.ts +25 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +34 -1
- package/src/__tests__/twilio-routes.test.ts +39 -3
- package/src/__tests__/twitter-cli-error-shaping.test.ts +2 -2
- package/src/__tests__/user-reference.test.ts +68 -0
- package/src/__tests__/web-search.test.ts +1 -1
- package/src/__tests__/work-item-output.test.ts +110 -0
- package/src/calls/call-domain.ts +8 -5
- package/src/calls/call-orchestrator.ts +85 -22
- package/src/calls/twilio-config.ts +17 -11
- package/src/calls/twilio-rest.ts +276 -0
- package/src/calls/twilio-routes.ts +39 -1
- package/src/cli/map.ts +6 -0
- package/src/commands/__tests__/cc-command-registry.test.ts +67 -0
- package/src/commands/cc-command-registry.ts +14 -1
- package/src/config/bundled-skills/claude-code/TOOLS.json +10 -3
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +15 -0
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +56 -0
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +185 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +199 -0
- package/src/config/bundled-skills/media-processing/TOOLS.json +320 -0
- package/src/config/bundled-skills/media-processing/services/capability-registry.ts +137 -0
- package/src/config/bundled-skills/media-processing/services/event-detection-service.ts +280 -0
- package/src/config/bundled-skills/media-processing/services/feedback-aggregation.ts +144 -0
- package/src/config/bundled-skills/media-processing/services/feedback-store.ts +136 -0
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +261 -0
- package/src/config/bundled-skills/media-processing/services/retrieval-service.ts +95 -0
- package/src/config/bundled-skills/media-processing/services/timeline-service.ts +267 -0
- package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +301 -0
- package/src/config/bundled-skills/media-processing/tools/detect-events.ts +110 -0
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +190 -0
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +195 -0
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +197 -0
- package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +166 -0
- package/src/config/bundled-skills/media-processing/tools/media-status.ts +75 -0
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +300 -0
- package/src/config/bundled-skills/media-processing/tools/recalibrate.ts +235 -0
- package/src/config/bundled-skills/media-processing/tools/select-tracking-profile.ts +142 -0
- package/src/config/bundled-skills/media-processing/tools/submit-feedback.ts +150 -0
- package/src/config/bundled-skills/messaging/SKILL.md +24 -5
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
- package/src/config/bundled-skills/twitter/SKILL.md +19 -3
- package/src/config/defaults.ts +2 -1
- package/src/config/schema.ts +9 -3
- package/src/config/skills.ts +5 -32
- package/src/config/system-prompt.ts +40 -0
- package/src/config/templates/IDENTITY.md +2 -2
- package/src/config/user-reference.ts +29 -0
- package/src/config/vellum-skills/catalog.json +58 -0
- package/src/config/vellum-skills/google-oauth-setup/SKILL.md +3 -3
- package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +3 -3
- package/src/config/vellum-skills/sms-setup/SKILL.md +118 -0
- package/src/config/vellum-skills/telegram-setup/SKILL.md +6 -1
- package/src/config/vellum-skills/twilio-setup/SKILL.md +76 -6
- package/src/daemon/auth-manager.ts +103 -0
- package/src/daemon/computer-use-session.ts +8 -1
- package/src/daemon/config-watcher.ts +253 -0
- package/src/daemon/handlers/config.ts +819 -22
- package/src/daemon/handlers/dictation.ts +182 -0
- package/src/daemon/handlers/identity.ts +14 -23
- package/src/daemon/handlers/index.ts +2 -0
- package/src/daemon/handlers/sessions.ts +2 -0
- package/src/daemon/handlers/shared.ts +3 -0
- package/src/daemon/handlers/skills.ts +6 -7
- package/src/daemon/handlers/work-items.ts +15 -7
- package/src/daemon/ipc-contract-inventory.json +10 -0
- package/src/daemon/ipc-contract.ts +114 -4
- package/src/daemon/ipc-handler.ts +87 -0
- package/src/daemon/lifecycle.ts +18 -4
- package/src/daemon/ride-shotgun-handler.ts +11 -1
- package/src/daemon/server.ts +111 -504
- package/src/daemon/session-agent-loop.ts +10 -15
- package/src/daemon/session-runtime-assembly.ts +115 -44
- package/src/daemon/session-tool-setup.ts +2 -0
- package/src/daemon/session.ts +19 -2
- package/src/inbound/public-ingress-urls.ts +3 -3
- package/src/memory/channel-guardian-store.ts +2 -1
- package/src/memory/db-connection.ts +28 -0
- package/src/memory/db-init.ts +1163 -0
- package/src/memory/db.ts +2 -2007
- package/src/memory/embedding-backend.ts +79 -11
- package/src/memory/indexer.ts +2 -0
- package/src/memory/job-handlers/media-processing.ts +100 -0
- package/src/memory/job-utils.ts +64 -4
- package/src/memory/jobs-store.ts +2 -1
- package/src/memory/jobs-worker.ts +11 -1
- package/src/memory/media-store.ts +759 -0
- package/src/memory/recall-cache.ts +107 -0
- package/src/memory/retriever.ts +36 -2
- package/src/memory/schema-migration.ts +984 -0
- package/src/memory/schema.ts +99 -0
- package/src/memory/search/entity.ts +208 -25
- package/src/memory/search/ranking.ts +6 -1
- package/src/memory/search/types.ts +26 -0
- package/src/messaging/provider-types.ts +2 -0
- package/src/messaging/providers/sms/adapter.ts +204 -0
- package/src/messaging/providers/sms/client.ts +93 -0
- package/src/messaging/providers/sms/types.ts +7 -0
- package/src/permissions/checker.ts +16 -2
- package/src/permissions/prompter.ts +14 -3
- package/src/permissions/trust-store.ts +7 -0
- package/src/runtime/approval-message-composer.ts +143 -0
- package/src/runtime/channel-approvals.ts +29 -7
- package/src/runtime/channel-guardian-service.ts +44 -18
- package/src/runtime/channel-readiness-service.ts +292 -0
- package/src/runtime/channel-readiness-types.ts +29 -0
- package/src/runtime/gateway-client.ts +2 -1
- package/src/runtime/http-server.ts +65 -28
- package/src/runtime/http-types.ts +3 -0
- package/src/runtime/routes/call-routes.ts +2 -1
- package/src/runtime/routes/channel-routes.ts +237 -103
- package/src/runtime/routes/run-routes.ts +7 -1
- package/src/runtime/run-orchestrator.ts +43 -3
- package/src/security/secret-scanner.ts +218 -0
- package/src/skills/frontmatter.ts +63 -0
- package/src/skills/slash-commands.ts +23 -0
- package/src/skills/vellum-catalog-remote.ts +107 -0
- package/src/tools/assets/materialize.ts +2 -2
- package/src/tools/browser/auto-navigate.ts +132 -24
- package/src/tools/browser/browser-manager.ts +67 -61
- package/src/tools/calls/call-start.ts +1 -0
- package/src/tools/claude-code/claude-code.ts +55 -3
- package/src/tools/credentials/vault.ts +1 -1
- package/src/tools/execution-target.ts +11 -1
- package/src/tools/executor.ts +10 -2
- package/src/tools/network/web-search.ts +1 -1
- package/src/tools/skills/vellum-catalog.ts +61 -156
- package/src/tools/terminal/parser.ts +21 -5
- package/src/tools/types.ts +2 -0
- package/src/twitter/router.ts +1 -1
- package/src/util/platform.ts +43 -1
- package/src/util/retry.ts +4 -4
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
import type { MemoryRecallResult, MemoryRecallOptions } from './search/types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* In-memory cache for memory recall results.
|
|
6
|
+
*
|
|
7
|
+
* The full retrieval pipeline (FTS5 + Qdrant + entity graph + RRF merge) is
|
|
8
|
+
* expensive. When the same query is issued multiple turns in a row (common
|
|
9
|
+
* when the conversation context hasn't changed), we can serve the cached
|
|
10
|
+
* result instantly.
|
|
11
|
+
*
|
|
12
|
+
* Invalidation: a monotonic version counter is bumped whenever new memory
|
|
13
|
+
* is indexed (segments, items, embeddings). Cache entries are only valid
|
|
14
|
+
* when their version matches the current global version.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
interface CacheEntry {
|
|
18
|
+
version: number;
|
|
19
|
+
createdAt: number;
|
|
20
|
+
result: MemoryRecallResult;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const MAX_ENTRIES = 32;
|
|
24
|
+
const TTL_MS = 60_000; // 60 seconds
|
|
25
|
+
|
|
26
|
+
let _version = 0;
|
|
27
|
+
const _cache = new Map<string, CacheEntry>();
|
|
28
|
+
|
|
29
|
+
/** Bump the global memory version, invalidating all cached recall results. */
|
|
30
|
+
export function bumpMemoryVersion(): void {
|
|
31
|
+
_version++;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Return the current memory version (for snapshot-based staleness checks). */
|
|
35
|
+
export function getMemoryVersion(): number {
|
|
36
|
+
return _version;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Build a deterministic cache key from the recall inputs. */
|
|
40
|
+
function buildCacheKey(
|
|
41
|
+
query: string,
|
|
42
|
+
conversationId: string,
|
|
43
|
+
options?: MemoryRecallOptions,
|
|
44
|
+
): string {
|
|
45
|
+
const parts = [
|
|
46
|
+
query,
|
|
47
|
+
conversationId,
|
|
48
|
+
options?.scopeId ?? '',
|
|
49
|
+
options?.scopePolicyOverride
|
|
50
|
+
? `${options.scopePolicyOverride.scopeId}:${options.scopePolicyOverride.fallbackToDefault}`
|
|
51
|
+
: '',
|
|
52
|
+
options?.excludeMessageIds ? [...options.excludeMessageIds].sort().join(',') : '',
|
|
53
|
+
options?.maxInjectTokensOverride != null ? String(options.maxInjectTokensOverride) : '',
|
|
54
|
+
];
|
|
55
|
+
return createHash('sha256').update(parts.join('\0')).digest('hex');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Look up a cached recall result. Returns undefined on miss or stale entry. */
|
|
59
|
+
export function getCachedRecall(
|
|
60
|
+
query: string,
|
|
61
|
+
conversationId: string,
|
|
62
|
+
options?: MemoryRecallOptions,
|
|
63
|
+
): MemoryRecallResult | undefined {
|
|
64
|
+
const key = buildCacheKey(query, conversationId, options);
|
|
65
|
+
const entry = _cache.get(key);
|
|
66
|
+
if (!entry) return undefined;
|
|
67
|
+
if (entry.version !== _version || Date.now() - entry.createdAt > TTL_MS) {
|
|
68
|
+
_cache.delete(key);
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
return entry.result;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Store a recall result in the cache. Evicts oldest entries when full.
|
|
76
|
+
*
|
|
77
|
+
* When `snapshotVersion` is provided, the entry is only stored if the
|
|
78
|
+
* snapshot still matches the current global version — this prevents a
|
|
79
|
+
* stale result from being cached under a version that was bumped while
|
|
80
|
+
* the retrieval pipeline was in flight.
|
|
81
|
+
*/
|
|
82
|
+
export function setCachedRecall(
|
|
83
|
+
query: string,
|
|
84
|
+
conversationId: string,
|
|
85
|
+
options: MemoryRecallOptions | undefined,
|
|
86
|
+
result: MemoryRecallResult,
|
|
87
|
+
snapshotVersion?: number,
|
|
88
|
+
): void {
|
|
89
|
+
// If a snapshot version was provided, only cache when it still matches
|
|
90
|
+
// the current version — otherwise the result may be stale.
|
|
91
|
+
if (snapshotVersion !== undefined && snapshotVersion !== _version) return;
|
|
92
|
+
|
|
93
|
+
const key = buildCacheKey(query, conversationId, options);
|
|
94
|
+
|
|
95
|
+
// Evict oldest entries if at capacity
|
|
96
|
+
if (_cache.size >= MAX_ENTRIES && !_cache.has(key)) {
|
|
97
|
+
const oldest = _cache.keys().next().value;
|
|
98
|
+
if (oldest !== undefined) _cache.delete(oldest);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
_cache.set(key, { version: _version, createdAt: Date.now(), result });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Clear the entire cache (useful for testing). */
|
|
105
|
+
export function clearRecallCache(): void {
|
|
106
|
+
_cache.clear();
|
|
107
|
+
}
|
package/src/memory/retriever.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { semanticSearch, isQdrantConnectionError } from './search/semantic.js';
|
|
|
19
19
|
import { entitySearch } from './search/entity.js';
|
|
20
20
|
import { mergeCandidates, applySourceCaps, rerankWithLLM, trimToTokenBudget, markItemUsage } from './search/ranking.js';
|
|
21
21
|
import { buildInjectedText, MEMORY_CONTEXT_ACK } from './search/formatting.js';
|
|
22
|
+
import { getCachedRecall, setCachedRecall, getMemoryVersion } from './recall-cache.js';
|
|
22
23
|
|
|
23
24
|
// Re-export public types and functions so existing importers continue to work
|
|
24
25
|
export type {
|
|
@@ -161,10 +162,12 @@ async function collectAndMergeCandidates(
|
|
|
161
162
|
|
|
162
163
|
// -- Phase 2: expensive searches (skipped on early termination) --
|
|
163
164
|
let semantic: Candidate[] = [];
|
|
165
|
+
let semanticSearchFailed = false;
|
|
164
166
|
if (queryVector && !canTerminateEarly) {
|
|
165
167
|
try {
|
|
166
168
|
semantic = await semanticSearch(queryVector, opts?.provider ?? 'unknown', opts?.model ?? 'unknown', config.memory.retrieval.semanticTopK, excludeMessageIds, scopeIds);
|
|
167
169
|
} catch (err) {
|
|
170
|
+
semanticSearchFailed = true;
|
|
168
171
|
if (isQdrantConnectionError(err)) {
|
|
169
172
|
log.warn({ err }, 'Qdrant is unavailable — semantic search disabled, memory recall will be degraded');
|
|
170
173
|
} else {
|
|
@@ -174,6 +177,7 @@ async function collectAndMergeCandidates(
|
|
|
174
177
|
}
|
|
175
178
|
|
|
176
179
|
let entity: Candidate[] = [];
|
|
180
|
+
let candidateDepths: Map<string, number> | undefined;
|
|
177
181
|
let relationSeedEntityCount = 0;
|
|
178
182
|
let relationTraversedEdgeCount = 0;
|
|
179
183
|
let relationNeighborEntityCount = 0;
|
|
@@ -186,6 +190,7 @@ async function collectAndMergeCandidates(
|
|
|
186
190
|
excludeMessageIds,
|
|
187
191
|
);
|
|
188
192
|
entity = entitySearchResult.candidates;
|
|
193
|
+
candidateDepths = entitySearchResult.candidateDepths;
|
|
189
194
|
relationSeedEntityCount = entitySearchResult.relationSeedEntityCount;
|
|
190
195
|
relationTraversedEdgeCount = entitySearchResult.relationTraversedEdgeCount;
|
|
191
196
|
relationNeighborEntityCount = entitySearchResult.relationNeighborEntityCount;
|
|
@@ -202,7 +207,10 @@ async function collectAndMergeCandidates(
|
|
|
202
207
|
const relationScoreMultiplier = config.memory.entity.enabled && config.memory.entity.relationRetrieval.enabled
|
|
203
208
|
? config.memory.entity.relationRetrieval.neighborScoreMultiplier
|
|
204
209
|
: undefined;
|
|
205
|
-
const
|
|
210
|
+
const depthMap = config.memory.entity.enabled && config.memory.entity.relationRetrieval.depthDecay
|
|
211
|
+
? candidateDepths
|
|
212
|
+
: undefined;
|
|
213
|
+
const merged = mergeCandidates(lexical, semantic, recency, [...entity, ...directItems], config.memory.retrieval.freshness, relationScoreMultiplier, depthMap);
|
|
206
214
|
|
|
207
215
|
return {
|
|
208
216
|
lexical,
|
|
@@ -214,6 +222,7 @@ async function collectAndMergeCandidates(
|
|
|
214
222
|
relationNeighborEntityCount,
|
|
215
223
|
relationExpandedItemCount,
|
|
216
224
|
earlyTerminated: canTerminateEarly,
|
|
225
|
+
semanticSearchFailed,
|
|
217
226
|
merged,
|
|
218
227
|
};
|
|
219
228
|
}
|
|
@@ -225,6 +234,7 @@ export async function buildMemoryRecall(
|
|
|
225
234
|
options?: MemoryRecallOptions,
|
|
226
235
|
): Promise<MemoryRecallResult> {
|
|
227
236
|
const start = Date.now();
|
|
237
|
+
const versionSnapshot = getMemoryVersion();
|
|
228
238
|
const excludeMessageIds = options?.excludeMessageIds?.filter((id) => id.length > 0) ?? [];
|
|
229
239
|
const signal = options?.signal;
|
|
230
240
|
if (!config.memory.enabled) {
|
|
@@ -234,6 +244,14 @@ export async function buildMemoryRecall(
|
|
|
234
244
|
return emptyResult({ enabled: true, degraded: false, reason: 'memory.aborted', latencyMs: Date.now() - start });
|
|
235
245
|
}
|
|
236
246
|
|
|
247
|
+
// Check recall cache — serves identical results instantly when the query
|
|
248
|
+
// and memory state haven't changed since the last recall.
|
|
249
|
+
const cached = getCachedRecall(query, conversationId, options);
|
|
250
|
+
if (cached) {
|
|
251
|
+
log.debug({ query: truncate(query, 120), latencyMs: Date.now() - start }, 'Memory recall served from cache');
|
|
252
|
+
return { ...cached, latencyMs: Date.now() - start };
|
|
253
|
+
}
|
|
254
|
+
|
|
237
255
|
const backendStatus = getMemoryBackendStatus(config);
|
|
238
256
|
let queryVector: number[] | null = null;
|
|
239
257
|
let provider: string | undefined;
|
|
@@ -326,7 +344,15 @@ export async function buildMemoryRecall(
|
|
|
326
344
|
relationNeighborEntityCount,
|
|
327
345
|
relationExpandedItemCount,
|
|
328
346
|
earlyTerminated,
|
|
347
|
+
semanticSearchFailed,
|
|
329
348
|
} = collected;
|
|
349
|
+
|
|
350
|
+
// Mark as degraded when semantic search failed — the recall is based on
|
|
351
|
+
// lexical/recency only and should not be cached.
|
|
352
|
+
if (semanticSearchFailed) {
|
|
353
|
+
degraded = true;
|
|
354
|
+
reason = reason ?? 'memory.semantic_search_failure';
|
|
355
|
+
}
|
|
330
356
|
let merged = applySourceCaps(collected.merged, config);
|
|
331
357
|
|
|
332
358
|
// LLM re-ranking: send top candidates to Haiku for relevance scoring
|
|
@@ -395,7 +421,7 @@ export async function buildMemoryRecall(
|
|
|
395
421
|
latencyMs,
|
|
396
422
|
}, 'Memory recall completed');
|
|
397
423
|
|
|
398
|
-
|
|
424
|
+
const result: MemoryRecallResult = {
|
|
399
425
|
enabled: true,
|
|
400
426
|
degraded,
|
|
401
427
|
reason,
|
|
@@ -418,6 +444,14 @@ export async function buildMemoryRecall(
|
|
|
418
444
|
latencyMs,
|
|
419
445
|
topCandidates,
|
|
420
446
|
};
|
|
447
|
+
|
|
448
|
+
// Only cache non-degraded results — degraded results (e.g. lexical-only
|
|
449
|
+
// fallback when embeddings fail) would delay quality recovery once the
|
|
450
|
+
// embedding backend comes back.
|
|
451
|
+
if (!result.degraded) {
|
|
452
|
+
setCachedRecall(query, conversationId, options, result, versionSnapshot);
|
|
453
|
+
}
|
|
454
|
+
return result;
|
|
421
455
|
}
|
|
422
456
|
|
|
423
457
|
export function stripMemoryRecallMessages<T extends { role: 'user' | 'assistant'; content: Array<{ type: string; text?: string }> }>(
|