@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
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic compiler for the "Time-Relevant Context" section of the
|
|
3
|
+
* memory brief. Reads active `time_contexts` rows plus due-soon live
|
|
4
|
+
* schedule jobs, sorts them by urgency bucket, and caps the output.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { and, eq, gte, lte } from "drizzle-orm";
|
|
8
|
+
|
|
9
|
+
import { getDueSoonSchedules } from "../schedule/schedule-store.js";
|
|
10
|
+
import type { BriefEntry } from "./brief-formatting.js";
|
|
11
|
+
import { renderBriefSection } from "./brief-formatting.js";
|
|
12
|
+
import type { DrizzleDb } from "./db-connection.js";
|
|
13
|
+
import { timeContexts } from "./schema/memory-brief.js";
|
|
14
|
+
|
|
15
|
+
const MAX_ENTRIES = 3;
|
|
16
|
+
const SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
|
|
17
|
+
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
|
|
18
|
+
|
|
19
|
+
/** Urgency buckets — lower number = higher priority. */
|
|
20
|
+
const enum Bucket {
|
|
21
|
+
HappeningNow = 0,
|
|
22
|
+
Overdue = 1,
|
|
23
|
+
Within24h = 2,
|
|
24
|
+
Within7d = 3,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface Candidate {
|
|
28
|
+
bucket: Bucket;
|
|
29
|
+
/** Epoch ms timestamp used for secondary sort within a bucket. */
|
|
30
|
+
sortKey: number;
|
|
31
|
+
entry: BriefEntry;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ────────────────────────────────────────────────────────────────────
|
|
35
|
+
// Public API
|
|
36
|
+
// ────────────────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Compile the time-relevant brief section.
|
|
40
|
+
*
|
|
41
|
+
* @param db Drizzle database instance
|
|
42
|
+
* @param now Current epoch-ms timestamp (injectable for deterministic tests)
|
|
43
|
+
* @returns Markdown string for the section, or `null` if nothing qualifies
|
|
44
|
+
*/
|
|
45
|
+
export function compileTimeBrief(
|
|
46
|
+
db: DrizzleDb,
|
|
47
|
+
scopeId: string,
|
|
48
|
+
now: number,
|
|
49
|
+
): string | null {
|
|
50
|
+
const candidates: Candidate[] = [];
|
|
51
|
+
|
|
52
|
+
collectTimeContexts(db, scopeId, now, candidates);
|
|
53
|
+
collectDueSoonSchedules(now, candidates);
|
|
54
|
+
|
|
55
|
+
// Sort: primary = bucket ascending, secondary = sortKey ascending (sooner first)
|
|
56
|
+
candidates.sort((a, b) => a.bucket - b.bucket || a.sortKey - b.sortKey);
|
|
57
|
+
|
|
58
|
+
const entries = candidates.slice(0, MAX_ENTRIES).map((c) => c.entry);
|
|
59
|
+
return renderBriefSection("Time-Relevant Context", entries, MAX_ENTRIES);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ────────────────────────────────────────────────────────────────────
|
|
63
|
+
// Internal collectors
|
|
64
|
+
// ────────────────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
function collectTimeContexts(
|
|
67
|
+
db: DrizzleDb,
|
|
68
|
+
scopeId: string,
|
|
69
|
+
now: number,
|
|
70
|
+
out: Candidate[],
|
|
71
|
+
): void {
|
|
72
|
+
// Active time contexts: scopeId match AND activeFrom <= now AND activeUntil >= now
|
|
73
|
+
// Uses idx_time_contexts_scope_active_until composite index
|
|
74
|
+
const rows = db
|
|
75
|
+
.select()
|
|
76
|
+
.from(timeContexts)
|
|
77
|
+
.where(
|
|
78
|
+
and(
|
|
79
|
+
eq(timeContexts.scopeId, scopeId),
|
|
80
|
+
lte(timeContexts.activeFrom, now),
|
|
81
|
+
gte(timeContexts.activeUntil, now),
|
|
82
|
+
),
|
|
83
|
+
)
|
|
84
|
+
.all();
|
|
85
|
+
|
|
86
|
+
for (const row of rows) {
|
|
87
|
+
const remaining = row.activeUntil - now;
|
|
88
|
+
let bucket: Bucket;
|
|
89
|
+
|
|
90
|
+
if (row.activeFrom <= now && row.activeUntil >= now) {
|
|
91
|
+
// Currently active — classify by how much time remains
|
|
92
|
+
if (remaining <= ONE_DAY_MS) {
|
|
93
|
+
bucket = Bucket.HappeningNow;
|
|
94
|
+
} else if (remaining <= SEVEN_DAYS_MS) {
|
|
95
|
+
bucket = Bucket.Within24h;
|
|
96
|
+
} else {
|
|
97
|
+
bucket = Bucket.Within7d;
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
bucket = Bucket.Within7d;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
out.push({
|
|
104
|
+
bucket,
|
|
105
|
+
sortKey: row.activeUntil,
|
|
106
|
+
entry: { text: row.summary },
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function collectDueSoonSchedules(now: number, out: Candidate[]): void {
|
|
112
|
+
const jobs = getDueSoonSchedules(now, SEVEN_DAYS_MS);
|
|
113
|
+
|
|
114
|
+
for (const job of jobs) {
|
|
115
|
+
const delta = job.nextRunAt - now;
|
|
116
|
+
let bucket: Bucket;
|
|
117
|
+
|
|
118
|
+
if (delta <= 0) {
|
|
119
|
+
bucket = Bucket.Overdue;
|
|
120
|
+
} else if (delta <= ONE_DAY_MS) {
|
|
121
|
+
bucket = Bucket.Within24h;
|
|
122
|
+
} else {
|
|
123
|
+
bucket = Bucket.Within7d;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const label = formatScheduleLabel(job.name, job.nextRunAt, now);
|
|
127
|
+
out.push({
|
|
128
|
+
bucket,
|
|
129
|
+
sortKey: job.nextRunAt,
|
|
130
|
+
entry: { text: label },
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ────────────────────────────────────────────────────────────────────
|
|
136
|
+
// Formatting
|
|
137
|
+
// ────────────────────────────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
function formatScheduleLabel(
|
|
140
|
+
name: string,
|
|
141
|
+
nextRunAt: number,
|
|
142
|
+
now: number,
|
|
143
|
+
): string {
|
|
144
|
+
const delta = nextRunAt - now;
|
|
145
|
+
|
|
146
|
+
if (delta <= 0) {
|
|
147
|
+
return `Scheduled: "${name}" — overdue`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const minutes = Math.round(delta / 60_000);
|
|
151
|
+
if (minutes < 60) {
|
|
152
|
+
return `Scheduled: "${name}" — in ${minutes} minute${minutes === 1 ? "" : "s"}`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const hours = Math.round(delta / 3_600_000);
|
|
156
|
+
if (hours < 24) {
|
|
157
|
+
return `Scheduled: "${name}" — in ${hours} hour${hours === 1 ? "" : "s"}`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const days = Math.round(delta / 86_400_000);
|
|
161
|
+
return `Scheduled: "${name}" — in ${days} day${days === 1 ? "" : "s"}`;
|
|
162
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Top-level memory brief composer.
|
|
3
|
+
*
|
|
4
|
+
* Composes the "Time-Relevant Context" and "Open Loops" sections into a
|
|
5
|
+
* single `<memory_brief>` XML-wrapped block. Omits empty sections and
|
|
6
|
+
* returns an empty string when neither section has content.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { renderBriefSection } from "./brief-formatting.js";
|
|
10
|
+
import type { OpenLoopBriefResult } from "./brief-open-loops.js";
|
|
11
|
+
import { compileOpenLoopBrief } from "./brief-open-loops.js";
|
|
12
|
+
import { compileTimeBrief } from "./brief-time.js";
|
|
13
|
+
import type { DrizzleDb } from "./db-connection.js";
|
|
14
|
+
|
|
15
|
+
/** Maximum number of open-loop bullets to include in the brief. */
|
|
16
|
+
const MAX_OPEN_LOOP_ENTRIES = 5;
|
|
17
|
+
|
|
18
|
+
export interface MemoryBriefResult {
|
|
19
|
+
/** Rendered `<memory_brief>` block, or empty string if nothing to show. */
|
|
20
|
+
text: string;
|
|
21
|
+
/** Forwarded from `compileOpenLoopBrief` for downstream tracking. */
|
|
22
|
+
resurfacedLoopId: string | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Compile the full memory brief block.
|
|
27
|
+
*
|
|
28
|
+
* @param db Drizzle database instance
|
|
29
|
+
* @param scopeId Memory scope (e.g. assistant instance ID)
|
|
30
|
+
* @param userMessageId Current user message ID — used for deterministic
|
|
31
|
+
* open-loop resurfacing
|
|
32
|
+
* @param now Current epoch-ms timestamp (injectable for tests)
|
|
33
|
+
* @returns `{ text, resurfacedLoopId }` — `text` is the
|
|
34
|
+
* rendered `<memory_brief>` block or empty string
|
|
35
|
+
*/
|
|
36
|
+
export function compileMemoryBrief(
|
|
37
|
+
db: DrizzleDb,
|
|
38
|
+
scopeId: string,
|
|
39
|
+
userMessageId: string,
|
|
40
|
+
now: number = Date.now(),
|
|
41
|
+
): MemoryBriefResult {
|
|
42
|
+
// Compile individual sections
|
|
43
|
+
const timeSection = compileTimeBrief(db, scopeId, now);
|
|
44
|
+
|
|
45
|
+
const openLoopResult: OpenLoopBriefResult = compileOpenLoopBrief(
|
|
46
|
+
scopeId,
|
|
47
|
+
userMessageId,
|
|
48
|
+
now,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// Convert open-loop bullets to a rendered section via the shared helper
|
|
52
|
+
const openLoopEntries = openLoopResult.bullets.map((b) => ({
|
|
53
|
+
text: b.summary,
|
|
54
|
+
}));
|
|
55
|
+
const openLoopSection = renderBriefSection(
|
|
56
|
+
"Open Loops",
|
|
57
|
+
openLoopEntries,
|
|
58
|
+
MAX_OPEN_LOOP_ENTRIES,
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// Collect non-empty sections
|
|
62
|
+
const sections: string[] = [];
|
|
63
|
+
if (timeSection) sections.push(timeSection);
|
|
64
|
+
if (openLoopSection) sections.push(openLoopSection);
|
|
65
|
+
|
|
66
|
+
// If no sections have content, return empty
|
|
67
|
+
if (sections.length === 0) {
|
|
68
|
+
return { text: "", resurfacedLoopId: openLoopResult.resurfacedLoopId };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const body = sections.join("\n\n");
|
|
72
|
+
const text = `<memory_brief>\n${body}\n</memory_brief>`;
|
|
73
|
+
|
|
74
|
+
return { text, resurfacedLoopId: openLoopResult.resurfacedLoopId };
|
|
75
|
+
}
|