@vellumai/assistant 0.4.49 → 0.4.50

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.
Files changed (239) hide show
  1. package/ARCHITECTURE.md +24 -33
  2. package/README.md +3 -3
  3. package/docs/architecture/memory.md +180 -119
  4. package/package.json +2 -2
  5. package/src/__tests__/agent-loop.test.ts +3 -1
  6. package/src/__tests__/anthropic-provider.test.ts +114 -23
  7. package/src/__tests__/approval-cascade.test.ts +1 -15
  8. package/src/__tests__/approval-routes-http.test.ts +2 -0
  9. package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
  10. package/src/__tests__/canonical-guardian-store.test.ts +95 -0
  11. package/src/__tests__/checker.test.ts +13 -0
  12. package/src/__tests__/config-schema.test.ts +1 -68
  13. package/src/__tests__/context-memory-e2e.test.ts +11 -100
  14. package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
  15. package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
  16. package/src/__tests__/credential-security-e2e.test.ts +1 -0
  17. package/src/__tests__/credential-vault-unit.test.ts +4 -0
  18. package/src/__tests__/credential-vault.test.ts +13 -1
  19. package/src/__tests__/cu-unified-flow.test.ts +532 -0
  20. package/src/__tests__/date-context.test.ts +93 -77
  21. package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
  22. package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
  23. package/src/__tests__/history-repair.test.ts +245 -0
  24. package/src/__tests__/host-cu-proxy.test.ts +165 -3
  25. package/src/__tests__/http-user-message-parity.test.ts +1 -0
  26. package/src/__tests__/invite-redemption-service.test.ts +65 -1
  27. package/src/__tests__/keychain-broker-client.test.ts +4 -4
  28. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
  29. package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
  30. package/src/__tests__/memory-recall-quality.test.ts +244 -407
  31. package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
  32. package/src/__tests__/memory-regressions.test.ts +477 -2841
  33. package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
  34. package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
  35. package/src/__tests__/mime-builder.test.ts +28 -0
  36. package/src/__tests__/native-web-search.test.ts +1 -0
  37. package/src/__tests__/oauth-cli.test.ts +572 -5
  38. package/src/__tests__/oauth-store.test.ts +120 -6
  39. package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
  40. package/src/__tests__/registry.test.ts +0 -1
  41. package/src/__tests__/relay-server.test.ts +46 -1
  42. package/src/__tests__/schedule-tools.test.ts +32 -0
  43. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  44. package/src/__tests__/secret-onetime-send.test.ts +1 -0
  45. package/src/__tests__/secure-keys.test.ts +7 -2
  46. package/src/__tests__/send-endpoint-busy.test.ts +3 -0
  47. package/src/__tests__/session-abort-tool-results.test.ts +1 -14
  48. package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
  49. package/src/__tests__/session-agent-loop.test.ts +19 -15
  50. package/src/__tests__/session-confirmation-signals.test.ts +1 -15
  51. package/src/__tests__/session-error.test.ts +124 -2
  52. package/src/__tests__/session-history-web-search.test.ts +918 -0
  53. package/src/__tests__/session-pre-run-repair.test.ts +1 -14
  54. package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
  55. package/src/__tests__/session-queue.test.ts +37 -27
  56. package/src/__tests__/session-runtime-assembly.test.ts +54 -0
  57. package/src/__tests__/session-slash-known.test.ts +1 -15
  58. package/src/__tests__/session-slash-queue.test.ts +1 -15
  59. package/src/__tests__/session-slash-unknown.test.ts +1 -15
  60. package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
  61. package/src/__tests__/session-workspace-injection.test.ts +3 -37
  62. package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
  63. package/src/__tests__/skills-install-extract.test.ts +93 -0
  64. package/src/__tests__/skillssh-registry.test.ts +451 -0
  65. package/src/__tests__/trust-store.test.ts +15 -0
  66. package/src/__tests__/voice-invite-redemption.test.ts +32 -1
  67. package/src/agent/ax-tree-compaction.test.ts +51 -0
  68. package/src/agent/loop.ts +39 -12
  69. package/src/approvals/AGENTS.md +1 -1
  70. package/src/approvals/guardian-request-resolvers.ts +14 -2
  71. package/src/bundler/compiler-tools.ts +66 -2
  72. package/src/calls/call-domain.ts +132 -0
  73. package/src/calls/call-store.ts +6 -0
  74. package/src/calls/relay-server.ts +43 -5
  75. package/src/calls/relay-setup-router.ts +17 -1
  76. package/src/calls/twilio-config.ts +1 -1
  77. package/src/calls/types.ts +3 -1
  78. package/src/cli/commands/doctor.ts +4 -3
  79. package/src/cli/commands/mcp.ts +46 -59
  80. package/src/cli/commands/memory.ts +16 -165
  81. package/src/cli/commands/oauth/apps.ts +31 -2
  82. package/src/cli/commands/oauth/connections.ts +431 -97
  83. package/src/cli/commands/oauth/providers.ts +15 -1
  84. package/src/cli/commands/sessions.ts +5 -2
  85. package/src/cli/commands/skills.ts +173 -1
  86. package/src/cli/http-client.ts +0 -20
  87. package/src/cli/main-screen.tsx +2 -2
  88. package/src/cli/program.ts +5 -6
  89. package/src/cli.ts +4 -10
  90. package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
  91. package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
  92. package/src/config/bundled-tool-registry.ts +2 -5
  93. package/src/config/schema.ts +1 -12
  94. package/src/config/schemas/memory-lifecycle.ts +0 -9
  95. package/src/config/schemas/memory-processing.ts +0 -180
  96. package/src/config/schemas/memory-retrieval.ts +32 -104
  97. package/src/config/schemas/memory.ts +0 -10
  98. package/src/config/types.ts +0 -4
  99. package/src/context/window-manager.ts +4 -1
  100. package/src/daemon/config-watcher.ts +61 -3
  101. package/src/daemon/daemon-control.ts +1 -1
  102. package/src/daemon/date-context.ts +114 -31
  103. package/src/daemon/handlers/sessions.ts +18 -13
  104. package/src/daemon/handlers/skills.ts +20 -1
  105. package/src/daemon/history-repair.ts +72 -8
  106. package/src/daemon/host-cu-proxy.ts +55 -26
  107. package/src/daemon/lifecycle.ts +31 -3
  108. package/src/daemon/mcp-reload-service.ts +2 -2
  109. package/src/daemon/message-types/computer-use.ts +1 -12
  110. package/src/daemon/message-types/memory.ts +4 -16
  111. package/src/daemon/message-types/messages.ts +1 -0
  112. package/src/daemon/message-types/sessions.ts +4 -0
  113. package/src/daemon/server.ts +12 -1
  114. package/src/daemon/session-agent-loop-handlers.ts +38 -0
  115. package/src/daemon/session-agent-loop.ts +334 -48
  116. package/src/daemon/session-error.ts +89 -6
  117. package/src/daemon/session-history.ts +17 -7
  118. package/src/daemon/session-media-retry.ts +6 -2
  119. package/src/daemon/session-memory.ts +69 -149
  120. package/src/daemon/session-process.ts +10 -1
  121. package/src/daemon/session-runtime-assembly.ts +49 -19
  122. package/src/daemon/session-surfaces.ts +4 -1
  123. package/src/daemon/session-tool-setup.ts +7 -1
  124. package/src/daemon/session.ts +12 -2
  125. package/src/instrument.ts +61 -1
  126. package/src/memory/admin.ts +2 -191
  127. package/src/memory/canonical-guardian-store.ts +38 -2
  128. package/src/memory/conversation-crud.ts +0 -33
  129. package/src/memory/conversation-queries.ts +22 -3
  130. package/src/memory/db-init.ts +28 -0
  131. package/src/memory/embedding-backend.ts +84 -8
  132. package/src/memory/embedding-types.ts +9 -1
  133. package/src/memory/indexer.ts +7 -46
  134. package/src/memory/items-extractor.ts +274 -76
  135. package/src/memory/job-handlers/backfill.ts +2 -127
  136. package/src/memory/job-handlers/cleanup.ts +2 -16
  137. package/src/memory/job-handlers/extraction.ts +2 -138
  138. package/src/memory/job-handlers/index-maintenance.ts +1 -6
  139. package/src/memory/job-handlers/summarization.ts +3 -148
  140. package/src/memory/job-utils.ts +21 -59
  141. package/src/memory/jobs-store.ts +1 -159
  142. package/src/memory/jobs-worker.ts +9 -52
  143. package/src/memory/migrations/104-core-indexes.ts +3 -3
  144. package/src/memory/migrations/149-oauth-tables.ts +2 -0
  145. package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
  146. package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
  147. package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
  148. package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
  149. package/src/memory/migrations/154-drop-fts.ts +20 -0
  150. package/src/memory/migrations/155-drop-conflicts.ts +7 -0
  151. package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
  152. package/src/memory/migrations/index.ts +7 -0
  153. package/src/memory/qdrant-client.ts +148 -51
  154. package/src/memory/raw-query.ts +1 -1
  155. package/src/memory/retriever.test.ts +294 -273
  156. package/src/memory/retriever.ts +421 -645
  157. package/src/memory/schema/calls.ts +2 -0
  158. package/src/memory/schema/memory-core.ts +3 -48
  159. package/src/memory/schema/oauth.ts +2 -0
  160. package/src/memory/search/formatting.ts +263 -176
  161. package/src/memory/search/lexical.ts +1 -254
  162. package/src/memory/search/ranking.ts +0 -455
  163. package/src/memory/search/semantic.ts +100 -14
  164. package/src/memory/search/staleness.ts +47 -0
  165. package/src/memory/search/tier-classifier.ts +21 -0
  166. package/src/memory/search/types.ts +15 -77
  167. package/src/memory/task-memory-cleanup.ts +4 -6
  168. package/src/messaging/providers/gmail/mime-builder.ts +17 -7
  169. package/src/oauth/byo-connection.test.ts +8 -1
  170. package/src/oauth/oauth-store.ts +113 -27
  171. package/src/oauth/seed-providers.ts +6 -0
  172. package/src/oauth/token-persistence.ts +11 -3
  173. package/src/permissions/defaults.ts +1 -0
  174. package/src/permissions/trust-store.ts +23 -1
  175. package/src/playbooks/playbook-compiler.ts +1 -1
  176. package/src/prompts/system-prompt.ts +18 -2
  177. package/src/providers/anthropic/client.ts +56 -126
  178. package/src/providers/types.ts +7 -1
  179. package/src/runtime/AGENTS.md +9 -0
  180. package/src/runtime/auth/route-policy.ts +6 -3
  181. package/src/runtime/guardian-reply-router.ts +24 -22
  182. package/src/runtime/http-server.ts +2 -2
  183. package/src/runtime/invite-redemption-service.ts +19 -1
  184. package/src/runtime/invite-service.ts +25 -0
  185. package/src/runtime/pending-interactions.ts +2 -2
  186. package/src/runtime/routes/brain-graph-routes.ts +10 -90
  187. package/src/runtime/routes/conversation-routes.ts +9 -1
  188. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
  189. package/src/runtime/routes/memory-item-routes.test.ts +754 -0
  190. package/src/runtime/routes/memory-item-routes.ts +503 -0
  191. package/src/runtime/routes/session-management-routes.ts +3 -3
  192. package/src/runtime/routes/settings-routes.ts +2 -2
  193. package/src/runtime/routes/trust-rules-routes.ts +14 -0
  194. package/src/runtime/routes/workspace-routes.ts +2 -1
  195. package/src/security/keychain-broker-client.ts +17 -4
  196. package/src/security/secure-keys.ts +25 -3
  197. package/src/security/token-manager.ts +36 -36
  198. package/src/skills/catalog-install.ts +74 -18
  199. package/src/skills/skillssh-registry.ts +503 -0
  200. package/src/tools/assets/search.ts +5 -1
  201. package/src/tools/computer-use/definitions.ts +0 -10
  202. package/src/tools/computer-use/registry.ts +1 -1
  203. package/src/tools/credentials/vault.ts +1 -3
  204. package/src/tools/memory/definitions.ts +4 -13
  205. package/src/tools/memory/handlers.test.ts +83 -103
  206. package/src/tools/memory/handlers.ts +50 -85
  207. package/src/tools/schedule/create.ts +8 -1
  208. package/src/tools/schedule/update.ts +8 -1
  209. package/src/tools/skills/load.ts +25 -2
  210. package/src/__tests__/clarification-resolver.test.ts +0 -193
  211. package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
  212. package/src/__tests__/conflict-policy.test.ts +0 -269
  213. package/src/__tests__/conflict-store.test.ts +0 -372
  214. package/src/__tests__/contradiction-checker.test.ts +0 -361
  215. package/src/__tests__/entity-extractor.test.ts +0 -211
  216. package/src/__tests__/entity-search.test.ts +0 -1117
  217. package/src/__tests__/profile-compiler.test.ts +0 -392
  218. package/src/__tests__/session-conflict-gate.test.ts +0 -1228
  219. package/src/__tests__/session-profile-injection.test.ts +0 -557
  220. package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
  221. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
  222. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
  223. package/src/daemon/session-conflict-gate.ts +0 -167
  224. package/src/daemon/session-dynamic-profile.ts +0 -77
  225. package/src/memory/clarification-resolver.ts +0 -417
  226. package/src/memory/conflict-intent.ts +0 -205
  227. package/src/memory/conflict-policy.ts +0 -127
  228. package/src/memory/conflict-store.ts +0 -410
  229. package/src/memory/contradiction-checker.ts +0 -508
  230. package/src/memory/entity-extractor.ts +0 -535
  231. package/src/memory/format-recall.ts +0 -47
  232. package/src/memory/fts-reconciler.ts +0 -165
  233. package/src/memory/job-handlers/conflict.ts +0 -200
  234. package/src/memory/profile-compiler.ts +0 -195
  235. package/src/memory/recall-cache.ts +0 -117
  236. package/src/memory/search/entity.ts +0 -535
  237. package/src/memory/search/query-expansion.test.ts +0 -70
  238. package/src/memory/search/query-expansion.ts +0 -118
  239. package/src/runtime/routes/mcp-routes.ts +0 -20
