@botbotgo/agent-harness 0.0.306 → 0.0.308
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/README.md +45 -9
- package/README.zh.md +45 -9
- package/dist/config/runtime/workspace.yaml +16 -0
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -1
- package/dist/procedural/index.d.ts +1 -0
- package/dist/procedural/index.js +1 -0
- package/dist/procedural/manager.d.ts +59 -0
- package/dist/procedural/manager.js +345 -0
- package/dist/resource/isolation.js +6 -1
- package/dist/resources/prompts/runtime/procedural-memory-manager.md +40 -0
- package/dist/runtime/adapter/model/invocation-request.d.ts +1 -0
- package/dist/runtime/adapter/model/invocation-request.js +4 -0
- package/dist/runtime/harness.d.ts +6 -0
- package/dist/runtime/harness.js +144 -14
- package/dist/runtime/support/runtime-prompts.d.ts +7 -0
- package/dist/runtime/support/runtime-prompts.js +14 -0
- package/dist/workspace/compile.js +135 -5
- package/dist/workspace/object-loader.js +27 -17
- package/dist/workspace/resource-compilers.js +3 -3
- package/dist/workspace/support/source-protocols.d.ts +19 -0
- package/dist/workspace/support/source-protocols.js +192 -0
- package/dist/workspace/support/workspace-ref-utils.d.ts +4 -0
- package/dist/workspace/support/workspace-ref-utils.js +4 -0
- package/package.json +1 -1
|
@@ -93,7 +93,9 @@ async function linkHarnessPackage(isolatedRoot) {
|
|
|
93
93
|
}
|
|
94
94
|
async function buildIsolatedResourceRoot(packageRoot) {
|
|
95
95
|
const packageJsonPath = path.join(packageRoot, "package.json");
|
|
96
|
-
const manifest =
|
|
96
|
+
const manifest = existsSync(packageJsonPath)
|
|
97
|
+
? JSON.parse(await readFile(packageJsonPath, "utf8"))
|
|
98
|
+
: {};
|
|
97
99
|
const isolatedRoot = createIsolatedSnapshotDir(packageRoot);
|
|
98
100
|
await mkdir(path.dirname(isolatedRoot), { recursive: true });
|
|
99
101
|
await cp(packageRoot, isolatedRoot, {
|
|
@@ -101,6 +103,9 @@ async function buildIsolatedResourceRoot(packageRoot) {
|
|
|
101
103
|
force: true,
|
|
102
104
|
filter: (source) => path.basename(source) !== "node_modules",
|
|
103
105
|
});
|
|
106
|
+
if (!existsSync(path.join(isolatedRoot, "package.json"))) {
|
|
107
|
+
await cp(path.join(HARNESS_PACKAGE_ROOT, "resources", "package.json"), path.join(isolatedRoot, "package.json"));
|
|
108
|
+
}
|
|
104
109
|
await mkdir(path.join(isolatedRoot, "node_modules"), { recursive: true });
|
|
105
110
|
await linkDeclaredDependencies(isolatedRoot, packageRoot, manifest);
|
|
106
111
|
await linkHarnessPackage(isolatedRoot);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
You are deciding whether a completed run should produce reusable procedural memory.
|
|
2
|
+
|
|
3
|
+
Focus areas:
|
|
4
|
+
{{focus}}
|
|
5
|
+
|
|
6
|
+
Session:
|
|
7
|
+
{{sessionId}}
|
|
8
|
+
|
|
9
|
+
Request:
|
|
10
|
+
{{requestId}}
|
|
11
|
+
|
|
12
|
+
Candidate:
|
|
13
|
+
{{candidateJson}}
|
|
14
|
+
|
|
15
|
+
Existing procedural memory:
|
|
16
|
+
{{existingRecords}}
|
|
17
|
+
|
|
18
|
+
Return exactly one JSON object.
|
|
19
|
+
|
|
20
|
+
Rules:
|
|
21
|
+
- Store only reusable experience, tactics, workflows, debugging lessons, or failure-prevention guidance.
|
|
22
|
+
- Ignore pure personal facts, profile facts, location changes, and one-off durable knowledge facts unless they imply a reusable procedure.
|
|
23
|
+
- Prefer concise, imperative procedural guidance that can help a future run act better.
|
|
24
|
+
- Avoid duplicating existing procedural memory.
|
|
25
|
+
- Default kind must be "procedural".
|
|
26
|
+
- Use scope "workspace" for repo/workspace habits, "project" for broader project rules, "agent" for agent-specific operating habits, and "user" only when the reusable procedure is clearly personal to the user.
|
|
27
|
+
|
|
28
|
+
If there is no reusable procedural lesson, return:
|
|
29
|
+
{"store":false}
|
|
30
|
+
|
|
31
|
+
If there is a reusable lesson, return:
|
|
32
|
+
{
|
|
33
|
+
"store": true,
|
|
34
|
+
"summary": "short summary",
|
|
35
|
+
"content": "clear reusable procedure or lesson",
|
|
36
|
+
"kind": "procedural",
|
|
37
|
+
"scope": "workspace",
|
|
38
|
+
"tags": ["workflow_patterns"],
|
|
39
|
+
"confidence": 0.78
|
|
40
|
+
}
|
|
@@ -2,6 +2,7 @@ import type { CompiledAgentBinding, MessageContent, TranscriptMessage } from "..
|
|
|
2
2
|
export declare function buildAgentMessages(history: TranscriptMessage[], input: MessageContent, options?: {
|
|
3
3
|
suppressExplicitResourceTurns?: boolean;
|
|
4
4
|
suppressAssistantTurns?: boolean;
|
|
5
|
+
suppressHistoryTurns?: boolean;
|
|
5
6
|
}): Array<{
|
|
6
7
|
role: string;
|
|
7
8
|
content: MessageContent;
|
|
@@ -84,6 +84,9 @@ function selectRelevantHistoryTurns(groupedHistory, inputText, options = {}) {
|
|
|
84
84
|
}
|
|
85
85
|
export function buildAgentMessages(history, input, options = {}) {
|
|
86
86
|
const inputText = extractMessageText(input).trim();
|
|
87
|
+
if (options.suppressHistoryTurns) {
|
|
88
|
+
return [{ role: "user", content: normalizeMessageContent(input) }];
|
|
89
|
+
}
|
|
87
90
|
const groupedHistory = history.reduce((groups, item) => {
|
|
88
91
|
const current = groups.at(-1);
|
|
89
92
|
if (current && current[0]?.requestId === item.requestId) {
|
|
@@ -156,6 +159,7 @@ export function buildInvocationRequest(binding, history, input, options = {}) {
|
|
|
156
159
|
const messages = buildAgentMessages(history, input, {
|
|
157
160
|
suppressExplicitResourceTurns: Boolean(memoryInstruction) && !hasExplicitResourceReference(inputText),
|
|
158
161
|
suppressAssistantTurns: Boolean(memoryInstruction) && !hasExplicitResourceReference(inputText),
|
|
162
|
+
suppressHistoryTurns: Boolean(memoryInstruction) && !hasExplicitResourceReference(inputText),
|
|
159
163
|
});
|
|
160
164
|
const contextualFollowUpInstruction = buildContextualFollowUpInstruction(inputText, Boolean(memoryInstruction));
|
|
161
165
|
const conversationLanguage = resolveConversationLanguage(history, inputText);
|
|
@@ -36,6 +36,12 @@ export declare class AgentHarnessRuntime {
|
|
|
36
36
|
private readonly knowledgeModule;
|
|
37
37
|
private readonly runtimeMemoryFormationSync;
|
|
38
38
|
private readonly unregisterRuntimeMemoryFormationSync;
|
|
39
|
+
private readonly proceduralMemoryConfig;
|
|
40
|
+
private readonly proceduralMemoryStore;
|
|
41
|
+
private readonly proceduralMemoryManager;
|
|
42
|
+
private readonly proceduralMemoryModule;
|
|
43
|
+
private readonly proceduralMemoryFormationSync;
|
|
44
|
+
private readonly unregisterProceduralMemoryFormationSync;
|
|
39
45
|
private readonly resolvedRuntimeAdapterOptions;
|
|
40
46
|
private readonly scheduleManager;
|
|
41
47
|
private readonly healthMonitor;
|
package/dist/runtime/harness.js
CHANGED
|
@@ -35,7 +35,7 @@ import { Mem0IngestionSync, Mem0SemanticRecall, readMem0RuntimeConfig, } from ".
|
|
|
35
35
|
import { createRuntimeMemoryManager, RuntimeMemoryFormationSync, readRuntimeMemoryFormationConfig, } from "./harness/system/runtime-memory-manager.js";
|
|
36
36
|
import { readRuntimeMemoryMaintenanceConfig, readRuntimeMemoryPolicyConfig, resolveMemoryNamespace, } from "./harness/system/runtime-memory-policy.js";
|
|
37
37
|
import { resolveRuntimeAdapterOptions } from "./support/runtime-adapter-options.js";
|
|
38
|
-
import { resolveKnowledgeStorePath } from "./support/runtime-layout.js";
|
|
38
|
+
import { resolveKnowledgeStorePath, resolveProceduralMemoryStorePath } from "./support/runtime-layout.js";
|
|
39
39
|
import { SystemScheduleManager } from "./scheduling/system-schedule-manager.js";
|
|
40
40
|
import { initializeHarnessRuntime, reclaimExpiredClaimedRequests as reclaimHarnessExpiredClaimedRequests, recoverStartupRequests as recoverHarnessStartupRequests, isStaleRunningRequest as isHarnessStaleRunningRequest, } from "./harness/run/startup-runtime.js";
|
|
41
41
|
import { traceStartupStage } from "./startup-tracing.js";
|
|
@@ -44,6 +44,7 @@ import { streamHarnessRun } from "./harness/run/stream-run.js";
|
|
|
44
44
|
import { defaultRequestedAgentId, prepareRunStart } from "./harness/run/start-run.js";
|
|
45
45
|
import { buildRequestInspectionRecord, buildSessionInspectionRecord, deleteSessionRecord, getPublicApproval, listPublicApprovals, } from "./harness/run/session-records.js";
|
|
46
46
|
import { createKnowledgeModule } from "../knowledge/index.js";
|
|
47
|
+
import { createProceduralMemoryManager, ProceduralMemoryFormationSync, readProceduralMemoryRuntimeConfig, } from "../procedural/index.js";
|
|
47
48
|
const ACTIVE_REQUEST_STATES = [
|
|
48
49
|
"queued",
|
|
49
50
|
"claimed",
|
|
@@ -132,6 +133,17 @@ function toPublicHarnessStreamItem(item) {
|
|
|
132
133
|
return item;
|
|
133
134
|
}
|
|
134
135
|
}
|
|
136
|
+
function mergeMemoryItems(...groups) {
|
|
137
|
+
const merged = new Map();
|
|
138
|
+
for (const group of groups) {
|
|
139
|
+
for (const item of group) {
|
|
140
|
+
if (!merged.has(item.id)) {
|
|
141
|
+
merged.set(item.id, item);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return Array.from(merged.values());
|
|
146
|
+
}
|
|
135
147
|
function normalizeSessionListText(content, limit) {
|
|
136
148
|
if (!content) {
|
|
137
149
|
return undefined;
|
|
@@ -216,6 +228,12 @@ export class AgentHarnessRuntime {
|
|
|
216
228
|
knowledgeModule;
|
|
217
229
|
runtimeMemoryFormationSync;
|
|
218
230
|
unregisterRuntimeMemoryFormationSync;
|
|
231
|
+
proceduralMemoryConfig;
|
|
232
|
+
proceduralMemoryStore;
|
|
233
|
+
proceduralMemoryManager;
|
|
234
|
+
proceduralMemoryModule;
|
|
235
|
+
proceduralMemoryFormationSync;
|
|
236
|
+
unregisterProceduralMemoryFormationSync;
|
|
219
237
|
resolvedRuntimeAdapterOptions;
|
|
220
238
|
scheduleManager;
|
|
221
239
|
healthMonitor;
|
|
@@ -461,6 +479,82 @@ export class AgentHarnessRuntime {
|
|
|
461
479
|
this.runtimeMemoryFormationSync = null;
|
|
462
480
|
this.unregisterRuntimeMemoryFormationSync = () => { };
|
|
463
481
|
}
|
|
482
|
+
const proceduralMemoryConfig = readProceduralMemoryRuntimeConfig(this.defaultRuntimeEntryBinding?.harnessRuntime.proceduralMemory);
|
|
483
|
+
this.proceduralMemoryConfig = proceduralMemoryConfig?.enabled ? proceduralMemoryConfig : null;
|
|
484
|
+
const proceduralStoreConfig = this.proceduralMemoryConfig?.store && Object.keys(this.proceduralMemoryConfig.store).length > 0
|
|
485
|
+
? this.proceduralMemoryConfig.store
|
|
486
|
+
: {
|
|
487
|
+
kind: "SqliteStore",
|
|
488
|
+
path: resolveProceduralMemoryStorePath(runtimeRoot),
|
|
489
|
+
};
|
|
490
|
+
this.proceduralMemoryStore = this.proceduralMemoryConfig
|
|
491
|
+
? resolveStoreFromConfig(this.stores, proceduralStoreConfig, runtimeRoot) ?? null
|
|
492
|
+
: null;
|
|
493
|
+
this.proceduralMemoryManager =
|
|
494
|
+
this.defaultRuntimeEntryBinding && this.proceduralMemoryConfig
|
|
495
|
+
? createProceduralMemoryManager({
|
|
496
|
+
workspace: this.workspace,
|
|
497
|
+
binding: this.defaultRuntimeEntryBinding,
|
|
498
|
+
config: this.proceduralMemoryConfig,
|
|
499
|
+
modelResolver: this.resolvedRuntimeAdapterOptions.modelResolver,
|
|
500
|
+
})
|
|
501
|
+
: null;
|
|
502
|
+
this.proceduralMemoryModule =
|
|
503
|
+
this.proceduralMemoryConfig && this.proceduralMemoryStore
|
|
504
|
+
? createKnowledgeModule({
|
|
505
|
+
store: this.proceduralMemoryStore,
|
|
506
|
+
policy: null,
|
|
507
|
+
maintenanceConfig: null,
|
|
508
|
+
resolveNamespace: (scope, context) => {
|
|
509
|
+
const binding = this.defaultRuntimeEntryBinding;
|
|
510
|
+
if (!binding) {
|
|
511
|
+
const identifier = scope === "session"
|
|
512
|
+
? context.sessionId
|
|
513
|
+
: scope === "agent"
|
|
514
|
+
? context.agentId
|
|
515
|
+
: scope === "workspace"
|
|
516
|
+
? context.workspaceId
|
|
517
|
+
: scope === "user"
|
|
518
|
+
? context.userId ?? "default"
|
|
519
|
+
: context.projectId ?? context.workspaceId;
|
|
520
|
+
return ["procedural", `${scope}s`, identifier ?? "default"];
|
|
521
|
+
}
|
|
522
|
+
return this.resolveMemoryNamespace(scope, binding, {
|
|
523
|
+
sessionId: context.sessionId,
|
|
524
|
+
agentId: context.agentId,
|
|
525
|
+
userId: context.userId,
|
|
526
|
+
projectId: context.projectId,
|
|
527
|
+
});
|
|
528
|
+
},
|
|
529
|
+
resolveVectorStore: async () => null,
|
|
530
|
+
transformCandidates: this.proceduralMemoryManager && this.defaultRuntimeEntryBinding
|
|
531
|
+
? ({ candidates, context, existingRecords }) => this.proceduralMemoryManager.transform({
|
|
532
|
+
candidates,
|
|
533
|
+
binding: this.defaultRuntimeEntryBinding,
|
|
534
|
+
sessionId: context.sessionId ?? `procedural-api-${context.requestId ?? "unknown"}`,
|
|
535
|
+
requestId: context.requestId ?? createPersistentId(new Date(context.recordedAt ?? new Date().toISOString())),
|
|
536
|
+
recordedAt: context.recordedAt ?? new Date().toISOString(),
|
|
537
|
+
existingRecords,
|
|
538
|
+
})
|
|
539
|
+
: undefined,
|
|
540
|
+
})
|
|
541
|
+
: null;
|
|
542
|
+
if (this.proceduralMemoryConfig && this.proceduralMemoryModule) {
|
|
543
|
+
this.proceduralMemoryFormationSync = new ProceduralMemoryFormationSync(this.persistence, this.proceduralMemoryConfig, (input) => this.proceduralMemoryModule.memorizeCandidates(input.candidates, {
|
|
544
|
+
sessionId: input.sessionId,
|
|
545
|
+
requestId: input.requestId,
|
|
546
|
+
agentId: input.agentId,
|
|
547
|
+
workspaceId: this.getWorkspaceId(this.defaultRuntimeEntryBinding),
|
|
548
|
+
userId: input.userId,
|
|
549
|
+
projectId: input.projectId,
|
|
550
|
+
recordedAt: input.recordedAt,
|
|
551
|
+
}, { storeCandidateLog: false }).then(() => undefined), runtimeRoot);
|
|
552
|
+
this.unregisterProceduralMemoryFormationSync = this.eventBus.registerProjection(this.proceduralMemoryFormationSync);
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
this.proceduralMemoryFormationSync = null;
|
|
556
|
+
this.unregisterProceduralMemoryFormationSync = () => { };
|
|
557
|
+
}
|
|
464
558
|
this.recoveryConfig = getRecoveryConfig(workspace.refs);
|
|
465
559
|
this.concurrencyConfig = getConcurrencyConfig(workspace.refs);
|
|
466
560
|
const healthConfig = readHealthMonitorConfig(workspace);
|
|
@@ -514,6 +608,9 @@ export class AgentHarnessRuntime {
|
|
|
514
608
|
scheduleBackgroundTask: (task) => this.scheduleBackgroundStartupTask(task),
|
|
515
609
|
});
|
|
516
610
|
this.scheduleBackgroundStartupTask(traceStartupStage("runtime.initialize.startupRecovery", () => this.recoverStartupRequests()));
|
|
611
|
+
if (this.proceduralMemoryStore) {
|
|
612
|
+
await this.proceduralMemoryStore.listNamespaces();
|
|
613
|
+
}
|
|
517
614
|
this.initialized = true;
|
|
518
615
|
}
|
|
519
616
|
subscribe(listener) {
|
|
@@ -604,26 +701,44 @@ export class AgentHarnessRuntime {
|
|
|
604
701
|
if (!binding) {
|
|
605
702
|
throw new Error("recall requires a runtime entry binding.");
|
|
606
703
|
}
|
|
607
|
-
|
|
704
|
+
const context = {
|
|
608
705
|
sessionId: input.sessionId,
|
|
609
706
|
agentId: input.agentId ?? binding.agent.id,
|
|
610
707
|
workspaceId: input.workspaceId ?? this.getWorkspaceId(binding),
|
|
611
708
|
userId: input.userId,
|
|
612
709
|
projectId: input.projectId,
|
|
613
|
-
}
|
|
710
|
+
};
|
|
711
|
+
const [knowledge, procedural] = await Promise.all([
|
|
712
|
+
this.knowledgeModule.recall(input, context),
|
|
713
|
+
this.proceduralMemoryModule && this.proceduralMemoryConfig?.retrieval?.enabled !== false
|
|
714
|
+
? this.proceduralMemoryModule.recall(input, context)
|
|
715
|
+
: Promise.resolve({ items: [] }),
|
|
716
|
+
]);
|
|
717
|
+
return {
|
|
718
|
+
items: mergeMemoryItems(knowledge.items, procedural.items),
|
|
719
|
+
};
|
|
614
720
|
}
|
|
615
721
|
async listMemories(input = {}) {
|
|
616
722
|
const binding = this.defaultRuntimeEntryBinding;
|
|
617
723
|
if (!binding) {
|
|
618
724
|
throw new Error("listMemories requires a runtime entry binding.");
|
|
619
725
|
}
|
|
620
|
-
|
|
726
|
+
const context = {
|
|
621
727
|
sessionId: input.sessionId,
|
|
622
728
|
agentId: input.agentId,
|
|
623
729
|
workspaceId: input.workspaceId ?? this.getWorkspaceId(binding),
|
|
624
730
|
userId: input.userId,
|
|
625
731
|
projectId: input.projectId,
|
|
626
|
-
}
|
|
732
|
+
};
|
|
733
|
+
const [knowledge, procedural] = await Promise.all([
|
|
734
|
+
this.knowledgeModule.list(input, context),
|
|
735
|
+
this.proceduralMemoryModule
|
|
736
|
+
? this.proceduralMemoryModule.list(input, context)
|
|
737
|
+
: Promise.resolve({ items: [] }),
|
|
738
|
+
]);
|
|
739
|
+
return {
|
|
740
|
+
items: mergeMemoryItems(knowledge.items, procedural.items),
|
|
741
|
+
};
|
|
627
742
|
}
|
|
628
743
|
async updateMemory(input) {
|
|
629
744
|
const binding = this.defaultRuntimeEntryBinding;
|
|
@@ -1029,27 +1144,40 @@ export class AgentHarnessRuntime {
|
|
|
1029
1144
|
return path.basename(workspaceRoot) || "default";
|
|
1030
1145
|
}
|
|
1031
1146
|
async buildRuntimeMemoryContext(binding, sessionId, input) {
|
|
1032
|
-
if (!this.runtimeMemoryPolicy) {
|
|
1147
|
+
if (!this.runtimeMemoryPolicy && !this.proceduralMemoryModule) {
|
|
1033
1148
|
return undefined;
|
|
1034
1149
|
}
|
|
1035
1150
|
const query = extractMessageText(input ?? "").trim();
|
|
1036
1151
|
if (!query) {
|
|
1037
1152
|
return undefined;
|
|
1038
1153
|
}
|
|
1039
|
-
const
|
|
1040
|
-
query,
|
|
1041
|
-
topK: this.runtimeMemoryPolicy?.retrieval.maxPromptMemories ?? 8,
|
|
1042
|
-
}, {
|
|
1154
|
+
const context = {
|
|
1043
1155
|
sessionId,
|
|
1044
1156
|
agentId: binding.agent.id,
|
|
1045
1157
|
workspaceId: this.getWorkspaceId(binding),
|
|
1046
|
-
}
|
|
1047
|
-
|
|
1158
|
+
};
|
|
1159
|
+
const proceduralTopK = this.proceduralMemoryConfig?.retrieval?.maxPromptItems ?? 4;
|
|
1160
|
+
const [knowledgeRecall, proceduralRecall] = await Promise.all([
|
|
1161
|
+
this.runtimeMemoryPolicy
|
|
1162
|
+
? this.knowledgeModule.buildPromptRecall({
|
|
1163
|
+
query,
|
|
1164
|
+
topK: this.runtimeMemoryPolicy?.retrieval.maxPromptMemories ?? 8,
|
|
1165
|
+
}, context)
|
|
1166
|
+
: Promise.resolve({ items: [], context: undefined }),
|
|
1167
|
+
this.proceduralMemoryModule && this.proceduralMemoryConfig?.retrieval?.enabled !== false
|
|
1168
|
+
? this.proceduralMemoryModule.buildPromptRecall({
|
|
1169
|
+
query,
|
|
1170
|
+
topK: proceduralTopK,
|
|
1171
|
+
}, context)
|
|
1172
|
+
: Promise.resolve({ items: [], context: undefined }),
|
|
1173
|
+
]);
|
|
1174
|
+
const promptParts = [knowledgeRecall.context?.trim(), proceduralRecall.context?.trim()].filter((value) => !!value);
|
|
1175
|
+
if (promptParts.length === 0) {
|
|
1048
1176
|
return undefined;
|
|
1049
1177
|
}
|
|
1050
1178
|
return {
|
|
1051
|
-
prompt:
|
|
1052
|
-
items:
|
|
1179
|
+
prompt: promptParts.join("\n\n"),
|
|
1180
|
+
items: mergeMemoryItems(knowledgeRecall.items, proceduralRecall.items),
|
|
1053
1181
|
};
|
|
1054
1182
|
}
|
|
1055
1183
|
async persistRuntimeMemoryCandidates(binding, sessionId, requestId, value) {
|
|
@@ -1374,11 +1502,13 @@ export class AgentHarnessRuntime {
|
|
|
1374
1502
|
this.unregisterRuntimeMemorySync();
|
|
1375
1503
|
this.unregisterMem0IngestionSync();
|
|
1376
1504
|
this.unregisterRuntimeMemoryFormationSync();
|
|
1505
|
+
this.unregisterProceduralMemoryFormationSync();
|
|
1377
1506
|
await Promise.allSettled(Array.from(this.backgroundTasks));
|
|
1378
1507
|
await this.sessionMemorySync?.close();
|
|
1379
1508
|
await this.runtimeMemorySync?.close();
|
|
1380
1509
|
await this.mem0IngestionSync?.close();
|
|
1381
1510
|
await this.runtimeMemoryFormationSync?.close();
|
|
1511
|
+
await this.proceduralMemoryFormationSync?.close();
|
|
1382
1512
|
await closeMcpClientsForWorkspace(this.workspace);
|
|
1383
1513
|
this.initialized = false;
|
|
1384
1514
|
}
|
|
@@ -28,3 +28,10 @@ export declare function renderRuntimeMemoryMutationReconciliationPrompt(input: {
|
|
|
28
28
|
candidate: MemoryCandidate;
|
|
29
29
|
existingRecords: MemoryRecord[];
|
|
30
30
|
}): string;
|
|
31
|
+
export declare function renderProceduralMemoryManagerPrompt(input: {
|
|
32
|
+
candidate: MemoryCandidate;
|
|
33
|
+
sessionId: string;
|
|
34
|
+
requestId: string;
|
|
35
|
+
focus: string[];
|
|
36
|
+
existingRecords: MemoryRecord[];
|
|
37
|
+
}): string;
|
|
@@ -53,3 +53,17 @@ export function renderRuntimeMemoryMutationReconciliationPrompt(input) {
|
|
|
53
53
|
existingRecords: existing,
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
+
export function renderProceduralMemoryManagerPrompt(input) {
|
|
57
|
+
const existing = input.existingRecords.length === 0
|
|
58
|
+
? "(none)"
|
|
59
|
+
: input.existingRecords
|
|
60
|
+
.map((record) => `- scope=${record.scope}; kind=${record.kind}; status=${record.status}; summary=${record.summary}; content=${record.content.replace(/\s+/g, " ").slice(0, 220)}`)
|
|
61
|
+
.join("\n");
|
|
62
|
+
return renderBundledTemplate("prompts/runtime/procedural-memory-manager.md", {
|
|
63
|
+
sessionId: input.sessionId,
|
|
64
|
+
requestId: input.requestId,
|
|
65
|
+
focus: input.focus.join(", "),
|
|
66
|
+
candidateJson: JSON.stringify(input.candidate, null, 2),
|
|
67
|
+
existingRecords: existing,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { readdir } from "node:fs/promises";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
4
5
|
import { ensureResourceSources } from "../resource/resource.js";
|
|
5
6
|
import { ensureExternalResourceSource, isExternalSourceLocator, resolveResourcePackageRoot } from "../resource/sources.js";
|
|
6
7
|
import { loadWorkspaceObjects, readToolModuleItems, readYamlItems } from "./object-loader.js";
|
|
@@ -10,10 +11,30 @@ import { validateAgent, validateTopology } from "./validate.js";
|
|
|
10
11
|
import { compileBinding } from "./agent-binding-compiler.js";
|
|
11
12
|
import { discoverSubagents, ensureDiscoverySources } from "./support/discovery.js";
|
|
12
13
|
import { collectAgentDiscoverySourceRefs, collectToolSourceRefs } from "./support/source-collectors.js";
|
|
13
|
-
import { getRoutingDefaultAgentId, getRuntimeResources, getRuntimeStorageRoots, getToolModuleDiscoveryConfig, getRoutingRules, resolveRefId, } from "./support/workspace-ref-utils.js";
|
|
14
|
+
import { getRoutingDefaultAgentId, getRuntimeSources, getRuntimeResources, getRuntimeStorageRoots, getToolModuleDiscoveryConfig, getRoutingRules, resolveRefId, } from "./support/workspace-ref-utils.js";
|
|
14
15
|
import { hydrateAgentMcpTools, hydrateResourceAndExternalTools } from "./tool-hydration.js";
|
|
15
16
|
import { traceStartupStage } from "../runtime/startup-tracing.js";
|
|
16
17
|
import { shouldSkipScanDirectory } from "../utils/fs.js";
|
|
18
|
+
import { ensureRemoteSkillSource, ensureToolPackageSource, isFileSourceUri, isHttpSourceUri, isNpmSourceUri, resolveFileSourcePath, } from "./support/source-protocols.js";
|
|
19
|
+
import { discoverToolModuleDefinitions } from "../tool-modules.js";
|
|
20
|
+
function mergeObjectValues(base, override) {
|
|
21
|
+
if (override === undefined) {
|
|
22
|
+
return base;
|
|
23
|
+
}
|
|
24
|
+
if (typeof base === "object" &&
|
|
25
|
+
base &&
|
|
26
|
+
typeof override === "object" &&
|
|
27
|
+
override &&
|
|
28
|
+
!Array.isArray(base) &&
|
|
29
|
+
!Array.isArray(override)) {
|
|
30
|
+
const merged = { ...base };
|
|
31
|
+
for (const [key, value] of Object.entries(override)) {
|
|
32
|
+
merged[key] = key in merged ? mergeObjectValues(merged[key], value) : value;
|
|
33
|
+
}
|
|
34
|
+
return merged;
|
|
35
|
+
}
|
|
36
|
+
return override;
|
|
37
|
+
}
|
|
17
38
|
function collectParsedResources(refs) {
|
|
18
39
|
const embeddings = new Map();
|
|
19
40
|
const mcpServers = new Map();
|
|
@@ -132,7 +153,7 @@ async function registerAttachedResourceTools(tools, resourceRoot, toolModuleDisc
|
|
|
132
153
|
sourcePath,
|
|
133
154
|
value: item,
|
|
134
155
|
});
|
|
135
|
-
tools.set(parsed.id, parsed);
|
|
156
|
+
tools.set(parsed.id, mergeParsedToolObject(tools.get(parsed.id), parsed));
|
|
136
157
|
}
|
|
137
158
|
for (const { item, sourcePath } of await readToolModuleItems(toolsRoot, { scope: toolModuleDiscoveryScope })) {
|
|
138
159
|
const parsed = parseToolObject({
|
|
@@ -141,7 +162,7 @@ async function registerAttachedResourceTools(tools, resourceRoot, toolModuleDisc
|
|
|
141
162
|
sourcePath,
|
|
142
163
|
value: item,
|
|
143
164
|
});
|
|
144
|
-
tools.set(parsed.id, parsed);
|
|
165
|
+
tools.set(parsed.id, mergeParsedToolObject(tools.get(parsed.id), parsed));
|
|
145
166
|
}
|
|
146
167
|
}
|
|
147
168
|
async function collectSkillRoots(root) {
|
|
@@ -160,6 +181,90 @@ async function collectSkillRoots(root) {
|
|
|
160
181
|
return [];
|
|
161
182
|
}
|
|
162
183
|
}
|
|
184
|
+
async function resolveConfiguredSkillSourceRoots(sources, workspaceRoot) {
|
|
185
|
+
const resolved = [];
|
|
186
|
+
for (const source of sources) {
|
|
187
|
+
if (isFileSourceUri(source)) {
|
|
188
|
+
const resolvedPath = resolveFileSourcePath(source, workspaceRoot);
|
|
189
|
+
resolved.push(resolvedPath.endsWith(`${path.sep}SKILL.md`) || resolvedPath.endsWith("/SKILL.md")
|
|
190
|
+
? path.dirname(resolvedPath)
|
|
191
|
+
: resolvedPath);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (isHttpSourceUri(source)) {
|
|
195
|
+
resolved.push(await ensureRemoteSkillSource(source));
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
throw new Error(`Unsupported skill source ${source}. Use file:// or https://.`);
|
|
199
|
+
}
|
|
200
|
+
return resolved;
|
|
201
|
+
}
|
|
202
|
+
async function registerToolFolderSource(tools, folderRoot, toolModuleDiscoveryScope) {
|
|
203
|
+
for (const { item, sourcePath } of await readYamlItems(folderRoot, undefined, { recursive: true })) {
|
|
204
|
+
const parsed = parseToolObject({
|
|
205
|
+
id: typeof item.id === "string" ? item.id : path.basename(sourcePath).replace(/\.(yaml|yml|json)$/i, ""),
|
|
206
|
+
kind: "tool",
|
|
207
|
+
sourcePath,
|
|
208
|
+
value: item,
|
|
209
|
+
});
|
|
210
|
+
tools.set(parsed.id, mergeParsedToolObject(tools.get(parsed.id), parsed));
|
|
211
|
+
}
|
|
212
|
+
for (const { item, sourcePath } of await readToolModuleItems(folderRoot, { scope: toolModuleDiscoveryScope })) {
|
|
213
|
+
const parsed = parseToolObject({
|
|
214
|
+
id: String(item.id),
|
|
215
|
+
kind: "tool",
|
|
216
|
+
sourcePath,
|
|
217
|
+
value: item,
|
|
218
|
+
});
|
|
219
|
+
tools.set(parsed.id, mergeParsedToolObject(tools.get(parsed.id), parsed));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async function registerToolPackageSource(tools, source, workspaceRoot) {
|
|
223
|
+
const installed = await ensureToolPackageSource(source, workspaceRoot);
|
|
224
|
+
const imported = await import(pathToFileURL(installed.entryPath).href);
|
|
225
|
+
const definitions = discoverToolModuleDefinitions("", imported);
|
|
226
|
+
for (const definition of definitions) {
|
|
227
|
+
const parsed = parseToolObject({
|
|
228
|
+
id: definition.implementationName,
|
|
229
|
+
kind: "tool",
|
|
230
|
+
sourcePath: installed.entryPath,
|
|
231
|
+
value: {
|
|
232
|
+
kind: "tool",
|
|
233
|
+
id: definition.implementationName,
|
|
234
|
+
type: "function",
|
|
235
|
+
name: definition.implementationName,
|
|
236
|
+
description: definition.description,
|
|
237
|
+
implementationName: definition.implementationName,
|
|
238
|
+
hasModuleSchema: definition.hasModuleSchema,
|
|
239
|
+
...(definition.modelSchema ? { modelSchema: definition.modelSchema } : {}),
|
|
240
|
+
...(definition.retryable !== undefined ? { retryable: definition.retryable } : {}),
|
|
241
|
+
...(definition.memory ? { config: { memory: definition.memory } } : {}),
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
tools.set(parsed.id, mergeParsedToolObject(tools.get(parsed.id), parsed));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function mergeParsedToolObject(current, incoming) {
|
|
248
|
+
if (!current) {
|
|
249
|
+
return incoming;
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
...current,
|
|
253
|
+
...incoming,
|
|
254
|
+
config: mergeObjectValues(current.config, incoming.config),
|
|
255
|
+
subprocess: incoming.subprocess ?? current.subprocess,
|
|
256
|
+
inputSchemaRef: incoming.inputSchemaRef ?? current.inputSchemaRef,
|
|
257
|
+
hasModuleSchema: incoming.hasModuleSchema ?? current.hasModuleSchema,
|
|
258
|
+
modelSchema: incoming.modelSchema ?? current.modelSchema,
|
|
259
|
+
embeddingModelRef: incoming.embeddingModelRef ?? current.embeddingModelRef,
|
|
260
|
+
backendOperation: incoming.backendOperation ?? current.backendOperation,
|
|
261
|
+
mcpRef: incoming.mcpRef ?? current.mcpRef,
|
|
262
|
+
bundleRefs: incoming.bundleRefs.length > 0 ? incoming.bundleRefs : current.bundleRefs,
|
|
263
|
+
hitl: incoming.hitl ?? current.hitl,
|
|
264
|
+
retryable: incoming.retryable ?? current.retryable,
|
|
265
|
+
sourcePath: incoming.sourcePath,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
163
268
|
async function registerWorkspaceSkillRegistry(skillCollectionRoots) {
|
|
164
269
|
const registry = new Map();
|
|
165
270
|
for (const skillsRoot of skillCollectionRoots) {
|
|
@@ -227,10 +332,29 @@ export async function loadWorkspace(workspaceRoot, options = {}) {
|
|
|
227
332
|
}
|
|
228
333
|
const { embeddings, mcpServers, models, vectorStores, tools } = collectParsedResources(loaded.refs);
|
|
229
334
|
const toolModuleDiscoveryConfig = getToolModuleDiscoveryConfig(loaded.refs);
|
|
335
|
+
const runtimeSources = getRuntimeSources(loaded.refs);
|
|
230
336
|
await traceStartupStage("workspace.hydrate.agentMcpTools", () => hydrateAgentMcpTools(loaded.agents, mcpServers, tools), {
|
|
231
337
|
workspaceRoot,
|
|
232
338
|
agentCount: loaded.agents.length,
|
|
233
339
|
});
|
|
340
|
+
for (const source of runtimeSources.tools) {
|
|
341
|
+
if (isFileSourceUri(source)) {
|
|
342
|
+
const folderRoot = resolveFileSourcePath(source, workspaceRoot);
|
|
343
|
+
await traceStartupStage("workspace.register.toolFolderSource", () => registerToolFolderSource(tools, folderRoot, toolModuleDiscoveryConfig.scope), {
|
|
344
|
+
workspaceRoot,
|
|
345
|
+
source,
|
|
346
|
+
});
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
if (isNpmSourceUri(source)) {
|
|
350
|
+
await traceStartupStage("workspace.register.toolPackageSource", () => registerToolPackageSource(tools, source, workspaceRoot), {
|
|
351
|
+
workspaceRoot,
|
|
352
|
+
source,
|
|
353
|
+
});
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
throw new Error(`Unsupported tool source ${source}. Use file:// or npm://.`);
|
|
357
|
+
}
|
|
234
358
|
const configuredResources = Array.from(new Set([
|
|
235
359
|
...getRuntimeResources(loaded.refs),
|
|
236
360
|
...(options.resources ?? []),
|
|
@@ -246,11 +370,16 @@ export async function loadWorkspace(workspaceRoot, options = {}) {
|
|
|
246
370
|
});
|
|
247
371
|
}
|
|
248
372
|
const localResourceRoot = resolveResourcePackageRoot(workspaceRoot);
|
|
249
|
-
const
|
|
373
|
+
const configuredSkillSourceRoots = await traceStartupStage("workspace.resolve.skillSources", () => resolveConfiguredSkillSourceRoots(runtimeSources.skills, workspaceRoot), {
|
|
374
|
+
workspaceRoot,
|
|
375
|
+
skillSourceCount: runtimeSources.skills.length,
|
|
376
|
+
});
|
|
377
|
+
const skillCollectionRoots = Array.from(new Set([
|
|
250
378
|
path.join(workspaceRoot, "modules", "skills"),
|
|
251
379
|
...(localResourceRoot ? [path.join(localResourceRoot, "skills")] : []),
|
|
380
|
+
...configuredSkillSourceRoots,
|
|
252
381
|
...resolvedConfiguredResources.map((resource) => path.join(resource.root, "skills")),
|
|
253
|
-
];
|
|
382
|
+
]));
|
|
254
383
|
const skillRegistry = await traceStartupStage("workspace.register.skillRegistry", () => registerWorkspaceSkillRegistry(skillCollectionRoots), {
|
|
255
384
|
workspaceRoot,
|
|
256
385
|
skillCollectionRootCount: skillCollectionRoots.length,
|
|
@@ -272,6 +401,7 @@ export async function loadWorkspace(workspaceRoot, options = {}) {
|
|
|
272
401
|
validateToolNameConflicts(tools);
|
|
273
402
|
const resources = Array.from(new Set([
|
|
274
403
|
...(localResourceRoot ? [localResourceRoot] : []),
|
|
404
|
+
...runtimeSources.tools.filter((source) => isNpmSourceUri(source)),
|
|
275
405
|
...collectedResources,
|
|
276
406
|
]));
|
|
277
407
|
await traceStartupStage("workspace.validate.resources", async () => {
|
|
@@ -6,6 +6,7 @@ import { resolveIsolatedResourceModulePath } from "../resource/isolation.js";
|
|
|
6
6
|
import { isExternalSourceLocator, resolveResourcePackageRoot } from "../resource/sources.js";
|
|
7
7
|
import { discoverToolModuleDefinitions, isSupportedToolModulePath, loadToolModuleDefinition } from "../tool-modules.js";
|
|
8
8
|
import { fileExists, shouldSkipScanDirectory } from "../utils/fs.js";
|
|
9
|
+
import { isFileSourceUri, readRuntimeSources, resolveFileSourcePath } from "./support/source-protocols.js";
|
|
9
10
|
import { readNamedYamlItems, readYamlItems, } from "./yaml-object-reader.js";
|
|
10
11
|
export { normalizeYamlItem, readYamlItems } from "./yaml-object-reader.js";
|
|
11
12
|
const CONVENTIONAL_OBJECT_DIRECTORIES = ["tools"];
|
|
@@ -682,23 +683,32 @@ function getMergedToolModuleDiscoveryScope(mergedObjects) {
|
|
|
682
683
|
: {};
|
|
683
684
|
return toolModuleDiscovery.scope === "top-level" ? "top-level" : "recursive";
|
|
684
685
|
}
|
|
685
|
-
async function loadConventionalObjectsForRoot(root, mergedObjects, toolModuleDiscoveryScope) {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
686
|
+
async function loadConventionalObjectsForRoot(root, runtimeRoot, mergedObjects, toolModuleDiscoveryScope) {
|
|
687
|
+
const runtimeDefaults = mergedObjects.get("runtime/default")?.item;
|
|
688
|
+
const configuredToolRoots = readRuntimeSources(runtimeDefaults).tools
|
|
689
|
+
.filter((source) => isFileSourceUri(source))
|
|
690
|
+
.map((source) => resolveFileSourcePath(source, runtimeRoot));
|
|
691
|
+
const conventionalToolRoots = CONVENTIONAL_OBJECT_DIRECTORIES.flatMap((directory) => conventionalDirectoryRoots(root, directory));
|
|
692
|
+
const objectRoots = root === runtimeRoot
|
|
693
|
+
? Array.from(new Set([
|
|
694
|
+
...conventionalToolRoots,
|
|
695
|
+
...configuredToolRoots,
|
|
696
|
+
]))
|
|
697
|
+
: conventionalToolRoots;
|
|
698
|
+
for (const objectRoot of objectRoots) {
|
|
699
|
+
for (const { item, sourcePath } of await readYamlItemsIgnoringNodeModules(objectRoot)) {
|
|
700
|
+
const workspaceObject = parseWorkspaceObject(item, sourcePath);
|
|
701
|
+
if (!workspaceObject) {
|
|
702
|
+
continue;
|
|
694
703
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
704
|
+
mergeWorkspaceObjectRecord(mergedObjects, workspaceObject, item, sourcePath);
|
|
705
|
+
}
|
|
706
|
+
for (const { item, sourcePath } of await readToolModuleItems(objectRoot, { scope: toolModuleDiscoveryScope })) {
|
|
707
|
+
const workspaceObject = parseWorkspaceObject(item, sourcePath);
|
|
708
|
+
if (!workspaceObject) {
|
|
709
|
+
continue;
|
|
701
710
|
}
|
|
711
|
+
mergeWorkspaceObjectRecord(mergedObjects, workspaceObject, item, sourcePath);
|
|
702
712
|
}
|
|
703
713
|
}
|
|
704
714
|
}
|
|
@@ -882,7 +892,7 @@ export async function readToolModuleItems(root, options = {}) {
|
|
|
882
892
|
const records = [];
|
|
883
893
|
for (const filePath of files) {
|
|
884
894
|
const sourceText = await readFile(filePath, "utf8");
|
|
885
|
-
const packageRoot =
|
|
895
|
+
const packageRoot = findToolPackageRoot(filePath);
|
|
886
896
|
const isolatedSourcePath = await resolveIsolatedResourceModulePath(packageRoot, filePath);
|
|
887
897
|
const imported = await import(pathToFileURL(isolatedSourcePath).href);
|
|
888
898
|
const definitions = discoverToolModuleDefinitions(sourceText, imported);
|
|
@@ -958,7 +968,7 @@ export async function loadWorkspaceObjects(workspaceRoot, options = {}) {
|
|
|
958
968
|
await loadConfigYamlForRoot(root, configRoot, mergedAgents, mergedObjects);
|
|
959
969
|
await loadModuleAgentsForRoot(root, mergedAgents);
|
|
960
970
|
if (root !== defaultRoot) {
|
|
961
|
-
await loadConventionalObjectsForRoot(root, mergedObjects, getMergedToolModuleDiscoveryScope(mergedObjects));
|
|
971
|
+
await loadConventionalObjectsForRoot(root, workspaceRoot, mergedObjects, getMergedToolModuleDiscoveryScope(mergedObjects));
|
|
962
972
|
}
|
|
963
973
|
await loadModuleObjectsForRoot(root, mergedObjects);
|
|
964
974
|
await loadRootObjects(root, mergedObjects);
|