@vellumai/assistant 0.5.5 → 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 +3 -4
- 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__/docker-signing-key-bootstrap.test.ts +207 -0
- package/src/__tests__/memory-regressions.test.ts +8 -30
- package/src/__tests__/require-fresh-approval.test.ts +4 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +4 -0
- package/src/__tests__/tool-executor.test.ts +4 -0
- package/src/cli/commands/conversations.ts +0 -18
- package/src/config/env.ts +8 -2
- package/src/config/feature-flag-registry.json +0 -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/daemon/config-watcher.ts +1 -4
- 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/handlers/conversations.ts +0 -11
- package/src/daemon/lifecycle.ts +3 -46
- package/src/followups/followup-store.ts +5 -2
- 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/indexer.ts +15 -106
- 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/permissions/defaults.ts +3 -3
- package/src/permissions/trust-client.ts +2 -13
- package/src/permissions/trust-store.ts +8 -3
- package/src/runtime/auth/route-policy.ts +14 -0
- package/src/runtime/auth/token-service.ts +133 -0
- package/src/runtime/http-server.ts +2 -0
- 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/memory-item-routes.test.ts +221 -3
- package/src/runtime/routes/memory-item-routes.ts +124 -2
- package/src/runtime/routes/upgrade-broadcast-routes.ts +151 -0
- package/src/schedule/schedule-store.ts +0 -21
- package/src/skills/inline-command-render.ts +5 -1
- package/src/skills/inline-command-runner.ts +30 -2
- 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/platform.ts +5 -5
- package/src/util/xml.ts +8 -0
- package/src/workspace/heartbeat-service.ts +5 -24
- 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
package/src/config/env.ts
CHANGED
|
@@ -51,9 +51,15 @@ export function getGatewayPort(): number {
|
|
|
51
51
|
return int("GATEWAY_PORT", DEFAULT_GATEWAY_PORT);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
/**
|
|
54
|
+
/**
|
|
55
|
+
* Resolve the gateway base URL for internal service-to-service calls.
|
|
56
|
+
*
|
|
57
|
+
* In containerized deployments the gateway runs in a separate container,
|
|
58
|
+
* reachable via `GATEWAY_INTERNAL_URL` (e.g. `http://gateway:7822`).
|
|
59
|
+
* Falls back to `http://127.0.0.1:<GATEWAY_PORT>` for local deployments.
|
|
60
|
+
*/
|
|
55
61
|
export function getGatewayInternalBaseUrl(): string {
|
|
56
|
-
return `http://127.0.0.1:${getGatewayPort()}`;
|
|
62
|
+
return str("GATEWAY_INTERNAL_URL") ?? `http://127.0.0.1:${getGatewayPort()}`;
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
// ── Ingress ──────────────────────────────────────────────────────────────────
|
|
@@ -25,14 +25,6 @@
|
|
|
25
25
|
"description": "Show the Contacts tab in Settings for viewing and managing contacts",
|
|
26
26
|
"defaultEnabled": true
|
|
27
27
|
},
|
|
28
|
-
{
|
|
29
|
-
"id": "custom-inference-provider",
|
|
30
|
-
"scope": "macos",
|
|
31
|
-
"key": "custom_inference_provider_enabled",
|
|
32
|
-
"label": "Custom Inference Provider",
|
|
33
|
-
"description": "Allow selecting a specific LLM provider and model for inference in Your Own mode",
|
|
34
|
-
"defaultEnabled": false
|
|
35
|
-
},
|
|
36
28
|
{
|
|
37
29
|
"id": "email-channel",
|
|
38
30
|
"scope": "assistant",
|
package/src/config/schema.ts
CHANGED
|
@@ -106,18 +106,6 @@ export {
|
|
|
106
106
|
MemoryDynamicBudgetConfigSchema,
|
|
107
107
|
MemoryRetrievalConfigSchema,
|
|
108
108
|
} from "./schemas/memory-retrieval.js";
|
|
109
|
-
export type {
|
|
110
|
-
MemorySimplifiedArchiveRecallConfig,
|
|
111
|
-
MemorySimplifiedBriefConfig,
|
|
112
|
-
MemorySimplifiedConfig,
|
|
113
|
-
MemorySimplifiedReducerConfig,
|
|
114
|
-
} from "./schemas/memory-simplified.js";
|
|
115
|
-
export {
|
|
116
|
-
MemorySimplifiedArchiveRecallConfigSchema,
|
|
117
|
-
MemorySimplifiedBriefConfigSchema,
|
|
118
|
-
MemorySimplifiedConfigSchema,
|
|
119
|
-
MemorySimplifiedReducerConfigSchema,
|
|
120
|
-
} from "./schemas/memory-simplified.js";
|
|
121
109
|
export type {
|
|
122
110
|
MemoryEmbeddingsConfig,
|
|
123
111
|
MemorySegmentationConfig,
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
MemorySummarizationConfigSchema,
|
|
11
11
|
} from "./memory-processing.js";
|
|
12
12
|
import { MemoryRetrievalConfigSchema } from "./memory-retrieval.js";
|
|
13
|
-
import { MemorySimplifiedConfigSchema } from "./memory-simplified.js";
|
|
14
13
|
import {
|
|
15
14
|
MemoryEmbeddingsConfigSchema,
|
|
16
15
|
MemorySegmentationConfigSchema,
|
|
@@ -46,9 +45,6 @@ export const MemoryConfigSchema = z
|
|
|
46
45
|
summarization: MemorySummarizationConfigSchema.default(
|
|
47
46
|
MemorySummarizationConfigSchema.parse({}),
|
|
48
47
|
),
|
|
49
|
-
simplified: MemorySimplifiedConfigSchema.default(
|
|
50
|
-
MemorySimplifiedConfigSchema.parse({}),
|
|
51
|
-
),
|
|
52
48
|
})
|
|
53
49
|
.describe(
|
|
54
50
|
"Long-term memory system — stores, retrieves, and manages persistent knowledge across conversations",
|
|
@@ -43,7 +43,7 @@ export const DaemonConfigSchema = z
|
|
|
43
43
|
.number({ error: "daemon.titleGenerationMaxTokens must be a number" })
|
|
44
44
|
.int("daemon.titleGenerationMaxTokens must be an integer")
|
|
45
45
|
.positive("daemon.titleGenerationMaxTokens must be a positive integer")
|
|
46
|
-
.default(
|
|
46
|
+
.default(50)
|
|
47
47
|
.describe(
|
|
48
48
|
"Maximum number of tokens for auto-generated conversation titles",
|
|
49
49
|
),
|
|
@@ -77,6 +77,10 @@ export const PermissionsConfigSchema = z
|
|
|
77
77
|
.describe(
|
|
78
78
|
"Permission mode — 'strict' requires explicit approval for all operations, 'workspace' allows operations within the workspace",
|
|
79
79
|
),
|
|
80
|
+
dangerouslySkipPermissions: z
|
|
81
|
+
.boolean({ error: "permissions.dangerouslySkipPermissions must be a boolean" })
|
|
82
|
+
.default(false)
|
|
83
|
+
.describe("Auto-accept all permission prompts without asking"),
|
|
80
84
|
})
|
|
81
85
|
.describe("Permission enforcement mode for tool operations");
|
|
82
86
|
|
|
@@ -538,12 +538,12 @@ export class ContextWindowManager {
|
|
|
538
538
|
}
|
|
539
539
|
|
|
540
540
|
const keepTurns = lo;
|
|
541
|
-
const
|
|
541
|
+
const rawKeepFromIndex =
|
|
542
542
|
keepTurns === 0
|
|
543
543
|
? messages.length
|
|
544
544
|
: (userTurnStarts[userTurnStarts.length - keepTurns] ??
|
|
545
545
|
messages.length);
|
|
546
|
-
|
|
546
|
+
const keepFromIndex = adjustForToolPairs(messages, rawKeepFromIndex);
|
|
547
547
|
return { keepFromIndex, keepTurns };
|
|
548
548
|
}
|
|
549
549
|
|
|
@@ -703,6 +703,57 @@ function isToolResultOnly(message: Message): boolean {
|
|
|
703
703
|
);
|
|
704
704
|
}
|
|
705
705
|
|
|
706
|
+
/**
|
|
707
|
+
* Walk the keep boundary backward to ensure tool_use/tool_result pairs are
|
|
708
|
+
* never split across the compaction boundary. If the first kept message is
|
|
709
|
+
* a user message containing tool_result blocks whose matching tool_use blocks
|
|
710
|
+
* live in the preceding (compacted-away) assistant message, include that
|
|
711
|
+
* assistant message in the kept set.
|
|
712
|
+
*/
|
|
713
|
+
function adjustForToolPairs(
|
|
714
|
+
messages: Message[],
|
|
715
|
+
keepFromIndex: number,
|
|
716
|
+
): number {
|
|
717
|
+
let idx = keepFromIndex;
|
|
718
|
+
while (idx > 0) {
|
|
719
|
+
const msg = messages[idx];
|
|
720
|
+
if (!msg || msg.role !== "user") break;
|
|
721
|
+
|
|
722
|
+
// Collect tool_use_ids referenced by tool_results in this user message
|
|
723
|
+
const referencedIds = new Set<string>();
|
|
724
|
+
for (const block of msg.content) {
|
|
725
|
+
if ((block.type === "tool_result" || block.type === "web_search_tool_result") && "tool_use_id" in block) {
|
|
726
|
+
referencedIds.add((block as { tool_use_id: string }).tool_use_id);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
if (referencedIds.size === 0) break;
|
|
730
|
+
|
|
731
|
+
// Check if the preceding assistant message contains matching tool_uses
|
|
732
|
+
const prev = messages[idx - 1];
|
|
733
|
+
if (!prev || prev.role !== "assistant") break;
|
|
734
|
+
|
|
735
|
+
const hasOrphanedPair = prev.content.some(
|
|
736
|
+
(block) =>
|
|
737
|
+
(block.type === "tool_use" || block.type === "server_tool_use") &&
|
|
738
|
+
"id" in block &&
|
|
739
|
+
referencedIds.has((block as { id: string }).id),
|
|
740
|
+
);
|
|
741
|
+
if (!hasOrphanedPair) break;
|
|
742
|
+
|
|
743
|
+
// Include the assistant message
|
|
744
|
+
idx--;
|
|
745
|
+
|
|
746
|
+
// The assistant message may itself be preceded by a tool_result user
|
|
747
|
+
// message that pairs with an even earlier assistant — continue the check
|
|
748
|
+
if (idx > 0 && messages[idx - 1]?.role === "user") {
|
|
749
|
+
idx--;
|
|
750
|
+
} else {
|
|
751
|
+
break;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
return idx;
|
|
755
|
+
}
|
|
756
|
+
|
|
706
757
|
export function getSummaryFromContextMessage(
|
|
707
758
|
message: Message | undefined,
|
|
708
759
|
): string | null {
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
} from "node:fs";
|
|
13
13
|
import { join } from "node:path";
|
|
14
14
|
|
|
15
|
-
import { getIsContainerized } from "../config/env-registry.js";
|
|
16
15
|
import { getConfig, invalidateConfigCache } from "../config/loader.js";
|
|
17
16
|
import { clearEmbeddingBackendCache } from "../memory/embedding-backend.js";
|
|
18
17
|
import { clearCache as clearTrustCache } from "../permissions/trust-store.js";
|
|
@@ -210,9 +209,7 @@ export class ConfigWatcher {
|
|
|
210
209
|
);
|
|
211
210
|
}
|
|
212
211
|
|
|
213
|
-
|
|
214
|
-
this.startSignalsWatcher();
|
|
215
|
-
}
|
|
212
|
+
this.startSignalsWatcher();
|
|
216
213
|
this.startSkillsWatchers(onConversationEvict);
|
|
217
214
|
}
|
|
218
215
|
|
|
@@ -33,7 +33,6 @@ import {
|
|
|
33
33
|
} from "../instrument.js";
|
|
34
34
|
import { commitAppTurnChanges } from "../memory/app-git-service.js";
|
|
35
35
|
import { getApp, listAppFiles, resolveAppDir } from "../memory/app-store.js";
|
|
36
|
-
import { insertCompactionEpisode } from "../memory/archive-store.js";
|
|
37
36
|
import {
|
|
38
37
|
addMessage,
|
|
39
38
|
deleteMessageById,
|
|
@@ -514,12 +513,6 @@ export async function runAgentLoopImpl(
|
|
|
514
513
|
compacted.summaryText,
|
|
515
514
|
ctx.contextCompactedMessageCount,
|
|
516
515
|
);
|
|
517
|
-
dualWriteCompactionEpisode(
|
|
518
|
-
ctx.conversationId,
|
|
519
|
-
ctx.memoryPolicy.scopeId,
|
|
520
|
-
compacted.summaryText,
|
|
521
|
-
compacted.summaryOutputTokens,
|
|
522
|
-
);
|
|
523
516
|
onEvent({
|
|
524
517
|
type: "context_compacted",
|
|
525
518
|
previousEstimatedInputTokens: compacted.previousEstimatedInputTokens,
|
|
@@ -787,12 +780,6 @@ export async function runAgentLoopImpl(
|
|
|
787
780
|
step.compactionResult.summaryText,
|
|
788
781
|
ctx.contextCompactedMessageCount,
|
|
789
782
|
);
|
|
790
|
-
dualWriteCompactionEpisode(
|
|
791
|
-
ctx.conversationId,
|
|
792
|
-
ctx.memoryPolicy.scopeId,
|
|
793
|
-
step.compactionResult.summaryText,
|
|
794
|
-
step.compactionResult.summaryOutputTokens,
|
|
795
|
-
);
|
|
796
783
|
onEvent({
|
|
797
784
|
type: "context_compacted",
|
|
798
785
|
previousEstimatedInputTokens:
|
|
@@ -977,12 +964,6 @@ export async function runAgentLoopImpl(
|
|
|
977
964
|
midLoopCompact.summaryText,
|
|
978
965
|
ctx.contextCompactedMessageCount,
|
|
979
966
|
);
|
|
980
|
-
dualWriteCompactionEpisode(
|
|
981
|
-
ctx.conversationId,
|
|
982
|
-
ctx.memoryPolicy.scopeId,
|
|
983
|
-
midLoopCompact.summaryText,
|
|
984
|
-
midLoopCompact.summaryOutputTokens,
|
|
985
|
-
);
|
|
986
967
|
onEvent({
|
|
987
968
|
type: "context_compacted",
|
|
988
969
|
previousEstimatedInputTokens:
|
|
@@ -1179,12 +1160,6 @@ export async function runAgentLoopImpl(
|
|
|
1179
1160
|
step.compactionResult.summaryText,
|
|
1180
1161
|
ctx.contextCompactedMessageCount,
|
|
1181
1162
|
);
|
|
1182
|
-
dualWriteCompactionEpisode(
|
|
1183
|
-
ctx.conversationId,
|
|
1184
|
-
ctx.memoryPolicy.scopeId,
|
|
1185
|
-
step.compactionResult.summaryText,
|
|
1186
|
-
step.compactionResult.summaryOutputTokens,
|
|
1187
|
-
);
|
|
1188
1163
|
onEvent({
|
|
1189
1164
|
type: "context_compacted",
|
|
1190
1165
|
previousEstimatedInputTokens:
|
|
@@ -1292,12 +1267,6 @@ export async function runAgentLoopImpl(
|
|
|
1292
1267
|
emergencyCompact.summaryText,
|
|
1293
1268
|
ctx.contextCompactedMessageCount,
|
|
1294
1269
|
);
|
|
1295
|
-
dualWriteCompactionEpisode(
|
|
1296
|
-
ctx.conversationId,
|
|
1297
|
-
ctx.memoryPolicy.scopeId,
|
|
1298
|
-
emergencyCompact.summaryText,
|
|
1299
|
-
emergencyCompact.summaryOutputTokens,
|
|
1300
|
-
);
|
|
1301
1270
|
onEvent({
|
|
1302
1271
|
type: "context_compacted",
|
|
1303
1272
|
previousEstimatedInputTokens:
|
|
@@ -1402,12 +1371,6 @@ export async function runAgentLoopImpl(
|
|
|
1402
1371
|
emergencyCompact.summaryText,
|
|
1403
1372
|
ctx.contextCompactedMessageCount,
|
|
1404
1373
|
);
|
|
1405
|
-
dualWriteCompactionEpisode(
|
|
1406
|
-
ctx.conversationId,
|
|
1407
|
-
ctx.memoryPolicy.scopeId,
|
|
1408
|
-
emergencyCompact.summaryText,
|
|
1409
|
-
emergencyCompact.summaryOutputTokens,
|
|
1410
|
-
);
|
|
1411
1374
|
onEvent({
|
|
1412
1375
|
type: "context_compacted",
|
|
1413
1376
|
previousEstimatedInputTokens:
|
|
@@ -1873,26 +1836,3 @@ function collapseRawResponses(rawResponses?: unknown[]): unknown | undefined {
|
|
|
1873
1836
|
if (!rawResponses || rawResponses.length === 0) return undefined;
|
|
1874
1837
|
return rawResponses.length === 1 ? rawResponses[0] : rawResponses;
|
|
1875
1838
|
}
|
|
1876
|
-
|
|
1877
|
-
/**
|
|
1878
|
-
* Dual-write a compaction summary as an archive episode so it becomes
|
|
1879
|
-
* searchable via vector recall. Called after each successful compaction
|
|
1880
|
-
* that produces a new summary.
|
|
1881
|
-
*/
|
|
1882
|
-
function dualWriteCompactionEpisode(
|
|
1883
|
-
conversationId: string,
|
|
1884
|
-
scopeId: string,
|
|
1885
|
-
summaryText: string,
|
|
1886
|
-
summaryOutputTokens: number,
|
|
1887
|
-
): void {
|
|
1888
|
-
const now = Date.now();
|
|
1889
|
-
insertCompactionEpisode({
|
|
1890
|
-
conversationId,
|
|
1891
|
-
scopeId,
|
|
1892
|
-
title: truncate(summaryText, 120, ""),
|
|
1893
|
-
summary: summaryText,
|
|
1894
|
-
tokenEstimate: summaryOutputTokens,
|
|
1895
|
-
startAt: now,
|
|
1896
|
-
endAt: now,
|
|
1897
|
-
});
|
|
1898
|
-
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { getConfig } from "../config/loader.js";
|
|
2
2
|
import { estimatePromptTokens } from "../context/token-estimator.js";
|
|
3
|
-
import { buildArchiveRecall } from "../memory/archive-recall.js";
|
|
4
|
-
import { compileMemoryBrief } from "../memory/brief.js";
|
|
5
|
-
import { getDb } from "../memory/db.js";
|
|
6
3
|
import { buildMemoryQuery } from "../memory/query-builder.js";
|
|
7
4
|
import { computeRecallBudget } from "../memory/retrieval-budget.js";
|
|
8
5
|
import {
|
|
@@ -12,11 +9,8 @@ import {
|
|
|
12
9
|
import type { ScopePolicyOverride } from "../memory/search/types.js";
|
|
13
10
|
import type { Message } from "../providers/types.js";
|
|
14
11
|
import type { Provider } from "../providers/types.js";
|
|
15
|
-
import { getLogger } from "../util/logger.js";
|
|
16
12
|
import type { ServerMessage } from "./message-protocol.js";
|
|
17
13
|
|
|
18
|
-
const log = getLogger("conversation-memory");
|
|
19
|
-
|
|
20
14
|
export interface MemoryRecallResult {
|
|
21
15
|
runMessages: Message[];
|
|
22
16
|
recall: Awaited<ReturnType<typeof buildMemoryRecall>>;
|
|
@@ -121,14 +115,6 @@ export async function prepareMemoryContext(
|
|
|
121
115
|
|
|
122
116
|
const runtimeConfig = getConfig();
|
|
123
117
|
|
|
124
|
-
// ── Simplified memory path ──────────────────────────────────────────
|
|
125
|
-
// When `memory.simplified.enabled` is true, inject the brief and
|
|
126
|
-
// optional archive recall instead of the legacy hybrid pipeline.
|
|
127
|
-
if (runtimeConfig.memory?.simplified?.enabled) {
|
|
128
|
-
return prepareSimplifiedMemoryContext(ctx, content, userMessageId, onEvent);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// ── Legacy memory path (fallback) ──────────────────────────────────
|
|
132
118
|
// Memory recall via the V2 hybrid pipeline
|
|
133
119
|
const recallQuery = buildMemoryQuery(content, ctx.messages);
|
|
134
120
|
const dynamicBudgetConfig = runtimeConfig.memory?.retrieval?.dynamicBudget;
|
|
@@ -221,106 +207,3 @@ export async function prepareMemoryContext(
|
|
|
221
207
|
recall,
|
|
222
208
|
};
|
|
223
209
|
}
|
|
224
|
-
|
|
225
|
-
// ── Simplified memory injection ─────────────────────────────────────────
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Build simplified memory context for a turn: compiles the `<memory_brief>`
|
|
229
|
-
* block and conditionally appends `<supporting_recall>` from the archive.
|
|
230
|
-
*
|
|
231
|
-
* Non-empty blocks are injected as text content blocks prepended to the
|
|
232
|
-
* last user message, following the same injection pattern as the legacy
|
|
233
|
-
* pipeline. Stripping is handled by `RUNTIME_INJECTION_PREFIXES` which
|
|
234
|
-
* already includes `<memory_brief>`.
|
|
235
|
-
*/
|
|
236
|
-
function prepareSimplifiedMemoryContext(
|
|
237
|
-
ctx: MemoryPrepareContext,
|
|
238
|
-
content: string,
|
|
239
|
-
userMessageId: string,
|
|
240
|
-
onEvent: (msg: ServerMessage) => void,
|
|
241
|
-
): MemoryRecallResult {
|
|
242
|
-
const start = Date.now();
|
|
243
|
-
|
|
244
|
-
// Build a no-op recall result matching the legacy shape.
|
|
245
|
-
const noopRecall = (): Awaited<ReturnType<typeof buildMemoryRecall>> =>
|
|
246
|
-
({
|
|
247
|
-
enabled: true,
|
|
248
|
-
degraded: false,
|
|
249
|
-
injectedText: "",
|
|
250
|
-
semanticHits: 0,
|
|
251
|
-
recencyHits: 0,
|
|
252
|
-
mergedCount: 0,
|
|
253
|
-
selectedCount: 0,
|
|
254
|
-
injectedTokens: 0,
|
|
255
|
-
latencyMs: 0,
|
|
256
|
-
topCandidates: [],
|
|
257
|
-
tier1Count: 0,
|
|
258
|
-
tier2Count: 0,
|
|
259
|
-
}) as Awaited<ReturnType<typeof buildMemoryRecall>>;
|
|
260
|
-
|
|
261
|
-
try {
|
|
262
|
-
const db = getDb();
|
|
263
|
-
|
|
264
|
-
// Step 1: Build the memory brief
|
|
265
|
-
const briefResult = compileMemoryBrief(db, ctx.scopeId, userMessageId);
|
|
266
|
-
|
|
267
|
-
// Step 2: Conditionally build supporting recall from the archive
|
|
268
|
-
const archiveResult = buildArchiveRecall(ctx.scopeId, content);
|
|
269
|
-
|
|
270
|
-
// Step 3: Assemble the injection blocks (non-empty only)
|
|
271
|
-
const blocks: string[] = [];
|
|
272
|
-
if (briefResult.text.length > 0) {
|
|
273
|
-
blocks.push(briefResult.text);
|
|
274
|
-
}
|
|
275
|
-
if (archiveResult.text.length > 0) {
|
|
276
|
-
blocks.push(archiveResult.text);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const latencyMs = Date.now() - start;
|
|
280
|
-
|
|
281
|
-
// Emit memory status for the simplified path
|
|
282
|
-
onEvent({
|
|
283
|
-
type: "memory_status",
|
|
284
|
-
enabled: true,
|
|
285
|
-
degraded: false,
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
// Inject non-empty blocks into the last user message
|
|
289
|
-
let runMessages = ctx.messages;
|
|
290
|
-
if (blocks.length > 0) {
|
|
291
|
-
const injectedText = blocks.join("\n\n");
|
|
292
|
-
const userTail = ctx.messages[ctx.messages.length - 1];
|
|
293
|
-
if (userTail && userTail.role === "user") {
|
|
294
|
-
runMessages = injectMemoryRecallAsUserBlock(ctx.messages, injectedText);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
log.debug(
|
|
298
|
-
{
|
|
299
|
-
briefLength: briefResult.text.length,
|
|
300
|
-
recallTrigger: archiveResult.trigger,
|
|
301
|
-
recallBullets: archiveResult.bullets.length,
|
|
302
|
-
latencyMs,
|
|
303
|
-
},
|
|
304
|
-
"Simplified memory injection completed",
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
return {
|
|
309
|
-
runMessages,
|
|
310
|
-
recall: {
|
|
311
|
-
...noopRecall(),
|
|
312
|
-
injectedText: blocks.length > 0 ? blocks.join("\n\n") : "",
|
|
313
|
-
latencyMs,
|
|
314
|
-
},
|
|
315
|
-
};
|
|
316
|
-
} catch (err) {
|
|
317
|
-
log.warn({ err }, "Simplified memory injection failed, returning no-op");
|
|
318
|
-
return {
|
|
319
|
-
runMessages: ctx.messages,
|
|
320
|
-
recall: {
|
|
321
|
-
...noopRecall(),
|
|
322
|
-
latencyMs: Date.now() - start,
|
|
323
|
-
},
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
}
|
|
@@ -961,8 +961,6 @@ const RUNTIME_INJECTION_PREFIXES = [
|
|
|
961
961
|
"<inbound_actor_context>",
|
|
962
962
|
"<interface_turn_context>",
|
|
963
963
|
"<turn_context>",
|
|
964
|
-
"<memory_brief>",
|
|
965
|
-
"<supporting_recall>",
|
|
966
964
|
"<memory_context __injected>",
|
|
967
965
|
"<memory_context>", // backward-compat: strip legacy blocks from pre-__injected history
|
|
968
966
|
"<voice_call_control>",
|
|
@@ -23,7 +23,6 @@ import {
|
|
|
23
23
|
queueGenerateConversationTitle,
|
|
24
24
|
UNTITLED_FALLBACK,
|
|
25
25
|
} from "../../memory/conversation-title-service.js";
|
|
26
|
-
import { reduceBeforeSwitch } from "../../memory/reducer-scheduler.js";
|
|
27
26
|
import * as pendingInteractions from "../../runtime/pending-interactions.js";
|
|
28
27
|
import { getSubagentManager } from "../../subagent/index.js";
|
|
29
28
|
import { truncate } from "../../util/truncate.js";
|
|
@@ -234,12 +233,6 @@ export async function handleConversationCreate(
|
|
|
234
233
|
conversationType: normalizeConversationType(conversation.conversationType),
|
|
235
234
|
});
|
|
236
235
|
|
|
237
|
-
// Reduce the previous dirty conversation before processing the initial
|
|
238
|
-
// message so its memory is fresh for the next read.
|
|
239
|
-
if (msg.initialMessage) {
|
|
240
|
-
await reduceBeforeSwitch(conversation.id);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
236
|
// Auto-send the initial message if provided, kick-starting the skill.
|
|
244
237
|
if (msg.initialMessage) {
|
|
245
238
|
// Queue title generation eagerly — some processMessage paths (guardian
|
|
@@ -350,10 +343,6 @@ export async function switchConversation(
|
|
|
350
343
|
return null;
|
|
351
344
|
}
|
|
352
345
|
|
|
353
|
-
// Reduce the previous dirty conversation before switching so its memory
|
|
354
|
-
// is fresh for the next read.
|
|
355
|
-
await reduceBeforeSwitch(conversationId);
|
|
356
|
-
|
|
357
346
|
// If the target conversation is headless-locked (actively executing a task run),
|
|
358
347
|
// skip rebinding so tool confirmations stay suppressed.
|
|
359
348
|
const existingConversation = ctx.conversations.get(conversationId);
|
package/src/daemon/lifecycle.ts
CHANGED
|
@@ -30,7 +30,6 @@ import {
|
|
|
30
30
|
getConversationType,
|
|
31
31
|
getMessages,
|
|
32
32
|
purgePrivateConversations,
|
|
33
|
-
sweepStaleReducerJobs,
|
|
34
33
|
} from "../memory/conversation-crud.js";
|
|
35
34
|
import { resolveConversationId } from "../memory/conversation-key-store.js";
|
|
36
35
|
import { initializeDb } from "../memory/db.js";
|
|
@@ -57,8 +56,8 @@ import { assistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
|
57
56
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
58
57
|
import {
|
|
59
58
|
initAuthSigningKey,
|
|
60
|
-
loadOrCreateSigningKey,
|
|
61
59
|
mintPairingBearerToken,
|
|
60
|
+
resolveSigningKey,
|
|
62
61
|
} from "../runtime/auth/token-service.js";
|
|
63
62
|
import { ensureVellumGuardianBinding } from "../runtime/guardian-vellum-migration.js";
|
|
64
63
|
import { RuntimeHttpServer } from "../runtime/http-server.js";
|
|
@@ -148,7 +147,7 @@ export async function runDaemon(): Promise<void> {
|
|
|
148
147
|
// Load (or generate + persist) the auth signing key so tokens survive
|
|
149
148
|
// daemon restarts. Must happen after ensureDataDir() creates the
|
|
150
149
|
// protected directory.
|
|
151
|
-
const signingKey =
|
|
150
|
+
const signingKey = await resolveSigningKey();
|
|
152
151
|
initAuthSigningKey(signingKey);
|
|
153
152
|
|
|
154
153
|
seedInterfaceFiles();
|
|
@@ -213,40 +212,16 @@ export async function runDaemon(): Promise<void> {
|
|
|
213
212
|
targetId: summaryId,
|
|
214
213
|
});
|
|
215
214
|
}
|
|
216
|
-
for (const obsId of deletedMemory.deletedObservationIds) {
|
|
217
|
-
enqueueMemoryJob("delete_qdrant_vectors", {
|
|
218
|
-
targetType: "observation",
|
|
219
|
-
targetId: obsId,
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
for (const chunkId of deletedMemory.deletedChunkIds) {
|
|
223
|
-
enqueueMemoryJob("delete_qdrant_vectors", {
|
|
224
|
-
targetType: "chunk",
|
|
225
|
-
targetId: chunkId,
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
for (const episodeId of deletedMemory.deletedEpisodeIds) {
|
|
229
|
-
enqueueMemoryJob("delete_qdrant_vectors", {
|
|
230
|
-
targetType: "episode",
|
|
231
|
-
targetId: episodeId,
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
215
|
if (
|
|
235
216
|
deletedMemory.segmentIds.length > 0 ||
|
|
236
217
|
deletedMemory.orphanedItemIds.length > 0 ||
|
|
237
|
-
deletedMemory.deletedSummaryIds.length > 0
|
|
238
|
-
deletedMemory.deletedObservationIds.length > 0 ||
|
|
239
|
-
deletedMemory.deletedChunkIds.length > 0 ||
|
|
240
|
-
deletedMemory.deletedEpisodeIds.length > 0
|
|
218
|
+
deletedMemory.deletedSummaryIds.length > 0
|
|
241
219
|
) {
|
|
242
220
|
log.info(
|
|
243
221
|
{
|
|
244
222
|
segments: deletedMemory.segmentIds.length,
|
|
245
223
|
orphanedItems: deletedMemory.orphanedItemIds.length,
|
|
246
224
|
deletedSummaries: deletedMemory.deletedSummaryIds.length,
|
|
247
|
-
deletedObservations: deletedMemory.deletedObservationIds.length,
|
|
248
|
-
deletedChunks: deletedMemory.deletedChunkIds.length,
|
|
249
|
-
deletedEpisodes: deletedMemory.deletedEpisodeIds.length,
|
|
250
225
|
},
|
|
251
226
|
"Enqueued Qdrant vector cleanup jobs for purged private conversations",
|
|
252
227
|
);
|
|
@@ -271,24 +246,6 @@ export async function runDaemon(): Promise<void> {
|
|
|
271
246
|
);
|
|
272
247
|
}
|
|
273
248
|
|
|
274
|
-
// Sweep dirty conversations whose tail messages are already past the
|
|
275
|
-
// idle delay — they should have been reduced while the daemon was down.
|
|
276
|
-
// Enqueue immediate reducer jobs so the memory worker picks them up.
|
|
277
|
-
try {
|
|
278
|
-
const sweepCount = sweepStaleReducerJobs();
|
|
279
|
-
if (sweepCount > 0) {
|
|
280
|
-
log.info(
|
|
281
|
-
{ sweepCount },
|
|
282
|
-
`Enqueued reducer jobs for ${sweepCount} stale dirty conversation(s)`,
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
} catch (err) {
|
|
286
|
-
log.warn(
|
|
287
|
-
{ err },
|
|
288
|
-
"Startup sweep for stale reducer jobs failed — continuing startup",
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
249
|
// Ensure a vellum guardian binding exists so the identity system works
|
|
293
250
|
// without requiring a manual bootstrap step.
|
|
294
251
|
try {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { and,
|
|
1
|
+
import { and, asc, eq, lte, or, sql } from "drizzle-orm";
|
|
2
2
|
import { v4 as uuid } from "uuid";
|
|
3
3
|
|
|
4
4
|
import { getDb } from "../memory/db.js";
|
|
@@ -217,7 +217,10 @@ export function getPendingAndOverdueFollowUps(): BriefFollowUp[] {
|
|
|
217
217
|
eq(followups.status, "nudged"),
|
|
218
218
|
),
|
|
219
219
|
)
|
|
220
|
-
.orderBy(
|
|
220
|
+
.orderBy(
|
|
221
|
+
sql`CASE WHEN ${followups.expectedResponseBy} IS NULL THEN 1 ELSE 0 END`,
|
|
222
|
+
asc(followups.expectedResponseBy),
|
|
223
|
+
)
|
|
221
224
|
.all();
|
|
222
225
|
|
|
223
226
|
return rows as BriefFollowUp[];
|