@@ -1,392 +0,0 @@
1
- import { mkdtempSync, rmSync } from "node:fs";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
- import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
5
-
6
- const testDir = mkdtempSync(join(tmpdir(), "profile-compiler-test-"));
7
-
8
- let profileEnabled = false;
9
- let profileMaxInjectTokens = 800;
10
-
11
- mock.module("../util/platform.js", () => ({
12
- getDataDir: () => testDir,
13
- isMacOS: () => process.platform === "darwin",
14
- isLinux: () => process.platform === "linux",
15
- isWindows: () => process.platform === "win32",
16
- getPidPath: () => join(testDir, "test.pid"),
17
- getDbPath: () => join(testDir, "test.db"),
18
- getLogPath: () => join(testDir, "test.log"),
19
- ensureDataDir: () => {},
20
- }));
21
-
22
- mock.module("../util/logger.js", () => ({
23
- getLogger: () =>
24
- new Proxy({} as Record<string, unknown>, {
25
- get: () => () => {},
26
- }),
27
- }));
28
-
29
- import { DEFAULT_CONFIG } from "../config/defaults.js";
30
-
31
- mock.module("../config/loader.js", () => ({
32
- getConfig: () => ({
33
- ...DEFAULT_CONFIG,
34
- memory: {
35
- ...DEFAULT_CONFIG.memory,
36
- profile: {
37
- enabled: profileEnabled,
38
- maxInjectTokens: profileMaxInjectTokens,
39
- },
40
- },
41
- }),
42
- }));
43
-
44
- import { estimateTextTokens } from "../context/token-estimator.js";
45
- import { getDb, initializeDb, resetDb } from "../memory/db.js";
46
- import { compileDynamicProfile } from "../memory/profile-compiler.js";
47
- import { memoryItems } from "../memory/schema.js";
48
-
49
- initializeDb();
50
-
51
- afterAll(() => {
52
- resetDb();
53
- try {
54
- rmSync(testDir, { recursive: true });
55
- } catch {
56
- /* best effort */
57
- }
58
- });
59
-
60
- function resetTables() {
61
- const db = getDb();
62
- db.run("DELETE FROM memory_item_sources");
63
- db.run("DELETE FROM memory_items");
64
- }
65
-
66
- function insertItem(input: {
67
- id: string;
68
- kind: string;
69
- subject: string;
70
- statement: string;
71
- verificationState: string;
72
- status?: string;
73
- invalidAt?: number | null;
74
- scopeId?: string;
75
- importance?: number;
76
- }) {
77
- const now = Date.now();
78
- const db = getDb();
79
- db.insert(memoryItems)
80
- .values({
81
- id: input.id,
82
- kind: input.kind,
83
- subject: input.subject,
84
- statement: input.statement,
85
- status: input.status ?? "active",
86
- confidence: 0.8,
87
- importance: input.importance ?? 0.6,
88
- fingerprint: `fp-${input.id}`,
89
- verificationState: input.verificationState,
90
- scopeId: input.scopeId ?? "default",
91
- firstSeenAt: now - 1000,
92
- lastSeenAt: now,
93
- validFrom: now - 1000,
94
- invalidAt: input.invalidAt ?? null,
95
- })
96
- .run();
97
- }
98
-
99
- describe("profile-compiler", () => {
100
- beforeEach(() => {
101
- resetTables();
102
- profileEnabled = false;
103
- profileMaxInjectTokens = 800;
104
- });
105
-
106
- test("returns empty profile when feature is disabled", () => {
107
- insertItem({
108
- id: "profile-disabled-1",
109
- kind: "profile",
110
- subject: "timezone",
111
- statement: "User timezone is PST.",
112
- verificationState: "user_confirmed",
113
- });
114
-
115
- const compiled = compileDynamicProfile();
116
- expect(compiled.text).toBe("");
117
- expect(compiled.selectedCount).toBe(0);
118
- expect(compiled.tokenEstimate).toBe(0);
119
- });
120
-
121
- test("selects active trusted memories and prefers stronger verification states", () => {
122
- profileEnabled = true;
123
-
124
- insertItem({
125
- id: "profile-strong",
126
- kind: "preference",
127
- subject: "package manager",
128
- statement: "Use pnpm for workspace installs.",
129
- verificationState: "user_confirmed",
130
- importance: 0.9,
131
- });
132
- insertItem({
133
- id: "profile-weak",
134
- kind: "preference",
135
- subject: "package manager",
136
- statement: "Use npm for workspace installs.",
137
- verificationState: "assistant_inferred",
138
- importance: 0.7,
139
- });
140
- insertItem({
141
- id: "profile-reported",
142
- kind: "profile",
143
- subject: "timezone",
144
- statement: "Timezone is America/Los_Angeles.",
145
- verificationState: "user_reported",
146
- importance: 0.8,
147
- });
148
- insertItem({
149
- id: "profile-superseded",
150
- kind: "profile",
151
- subject: "location",
152
- statement: "Location is Seattle.",
153
- verificationState: "user_confirmed",
154
- status: "superseded",
155
- });
156
- insertItem({
157
- id: "profile-invalid",
158
- kind: "profile",
159
- subject: "editor",
160
- statement: "Primary editor is Neovim.",
161
- verificationState: "user_confirmed",
162
- invalidAt: Date.now(),
163
- });
164
- insertItem({
165
- id: "profile-untrusted",
166
- kind: "profile",
167
- subject: "phone",
168
- statement: "Phone number is 555-0100.",
169
- verificationState: "legacy_import",
170
- });
171
- insertItem({
172
- id: "profile-project",
173
- kind: "project",
174
- subject: "repo",
175
- statement: "Project uses TypeScript.",
176
- verificationState: "user_confirmed",
177
- });
178
-
179
- const compiled = compileDynamicProfile();
180
- expect(compiled.selectedCount).toBe(2);
181
- expect(compiled.text).toContain("package manager: Use pnpm");
182
- expect(compiled.text).toContain(
183
- "timezone: Timezone is America/Los_Angeles",
184
- );
185
- expect(compiled.text).not.toContain("Use npm");
186
- expect(compiled.text).not.toContain("Location is Seattle");
187
- expect(compiled.text).not.toContain("Project uses TypeScript");
188
- });
189
-
190
- test("enforces strict token cap", () => {
191
- profileEnabled = true;
192
- profileMaxInjectTokens = 500;
193
-
194
- insertItem({
195
- id: "profile-budget-1",
196
- kind: "profile",
197
- subject: "timezone",
198
- statement: "Timezone is Pacific time with daylight savings observed.",
199
- verificationState: "user_confirmed",
200
- importance: 0.9,
201
- });
202
- insertItem({
203
- id: "profile-budget-2",
204
- kind: "preference",
205
- subject: "coding style",
206
- statement: "Prefers explicit types and no wildcard exports.",
207
- verificationState: "user_confirmed",
208
- importance: 0.8,
209
- });
210
- insertItem({
211
- id: "profile-budget-3",
212
- kind: "constraint",
213
- subject: "deployment",
214
- statement: "Never deploy on Friday afternoons.",
215
- verificationState: "user_confirmed",
216
- importance: 0.7,
217
- });
218
-
219
- const full = compileDynamicProfile({ maxInjectTokensOverride: 500 });
220
- expect(full.selectedCount).toBeGreaterThan(1);
221
-
222
- const tightBudget = Math.max(1, full.tokenEstimate - 5);
223
- const limited = compileDynamicProfile({
224
- maxInjectTokensOverride: tightBudget,
225
- });
226
-
227
- expect(limited.tokenEstimate).toBeLessThanOrEqual(tightBudget);
228
- expect(estimateTextTokens(limited.text)).toBeLessThanOrEqual(tightBudget);
229
- expect(limited.selectedCount).toBeLessThan(full.selectedCount);
230
- });
231
-
232
- test("without includeDefaultFallback, queries only the specified scope", () => {
233
- profileEnabled = true;
234
-
235
- // Item in 'default' scope
236
- insertItem({
237
- id: "fb-default-only",
238
- kind: "profile",
239
- subject: "timezone",
240
- statement: "Timezone is America/New_York.",
241
- verificationState: "user_confirmed",
242
- scopeId: "default",
243
- });
244
- // Item in 'project-x' scope
245
- insertItem({
246
- id: "fb-project-only",
247
- kind: "preference",
248
- subject: "language",
249
- statement: "Prefers TypeScript.",
250
- verificationState: "user_confirmed",
251
- scopeId: "project-x",
252
- });
253
-
254
- // Query project-x WITHOUT fallback — should only see the project-x item
255
- const compiled = compileDynamicProfile({ scopeId: "project-x" });
256
- expect(compiled.selectedCount).toBe(1);
257
- expect(compiled.text).toContain("language: Prefers TypeScript");
258
- expect(compiled.text).not.toContain("timezone");
259
- });
260
-
261
- test("includeDefaultFallback: true with non-default scope queries both scopes", () => {
262
- profileEnabled = true;
263
-
264
- insertItem({
265
- id: "fb-default-tz",
266
- kind: "profile",
267
- subject: "timezone",
268
- statement: "Timezone is America/New_York.",
269
- verificationState: "user_confirmed",
270
- scopeId: "default",
271
- });
272
- insertItem({
273
- id: "fb-proj-lang",
274
- kind: "preference",
275
- subject: "language",
276
- statement: "Prefers TypeScript.",
277
- verificationState: "user_confirmed",
278
- scopeId: "project-x",
279
- });
280
-
281
- const compiled = compileDynamicProfile({
282
- scopeId: "project-x",
283
- includeDefaultFallback: true,
284
- });
285
- expect(compiled.selectedCount).toBe(2);
286
- expect(compiled.text).toContain("timezone: Timezone is America/New_York");
287
- expect(compiled.text).toContain("language: Prefers TypeScript");
288
- });
289
-
290
- test("includeDefaultFallback: true with default scope does not duplicate", () => {
291
- profileEnabled = true;
292
-
293
- insertItem({
294
- id: "fb-default-nodup",
295
- kind: "profile",
296
- subject: "timezone",
297
- statement: "Timezone is America/Chicago.",
298
- verificationState: "user_confirmed",
299
- scopeId: "default",
300
- });
301
-
302
- // When scopeId is already 'default', fallback should be a no-op — still just queries 'default'
303
- const compiled = compileDynamicProfile({
304
- scopeId: "default",
305
- includeDefaultFallback: true,
306
- });
307
- expect(compiled.selectedCount).toBe(1);
308
- expect(compiled.text).toContain("timezone: Timezone is America/Chicago");
309
- });
310
-
311
- test("includeDefaultFallback: scoped entry wins over default with same subject", () => {
312
- profileEnabled = true;
313
-
314
- // Default scope entry — higher trust/importance so it would normally rank first
315
- insertItem({
316
- id: "fb-default-lang",
317
- kind: "preference",
318
- subject: "language",
319
- statement: "Prefers JavaScript.",
320
- verificationState: "user_confirmed",
321
- scopeId: "default",
322
- importance: 0.95,
323
- });
324
- // Scoped entry — lower importance but should still win because it is scoped
325
- insertItem({
326
- id: "fb-proj-lang",
327
- kind: "preference",
328
- subject: "language",
329
- statement: "Prefers TypeScript.",
330
- verificationState: "assistant_inferred",
331
- scopeId: "project-x",
332
- importance: 0.5,
333
- });
334
- // Default-only entry with no scoped counterpart — should still appear (gap-filling)
335
- insertItem({
336
- id: "fb-default-tz",
337
- kind: "profile",
338
- subject: "timezone",
339
- statement: "Timezone is America/New_York.",
340
- verificationState: "user_confirmed",
341
- scopeId: "default",
342
- });
343
-
344
- const compiled = compileDynamicProfile({
345
- scopeId: "project-x",
346
- includeDefaultFallback: true,
347
- });
348
-
349
- // Scoped entry should win deduplication for 'language'
350
- expect(compiled.text).toContain("language: Prefers TypeScript");
351
- expect(compiled.text).not.toContain("Prefers JavaScript");
352
- // Default-only entry should still fill in as fallback
353
- expect(compiled.text).toContain("timezone: Timezone is America/New_York");
354
- expect(compiled.selectedCount).toBe(2);
355
- });
356
-
357
- test("uses lower-ranked fallback for same subject when top candidate exceeds budget", () => {
358
- profileEnabled = true;
359
-
360
- insertItem({
361
- id: "profile-long-primary",
362
- kind: "profile",
363
- subject: "deployment policy",
364
- statement:
365
- "Prefer geographically isolated blue-green rollout windows with mandatory canary burn-in and staged health-check gates before each region is promoted.",
366
- verificationState: "user_confirmed",
367
- importance: 0.95,
368
- });
369
- insertItem({
370
- id: "profile-short-fallback",
371
- kind: "profile",
372
- subject: "deployment policy",
373
- statement: "Deploy only on weekdays.",
374
- verificationState: "user_confirmed",
375
- importance: 0.7,
376
- });
377
-
378
- const budget =
379
- estimateTextTokens(
380
- "<dynamic-user-profile>\n- deployment policy: Deploy only on weekdays.\n</dynamic-user-profile>",
381
- ) + 2;
382
- const compiled = compileDynamicProfile({ maxInjectTokensOverride: budget });
383
-
384
- expect(compiled.tokenEstimate).toBeLessThanOrEqual(budget);
385
- expect(compiled.text).toContain(
386
- "deployment policy: Deploy only on weekdays.",
387
- );
388
- expect(compiled.text).not.toContain(
389
- "geographically isolated blue-green rollout",
390
- );
391
- });
392
- });