@botbotgo/agent-harness 0.0.475 → 0.0.476
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 +3 -1234
- package/README.zh.md +3 -1191
- package/dist/acp.js +1 -1
- package/dist/api.js +1 -404
- package/dist/benchmark/checkpoint-resume-cost-benchmark.js +1 -55
- package/dist/benchmark/deepagent-local-model-benchmark.js +2 -35
- package/dist/benchmark/upstream-runtime-ab-benchmark.js +1 -179
- package/dist/cli/chat-interactive.js +25 -244
- package/dist/cli/chat-rendering.js +6 -100
- package/dist/cli/chat-stream.js +23 -512
- package/dist/cli/chat-ui.js +21 -199
- package/dist/cli/chat-workspace.js +2 -210
- package/dist/cli/main.js +21 -428
- package/dist/cli/managed-service-commands.js +9 -63
- package/dist/cli/managed-service.js +2 -137
- package/dist/cli/options-init-chat.js +1 -108
- package/dist/cli/options-runtime.js +1 -158
- package/dist/cli/options-serve.js +1 -282
- package/dist/cli/options.js +2 -19
- package/dist/cli/process-guards.js +1 -139
- package/dist/cli/request-tree.js +7 -296
- package/dist/cli/runtime-commands.js +12 -258
- package/dist/cli/runtime-output.js +16 -155
- package/dist/cli/server-commands.js +16 -270
- package/dist/cli/workspace.js +1 -67
- package/dist/cli.js +1 -7
- package/dist/client/acp.js +1 -1
- package/dist/client/in-process.js +1 -67
- package/dist/client/index.js +1 -2
- package/dist/client/types.js +0 -1
- package/dist/client.js +1 -1
- package/dist/contracts/core.js +1 -1
- package/dist/contracts/runtime-evaluation.js +0 -1
- package/dist/contracts/runtime-memory.js +0 -1
- package/dist/contracts/runtime-observability.js +0 -1
- package/dist/contracts/runtime-requests.js +0 -1
- package/dist/contracts/runtime-scheduling.js +0 -1
- package/dist/contracts/runtime.js +1 -27
- package/dist/contracts/types.js +1 -3
- package/dist/contracts/workspace.js +0 -1
- package/dist/flow/build-flow-graph.js +1 -50
- package/dist/flow/export-mermaid.js +2 -464
- package/dist/flow/export-sequence-mermaid.js +2 -325
- package/dist/flow/flow-graph-normalization.js +1 -214
- package/dist/flow/flow-graph-runtime.js +1 -107
- package/dist/flow/flow-graph-upstream.js +1 -494
- package/dist/flow/index.js +1 -3
- package/dist/flow/types.js +0 -1
- package/dist/index.js +1 -5
- package/dist/init-project.js +1 -1
- package/dist/knowledge/config.js +1 -32
- package/dist/knowledge/contracts.js +0 -1
- package/dist/knowledge/index.js +1 -2
- package/dist/knowledge/module.js +12 -909
- package/dist/knowledge/procedural/config.js +1 -125
- package/dist/knowledge/procedural/index.js +1 -2
- package/dist/knowledge/procedural/manager.js +9 -345
- package/dist/mcp.js +1 -2
- package/dist/package-version.d.ts +1 -1
- package/dist/package-version.js +1 -2
- package/dist/persistence/file-store.js +3 -758
- package/dist/persistence/sqlite-request-context-store.js +5 -54
- package/dist/persistence/sqlite-request-queue-store.js +10 -108
- package/dist/persistence/sqlite-runtime.js +1 -86
- package/dist/persistence/sqlite-store.js +62 -810
- package/dist/persistence/types.js +0 -1
- package/dist/projections/presentation.js +37 -206
- package/dist/projections/request-events.js +2 -502
- package/dist/projections/upstream-events.js +1 -201
- package/dist/protocol/a2a/http-discovery.js +1 -178
- package/dist/protocol/a2a/http-rpc.js +6 -622
- package/dist/protocol/a2a/http.js +1 -138
- package/dist/protocol/a2a/task-state.js +3 -317
- package/dist/protocol/acp/client.js +8 -294
- package/dist/protocol/acp/harness-client.js +1 -218
- package/dist/protocol/acp/http.js +5 -130
- package/dist/protocol/acp/server.js +1 -310
- package/dist/protocol/acp/stdio.js +2 -69
- package/dist/protocol/ag-ui/http.js +3 -378
- package/dist/protocol/mcp/server.js +1 -428
- package/dist/resource/backend/workspace-scoped-backend.js +1 -319
- package/dist/resource/isolation.js +1 -237
- package/dist/resource/mcp/tool-support.js +3 -296
- package/dist/resource/mcp-tool-support.js +1 -2
- package/dist/resource/providers/resource-provider.js +1 -215
- package/dist/resource/resource-impl.js +1 -3
- package/dist/resource/resource-types.js +0 -1
- package/dist/resource/resource.js +1 -1
- package/dist/resource/sources.js +1 -247
- package/dist/resource/tools/function-tool-resolver.js +2 -272
- package/dist/runtime/adapter/compat/deepagent-compat.js +1 -29
- package/dist/runtime/adapter/compat/openai-compatible.js +1 -55
- package/dist/runtime/adapter/direct-builtin-utility.js +2 -90
- package/dist/runtime/adapter/flow/execution-context.js +1 -71
- package/dist/runtime/adapter/flow/invocation-flow.js +8 -425
- package/dist/runtime/adapter/flow/invoke-runtime.js +1 -20
- package/dist/runtime/adapter/flow/stream-runtime.js +11 -1395
- package/dist/runtime/adapter/invocation-result.js +2 -473
- package/dist/runtime/adapter/local-tool-invocation.js +6 -638
- package/dist/runtime/adapter/middleware/context-hygiene.js +1 -83
- package/dist/runtime/adapter/middleware-assembly.js +5 -477
- package/dist/runtime/adapter/model/invocation-request.js +3 -183
- package/dist/runtime/adapter/model/message-assembly.js +1 -28
- package/dist/runtime/adapter/model/model-providers.js +23 -1115
- package/dist/runtime/adapter/model/prompted-json-tool-call-capture.js +1 -40
- package/dist/runtime/adapter/model/prompted-json-tool-policy.js +1 -22
- package/dist/runtime/adapter/resilience.js +1 -104
- package/dist/runtime/adapter/runtime-adapter-support.js +3 -141
- package/dist/runtime/adapter/runtime-shell.js +5 -166
- package/dist/runtime/adapter/stream-event-projection.js +2 -622
- package/dist/runtime/adapter/stream-text-consumption.js +1 -18
- package/dist/runtime/adapter/terminal-status.js +2 -67
- package/dist/runtime/adapter/tool/builtin-middleware-tools.js +6 -627
- package/dist/runtime/adapter/tool/declared-middleware.js +1 -154
- package/dist/runtime/adapter/tool/interrupt-policy.js +1 -34
- package/dist/runtime/adapter/tool/provider-tool.js +1 -25
- package/dist/runtime/adapter/tool/resolved-tool.js +1 -225
- package/dist/runtime/adapter/tool/tool-arguments.js +3 -486
- package/dist/runtime/adapter/tool/tool-hitl.js +1 -346
- package/dist/runtime/adapter/tool/tool-name-mapping.js +1 -128
- package/dist/runtime/adapter/tool/tool-output-artifacts.js +2 -88
- package/dist/runtime/adapter/tool/tool-replay.js +1 -37
- package/dist/runtime/adapter/tool-resolution.js +1 -86
- package/dist/runtime/adapter/upstream-configurable-keys.js +1 -2
- package/dist/runtime/agent-runtime-adapter.js +60 -2338
- package/dist/runtime/agent-runtime-assembly.js +7 -249
- package/dist/runtime/env/runtime-env.js +1 -62
- package/dist/runtime/harness/background-runtime.js +1 -8
- package/dist/runtime/harness/bindings.js +1 -58
- package/dist/runtime/harness/events/event-bus.js +1 -16
- package/dist/runtime/harness/events/event-sink.js +1 -61
- package/dist/runtime/harness/events/events.js +1 -80
- package/dist/runtime/harness/events/listener-runtime.js +1 -13
- package/dist/runtime/harness/events/runtime-event-operations.js +1 -9
- package/dist/runtime/harness/events/streaming.js +1 -100
- package/dist/runtime/harness/events/timeline.js +1 -52
- package/dist/runtime/harness/public-shapes.js +1 -186
- package/dist/runtime/harness/run/artifact-paths.js +1 -15
- package/dist/runtime/harness/run/governance.js +1 -295
- package/dist/runtime/harness/run/helpers.js +1 -71
- package/dist/runtime/harness/run/inspection.js +1 -409
- package/dist/runtime/harness/run/operator-overview.js +1 -80
- package/dist/runtime/harness/run/queue-diagnostics.js +1 -15
- package/dist/runtime/harness/run/recovery.js +1 -162
- package/dist/runtime/harness/run/resources.js +1 -60
- package/dist/runtime/harness/run/resume.js +1 -56
- package/dist/runtime/harness/run/routing.js +1 -48
- package/dist/runtime/harness/run/run-lifecycle.js +1 -66
- package/dist/runtime/harness/run/run-operations.js +1 -217
- package/dist/runtime/harness/run/run-queue.js +1 -43
- package/dist/runtime/harness/run/run-slot-acquisition.js +1 -157
- package/dist/runtime/harness/run/session-records.js +1 -97
- package/dist/runtime/harness/run/start-run.js +1 -120
- package/dist/runtime/harness/run/startup-runtime.js +1 -69
- package/dist/runtime/harness/run/stream-run.js +8 -1418
- package/dist/runtime/harness/run/surface-semantics.js +1 -79
- package/dist/runtime/harness/runtime-defaults.js +1 -39
- package/dist/runtime/harness/system/boundary-analysis.js +1 -234
- package/dist/runtime/harness/system/health-monitor.js +1 -258
- package/dist/runtime/harness/system/inventory.js +1 -129
- package/dist/runtime/harness/system/mem0-ingestion-sync.js +5 -345
- package/dist/runtime/harness/system/policy-engine.js +1 -175
- package/dist/runtime/harness/system/runtime-memory-candidates.js +4 -110
- package/dist/runtime/harness/system/runtime-memory-consolidation.js +1 -51
- package/dist/runtime/harness/system/runtime-memory-manager.js +10 -693
- package/dist/runtime/harness/system/runtime-memory-policy.js +1 -155
- package/dist/runtime/harness/system/runtime-memory-records.js +11 -577
- package/dist/runtime/harness/system/runtime-memory-sync.js +5 -206
- package/dist/runtime/harness/system/session-memory-sync.js +3 -113
- package/dist/runtime/harness/system/skill-requirements.js +1 -112
- package/dist/runtime/harness/system/store.js +9 -365
- package/dist/runtime/harness/tool-gateway/index.js +1 -2
- package/dist/runtime/harness/tool-gateway/policy.js +1 -45
- package/dist/runtime/harness/tool-gateway/validation.js +1 -176
- package/dist/runtime/harness/tool-schema.js +1 -3
- package/dist/runtime/harness.js +3 -1490
- package/dist/runtime/index.js +1 -3
- package/dist/runtime/layout/runtime-layout.js +1 -31
- package/dist/runtime/maintenance/checkpoint-maintenance.js +2 -178
- package/dist/runtime/maintenance/file-checkpoint-saver.js +1 -106
- package/dist/runtime/maintenance/runtime-record-maintenance.js +2 -169
- package/dist/runtime/maintenance/sqlite-checkpoint-saver.js +4 -289
- package/dist/runtime/parsing/output-content.js +10 -550
- package/dist/runtime/parsing/output-parsing.js +1 -4
- package/dist/runtime/parsing/output-recovery.js +3 -213
- package/dist/runtime/parsing/output-tool-args.js +7 -663
- package/dist/runtime/parsing/stream-event-parsing.js +3 -362
- package/dist/runtime/prompts/runtime-prompts.js +4 -73
- package/dist/runtime/scheduling/system-schedule-manager.js +11 -532
- package/dist/runtime/skills/skill-metadata.js +1 -197
- package/dist/runtime/startup-tracing.js +2 -37
- package/dist/runtime/support/compiled-binding.js +1 -290
- package/dist/runtime/support/embedding-models.js +1 -118
- package/dist/runtime/support/harness-support.js +5 -137
- package/dist/runtime/support/llamaindex.js +1 -108
- package/dist/runtime/support/runtime-adapter-options.js +1 -29
- package/dist/runtime/support/runtime-factories.js +1 -51
- package/dist/runtime/support/vector-stores.js +9 -270
- package/dist/scaffold/init-project.js +54 -233
- package/dist/tooling/extensions.js +1 -311
- package/dist/tooling/module-loader.js +1 -55
- package/dist/tools.js +1 -176
- package/dist/utils/agent-display.js +1 -18
- package/dist/utils/bundled-text.js +4 -39
- package/dist/utils/compiled-binding.js +1 -33
- package/dist/utils/fs.js +2 -45
- package/dist/utils/id.js +1 -9
- package/dist/utils/message-content.js +1 -30
- package/dist/utils/object.js +1 -6
- package/dist/workspace/agent-binding-compiler.js +3 -613
- package/dist/workspace/compile.js +1 -472
- package/dist/workspace/framework-contract-validation.js +2 -322
- package/dist/workspace/index.js +1 -1
- package/dist/workspace/object-loader-paths.js +1 -71
- package/dist/workspace/object-loader-readers.js +1 -187
- package/dist/workspace/object-loader.js +1 -754
- package/dist/workspace/resource-compilers.js +1 -374
- package/dist/workspace/support/agent-capabilities.js +1 -37
- package/dist/workspace/support/agent-execution-config.js +1 -44
- package/dist/workspace/support/discovery.js +1 -147
- package/dist/workspace/support/source-collectors.js +1 -30
- package/dist/workspace/support/source-protocols.js +2 -192
- package/dist/workspace/support/workspace-ref-utils.js +1 -362
- package/dist/workspace/tool-hydration.js +1 -280
- package/dist/workspace/validate.js +1 -99
- package/dist/workspace/yaml-object-reader.js +1 -285
- package/package.json +7 -3
|
@@ -1,693 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"approval.resolved",
|
|
12
|
-
]);
|
|
13
|
-
function asRecord(value) {
|
|
14
|
-
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
|
|
15
|
-
}
|
|
16
|
-
function asBoolean(value) {
|
|
17
|
-
return typeof value === "boolean" ? value : undefined;
|
|
18
|
-
}
|
|
19
|
-
function asPositiveInteger(value) {
|
|
20
|
-
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : undefined;
|
|
21
|
-
}
|
|
22
|
-
function asNonEmptyString(value) {
|
|
23
|
-
return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
|
|
24
|
-
}
|
|
25
|
-
function asMemoryScopes(value) {
|
|
26
|
-
if (!Array.isArray(value)) {
|
|
27
|
-
return undefined;
|
|
28
|
-
}
|
|
29
|
-
const scopes = value
|
|
30
|
-
.filter((item) => typeof item === "string")
|
|
31
|
-
.filter((item) => ["session", "agent", "workspace", "user", "project"].includes(item));
|
|
32
|
-
return scopes.length > 0 ? Array.from(new Set(scopes)) : undefined;
|
|
33
|
-
}
|
|
34
|
-
function normalizeScopeList(scopes) {
|
|
35
|
-
return Array.from(new Set(scopes));
|
|
36
|
-
}
|
|
37
|
-
function asStrategy(value) {
|
|
38
|
-
return value === "rules" || value === "model" ? value : undefined;
|
|
39
|
-
}
|
|
40
|
-
export function readRuntimeMemoryFormationConfig(runtimeMemory, workspaceRoot) {
|
|
41
|
-
if (runtimeMemory?.enabled !== true) {
|
|
42
|
-
return undefined;
|
|
43
|
-
}
|
|
44
|
-
const formation = asRecord(runtimeMemory.formation);
|
|
45
|
-
const hotPath = asRecord(formation?.hotPath);
|
|
46
|
-
const manager = asRecord(formation?.manager);
|
|
47
|
-
const background = asRecord(formation?.background);
|
|
48
|
-
const ingestion = asRecord(runtimeMemory.ingestion);
|
|
49
|
-
const workspaceBaseName = path.basename(workspaceRoot) || "agent-harness";
|
|
50
|
-
const resolved = {
|
|
51
|
-
enabled: true,
|
|
52
|
-
hotPath: {
|
|
53
|
-
enabled: asBoolean(hotPath?.enabled) ?? true,
|
|
54
|
-
},
|
|
55
|
-
manager: {
|
|
56
|
-
enabled: asBoolean(manager?.enabled) ?? true,
|
|
57
|
-
strategy: asStrategy(manager?.strategy) ?? "model",
|
|
58
|
-
modelRef: asNonEmptyString(manager?.modelRef),
|
|
59
|
-
prompt: resolvePromptValue(manager?.prompt, workspaceRoot),
|
|
60
|
-
maxContextRecords: asPositiveInteger(manager?.maxContextRecords) ?? 12,
|
|
61
|
-
},
|
|
62
|
-
background: {
|
|
63
|
-
enabled: asBoolean(background?.enabled) ?? true,
|
|
64
|
-
maxMessagesPerRequest: asPositiveInteger(background?.maxMessagesPerRequest) ?? asPositiveInteger(ingestion?.maxMessagesPerRequest) ?? 40,
|
|
65
|
-
scopes: normalizeScopeList(asMemoryScopes(background?.scopes) ?? ["user", "project", "workspace"]),
|
|
66
|
-
stateStorePath: asNonEmptyString(background?.stateStorePath) ?? `knowledge/${workspaceBaseName}-memory-formation-state.json`,
|
|
67
|
-
writeOnApprovalResolution: asBoolean(background?.writeOnApprovalResolution) ?? asBoolean(ingestion?.writeOnApprovalResolution) ?? true,
|
|
68
|
-
writeOnRequestCompletion: asBoolean(background?.writeOnRequestCompletion) ?? asBoolean(ingestion?.writeOnRequestCompletion) ?? true,
|
|
69
|
-
},
|
|
70
|
-
};
|
|
71
|
-
if (!resolved.hotPath.enabled && !resolved.background.enabled) {
|
|
72
|
-
return undefined;
|
|
73
|
-
}
|
|
74
|
-
return resolved;
|
|
75
|
-
}
|
|
76
|
-
function excerpt(message) {
|
|
77
|
-
if (!message?.content) {
|
|
78
|
-
return undefined;
|
|
79
|
-
}
|
|
80
|
-
const normalized = extractMessageText(message.content).replace(/\s+/g, " ").trim();
|
|
81
|
-
if (!normalized) {
|
|
82
|
-
return undefined;
|
|
83
|
-
}
|
|
84
|
-
return normalized.length > 240 ? `${normalized.slice(0, 237)}...` : normalized;
|
|
85
|
-
}
|
|
86
|
-
function summarizeApprovals(approvals) {
|
|
87
|
-
if (approvals.length === 0) {
|
|
88
|
-
return "No approvals were recorded in this run.";
|
|
89
|
-
}
|
|
90
|
-
return approvals
|
|
91
|
-
.map((approval) => `${approval.toolName} (${approval.status})`)
|
|
92
|
-
.join(", ");
|
|
93
|
-
}
|
|
94
|
-
export function createBackgroundMemoryCandidates(input) {
|
|
95
|
-
const latestUser = excerpt(input.messages.filter((message) => message.role === "user").at(-1));
|
|
96
|
-
const latestAssistant = excerpt(input.messages.filter((message) => message.role === "assistant").at(-1));
|
|
97
|
-
const userContext = latestUser ?? "(none)";
|
|
98
|
-
const assistantContext = latestAssistant ?? "(none)";
|
|
99
|
-
const approvals = summarizeApprovals(input.approvals);
|
|
100
|
-
const summarySeed = latestUser ?? latestAssistant ?? `Run ${input.requestId}`;
|
|
101
|
-
const baseSummary = summarySeed.length > 120 ? `${summarySeed.slice(0, 117)}...` : summarySeed;
|
|
102
|
-
const sourceRefBase = `runtime://sessions/${input.session.sessionId}/requests/${input.requestId}/background-reflection`;
|
|
103
|
-
return [{
|
|
104
|
-
kind: "episodic",
|
|
105
|
-
sourceType: "runtime-transcript",
|
|
106
|
-
sourceRef: sourceRefBase,
|
|
107
|
-
summary: baseSummary,
|
|
108
|
-
content: [
|
|
109
|
-
"Transcript evidence for durable knowledge extraction.",
|
|
110
|
-
`Trigger: ${input.trigger}`,
|
|
111
|
-
"",
|
|
112
|
-
"Latest user message:",
|
|
113
|
-
userContext,
|
|
114
|
-
"",
|
|
115
|
-
"Latest assistant response:",
|
|
116
|
-
assistantContext,
|
|
117
|
-
"",
|
|
118
|
-
"Approval snapshot:",
|
|
119
|
-
approvals,
|
|
120
|
-
].join("\n"),
|
|
121
|
-
confidence: 0.72,
|
|
122
|
-
observedAt: input.recordedAt,
|
|
123
|
-
provenance: {
|
|
124
|
-
scopeOptions: input.scopes,
|
|
125
|
-
trigger: input.trigger,
|
|
126
|
-
},
|
|
127
|
-
tags: ["background-extraction", "langmem-aligned", "reusable_summaries", input.trigger],
|
|
128
|
-
}];
|
|
129
|
-
}
|
|
130
|
-
function normalizeKind(value, fallback) {
|
|
131
|
-
if (value === "semantic" || value === "episodic" || value === "procedural") {
|
|
132
|
-
return value;
|
|
133
|
-
}
|
|
134
|
-
return fallback;
|
|
135
|
-
}
|
|
136
|
-
function normalizeScope(value, fallback) {
|
|
137
|
-
if (value === "session" || value === "agent" || value === "workspace" || value === "user" || value === "project") {
|
|
138
|
-
return value;
|
|
139
|
-
}
|
|
140
|
-
return fallback;
|
|
141
|
-
}
|
|
142
|
-
function extractAllowedScopes(candidate) {
|
|
143
|
-
const provenance = typeof candidate.provenance === "object" && candidate.provenance !== null && !Array.isArray(candidate.provenance)
|
|
144
|
-
? candidate.provenance
|
|
145
|
-
: undefined;
|
|
146
|
-
const scopeOptions = provenance?.scopeOptions;
|
|
147
|
-
if (!Array.isArray(scopeOptions)) {
|
|
148
|
-
return undefined;
|
|
149
|
-
}
|
|
150
|
-
const allowed = scopeOptions
|
|
151
|
-
.filter((item) => item === "session" || item === "agent" || item === "workspace" || item === "user" || item === "project");
|
|
152
|
-
return allowed.length > 0 ? Array.from(new Set(allowed)) : undefined;
|
|
153
|
-
}
|
|
154
|
-
function normalizeScopeWithAllowedSet(value, fallback, allowedScopes) {
|
|
155
|
-
const normalized = normalizeScope(value, fallback);
|
|
156
|
-
if (!allowedScopes || allowedScopes.length === 0) {
|
|
157
|
-
return normalized;
|
|
158
|
-
}
|
|
159
|
-
if (normalized && allowedScopes.includes(normalized)) {
|
|
160
|
-
return normalized;
|
|
161
|
-
}
|
|
162
|
-
if (fallback && allowedScopes.includes(fallback)) {
|
|
163
|
-
return fallback;
|
|
164
|
-
}
|
|
165
|
-
return allowedScopes[0];
|
|
166
|
-
}
|
|
167
|
-
function normalizeTags(value, fallback) {
|
|
168
|
-
const fallbackTags = Array.isArray(fallback)
|
|
169
|
-
? fallback.filter((item) => typeof item === "string" && item.trim().length > 0)
|
|
170
|
-
: [];
|
|
171
|
-
if (!Array.isArray(value)) {
|
|
172
|
-
return fallbackTags.length > 0 ? fallbackTags : fallback;
|
|
173
|
-
}
|
|
174
|
-
const tags = value.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
175
|
-
const merged = Array.from(new Set([...fallbackTags, ...tags]));
|
|
176
|
-
return merged.length > 0 ? merged : fallback;
|
|
177
|
-
}
|
|
178
|
-
function normalizeConfidence(value, fallback) {
|
|
179
|
-
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
180
|
-
return fallback;
|
|
181
|
-
}
|
|
182
|
-
return Math.min(1, Math.max(0, value));
|
|
183
|
-
}
|
|
184
|
-
function normalizeOperationalRule(value, fallback) {
|
|
185
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
186
|
-
return fallback;
|
|
187
|
-
}
|
|
188
|
-
const typed = value;
|
|
189
|
-
const trigger = typeof typed.trigger === "string" && typed.trigger.trim().length > 0 ? typed.trigger.trim() : undefined;
|
|
190
|
-
const action = typeof typed.action === "string" && typed.action.trim().length > 0 ? typed.action.trim() : undefined;
|
|
191
|
-
const target = typeof typed.target === "string" && typed.target.trim().length > 0 ? typed.target.trim() : undefined;
|
|
192
|
-
const effect = typed.effect === "apply" || typed.effect === "invalidate"
|
|
193
|
-
? typed.effect
|
|
194
|
-
: undefined;
|
|
195
|
-
if (!trigger || !action || !target) {
|
|
196
|
-
return fallback;
|
|
197
|
-
}
|
|
198
|
-
return { trigger, action, target, ...(effect ? { effect } : {}) };
|
|
199
|
-
}
|
|
200
|
-
function normalizeKnowledgeMutation(value, fallback) {
|
|
201
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
202
|
-
return fallback;
|
|
203
|
-
}
|
|
204
|
-
const typed = value;
|
|
205
|
-
const identity = typeof typed.identity === "string" && typed.identity.trim().length > 0 ? typed.identity.trim() : undefined;
|
|
206
|
-
const operation = typed.operation === "create" || typed.operation === "update" || typed.operation === "delete"
|
|
207
|
-
? typed.operation
|
|
208
|
-
: undefined;
|
|
209
|
-
if (!identity) {
|
|
210
|
-
return fallback;
|
|
211
|
-
}
|
|
212
|
-
return { identity, ...(operation ? { operation } : {}) };
|
|
213
|
-
}
|
|
214
|
-
function asCandidateOutputs(parsed, candidate, allowedScopes, recordedAt) {
|
|
215
|
-
const rawMutations = Array.isArray(parsed.mutations) ? parsed.mutations : undefined;
|
|
216
|
-
const outputs = rawMutations && rawMutations.length > 0
|
|
217
|
-
? rawMutations.filter((item) => typeof item === "object" && item !== null && !Array.isArray(item))
|
|
218
|
-
: [parsed];
|
|
219
|
-
return outputs.map((output) => ({
|
|
220
|
-
...candidate,
|
|
221
|
-
content: typeof output.content === "string" && output.content.trim().length > 0 ? output.content.trim() : candidate.content,
|
|
222
|
-
summary: typeof output.summary === "string" && output.summary.trim().length > 0 ? output.summary.trim() : candidate.summary,
|
|
223
|
-
kind: normalizeKind(output.kind, normalizeKind(candidate.kind, undefined)),
|
|
224
|
-
scope: normalizeScopeWithAllowedSet(output.scope, normalizeScope(candidate.scope, undefined), allowedScopes),
|
|
225
|
-
tags: normalizeTags(output.tags, candidate.tags),
|
|
226
|
-
confidence: normalizeConfidence(output.confidence, candidate.confidence),
|
|
227
|
-
knowledgeMutation: normalizeKnowledgeMutation(output.knowledgeMutation, candidate.knowledgeMutation),
|
|
228
|
-
operationalRule: normalizeOperationalRule(output.operationalRule, candidate.operationalRule),
|
|
229
|
-
observedAt: candidate.observedAt ?? recordedAt,
|
|
230
|
-
}));
|
|
231
|
-
}
|
|
232
|
-
function extractText(value) {
|
|
233
|
-
if (typeof value === "string") {
|
|
234
|
-
return value;
|
|
235
|
-
}
|
|
236
|
-
if (typeof value !== "object" || value === null) {
|
|
237
|
-
return undefined;
|
|
238
|
-
}
|
|
239
|
-
if ("content" in value && typeof value.content === "string") {
|
|
240
|
-
return String(value.content);
|
|
241
|
-
}
|
|
242
|
-
const kwargs = "kwargs" in value && typeof value.kwargs === "object" && value.kwargs !== null
|
|
243
|
-
? value.kwargs
|
|
244
|
-
: undefined;
|
|
245
|
-
if (typeof kwargs?.content === "string") {
|
|
246
|
-
return kwargs.content;
|
|
247
|
-
}
|
|
248
|
-
return undefined;
|
|
249
|
-
}
|
|
250
|
-
function normalizeCompareText(value) {
|
|
251
|
-
return value.toLowerCase().replace(/\s+/g, " ").trim();
|
|
252
|
-
}
|
|
253
|
-
function tokenizeText(value) {
|
|
254
|
-
return new Set(value
|
|
255
|
-
.toLowerCase()
|
|
256
|
-
.split(/[^a-z0-9_]+/i)
|
|
257
|
-
.map((token) => token.trim())
|
|
258
|
-
.filter((token) => token.length > 2));
|
|
259
|
-
}
|
|
260
|
-
function jaccardTextSimilarity(left, right) {
|
|
261
|
-
const leftTokens = tokenizeText(left);
|
|
262
|
-
const rightTokens = tokenizeText(right);
|
|
263
|
-
if (leftTokens.size === 0 || rightTokens.size === 0) {
|
|
264
|
-
return 0;
|
|
265
|
-
}
|
|
266
|
-
let intersection = 0;
|
|
267
|
-
for (const token of leftTokens) {
|
|
268
|
-
if (rightTokens.has(token)) {
|
|
269
|
-
intersection += 1;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
const union = new Set([...leftTokens, ...rightTokens]).size;
|
|
273
|
-
return union === 0 ? 0 : intersection / union;
|
|
274
|
-
}
|
|
275
|
-
function containsDeletionSignal(value) {
|
|
276
|
-
const normalized = normalizeCompareText(value);
|
|
277
|
-
if (!normalized) {
|
|
278
|
-
return false;
|
|
279
|
-
}
|
|
280
|
-
return [
|
|
281
|
-
"delete",
|
|
282
|
-
"deleted",
|
|
283
|
-
"remove",
|
|
284
|
-
"removed",
|
|
285
|
-
"revoked",
|
|
286
|
-
"forbidden",
|
|
287
|
-
"not allowed",
|
|
288
|
-
"no longer",
|
|
289
|
-
"unavailable",
|
|
290
|
-
"disabled",
|
|
291
|
-
"deprecated",
|
|
292
|
-
"取消",
|
|
293
|
-
"撤销",
|
|
294
|
-
"删除",
|
|
295
|
-
"已删除",
|
|
296
|
-
"删掉",
|
|
297
|
-
"移除",
|
|
298
|
-
"不再",
|
|
299
|
-
"不可用",
|
|
300
|
-
"不能",
|
|
301
|
-
"无法",
|
|
302
|
-
"禁用",
|
|
303
|
-
"废弃",
|
|
304
|
-
].some((signal) => normalized.includes(signal));
|
|
305
|
-
}
|
|
306
|
-
function containsRuleTarget(candidate, record) {
|
|
307
|
-
const target = record.operationalRule?.target?.trim().toLowerCase();
|
|
308
|
-
if (!target) {
|
|
309
|
-
return false;
|
|
310
|
-
}
|
|
311
|
-
const candidateText = `${candidate.summary ?? ""}\n${candidate.content}`.toLowerCase();
|
|
312
|
-
return candidateText.includes(target);
|
|
313
|
-
}
|
|
314
|
-
function extractSalientLiterals(value) {
|
|
315
|
-
const results = new Set();
|
|
316
|
-
const normalized = value.trim();
|
|
317
|
-
if (!normalized) {
|
|
318
|
-
return results;
|
|
319
|
-
}
|
|
320
|
-
for (const match of normalized.matchAll(/[`'"]([^`'"]{1,64})[`'"]/g)) {
|
|
321
|
-
const literal = match[1]?.trim().toLowerCase();
|
|
322
|
-
if (literal && literal.length >= 2) {
|
|
323
|
-
results.add(literal);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
for (const match of normalized.matchAll(/\b[a-z][a-z0-9._/-]{1,63}\b/gi)) {
|
|
327
|
-
const token = match[0]?.trim().toLowerCase();
|
|
328
|
-
if (token && !["system", "startup", "command", "memory", "instruction", "user", "deleted"].includes(token)) {
|
|
329
|
-
results.add(token);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
return results;
|
|
333
|
-
}
|
|
334
|
-
function sharesSalientLiteral(candidate, record) {
|
|
335
|
-
const candidateLiterals = extractSalientLiterals(`${candidate.summary ?? ""}\n${candidate.content}`);
|
|
336
|
-
const recordLiterals = extractSalientLiterals(`${record.summary}\n${record.content}`);
|
|
337
|
-
if (candidateLiterals.size === 0 || recordLiterals.size === 0) {
|
|
338
|
-
return false;
|
|
339
|
-
}
|
|
340
|
-
for (const literal of candidateLiterals) {
|
|
341
|
-
if (recordLiterals.has(literal)) {
|
|
342
|
-
return true;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
return false;
|
|
346
|
-
}
|
|
347
|
-
function selectMutationFallbackMatch(candidate, existingRecords) {
|
|
348
|
-
const candidateScope = normalizeScope(candidate.scope, undefined);
|
|
349
|
-
return existingRecords
|
|
350
|
-
.filter((record) => record.status === "active" && !!record.knowledgeIdentity && record.knowledgeOperation !== "delete")
|
|
351
|
-
.map((record) => {
|
|
352
|
-
let score = 0;
|
|
353
|
-
if (candidateScope && record.scope === candidateScope) {
|
|
354
|
-
score += 2;
|
|
355
|
-
}
|
|
356
|
-
if (containsRuleTarget(candidate, record)) {
|
|
357
|
-
score += 10;
|
|
358
|
-
}
|
|
359
|
-
if (sharesSalientLiteral(candidate, record)) {
|
|
360
|
-
score += 6;
|
|
361
|
-
}
|
|
362
|
-
if (record.operationalRule) {
|
|
363
|
-
score += 2;
|
|
364
|
-
}
|
|
365
|
-
return { record, score };
|
|
366
|
-
})
|
|
367
|
-
.filter((item) => item.score > 0)
|
|
368
|
-
.sort((left, right) => right.score - left.score || right.record.lastConfirmedAt.localeCompare(left.record.lastConfirmedAt))
|
|
369
|
-
.map((item) => item.record)
|
|
370
|
-
.at(0);
|
|
371
|
-
}
|
|
372
|
-
function applyDeterministicMutationFallback(candidate, existingRecords) {
|
|
373
|
-
const currentMutation = candidate.knowledgeMutation;
|
|
374
|
-
if (currentMutation && currentMutation.operation !== "create") {
|
|
375
|
-
return candidate;
|
|
376
|
-
}
|
|
377
|
-
if (!containsDeletionSignal(`${candidate.summary ?? ""}\n${candidate.content}`)) {
|
|
378
|
-
return candidate;
|
|
379
|
-
}
|
|
380
|
-
const matchingRule = selectMutationFallbackMatch(candidate, existingRecords);
|
|
381
|
-
if (!matchingRule?.knowledgeIdentity) {
|
|
382
|
-
return candidate;
|
|
383
|
-
}
|
|
384
|
-
return {
|
|
385
|
-
...candidate,
|
|
386
|
-
scope: matchingRule.scope,
|
|
387
|
-
knowledgeMutation: {
|
|
388
|
-
identity: matchingRule.knowledgeIdentity,
|
|
389
|
-
operation: "delete",
|
|
390
|
-
},
|
|
391
|
-
...(matchingRule.operationalRule
|
|
392
|
-
? {
|
|
393
|
-
operationalRule: {
|
|
394
|
-
...matchingRule.operationalRule,
|
|
395
|
-
effect: "invalidate",
|
|
396
|
-
},
|
|
397
|
-
}
|
|
398
|
-
: {}),
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
function selectRelatedContextRecords(candidate, existingRecords, maxRecords) {
|
|
402
|
-
const candidateScope = normalizeScope(candidate.scope, undefined);
|
|
403
|
-
const candidateSummary = normalizeCompareText(candidate.summary?.trim() || candidate.content);
|
|
404
|
-
const candidateContent = normalizeCompareText(candidate.content);
|
|
405
|
-
const candidateSourceRef = typeof candidate.sourceRef === "string" ? candidate.sourceRef.trim() : "";
|
|
406
|
-
return existingRecords
|
|
407
|
-
.filter((record) => record.status === "active")
|
|
408
|
-
.map((record) => {
|
|
409
|
-
let score = 0;
|
|
410
|
-
if (candidateScope && record.scope === candidateScope) {
|
|
411
|
-
score += 4;
|
|
412
|
-
}
|
|
413
|
-
if (candidateSourceRef && record.sourceRefs.includes(candidateSourceRef)) {
|
|
414
|
-
score += 12;
|
|
415
|
-
}
|
|
416
|
-
if (normalizeCompareText(record.summary) === candidateSummary) {
|
|
417
|
-
score += 10;
|
|
418
|
-
}
|
|
419
|
-
if (normalizeCompareText(record.content) === candidateContent) {
|
|
420
|
-
score += 9;
|
|
421
|
-
}
|
|
422
|
-
score += jaccardTextSimilarity(record.summary, candidate.summary ?? candidate.content) * 4;
|
|
423
|
-
score += jaccardTextSimilarity(record.content, candidate.content) * 3;
|
|
424
|
-
if (score > 0 && record.lastConfirmedAt) {
|
|
425
|
-
score += Math.max(0, 1 - ((Date.now() - Date.parse(record.lastConfirmedAt)) / (1000 * 60 * 60 * 24 * 365)));
|
|
426
|
-
}
|
|
427
|
-
return { record, score };
|
|
428
|
-
})
|
|
429
|
-
.filter((item) => item.score > 0)
|
|
430
|
-
.sort((left, right) => right.score - left.score || right.record.lastConfirmedAt.localeCompare(left.record.lastConfirmedAt))
|
|
431
|
-
.slice(0, maxRecords)
|
|
432
|
-
.map((item) => item.record);
|
|
433
|
-
}
|
|
434
|
-
function tryParseJsonObject(text) {
|
|
435
|
-
try {
|
|
436
|
-
const parsed = JSON.parse(text);
|
|
437
|
-
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : null;
|
|
438
|
-
}
|
|
439
|
-
catch {
|
|
440
|
-
const match = text.match(/\{[\s\S]*\}/);
|
|
441
|
-
if (!match) {
|
|
442
|
-
return null;
|
|
443
|
-
}
|
|
444
|
-
try {
|
|
445
|
-
const parsed = JSON.parse(match[0]);
|
|
446
|
-
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : null;
|
|
447
|
-
}
|
|
448
|
-
catch {
|
|
449
|
-
return null;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
async function reconcileKnowledgeMutation(input) {
|
|
454
|
-
const eligibleExisting = input.existingRecords.filter((record) => typeof record.knowledgeIdentity === "string" && record.knowledgeIdentity.trim().length > 0);
|
|
455
|
-
if (eligibleExisting.length === 0) {
|
|
456
|
-
return input.candidate;
|
|
457
|
-
}
|
|
458
|
-
const currentMutation = input.candidate.knowledgeMutation;
|
|
459
|
-
if (!currentMutation || currentMutation.operation !== "create") {
|
|
460
|
-
return input.candidate;
|
|
461
|
-
}
|
|
462
|
-
try {
|
|
463
|
-
const response = await input.invoker.invoke(renderRuntimeMemoryMutationReconciliationPrompt({
|
|
464
|
-
candidate: input.candidate,
|
|
465
|
-
existingRecords: eligibleExisting,
|
|
466
|
-
}), {});
|
|
467
|
-
const parsed = tryParseJsonObject(extractText(response) ?? "");
|
|
468
|
-
if (!parsed || parsed.reuseExistingIdentity !== true) {
|
|
469
|
-
return input.candidate;
|
|
470
|
-
}
|
|
471
|
-
const identity = typeof parsed.identity === "string" ? parsed.identity.trim() : "";
|
|
472
|
-
const matched = eligibleExisting.find((record) => record.knowledgeIdentity === identity);
|
|
473
|
-
if (!matched) {
|
|
474
|
-
return input.candidate;
|
|
475
|
-
}
|
|
476
|
-
const operation = parsed.operation === "update" || parsed.operation === "delete"
|
|
477
|
-
? parsed.operation
|
|
478
|
-
: currentMutation.operation;
|
|
479
|
-
const reconciledScope = normalizeScopeWithAllowedSet(parsed.scope, matched.scope, extractAllowedScopes(input.candidate));
|
|
480
|
-
return {
|
|
481
|
-
...input.candidate,
|
|
482
|
-
knowledgeMutation: {
|
|
483
|
-
identity,
|
|
484
|
-
operation,
|
|
485
|
-
},
|
|
486
|
-
scope: reconciledScope ?? input.candidate.scope ?? matched.scope,
|
|
487
|
-
operationalRule: operation === "delete" && matched.operationalRule
|
|
488
|
-
? {
|
|
489
|
-
...matched.operationalRule,
|
|
490
|
-
effect: "invalidate",
|
|
491
|
-
}
|
|
492
|
-
: input.candidate.operationalRule,
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
catch {
|
|
496
|
-
return input.candidate;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
export async function runModelMemoryManager(input) {
|
|
500
|
-
let resolvedModel;
|
|
501
|
-
try {
|
|
502
|
-
resolvedModel = await createResolvedModel(input.model, input.modelResolver);
|
|
503
|
-
}
|
|
504
|
-
catch {
|
|
505
|
-
return input.candidates;
|
|
506
|
-
}
|
|
507
|
-
const invoker = resolvedModel;
|
|
508
|
-
if (typeof invoker.invoke !== "function") {
|
|
509
|
-
return input.candidates;
|
|
510
|
-
}
|
|
511
|
-
const callableInvoker = invoker;
|
|
512
|
-
const transformed = [];
|
|
513
|
-
for (const candidate of input.candidates) {
|
|
514
|
-
const allowedScopes = extractAllowedScopes(candidate);
|
|
515
|
-
const prompt = renderRuntimeMemoryManagerPrompt({
|
|
516
|
-
candidate,
|
|
517
|
-
sessionId: input.sessionId,
|
|
518
|
-
requestId: input.requestId,
|
|
519
|
-
existingRecords: selectRelatedContextRecords(candidate, input.existingRecords, input.maxContextRecords ?? input.existingRecords.length),
|
|
520
|
-
template: input.promptTemplate,
|
|
521
|
-
});
|
|
522
|
-
let response;
|
|
523
|
-
try {
|
|
524
|
-
response = await callableInvoker.invoke(prompt, {});
|
|
525
|
-
}
|
|
526
|
-
catch {
|
|
527
|
-
transformed.push(candidate);
|
|
528
|
-
continue;
|
|
529
|
-
}
|
|
530
|
-
const parsed = tryParseJsonObject(extractText(response) ?? "");
|
|
531
|
-
if (!parsed) {
|
|
532
|
-
transformed.push(candidate);
|
|
533
|
-
continue;
|
|
534
|
-
}
|
|
535
|
-
if (parsed.store === false) {
|
|
536
|
-
continue;
|
|
537
|
-
}
|
|
538
|
-
const refinedCandidates = asCandidateOutputs(parsed, candidate, allowedScopes, input.recordedAt);
|
|
539
|
-
for (const refinedCandidate of refinedCandidates) {
|
|
540
|
-
if (isNonDurableTranscriptWorkflowMemory(refinedCandidate, candidate)) {
|
|
541
|
-
continue;
|
|
542
|
-
}
|
|
543
|
-
const reconciledCandidate = await reconcileKnowledgeMutation({
|
|
544
|
-
invoker: callableInvoker,
|
|
545
|
-
candidate: refinedCandidate,
|
|
546
|
-
existingRecords: selectRelatedContextRecords(refinedCandidate, input.existingRecords, input.maxContextRecords ?? input.existingRecords.length),
|
|
547
|
-
});
|
|
548
|
-
transformed.push(applyDeterministicMutationFallback(reconciledCandidate, input.existingRecords));
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
return transformed;
|
|
552
|
-
}
|
|
553
|
-
export function createRuntimeMemoryManager(input) {
|
|
554
|
-
return {
|
|
555
|
-
async transform({ candidates, binding, sessionId, requestId, recordedAt, existingRecords }) {
|
|
556
|
-
if (input.config.manager.enabled !== true || candidates.length === 0) {
|
|
557
|
-
return candidates;
|
|
558
|
-
}
|
|
559
|
-
const contextRecords = existingRecords
|
|
560
|
-
.filter((record) => record.status === "active");
|
|
561
|
-
if (input.config.manager.strategy === "rules") {
|
|
562
|
-
return candidates;
|
|
563
|
-
}
|
|
564
|
-
if (!binding.langchainAgentParams?.model) {
|
|
565
|
-
return candidates;
|
|
566
|
-
}
|
|
567
|
-
const primaryModel = (() => {
|
|
568
|
-
if (!input.config.manager.modelRef) {
|
|
569
|
-
return binding.langchainAgentParams.model;
|
|
570
|
-
}
|
|
571
|
-
const configured = input.workspace.models.get(resolveRefId(input.config.manager.modelRef));
|
|
572
|
-
return configured ? compileModel(configured) : binding.langchainAgentParams.model;
|
|
573
|
-
})();
|
|
574
|
-
return runModelMemoryManager({
|
|
575
|
-
workspace: input.workspace,
|
|
576
|
-
binding,
|
|
577
|
-
model: primaryModel,
|
|
578
|
-
candidates,
|
|
579
|
-
sessionId,
|
|
580
|
-
requestId,
|
|
581
|
-
recordedAt,
|
|
582
|
-
existingRecords: contextRecords,
|
|
583
|
-
maxContextRecords: input.config.manager.maxContextRecords,
|
|
584
|
-
promptTemplate: input.config.manager.prompt,
|
|
585
|
-
modelResolver: input.modelResolver,
|
|
586
|
-
});
|
|
587
|
-
},
|
|
588
|
-
};
|
|
589
|
-
}
|
|
590
|
-
function fingerprintMessages(messages, scopes) {
|
|
591
|
-
const serialized = messages
|
|
592
|
-
.map((message) => `${message.role}\n${message.createdAt}\n${extractMessageText(message.content)}`)
|
|
593
|
-
.join("\n---\n");
|
|
594
|
-
return `${scopes.join(",")}::${serialized}`;
|
|
595
|
-
}
|
|
596
|
-
export class RuntimeMemoryFormationSync {
|
|
597
|
-
persistence;
|
|
598
|
-
config;
|
|
599
|
-
writer;
|
|
600
|
-
stateStore;
|
|
601
|
-
options;
|
|
602
|
-
pending = new Set();
|
|
603
|
-
syncChain = Promise.resolve();
|
|
604
|
-
name = "runtime-memory-formation-sync";
|
|
605
|
-
constructor(persistence, config, writer, runtimeRoot, stateStore = new FileBackedStore(path.join(runtimeRoot, config.background.stateStorePath)), options = {}) {
|
|
606
|
-
this.persistence = persistence;
|
|
607
|
-
this.config = config;
|
|
608
|
-
this.writer = writer;
|
|
609
|
-
this.stateStore = stateStore;
|
|
610
|
-
this.options = options;
|
|
611
|
-
}
|
|
612
|
-
shouldHandle(event) {
|
|
613
|
-
if (!this.config.background.enabled || !FORMATION_EVENT_TYPES.has(event.eventType)) {
|
|
614
|
-
return false;
|
|
615
|
-
}
|
|
616
|
-
if (event.eventType === "approval.resolved") {
|
|
617
|
-
return this.config.background.writeOnApprovalResolution;
|
|
618
|
-
}
|
|
619
|
-
return this.config.background.writeOnRequestCompletion && event.payload.state === "completed";
|
|
620
|
-
}
|
|
621
|
-
async handleEvent(event) {
|
|
622
|
-
if (!this.shouldHandle(event)) {
|
|
623
|
-
return;
|
|
624
|
-
}
|
|
625
|
-
const trigger = event.eventType === "approval.resolved" ? "approval.resolved" : "request.completed";
|
|
626
|
-
const task = this.syncChain
|
|
627
|
-
.then(() => this.reflectRun(event.sessionId, event.requestId, trigger, event.timestamp))
|
|
628
|
-
.catch(() => {
|
|
629
|
-
// Fail open: reflection should not block runtime progress.
|
|
630
|
-
});
|
|
631
|
-
this.syncChain = task
|
|
632
|
-
.catch(() => {
|
|
633
|
-
// Fail open: reflection should not block runtime progress.
|
|
634
|
-
})
|
|
635
|
-
.finally(() => {
|
|
636
|
-
this.pending.delete(task);
|
|
637
|
-
});
|
|
638
|
-
this.pending.add(task);
|
|
639
|
-
}
|
|
640
|
-
async reflectRun(sessionId, requestId, trigger, recordedAt) {
|
|
641
|
-
const [session, run, allMessages, approvals] = await Promise.all([
|
|
642
|
-
this.persistence.getSession(sessionId),
|
|
643
|
-
this.persistence.getRequest(requestId),
|
|
644
|
-
this.persistence.listSessionMessages(sessionId, this.config.background.maxMessagesPerRequest),
|
|
645
|
-
this.persistence.getRequestApprovals(sessionId, requestId),
|
|
646
|
-
]);
|
|
647
|
-
if (!session || !run) {
|
|
648
|
-
return;
|
|
649
|
-
}
|
|
650
|
-
const messages = allMessages.filter((message) => message.requestId === requestId);
|
|
651
|
-
if (messages.length === 0) {
|
|
652
|
-
return;
|
|
653
|
-
}
|
|
654
|
-
const fingerprint = fingerprintMessages(messages, this.config.background.scopes);
|
|
655
|
-
const namespace = ["memories", "formation", "sessions", sessionId, "requests"];
|
|
656
|
-
const cursor = await this.stateStore.get(namespace, requestId);
|
|
657
|
-
const existing = cursor?.value;
|
|
658
|
-
if (existing?.fingerprint === fingerprint && existing.trigger === trigger) {
|
|
659
|
-
return;
|
|
660
|
-
}
|
|
661
|
-
const candidates = createBackgroundMemoryCandidates({
|
|
662
|
-
session,
|
|
663
|
-
requestId,
|
|
664
|
-
agentId: run.agentId ?? session.agentId,
|
|
665
|
-
trigger,
|
|
666
|
-
recordedAt,
|
|
667
|
-
messages,
|
|
668
|
-
approvals,
|
|
669
|
-
scopes: this.config.background.scopes,
|
|
670
|
-
});
|
|
671
|
-
if (candidates.length === 0) {
|
|
672
|
-
return;
|
|
673
|
-
}
|
|
674
|
-
await this.writer({
|
|
675
|
-
candidates,
|
|
676
|
-
sessionId,
|
|
677
|
-
requestId,
|
|
678
|
-
agentId: run.agentId ?? session.agentId,
|
|
679
|
-
userId: this.options.userId,
|
|
680
|
-
projectId: this.options.projectId,
|
|
681
|
-
recordedAt,
|
|
682
|
-
});
|
|
683
|
-
await this.stateStore.put(namespace, requestId, {
|
|
684
|
-
fingerprint,
|
|
685
|
-
candidateCount: candidates.length,
|
|
686
|
-
syncedAt: new Date().toISOString(),
|
|
687
|
-
trigger,
|
|
688
|
-
});
|
|
689
|
-
}
|
|
690
|
-
async close() {
|
|
691
|
-
await Promise.allSettled(Array.from(this.pending));
|
|
692
|
-
}
|
|
693
|
-
}
|
|
1
|
+
import k from"node:path";import{extractMessageText as A}from"../../../utils/message-content.js";import{createResolvedModel as v}from"../../adapter/model/model-providers.js";import{FileBackedStore as q}from"./store.js";import{compileModel as L}from"../../../workspace/resource-compilers.js";import{resolvePromptValue as N,resolveRefId as E}from"../../../workspace/support/workspace-ref-utils.js";import{renderRuntimeMemoryManagerPrompt as B,renderRuntimeMemoryMutationReconciliationPrompt as D}from"../../prompts/runtime-prompts.js";import{isNonDurableTranscriptWorkflowMemory as F}from"./runtime-memory-policy.js";const _=new Set(["request.state.changed","approval.resolved"]);function u(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)?e:void 0}function f(e){return typeof e=="boolean"?e:void 0}function h(e){return typeof e=="number"&&Number.isInteger(e)&&e>0?e:void 0}function x(e){return typeof e=="string"&&e.trim().length>0?e.trim():void 0}function J(e){if(!Array.isArray(e))return;const t=e.filter(n=>typeof n=="string").filter(n=>["session","agent","workspace","user","project"].includes(n));return t.length>0?Array.from(new Set(t)):void 0}function K(e){return Array.from(new Set(e))}function H(e){return e==="rules"||e==="model"?e:void 0}function pe(e,t){if(e?.enabled!==!0)return;const n=u(e.formation),r=u(n?.hotPath),o=u(n?.manager),i=u(n?.background),s=u(e.ingestion),a=k.basename(t)||"agent-harness",c={enabled:!0,hotPath:{enabled:f(r?.enabled)??!0},manager:{enabled:f(o?.enabled)??!0,strategy:H(o?.strategy)??"model",modelRef:x(o?.modelRef),prompt:N(o?.prompt,t),maxContextRecords:h(o?.maxContextRecords)??12},background:{enabled:f(i?.enabled)??!0,maxMessagesPerRequest:h(i?.maxMessagesPerRequest)??h(s?.maxMessagesPerRequest)??40,scopes:K(J(i?.scopes)??["user","project","workspace"]),stateStorePath:x(i?.stateStorePath)??`knowledge/${a}-memory-formation-state.json`,writeOnApprovalResolution:f(i?.writeOnApprovalResolution)??f(s?.writeOnApprovalResolution)??!0,writeOnRequestCompletion:f(i?.writeOnRequestCompletion)??f(s?.writeOnRequestCompletion)??!0}};if(!(!c.hotPath.enabled&&!c.background.enabled))return c}function b(e){if(!e?.content)return;const t=A(e.content).replace(/\s+/g," ").trim();if(t)return t.length>240?`${t.slice(0,237)}...`:t}function V(e){return e.length===0?"No approvals were recorded in this run.":e.map(t=>`${t.toolName} (${t.status})`).join(", ")}function W(e){const t=b(e.messages.filter(l=>l.role==="user").at(-1)),n=b(e.messages.filter(l=>l.role==="assistant").at(-1)),r=t??"(none)",o=n??"(none)",i=V(e.approvals),s=t??n??`Run ${e.requestId}`,a=s.length>120?`${s.slice(0,117)}...`:s;return[{kind:"episodic",sourceType:"runtime-transcript",sourceRef:`runtime://sessions/${e.session.sessionId}/requests/${e.requestId}/background-reflection`,summary:a,content:["Transcript evidence for durable knowledge extraction.",`Trigger: ${e.trigger}`,"","Latest user message:",r,"","Latest assistant response:",o,"","Approval snapshot:",i].join(`
|
|
2
|
+
`),confidence:.72,observedAt:e.recordedAt,provenance:{scopeOptions:e.scopes,trigger:e.trigger},tags:["background-extraction","langmem-aligned","reusable_summaries",e.trigger]}]}function S(e,t){return e==="semantic"||e==="episodic"||e==="procedural"?e:t}function p(e,t){return e==="session"||e==="agent"||e==="workspace"||e==="user"||e==="project"?e:t}function M(e){const n=(typeof e.provenance=="object"&&e.provenance!==null&&!Array.isArray(e.provenance)?e.provenance:void 0)?.scopeOptions;if(!Array.isArray(n))return;const r=n.filter(o=>o==="session"||o==="agent"||o==="workspace"||o==="user"||o==="project");return r.length>0?Array.from(new Set(r)):void 0}function C(e,t,n){const r=p(e,t);return!n||n.length===0||r&&n.includes(r)?r:t&&n.includes(t)?t:n[0]}function U(e,t){const n=Array.isArray(t)?t.filter(i=>typeof i=="string"&&i.trim().length>0):[];if(!Array.isArray(e))return n.length>0?n:t;const r=e.filter(i=>typeof i=="string"&&i.trim().length>0),o=Array.from(new Set([...n,...r]));return o.length>0?o:t}function Y(e,t){return typeof e!="number"||Number.isNaN(e)?t:Math.min(1,Math.max(0,e))}function G(e,t){if(typeof e!="object"||e===null||Array.isArray(e))return t;const n=e,r=typeof n.trigger=="string"&&n.trigger.trim().length>0?n.trigger.trim():void 0,o=typeof n.action=="string"&&n.action.trim().length>0?n.action.trim():void 0,i=typeof n.target=="string"&&n.target.trim().length>0?n.target.trim():void 0,s=n.effect==="apply"||n.effect==="invalidate"?n.effect:void 0;return!r||!o||!i?t:{trigger:r,action:o,target:i,...s?{effect:s}:{}}}function Q(e,t){if(typeof e!="object"||e===null||Array.isArray(e))return t;const n=e,r=typeof n.identity=="string"&&n.identity.trim().length>0?n.identity.trim():void 0,o=n.operation==="create"||n.operation==="update"||n.operation==="delete"?n.operation:void 0;return r?{identity:r,...o?{operation:o}:{}}:t}function X(e,t,n,r){const o=Array.isArray(e.mutations)?e.mutations:void 0;return(o&&o.length>0?o.filter(s=>typeof s=="object"&&s!==null&&!Array.isArray(s)):[e]).map(s=>({...t,content:typeof s.content=="string"&&s.content.trim().length>0?s.content.trim():t.content,summary:typeof s.summary=="string"&&s.summary.trim().length>0?s.summary.trim():t.summary,kind:S(s.kind,S(t.kind,void 0)),scope:C(s.scope,p(t.scope,void 0),n),tags:U(s.tags,t.tags),confidence:Y(s.confidence,t.confidence),knowledgeMutation:Q(s.knowledgeMutation,t.knowledgeMutation),operationalRule:G(s.operationalRule,t.operationalRule),observedAt:t.observedAt??r}))}function z(e){if(typeof e=="string")return e;if(typeof e!="object"||e===null)return;if("content"in e&&typeof e.content=="string")return String(e.content);const t="kwargs"in e&&typeof e.kwargs=="object"&&e.kwargs!==null?e.kwargs:void 0;if(typeof t?.content=="string")return t.content}function m(e){return e.toLowerCase().replace(/\s+/g," ").trim()}function T(e){return new Set(e.toLowerCase().split(/[^a-z0-9_]+/i).map(t=>t.trim()).filter(t=>t.length>2))}function j(e,t){const n=T(e),r=T(t);if(n.size===0||r.size===0)return 0;let o=0;for(const s of n)r.has(s)&&(o+=1);const i=new Set([...n,...r]).size;return i===0?0:o/i}function Z(e){const t=m(e);return t?["delete","deleted","remove","removed","revoked","forbidden","not allowed","no longer","unavailable","disabled","deprecated","\u53D6\u6D88","\u64A4\u9500","\u5220\u9664","\u5DF2\u5220\u9664","\u5220\u6389","\u79FB\u9664","\u4E0D\u518D","\u4E0D\u53EF\u7528","\u4E0D\u80FD","\u65E0\u6CD5","\u7981\u7528","\u5E9F\u5F03"].some(n=>t.includes(n)):!1}function ee(e,t){const n=t.operationalRule?.target?.trim().toLowerCase();return n?`${e.summary??""}
|
|
3
|
+
${e.content}`.toLowerCase().includes(n):!1}function I(e){const t=new Set,n=e.trim();if(!n)return t;for(const r of n.matchAll(/[`'"]([^`'"]{1,64})[`'"]/g)){const o=r[1]?.trim().toLowerCase();o&&o.length>=2&&t.add(o)}for(const r of n.matchAll(/\b[a-z][a-z0-9._/-]{1,63}\b/gi)){const o=r[0]?.trim().toLowerCase();o&&!["system","startup","command","memory","instruction","user","deleted"].includes(o)&&t.add(o)}return t}function te(e,t){const n=I(`${e.summary??""}
|
|
4
|
+
${e.content}`),r=I(`${t.summary}
|
|
5
|
+
${t.content}`);if(n.size===0||r.size===0)return!1;for(const o of n)if(r.has(o))return!0;return!1}function ne(e,t){const n=p(e.scope,void 0);return t.filter(r=>r.status==="active"&&!!r.knowledgeIdentity&&r.knowledgeOperation!=="delete").map(r=>{let o=0;return n&&r.scope===n&&(o+=2),ee(e,r)&&(o+=10),te(e,r)&&(o+=6),r.operationalRule&&(o+=2),{record:r,score:o}}).filter(r=>r.score>0).sort((r,o)=>o.score-r.score||o.record.lastConfirmedAt.localeCompare(r.record.lastConfirmedAt)).map(r=>r.record).at(0)}function re(e,t){const n=e.knowledgeMutation;if(n&&n.operation!=="create"||!Z(`${e.summary??""}
|
|
6
|
+
${e.content}`))return e;const r=ne(e,t);return r?.knowledgeIdentity?{...e,scope:r.scope,knowledgeMutation:{identity:r.knowledgeIdentity,operation:"delete"},...r.operationalRule?{operationalRule:{...r.operationalRule,effect:"invalidate"}}:{}}:e}function P(e,t,n){const r=p(e.scope,void 0),o=m(e.summary?.trim()||e.content),i=m(e.content),s=typeof e.sourceRef=="string"?e.sourceRef.trim():"";return t.filter(a=>a.status==="active").map(a=>{let c=0;return r&&a.scope===r&&(c+=4),s&&a.sourceRefs.includes(s)&&(c+=12),m(a.summary)===o&&(c+=10),m(a.content)===i&&(c+=9),c+=j(a.summary,e.summary??e.content)*4,c+=j(a.content,e.content)*3,c>0&&a.lastConfirmedAt&&(c+=Math.max(0,1-(Date.now()-Date.parse(a.lastConfirmedAt))/(1e3*60*60*24*365))),{record:a,score:c}}).filter(a=>a.score>0).sort((a,c)=>c.score-a.score||c.record.lastConfirmedAt.localeCompare(a.record.lastConfirmedAt)).slice(0,n).map(a=>a.record)}function $(e){try{const t=JSON.parse(e);return typeof t=="object"&&t!==null&&!Array.isArray(t)?t:null}catch{const t=e.match(/\{[\s\S]*\}/);if(!t)return null;try{const n=JSON.parse(t[0]);return typeof n=="object"&&n!==null&&!Array.isArray(n)?n:null}catch{return null}}}async function oe(e){const t=e.existingRecords.filter(r=>typeof r.knowledgeIdentity=="string"&&r.knowledgeIdentity.trim().length>0);if(t.length===0)return e.candidate;const n=e.candidate.knowledgeMutation;if(!n||n.operation!=="create")return e.candidate;try{const r=await e.invoker.invoke(D({candidate:e.candidate,existingRecords:t}),{}),o=$(z(r)??"");if(!o||o.reuseExistingIdentity!==!0)return e.candidate;const i=typeof o.identity=="string"?o.identity.trim():"",s=t.find(l=>l.knowledgeIdentity===i);if(!s)return e.candidate;const a=o.operation==="update"||o.operation==="delete"?o.operation:n.operation,c=C(o.scope,s.scope,M(e.candidate));return{...e.candidate,knowledgeMutation:{identity:i,operation:a},scope:c??e.candidate.scope??s.scope,operationalRule:a==="delete"&&s.operationalRule?{...s.operationalRule,effect:"invalidate"}:e.candidate.operationalRule}}catch{return e.candidate}}async function se(e){let t;try{t=await v(e.model,e.modelResolver)}catch{return e.candidates}const n=t;if(typeof n.invoke!="function")return e.candidates;const r=n,o=[];for(const i of e.candidates){const s=M(i),a=B({candidate:i,sessionId:e.sessionId,requestId:e.requestId,existingRecords:P(i,e.existingRecords,e.maxContextRecords??e.existingRecords.length),template:e.promptTemplate});let c;try{c=await r.invoke(a,{})}catch{o.push(i);continue}const l=$(z(c)??"");if(!l){o.push(i);continue}if(l.store===!1)continue;const g=X(l,i,s,e.recordedAt);for(const d of g){if(F(d,i))continue;const R=await oe({invoker:r,candidate:d,existingRecords:P(d,e.existingRecords,e.maxContextRecords??e.existingRecords.length)});o.push(re(R,e.existingRecords))}}return o}function ye(e){return{async transform({candidates:t,binding:n,sessionId:r,requestId:o,recordedAt:i,existingRecords:s}){if(e.config.manager.enabled!==!0||t.length===0)return t;const a=s.filter(l=>l.status==="active");if(e.config.manager.strategy==="rules"||!n.langchainAgentParams?.model)return t;const c=(()=>{if(!e.config.manager.modelRef)return n.langchainAgentParams.model;const l=e.workspace.models.get(E(e.config.manager.modelRef));return l?L(l):n.langchainAgentParams.model})();return se({workspace:e.workspace,binding:n,model:c,candidates:t,sessionId:r,requestId:o,recordedAt:i,existingRecords:a,maxContextRecords:e.config.manager.maxContextRecords,promptTemplate:e.config.manager.prompt,modelResolver:e.modelResolver})}}}function ie(e,t){const n=e.map(r=>`${r.role}
|
|
7
|
+
${r.createdAt}
|
|
8
|
+
${A(r.content)}`).join(`
|
|
9
|
+
---
|
|
10
|
+
`);return`${t.join(",")}::${n}`}class he{persistence;config;writer;stateStore;options;pending=new Set;syncChain=Promise.resolve();name="runtime-memory-formation-sync";constructor(t,n,r,o,i=new q(k.join(o,n.background.stateStorePath)),s={}){this.persistence=t,this.config=n,this.writer=r,this.stateStore=i,this.options=s}shouldHandle(t){return!this.config.background.enabled||!_.has(t.eventType)?!1:t.eventType==="approval.resolved"?this.config.background.writeOnApprovalResolution:this.config.background.writeOnRequestCompletion&&t.payload.state==="completed"}async handleEvent(t){if(!this.shouldHandle(t))return;const n=t.eventType==="approval.resolved"?"approval.resolved":"request.completed",r=this.syncChain.then(()=>this.reflectRun(t.sessionId,t.requestId,n,t.timestamp)).catch(()=>{});this.syncChain=r.catch(()=>{}).finally(()=>{this.pending.delete(r)}),this.pending.add(r)}async reflectRun(t,n,r,o){const[i,s,a,c]=await Promise.all([this.persistence.getSession(t),this.persistence.getRequest(n),this.persistence.listSessionMessages(t,this.config.background.maxMessagesPerRequest),this.persistence.getRequestApprovals(t,n)]);if(!i||!s)return;const l=a.filter(O=>O.requestId===n);if(l.length===0)return;const g=ie(l,this.config.background.scopes),d=["memories","formation","sessions",t,"requests"],w=(await this.stateStore.get(d,n))?.value;if(w?.fingerprint===g&&w.trigger===r)return;const y=W({session:i,requestId:n,agentId:s.agentId??i.agentId,trigger:r,recordedAt:o,messages:l,approvals:c,scopes:this.config.background.scopes});y.length!==0&&(await this.writer({candidates:y,sessionId:t,requestId:n,agentId:s.agentId??i.agentId,userId:this.options.userId,projectId:this.options.projectId,recordedAt:o}),await this.stateStore.put(d,n,{fingerprint:g,candidateCount:y.length,syncedAt:new Date().toISOString(),trigger:r}))}async close(){await Promise.allSettled(Array.from(this.pending))}}export{he as RuntimeMemoryFormationSync,W as createBackgroundMemoryCandidates,ye as createRuntimeMemoryManager,pe as readRuntimeMemoryFormationConfig,se as runModelMemoryManager};
|